package repository import ( "context" "errors" dbgen "Yimaru-Backend/gen/db" "Yimaru-Backend/internal/domain" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" ) func moduleToDomain(m dbgen.Module) domain.Module { out := domain.Module{ ID: m.ID, ProgramID: m.ProgramID, CourseID: m.CourseID, Name: m.Name, } out.Description = fromPgText(m.Description) out.Icon = fromPgText(m.Icon) out.CreatedAt = m.CreatedAt.Time if m.UpdatedAt.Valid { t := m.UpdatedAt.Time out.UpdatedAt = &t } out.SortOrder = int(m.SortOrder) return out } func (s *Store) CreateModule(ctx context.Context, programID, courseID int64, input domain.CreateModuleInput) (domain.Module, error) { if input.SortOrder != nil { q, tx, err := s.BeginTx(ctx) if err != nil { return domain.Module{}, err } defer func() { _ = tx.Rollback(ctx) }() target := int32(*input.SortOrder) if _, err := tx.Exec(ctx, `UPDATE modules SET sort_order = sort_order + 1 WHERE course_id = $1 AND sort_order >= $2`, courseID, target, ); err != nil { return domain.Module{}, err } m, err := q.CreateModule(ctx, dbgen.CreateModuleParams{ ProgramID: programID, CourseID: courseID, Name: input.Name, Description: toPgText(input.Description), Icon: toPgText(input.Icon), SortOrder: pgtype.Int4{Int32: target, Valid: true}, }) if err != nil { return domain.Module{}, err } if err := tx.Commit(ctx); err != nil { return domain.Module{}, err } return moduleToDomain(m), nil } m, err := s.queries.CreateModule(ctx, dbgen.CreateModuleParams{ ProgramID: programID, CourseID: courseID, Name: input.Name, Description: toPgText(input.Description), Icon: toPgText(input.Icon), SortOrder: pgtype.Int4{Valid: false}, }) if err != nil { return domain.Module{}, err } return moduleToDomain(m), nil } func (s *Store) ListModuleIDsByCourse(ctx context.Context, courseID int64) ([]int64, error) { return s.queries.ListModuleIDsByCourse(ctx, courseID) } func (s *Store) GetModuleByID(ctx context.Context, id int64) (domain.Module, error) { m, err := s.queries.GetModuleByID(ctx, id) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return domain.Module{}, pgx.ErrNoRows } return domain.Module{}, err } out := moduleToDomain(dbgen.Module{ ID: m.ID, ProgramID: m.ProgramID, CourseID: m.CourseID, Name: m.Name, Description: m.Description, Icon: m.Icon, SortOrder: m.SortOrder, CreatedAt: m.CreatedAt, UpdatedAt: m.UpdatedAt, }) out.HasPractice = m.HasPractice return out, nil } func (s *Store) ListModulesByProgramAndCourse(ctx context.Context, programID, courseID int64, limit, offset int32) ([]domain.Module, int64, error) { rows, err := s.queries.ListModulesByProgramAndCourse(ctx, dbgen.ListModulesByProgramAndCourseParams{ ProgramID: programID, CourseID: courseID, Limit: limit, Offset: offset, }) if err != nil { return nil, 0, err } if len(rows) == 0 { return []domain.Module{}, 0, nil } var total int64 out := make([]domain.Module, 0, len(rows)) for i, r := range rows { if i == 0 { total = r.TotalCount } mod := moduleToDomain(dbgen.Module{ ID: r.ID, ProgramID: r.ProgramID, CourseID: r.CourseID, Name: r.Name, Description: r.Description, Icon: r.Icon, CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt, SortOrder: r.SortOrder, }) mod.HasPractice = r.HasPractice out = append(out, mod) } return out, total, nil } func (s *Store) UpdateModule(ctx context.Context, id int64, input domain.UpdateModuleInput) (domain.Module, error) { cur, err := s.GetModuleByID(ctx, id) if err != nil { return domain.Module{}, err } q, tx, err := s.BeginTx(ctx) if err != nil { return domain.Module{}, err } defer func() { _ = tx.Rollback(ctx) }() sortParam := optionalInt4Update(input.SortOrder) if input.SortOrder != nil { oldPos := int32(cur.SortOrder) newPos := int32(*input.SortOrder) if oldPos != newPos { if err := repositionModuleSortOrder(ctx, tx, cur.CourseID, id, oldPos, newPos); err != nil { return domain.Module{}, err } } sortParam = pgtype.Int4{Valid: false} } var nameText pgtype.Text if input.Name != nil { nameText = pgtype.Text{String: *input.Name, Valid: true} } else { nameText = pgtype.Text{Valid: false} } m, err := q.UpdateModule(ctx, dbgen.UpdateModuleParams{ ID: id, Name: nameText, Description: optionalTextUpdate(input.Description), Icon: optionalTextUpdate(input.Icon), SortOrder: sortParam, }) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return domain.Module{}, pgx.ErrNoRows } return domain.Module{}, err } if err := tx.Commit(ctx); err != nil { return domain.Module{}, err } out := moduleToDomain(m) out.HasPractice = cur.HasPractice return out, nil } func (s *Store) DeleteModule(ctx context.Context, id int64) error { return s.queries.DeleteModule(ctx, id) }