fix: course-management practice detail without sub_module_practices row
- resolveCourseManagementPractice falls back to SUB_MODULE PRACTICE question_sets - Synthetic practice uses id 0 and question_set_id for orphan sets - Align GET practice and /detail with resolver; sync question_count after load Made-with: Cursor
This commit is contained in:
parent
5fbca53534
commit
9154dec067
|
|
@ -1,6 +1,7 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
dbgen "Yimaru-Backend/gen/db"
|
||||
"Yimaru-Backend/internal/domain"
|
||||
"errors"
|
||||
|
|
@ -2244,9 +2245,62 @@ func (h *Handler) GetSubModulePractices(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
func practiceRowFromSubModuleQuestionSet(qs domain.QuestionSet) dbgen.GetSubModulePracticeByIDRow {
|
||||
row := dbgen.GetSubModulePracticeByIDRow{
|
||||
ID: 0,
|
||||
Title: qs.Title,
|
||||
QuestionSetID: qs.ID,
|
||||
DisplayOrder: 0,
|
||||
Status: qs.Status,
|
||||
SetType: qs.SetType,
|
||||
IsActive: !strings.EqualFold(qs.Status, "ARCHIVED"),
|
||||
}
|
||||
if qs.OwnerID != nil {
|
||||
row.SubModuleID = *qs.OwnerID
|
||||
}
|
||||
if qs.Description != nil {
|
||||
row.Description = pgtype.Text{String: *qs.Description, Valid: true}
|
||||
}
|
||||
if qs.IntroVideoURL != nil {
|
||||
row.IntroVideoUrl = pgtype.Text{String: *qs.IntroVideoURL, Valid: true}
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
// resolveCourseManagementPractice loads an active sub_module_practices row, or a SUB_MODULE-owned PRACTICE
|
||||
// question_set with no bridge row (id is always question_sets.id in that case).
|
||||
func (h *Handler) resolveCourseManagementPractice(ctx context.Context, id int64) (dbgen.GetSubModulePracticeByIDRow, error) {
|
||||
row, err := h.analyticsDB.GetSubModulePracticeByID(ctx, id)
|
||||
if err == nil {
|
||||
return row, nil
|
||||
}
|
||||
if !errors.Is(err, pgx.ErrNoRows) {
|
||||
return dbgen.GetSubModulePracticeByIDRow{}, err
|
||||
}
|
||||
qs, err := h.questionsSvc.GetQuestionSetByID(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return dbgen.GetSubModulePracticeByIDRow{}, pgx.ErrNoRows
|
||||
}
|
||||
return dbgen.GetSubModulePracticeByIDRow{}, err
|
||||
}
|
||||
if !strings.EqualFold(qs.SetType, string(domain.QuestionSetTypePractice)) {
|
||||
return dbgen.GetSubModulePracticeByIDRow{}, pgx.ErrNoRows
|
||||
}
|
||||
if qs.OwnerType == nil || !strings.EqualFold(*qs.OwnerType, "SUB_MODULE") || qs.OwnerID == nil {
|
||||
return dbgen.GetSubModulePracticeByIDRow{}, pgx.ErrNoRows
|
||||
}
|
||||
out := practiceRowFromSubModuleQuestionSet(qs)
|
||||
_, total, err := h.questionsSvc.GetQuestionSetItemsPaginated(ctx, qs.ID, nil, 1, 0)
|
||||
if err == nil {
|
||||
out.QuestionCount = total
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetSubModulePracticeByID godoc
|
||||
// @Summary Get practice detail
|
||||
// @Description Returns one active practice. practiceId may be sub_module_practices.id or the linked question_sets.id.
|
||||
// @Description Returns one practice. practiceId may be sub_module_practices.id, question_sets.id, or (if no bridge row) a SUB_MODULE PRACTICE question set id.
|
||||
// @Tags course-management
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
|
|
@ -2265,13 +2319,19 @@ func (h *Handler) GetSubModulePracticeByID(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
practice, err := h.analyticsDB.GetSubModulePracticeByID(c.Context(), practiceID)
|
||||
practice, err := h.resolveCourseManagementPractice(c.Context(), practiceID)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
|
||||
Message: "Practice not found",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||
Message: "Failed to load practice",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(domain.Response{
|
||||
Message: "Practice retrieved successfully",
|
||||
|
|
@ -2283,7 +2343,7 @@ func (h *Handler) GetSubModulePracticeByID(c *fiber.Ctx) error {
|
|||
|
||||
// GetSubModulePracticeDetail godoc
|
||||
// @Summary Get practice with full question list
|
||||
// @Description Returns one active practice with question-set fields and the ordered question list (full item detail). practiceId may be sub_module_practices.id or the linked question_sets.id.
|
||||
// @Description Returns practice metadata and ordered questions. practiceId may be sub_module_practices.id, linked question_sets.id, or a SUB_MODULE PRACTICE set id when no sub_module_practices row exists.
|
||||
// @Tags course-management
|
||||
// @Produce json
|
||||
// @Param practiceId path int true "Practice row id or question set id"
|
||||
|
|
@ -2300,16 +2360,23 @@ func (h *Handler) GetSubModulePracticeDetail(c *fiber.Ctx) error {
|
|||
Error: "practiceId must be a positive integer",
|
||||
})
|
||||
}
|
||||
practice, err := h.analyticsDB.GetSubModulePracticeByID(c.Context(), practiceID)
|
||||
practice, err := h.resolveCourseManagementPractice(c.Context(), practiceID)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
|
||||
Message: "Practice not found",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||
Message: "Failed to load practice",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
const pageSize int32 = 500
|
||||
var allItems []domain.QuestionSetItemWithQuestion
|
||||
var offset int32
|
||||
var totalCount int64
|
||||
for {
|
||||
batch, total, err := h.questionsSvc.GetQuestionSetItemsPaginated(c.Context(), practice.QuestionSetID, nil, pageSize, offset)
|
||||
if err != nil {
|
||||
|
|
@ -2318,12 +2385,14 @@ func (h *Handler) GetSubModulePracticeDetail(c *fiber.Ctx) error {
|
|||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
totalCount = total
|
||||
allItems = append(allItems, batch...)
|
||||
if int64(len(allItems)) >= total || len(batch) == 0 {
|
||||
break
|
||||
}
|
||||
offset += pageSize
|
||||
}
|
||||
practice.QuestionCount = totalCount
|
||||
return c.JSON(domain.Response{
|
||||
Message: "Practice retrieved successfully",
|
||||
Success: true,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user