Yimaru-BackEnd/internal/repository/exam_prep_units.go
Yared Yemane 14d94ec723 Honor optional sort_order when creating exam-prep units.
Expose sort_order on CreateExamPrepUnitInput; insert applies explicit index with sibling shifting (aligned with LMS course create). Updated Swagger and LMS-Personas Postman collection.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 07:18:35 -07:00

163 lines
4.8 KiB
Go

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 examPrepUnitToDomain(u dbgen.ExamPrepUnit) domain.ExamPrepUnit {
out := domain.ExamPrepUnit{
ID: u.ID,
CatalogCourseID: u.CatalogCourseID,
Name: u.Name,
SortOrder: int(u.SortOrder),
}
out.Description = fromPgText(u.Description)
out.Thumbnail = fromPgText(u.Thumbnail)
out.CreatedAt = u.CreatedAt.Time
if u.UpdatedAt.Valid {
t := u.UpdatedAt.Time
out.UpdatedAt = &t
}
return out
}
func (s *Store) CreateExamPrepUnit(ctx context.Context, catalogCourseID int64, input domain.CreateExamPrepUnitInput) (domain.ExamPrepUnit, error) {
if input.SortOrder != nil {
q, tx, err := s.BeginTx(ctx)
if err != nil {
return domain.ExamPrepUnit{}, err
}
defer func() { _ = tx.Rollback(ctx) }()
target := int32(*input.SortOrder)
if _, err := tx.Exec(ctx,
`UPDATE exam_prep.units SET sort_order = sort_order + 1 WHERE catalog_course_id = $1 AND sort_order >= $2`,
catalogCourseID, target,
); err != nil {
return domain.ExamPrepUnit{}, err
}
u, err := q.ExamPrepCreateUnit(ctx, dbgen.ExamPrepCreateUnitParams{
CatalogCourseID: catalogCourseID,
Name: input.Name,
Description: toPgText(input.Description),
Thumbnail: toPgText(input.Thumbnail),
SortOrder: pgtype.Int4{Int32: target, Valid: true},
})
if err != nil {
return domain.ExamPrepUnit{}, err
}
if err := tx.Commit(ctx); err != nil {
return domain.ExamPrepUnit{}, err
}
return examPrepUnitToDomain(u), nil
}
u, err := s.queries.ExamPrepCreateUnit(ctx, dbgen.ExamPrepCreateUnitParams{
CatalogCourseID: catalogCourseID,
Name: input.Name,
Description: toPgText(input.Description),
Thumbnail: toPgText(input.Thumbnail),
SortOrder: pgtype.Int4{Valid: false},
})
if err != nil {
return domain.ExamPrepUnit{}, err
}
return examPrepUnitToDomain(u), nil
}
func (s *Store) GetExamPrepUnitByID(ctx context.Context, id int64) (domain.ExamPrepUnit, error) {
u, err := s.queries.ExamPrepGetUnitByID(ctx, id)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.ExamPrepUnit{}, pgx.ErrNoRows
}
return domain.ExamPrepUnit{}, err
}
out := examPrepUnitToDomain(dbgen.ExamPrepUnit{
ID: u.ID,
CatalogCourseID: u.CatalogCourseID,
Name: u.Name,
Description: u.Description,
Thumbnail: u.Thumbnail,
SortOrder: u.SortOrder,
CreatedAt: u.CreatedAt,
UpdatedAt: u.UpdatedAt,
})
out.HasPractice = u.HasPractice
return out, nil
}
func (s *Store) ListExamPrepUnitsByCatalogCourse(ctx context.Context, catalogCourseID int64, limit, offset int32) ([]domain.ExamPrepUnit, int64, error) {
rows, err := s.queries.ExamPrepListUnitsByCatalogCourse(ctx, dbgen.ExamPrepListUnitsByCatalogCourseParams{
CatalogCourseID: catalogCourseID,
Limit: limit,
Offset: offset,
})
if err != nil {
return nil, 0, err
}
if len(rows) == 0 {
return []domain.ExamPrepUnit{}, 0, nil
}
var total int64
out := make([]domain.ExamPrepUnit, 0, len(rows))
for i, r := range rows {
if i == 0 {
total = r.TotalCount
}
item := examPrepUnitToDomain(dbgen.ExamPrepUnit{
ID: r.ID,
CatalogCourseID: r.CatalogCourseID,
Name: r.Name,
Description: r.Description,
Thumbnail: r.Thumbnail,
SortOrder: r.SortOrder,
CreatedAt: r.CreatedAt,
UpdatedAt: r.UpdatedAt,
})
item.ModulesCount = &r.ModulesCount
item.LessonsCount = &r.LessonsCount
item.PracticesCount = &r.PracticesCount
item.HasPractice = r.HasPractice
out = append(out, item)
}
return out, total, nil
}
func (s *Store) ListExamPrepUnitIDsByCatalogCourse(ctx context.Context, catalogCourseID int64) ([]int64, error) {
return s.queries.ExamPrepListUnitIDsByCatalogCourse(ctx, catalogCourseID)
}
func (s *Store) UpdateExamPrepUnit(ctx context.Context, id int64, input domain.UpdateExamPrepUnitInput) (domain.ExamPrepUnit, error) {
var nameText pgtype.Text
if input.Name != nil {
nameText = pgtype.Text{String: *input.Name, Valid: true}
} else {
nameText = pgtype.Text{Valid: false}
}
u, err := s.queries.ExamPrepUpdateUnit(ctx, dbgen.ExamPrepUpdateUnitParams{
ID: id,
Name: nameText,
Description: optionalTextUpdate(input.Description),
Thumbnail: optionalTextUpdate(input.Thumbnail),
SortOrder: optionalInt4Update(input.SortOrder),
})
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.ExamPrepUnit{}, pgx.ErrNoRows
}
return domain.ExamPrepUnit{}, err
}
return examPrepUnitToDomain(u), nil
}
func (s *Store) DeleteExamPrepUnit(ctx context.Context, id int64) error {
return s.queries.ExamPrepDeleteUnit(ctx, id)
}