package dal import ( "eai_agentplatform/backend/internal/model" ) // Question 题目仓库。 type QuestionDAO struct{ *QueryBuilder } // List 获取题目列表(id 升序)。 // // status 沿用本站列表的档位约定(与 CourseDAO.List 一致): // // "" → 只看 active(学员侧默认) // "all" → 不过滤(管理端题库默认,含已停用) // 其它 → 按该 status 过滤 // // domain 为空表示不过滤该维度。 func (r QuestionDAO) List(domain, status string) []model.Question { q := r.Type(&model.Question{}) if domain != "" { q = q.Where("domain = ?", domain) } switch status { case "": q = q.Where("status = ?", "active") case "all": // 不过滤 default: q = q.Where("status = ?", status) } var items []model.Question if q.Order("id ASC").Find(&items) { return items } return nil } // ListByIDs 按 ID 批量取题,**不过滤 status**(id 升序)。 // // 判分专用:考试会话里下发的题目即使事后被停用,也必须能判分, // 否则学员交了卷却判不出来。要「只取可作答题目」用 ListActiveByIDs。 func (r QuestionDAO) ListByIDs(ids []uint) []model.Question { if len(ids) == 0 { return nil } var items []model.Question if r.Type(&items).Where("id IN ?", ids).Order("id ASC").Find(&items) { return items } return nil } // ListActiveByIDs 按 ID 批量取「已启用」的题(id 升序)。 // // 下发专用:错题重练等场景要剔除已停用/删除的题目。 func (r QuestionDAO) ListActiveByIDs(ids []uint) []model.Question { if len(ids) == 0 { return nil } var items []model.Question if r.Type(&items).Where("id IN ? AND status = ?", ids, "active").Order("id ASC").Find(&items) { return items } return nil } // ActivePool 返回「岗位应学范围内已启用题目」的查询构建器,供抽题逻辑继续细分。 // // 口径(P0 岗位考试抽题,抽题主流程与蓝图抽题共用,避免两处手写漂移): // - 只取 status=active // - domains 非空时限定 domain IN domains // - courseIDs 非空时「题目属于这些课程,或未绑定课程但域匹配」(宽松口径,沿用原行为) // // 两个参数都为空表示全题库。 func (r QuestionDAO) ActivePool(domains []string, courseIDs []uint) *QueryBuilder { q := r.Type(&model.Question{}).Where("status = ?", "active") if len(domains) > 0 { q = q.Where("domain IN ?", domains) } if len(courseIDs) > 0 { q = q.Where("course_id IN ? OR course_id IS NULL", courseIDs) } return q } // GetByID 按 ID 获取题目。 func (r QuestionDAO) GetByID(id uint) (model.Question, bool) { var q model.Question if r.Type(&q).Where("id = ?", id).First(&q) { return q, true } return model.Question{}, false } // GetActiveIDs 获取指定状态的题目 ID 列表。 func (r QuestionDAO) GetActiveIDs(ids []uint) []uint { var result []uint r.Inner().Model(&model.Question{}).Where("id IN ? AND status = ?", ids, "active").Pluck("id", &result) return result } // DomainMap 返回「题目 ID → 知识域」映射。 // // 学员能力雷达要拿答题明细里的题目 ID 反查所属域;只取两列,避免整表加载题干。 func (r QuestionDAO) DomainMap() map[uint]string { var rows []model.Question r.Inner().Model(&model.Question{}).Select("id", "domain").Find(&rows) out := make(map[uint]string, len(rows)) for _, q := range rows { out[q.ID] = q.Domain } return out } // CountByType 统计某类题目数量。 func (r QuestionDAO) CountByType(domain, qType string) int64 { q := r.Inner().Model(&model.Question{}) if domain != "" { q = q.Where("domain = ?", domain) } var c int64 q.Where("type = ? AND status = ?", qType, "active").Count(&c) return c } // Insert 创建题目。 func (r QuestionDAO) Insert(item *model.Question) bool { return r.QueryBuilder.Insert(item) } // Update 更新题目。 func (r QuestionDAO) Update(item *model.Question) bool { return r.Save(item) } // Delete 软删除。 func (r QuestionDAO) Delete(id uint) bool { return r.Type(&model.Question{}).Where("id = ?", id).Updates(map[string]any{"status": "inactive"}) } // PickRandom 随机抽取 N 道题。 func (r QuestionDAO) PickRandom(domain, qType string, count int, excludeIDs []uint) []model.Question { q := r.Type(&model.Question{}).Where("status = ? AND type = ?", "active", qType) if domain != "" { q = q.Where("domain = ?", domain) } if len(excludeIDs) > 0 { q = q.Where("id NOT IN ?", excludeIDs) } var items []model.Question if q.Order("RANDOM()").Limit(count).Find(&items) { return items } return nil }