package store import ( "database/sql" "fmt" "krafttrainer/internal/model" ) // ListSets gibt alle nicht-gelöschten Sets eines Nutzers mit ihren Übungen zurück. func (s *Store) ListSets(userID int64) ([]model.TrainingSet, error) { rows, err := s.db.Query(` SELECT id, name, created_at FROM training_sets WHERE deleted_at IS NULL AND user_id = ? ORDER BY name`, userID) if err != nil { return nil, fmt.Errorf("Sets abfragen: %w", err) } defer rows.Close() var sets []model.TrainingSet for rows.Next() { var ts model.TrainingSet if err := rows.Scan(&ts.ID, &ts.Name, &ts.CreatedAt); err != nil { return nil, fmt.Errorf("Set scannen: %w", err) } sets = append(sets, ts) } if err := rows.Err(); err != nil { return nil, err } if sets == nil { sets = []model.TrainingSet{} } for i := range sets { exercises, err := s.getSetExercises(sets[i].ID) if err != nil { return nil, err } sets[i].Exercises = exercises } return sets, nil } // GetSet gibt ein einzelnes Set mit Übungen zurück (intern, ohne User-Scope). func (s *Store) GetSet(id int64) (*model.TrainingSet, error) { var ts model.TrainingSet err := s.db.QueryRow(` SELECT id, name, created_at, deleted_at FROM training_sets WHERE id = ?`, id, ).Scan(&ts.ID, &ts.Name, &ts.CreatedAt, &ts.DeletedAt) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, fmt.Errorf("Set abfragen: %w", err) } exercises, err := s.getSetExercises(id) if err != nil { return nil, err } ts.Exercises = exercises return &ts, nil } // CreateSet legt ein neues Set an (in einer Transaktion). func (s *Store) CreateSet(userID int64, req *model.CreateSetRequest) (*model.TrainingSet, error) { tx, err := s.db.Begin() if err != nil { return nil, fmt.Errorf("Transaktion starten: %w", err) } defer tx.Rollback() for _, eid := range req.ExerciseIDs { var exists bool err := tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM exercises WHERE id = ? AND user_id = ? AND deleted_at IS NULL)`, eid, userID).Scan(&exists) if err != nil { return nil, fmt.Errorf("Übung prüfen: %w", err) } if !exists { return nil, fmt.Errorf("Übung %d existiert nicht", eid) } } result, err := tx.Exec(`INSERT INTO training_sets (name, user_id) VALUES (?, ?)`, req.Name, userID) if err != nil { return nil, fmt.Errorf("Set erstellen: %w", err) } id, _ := result.LastInsertId() for pos, eid := range req.ExerciseIDs { _, err := tx.Exec(`INSERT INTO set_exercises (set_id, exercise_id, position) VALUES (?, ?, ?)`, id, eid, pos) if err != nil { return nil, fmt.Errorf("Set-Übung zuordnen: %w", err) } } if err := tx.Commit(); err != nil { return nil, fmt.Errorf("Transaktion committen: %w", err) } return s.GetSet(id) } // UpdateSet aktualisiert ein Set eines Nutzers (Name + Übungszuordnungen). func (s *Store) UpdateSet(id, userID int64, req *model.UpdateSetRequest) (*model.TrainingSet, error) { tx, err := s.db.Begin() if err != nil { return nil, fmt.Errorf("Transaktion starten: %w", err) } defer tx.Rollback() var exists bool err = tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM training_sets WHERE id = ? AND user_id = ? AND deleted_at IS NULL)`, id, userID).Scan(&exists) if err != nil { return nil, fmt.Errorf("Set prüfen: %w", err) } if !exists { return nil, nil } for _, eid := range req.ExerciseIDs { var eExists bool err := tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM exercises WHERE id = ? AND user_id = ? AND deleted_at IS NULL)`, eid, userID).Scan(&eExists) if err != nil { return nil, fmt.Errorf("Übung prüfen: %w", err) } if !eExists { return nil, fmt.Errorf("Übung %d existiert nicht", eid) } } _, err = tx.Exec(`UPDATE training_sets SET name = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, req.Name, id) if err != nil { return nil, fmt.Errorf("Set aktualisieren: %w", err) } _, err = tx.Exec(`DELETE FROM set_exercises WHERE set_id = ?`, id) if err != nil { return nil, fmt.Errorf("Set-Übungen löschen: %w", err) } for pos, eid := range req.ExerciseIDs { _, err := tx.Exec(`INSERT INTO set_exercises (set_id, exercise_id, position) VALUES (?, ?, ?)`, id, eid, pos) if err != nil { return nil, fmt.Errorf("Set-Übung zuordnen: %w", err) } } if err := tx.Commit(); err != nil { return nil, fmt.Errorf("Transaktion committen: %w", err) } return s.GetSet(id) } // SoftDeleteSet markiert ein Set eines Nutzers als gelöscht. func (s *Store) SoftDeleteSet(id, userID int64) error { result, err := s.db.Exec(` UPDATE training_sets SET deleted_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND user_id = ? AND deleted_at IS NULL`, id, userID, ) if err != nil { return fmt.Errorf("Set löschen: %w", err) } rows, _ := result.RowsAffected() if rows == 0 { return sql.ErrNoRows } return nil } // getSetExercises lädt die Übungen eines Sets sortiert nach Position. func (s *Store) getSetExercises(setID int64) ([]model.Exercise, error) { rows, err := s.db.Query(` SELECT e.id, e.name, e.description, e.muscle_group, e.weight_step_kg, e.created_at, e.updated_at FROM exercises e JOIN set_exercises se ON se.exercise_id = e.id WHERE se.set_id = ? ORDER BY se.position`, setID, ) if err != nil { return nil, fmt.Errorf("Set-Übungen abfragen: %w", err) } defer rows.Close() var exercises []model.Exercise for rows.Next() { var e model.Exercise if err := rows.Scan(&e.ID, &e.Name, &e.Description, &e.MuscleGroup, &e.WeightStepKg, &e.CreatedAt, &e.UpdatedAt); err != nil { return nil, fmt.Errorf("Übung scannen: %w", err) } exercises = append(exercises, e) } if exercises == nil { exercises = []model.Exercise{} } return exercises, rows.Err() }