refactor: 后端仓库层收口(A3:考试/部门/学习/证书/档案/公司介绍)

把 A 档剩余对象的 api 裸查询全部收进仓库,api 层裸 store.DB 从 182 降到 99,
剩下的全是 B 档(任务/项目/笔记等尚无仓库的对象)与 C 档(报表聚合查询)。

按对象补齐的仓库方法:
- QuestionRepo:List 重写(status 档位改为显式 all/空/具体值)、
  ListByIDs(判分不过滤 status)、ListActiveByIDs(下发剔除停用)、
  ActivePool(抽题口径,主流程与蓝图共用)、DomainMap(能力雷达反查域)
- ExamPaperRepo.List;ExamRecordRepo.ListByUserChronological(趋势图正序)
- DepartmentRepo.ListByStatus / CountByName
- UserRepo.ListEmployees / CountActiveByDepartment / RenameDepartment
- LearningProgressRepo.ListAll;CertificateRepo.ListAll / GetByExamRecord
- MediaFileRepo.ListApprovedByBindType

顺带修掉两处隐患:
- CertificateRepo.GetByUserAndExam 按不存在的 exam_id 列查,一调即 SQL 报错,
  换成按 exam_record_id 的 GetByExamRecord(颁发幂等本来就该按考试记录)
- exam.go 与 system.go 各声明了一个 ExamRecordRepo 变量,同一个仓库两份变量
  会导致测试覆写时行为分叉,统一为一个 examRecordRepo

考证来源(趋势图正序 vs 列表页倒序)与抽题口径(岗位蓝图/岗位知识映射两条路径)
各自抽成单一出处,避免两处手写漂移。聚合与百分比计算仍留在 handler,未搬进仓库。

验证:tmp 验证程序走真实路由 + 真实 HTTP,对 DB 副本跑 111 项断言全绿
(覆盖停用题仍可判分、错题重练剔除停用题、趋势正序、改名同步 user.department
且 updated_at 仍刷新、未通过的正式考不发证书、公司介绍只出 approved 素材等)。
另对其中 8 条关键语义做了变异测试:逐条注入反向实现,确认断言确实会失败,
并因此发现并修掉验证程序自身一处漏洞(写语句的约束错误只在 rows.Err() 浮出,
原先未检查,导致一条断言实为空断言)。

原始 data/eai_agentplatform.db 全程未触碰,md5 复核一致。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
eaiadmin
2026-09-19 01:41:33 +08:00
co-authored by Claude Code
parent 14f303459e
commit 19cf6fb5f2
21 changed files with 498 additions and 350 deletions
@@ -11,6 +11,8 @@ import (
"eai_agentplatform/backend/internal/web"
)
// 本文件用到的仓库:questionRepo/examRecordRepo 声明在 exam.go,learningRepo 声明在 learning.go。
// domainLabels 能力雷达维度(正式考试按域聚合的掌握度)。
var domainLabels = map[string]string{
"company": "公司认知",
@@ -38,8 +40,7 @@ func MyProfile(c *gin.Context) {
}
// 学习进度(公司/产品/课程浏览)
var lps []model.LearningProgress
store.DB.Where("user_id = ?", u.ID).Find(&lps)
lps := learningRepo.ListByUser(u.ID)
companyViewed := false
productViewed, courseViewed := 0, 0
for _, lp := range lps {
@@ -61,17 +62,11 @@ func MyProfile(c *gin.Context) {
var selfTestCount int64
store.DB.Model(&model.PointEvent{}).Where("user_id = ? AND event_type = ?", u.ID, "self_test").Count(&selfTestCount)
// 正式考试记录 + 按域能力聚合
var recs []model.ExamRecord
store.DB.Where("user_id = ?", u.ID).Order("submitted_at ASC").Find(&recs)
// 正式考试记录 + 按域能力聚合(趋势图要按时间正序)
recs := examRecordRepo.ListByUserChronological(u.ID)
// 题目 → 域映射(用于从答题明细反推各域掌握度)
var questions []model.Question
store.DB.Select("id", "domain").Find(&questions)
qDomain := map[uint]string{}
for _, q := range questions {
qDomain[q.ID] = q.Domain
}
qDomain := questionRepo.DomainMap()
type domainAgg struct {
Correct int `json:"correct"`