refactor: 后端仓库层更名为数据访问层(internal/repository → internal/dal)
「仓库层」是 repository 的直译,中文里与「代码仓库 / git 仓库」同词, 而这一层做的事就是数据访问。名字改成它实际在做的事。 改名口径(纯机械替换,无逻辑改动): - 包:internal/repository → internal/dal(package repository → package dal) - 类型:XxxRepo → XxxDAO(TaskRecordDAO / SpecialistDAO / PositionDAO …) - 变量:xxxRepo → xxxDAO - import 路径、包限定符、日志前缀 [repository] → [dal] 同步 - 注释里的「仓库层」→「数据访问层」;core.go 包注释补上 DAL/DAO 全称 命名规范补登(AR09 是命名问题的最高依据,改了名就得回去登记): - AR09 §3.1 术语表新增「数据访问层 dal / DAO」一行 - AR09 §5.6 缩写表新增 DAO / dal —— 原文是「只有下表内的缩写允许使用」, 不登记就是自己破自己的规矩 - PROJECT_STATE.md 新增 D27 记录本次更名决策 验证:全部在 db 副本上做,生产库 data/eai_agentplatform.db 未触碰。 - 等价性对照:拿 HEAD 源码 + 仅改名 造出第二棵树,两棵树各自起 httptest 服务跑同一份探针(60 个 GET + 13 个写/回读,覆盖专员/技能/应用/ 任务/交付物/项目/岗位/考试/知识/积分/管理端只读等),逐端点比对响应体: 73 项里 52 项字节完全一致、21 项仅运行期时间戳不同、内容差异 0。 - 探针非空:往改名后的树注入「SpecialistDAO.List 限 3 条」变异, /api/specialists 立刻被抓出 —— 证明上面那个 0 不是没测到。 - 暂存区自洽:把索引整个导出成源码树,go build / go vet / go test ./... 全绿。 - gofmt:因 import 排序变化而错位的 19 个文件已修;另 2 个文件(skill_definition.go、 seed.go)的格式问题是工作区里别人的在制品带来的,未替其改动。 未纳入本次提交:工作区里正在进行中的「文生语音技能 + 技能展示色/交互卡」 (tts_handlers.go、text_to_speech/manifest.go、skillCatalog.js 等), 以及 router.go / skill_definition.go / seed.go 三个文件里属于该在制品的改动 —— 这三个文件只把「改名那一版」放进索引,工作区原样保留。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -14,25 +14,25 @@ import (
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
|
||||
"eai_agentplatform/backend/internal/auth"
|
||||
"eai_agentplatform/backend/internal/dal"
|
||||
"eai_agentplatform/backend/internal/middleware"
|
||||
"eai_agentplatform/backend/internal/model"
|
||||
"eai_agentplatform/backend/internal/repository"
|
||||
"eai_agentplatform/backend/internal/web"
|
||||
)
|
||||
|
||||
// 考试域仓库,包内共享(知识源摄入也会写题目,见 knowledge.go)。
|
||||
var (
|
||||
mistakeRepo repository.MistakeRecordRepo
|
||||
questionRepo repository.QuestionRepo
|
||||
paperRepo repository.ExamPaperRepo
|
||||
examRecordRepo repository.ExamRecordRepo
|
||||
mistakeDAO dal.MistakeRecordDAO
|
||||
questionDAO dal.QuestionDAO
|
||||
paperDAO dal.ExamPaperDAO
|
||||
examRecordDAO dal.ExamRecordDAO
|
||||
)
|
||||
|
||||
func init() {
|
||||
mistakeRepo = repository.MistakeRecordRepo{}
|
||||
questionRepo = repository.QuestionRepo{}
|
||||
paperRepo = repository.ExamPaperRepo{}
|
||||
examRecordRepo = repository.ExamRecordRepo{}
|
||||
mistakeDAO = dal.MistakeRecordDAO{}
|
||||
questionDAO = dal.QuestionDAO{}
|
||||
paperDAO = dal.ExamPaperDAO{}
|
||||
examRecordDAO = dal.ExamRecordDAO{}
|
||||
}
|
||||
|
||||
// Option 题目选项
|
||||
@@ -84,7 +84,7 @@ func questionView(q model.Question) gin.H {
|
||||
// ListQuestions GET /api/exam/questions?domain=&status=
|
||||
func ListQuestions(c *gin.Context) {
|
||||
// 管理端题库:status 缺省要看到全部(含已停用),按约定传 "all"
|
||||
items := questionRepo.List(c.Query("domain"), orDefault(c.Query("status"), "all"))
|
||||
items := questionDAO.List(c.Query("domain"), orDefault(c.Query("status"), "all"))
|
||||
out := make([]gin.H, 0, len(items))
|
||||
for _, it := range items {
|
||||
out = append(out, questionView(it))
|
||||
@@ -119,7 +119,7 @@ func CreateQuestion(c *gin.Context) {
|
||||
Explanation: req.Explanation,
|
||||
Status: "active",
|
||||
}
|
||||
if !questionRepo.Insert(&q) {
|
||||
if !questionDAO.Insert(&q) {
|
||||
web.Fail(c, web.NewBadRequest("创建题目失败"))
|
||||
return
|
||||
}
|
||||
@@ -132,7 +132,7 @@ func UpdateQuestion(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
q, found := questionRepo.GetByID(id)
|
||||
q, found := questionDAO.GetByID(id)
|
||||
if !found {
|
||||
web.Fail(c, web.NewNotFoundError("题目不存在"))
|
||||
return
|
||||
@@ -159,7 +159,7 @@ func UpdateQuestion(c *gin.Context) {
|
||||
q.Options = string(optsJSON)
|
||||
q.Answer = string(ansJSON)
|
||||
q.Explanation = req.Explanation
|
||||
if !questionRepo.Update(&q) {
|
||||
if !questionDAO.Update(&q) {
|
||||
web.Fail(c, web.NewBadRequest("更新题目失败"))
|
||||
return
|
||||
}
|
||||
@@ -172,11 +172,11 @@ func DeleteQuestion(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if _, found := questionRepo.GetByID(id); !found {
|
||||
if _, found := questionDAO.GetByID(id); !found {
|
||||
web.Fail(c, web.NewNotFoundError("题目不存在"))
|
||||
return
|
||||
}
|
||||
if !questionRepo.Delete(id) {
|
||||
if !questionDAO.Delete(id) {
|
||||
web.Fail(c, web.NewBadRequest("停用题目失败"))
|
||||
return
|
||||
}
|
||||
@@ -187,7 +187,7 @@ func DeleteQuestion(c *gin.Context) {
|
||||
|
||||
// ListPapers GET /api/exam/papers
|
||||
func ListPapers(c *gin.Context) {
|
||||
web.OK(c, paperRepo.List())
|
||||
web.OK(c, paperDAO.List())
|
||||
}
|
||||
|
||||
// CreatePaper POST /api/exam/papers (admin)
|
||||
@@ -208,7 +208,7 @@ func CreatePaper(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
p.Status = "active"
|
||||
if !paperRepo.Insert(&p) {
|
||||
if !paperDAO.Insert(&p) {
|
||||
web.Fail(c, web.NewBadRequest("创建考试失败"))
|
||||
return
|
||||
}
|
||||
@@ -221,7 +221,7 @@ func UpdatePaper(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
p, found := paperRepo.GetByID(id)
|
||||
p, found := paperDAO.GetByID(id)
|
||||
if !found {
|
||||
web.Fail(c, web.NewNotFoundError("考试配置不存在"))
|
||||
return
|
||||
@@ -253,7 +253,7 @@ func UpdatePaper(c *gin.Context) {
|
||||
if req.Status != "" {
|
||||
p.Status = req.Status
|
||||
}
|
||||
if !paperRepo.Update(&p) {
|
||||
if !paperDAO.Update(&p) {
|
||||
web.Fail(c, web.NewBadRequest("更新考试失败"))
|
||||
return
|
||||
}
|
||||
@@ -266,11 +266,11 @@ func DeletePaper(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if _, found := paperRepo.GetByID(id); !found {
|
||||
if _, found := paperDAO.GetByID(id); !found {
|
||||
web.Fail(c, web.NewNotFoundError("考试配置不存在"))
|
||||
return
|
||||
}
|
||||
if !paperRepo.Delete(id) {
|
||||
if !paperDAO.Delete(id) {
|
||||
web.Fail(c, web.NewBadRequest("停用考试失败"))
|
||||
return
|
||||
}
|
||||
@@ -282,7 +282,7 @@ func DeletePaper(c *gin.Context) {
|
||||
// ExamList GET /api/exam/list —— 我的考试列表(含完成状态)
|
||||
func ExamList(c *gin.Context) {
|
||||
u := middleware.CurrentUser(c)
|
||||
items := paperRepo.GetActive()
|
||||
items := paperDAO.GetActive()
|
||||
|
||||
type row struct {
|
||||
ID uint `json:"id"`
|
||||
@@ -298,7 +298,7 @@ func ExamList(c *gin.Context) {
|
||||
out := make([]row, 0, len(items))
|
||||
for _, p := range items {
|
||||
st := "available"
|
||||
if p.Type == "formal" && u != nil && examRecordRepo.HasTaken(u.ID, p.ID) {
|
||||
if p.Type == "formal" && u != nil && examRecordDAO.HasTaken(u.ID, p.ID) {
|
||||
st = "completed"
|
||||
}
|
||||
out = append(out, row{
|
||||
@@ -318,7 +318,7 @@ func ExamCover(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
id := uint(id64)
|
||||
p, found := paperRepo.GetByID(id)
|
||||
p, found := paperDAO.GetByID(id)
|
||||
if !found || p.Status != "active" {
|
||||
web.Fail(c, web.NewNotFoundError("考试不存在或已停用"))
|
||||
return
|
||||
@@ -351,18 +351,18 @@ func pickQuestions(p model.ExamPaper) ([]model.Question, error) {
|
||||
var courseIDs []uint
|
||||
if p.PositionID != nil {
|
||||
// 岗位考试:优先蓝图,其次岗位知识映射
|
||||
if bps := positionRepo.Blueprints(*p.PositionID); len(bps) > 0 {
|
||||
if bps := positionDAO.Blueprints(*p.PositionID); len(bps) > 0 {
|
||||
return pickQuestionsByBlueprint(p, bps)
|
||||
}
|
||||
// 岗位考试:圈定岗位应学范围
|
||||
domains, courseIDs = positionScope(positionRepo.Knowledge(*p.PositionID))
|
||||
domains, courseIDs = positionScope(positionDAO.Knowledge(*p.PositionID))
|
||||
} else {
|
||||
// 原有逻辑:按 exam_paper.domain 抽题
|
||||
domains = splitDomains(p.Domain)
|
||||
}
|
||||
|
||||
var qs []model.Question
|
||||
if !questionRepo.ActivePool(domains, courseIDs).Order("id ASC").Find(&qs) {
|
||||
if !questionDAO.ActivePool(domains, courseIDs).Order("id ASC").Find(&qs) {
|
||||
return nil, fmt.Errorf("查询题库失败")
|
||||
}
|
||||
if len(qs) < p.QuestionCount {
|
||||
@@ -377,8 +377,8 @@ func pickQuestions(p model.ExamPaper) ([]model.Question, error) {
|
||||
// pickQuestionsByBlueprint 按岗位考试蓝图逐条抽题:先在岗位应学范围内,再按 (domain, type) 细分抽样。
|
||||
func pickQuestionsByBlueprint(p model.ExamPaper, bps []model.PositionExamBlueprint) ([]model.Question, error) {
|
||||
// 圈定岗位应学范围(与 P0 岗位知识映射抽题一致)
|
||||
domains, courseIDs := positionScope(positionRepo.Knowledge(*p.PositionID))
|
||||
base := questionRepo.ActivePool(domains, courseIDs)
|
||||
domains, courseIDs := positionScope(positionDAO.Knowledge(*p.PositionID))
|
||||
base := questionDAO.ActivePool(domains, courseIDs)
|
||||
|
||||
out := make([]model.Question, 0, p.QuestionCount)
|
||||
used := map[uint]bool{}
|
||||
@@ -456,12 +456,12 @@ func validatePaperPosition(c *gin.Context, positionID *uint) bool {
|
||||
if positionID == nil {
|
||||
return true
|
||||
}
|
||||
pos, found := positionRepo.GetByID(*positionID)
|
||||
pos, found := positionDAO.GetByID(*positionID)
|
||||
if !found || pos.Status != "active" {
|
||||
web.Fail(c, web.NewBadRequest("关联岗位不存在或已停用"))
|
||||
return false
|
||||
}
|
||||
if positionRepo.CountKnowledge(*positionID) == 0 {
|
||||
if positionDAO.CountKnowledge(*positionID) == 0 {
|
||||
web.Fail(c, web.NewBadRequest("岗位考试必须先配置岗位知识映射"))
|
||||
return false
|
||||
}
|
||||
@@ -473,7 +473,7 @@ func validatePaperBlueprintCount(c *gin.Context, p model.ExamPaper) bool {
|
||||
if p.PositionID == nil {
|
||||
return true
|
||||
}
|
||||
bps := positionRepo.Blueprints(*p.PositionID)
|
||||
bps := positionDAO.Blueprints(*p.PositionID)
|
||||
if len(bps) == 0 {
|
||||
return true
|
||||
}
|
||||
@@ -509,12 +509,12 @@ func ExamStart(c *gin.Context) {
|
||||
web.Fail(c, web.NewBadRequest("paper_id 必填"))
|
||||
return
|
||||
}
|
||||
p, found := paperRepo.GetByID(req.PaperID)
|
||||
p, found := paperDAO.GetByID(req.PaperID)
|
||||
if !found || p.Status != "active" {
|
||||
web.Fail(c, web.NewNotFoundError("考试不存在或已停用"))
|
||||
return
|
||||
}
|
||||
if p.Type == "formal" && u != nil && examRecordRepo.HasTaken(u.ID, p.ID) {
|
||||
if p.Type == "formal" && u != nil && examRecordDAO.HasTaken(u.ID, p.ID) {
|
||||
web.Fail(c, web.NewConflictError("已参加过该正式考试"))
|
||||
return
|
||||
}
|
||||
@@ -676,7 +676,7 @@ func ExamSubmit(c *gin.Context) {
|
||||
web.Fail(c, web.NewBadRequest("练习会话无题目"))
|
||||
return
|
||||
}
|
||||
qs := questionRepo.ListByIDs(ids)
|
||||
qs := questionDAO.ListByIDs(ids)
|
||||
qmap := make(map[uint]model.Question, len(qs))
|
||||
for _, q := range qs {
|
||||
qmap[q.ID] = q
|
||||
@@ -755,14 +755,14 @@ func ExamSubmit(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
p, found := paperRepo.GetByID(uint(pid))
|
||||
p, found := paperDAO.GetByID(uint(pid))
|
||||
if !found {
|
||||
web.Fail(c, web.NewNotFoundError("考试不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 正式考不可重复交卷
|
||||
if stype == "formal" && u != nil && examRecordRepo.HasTaken(u.ID, p.ID) {
|
||||
if stype == "formal" && u != nil && examRecordDAO.HasTaken(u.ID, p.ID) {
|
||||
web.Fail(c, web.NewConflictError("已参加过该正式考试"))
|
||||
return
|
||||
}
|
||||
@@ -772,7 +772,7 @@ func ExamSubmit(c *gin.Context) {
|
||||
web.Fail(c, web.NewBadRequest("考试会话无题目"))
|
||||
return
|
||||
}
|
||||
questions := questionRepo.ListByIDs(ids)
|
||||
questions := questionDAO.ListByIDs(ids)
|
||||
qmap := make(map[uint]model.Question, len(questions))
|
||||
for _, q := range questions {
|
||||
qmap[q.ID] = q
|
||||
@@ -867,7 +867,7 @@ func ExamSubmit(c *gin.Context) {
|
||||
CorrectCount: correctCount, WrongCount: wrongCount,
|
||||
DetailJSON: string(detailJSON), SubmittedAt: time.Now(),
|
||||
}
|
||||
examRecordRepo.Insert(&rec)
|
||||
examRecordDAO.Insert(&rec)
|
||||
if passed {
|
||||
awardPoints(u.ID, "formal_pass", ptFormalPass, "paper", p.ID)
|
||||
issueCertificate(u, rec)
|
||||
@@ -897,7 +897,7 @@ func splitIDs(s string) []uint {
|
||||
// ExamRecordList GET /api/exam/record?page=&size= —— 我的考试记录
|
||||
func ExamRecordList(c *gin.Context) {
|
||||
u := middleware.CurrentUser(c)
|
||||
items := examRecordRepo.ListByUser(u.ID)
|
||||
items := examRecordDAO.ListByUser(u.ID)
|
||||
if items == nil {
|
||||
items = []model.ExamRecord{}
|
||||
}
|
||||
@@ -911,7 +911,7 @@ func ExamRecordDetail(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
rec, found := examRecordRepo.GetByID(id)
|
||||
rec, found := examRecordDAO.GetByID(id)
|
||||
if !found {
|
||||
web.Fail(c, web.NewNotFoundError("考试记录不存在"))
|
||||
return
|
||||
@@ -947,7 +947,7 @@ func mistakePayload(userID, questionID uint, source string, q model.Question, us
|
||||
|
||||
// recordMistake 答错入本:按 (user_id, question_id, source) 去重,再次答错则更新并重置为未掌握。
|
||||
func recordMistake(userID, questionID uint, q model.Question, userAns any, correct []string, source string) {
|
||||
mistakeRepo.RecordWrong(mistakePayload(userID, questionID, source, q, userAns, correct))
|
||||
mistakeDAO.RecordWrong(mistakePayload(userID, questionID, source, q, userAns, correct))
|
||||
}
|
||||
|
||||
// mistakeView 错题出参(answer 反序列化,便于前端直接展示)
|
||||
@@ -967,7 +967,7 @@ type mistakeView struct {
|
||||
// MyMistakes GET /api/exam/mistakes —— 我的错题本
|
||||
func MyMistakes(c *gin.Context) {
|
||||
u := middleware.CurrentUser(c)
|
||||
items := mistakeRepo.ListByUser(u.ID)
|
||||
items := mistakeDAO.ListByUser(u.ID)
|
||||
out := make([]mistakeView, 0, len(items))
|
||||
for _, it := range items {
|
||||
var userAns any
|
||||
@@ -994,7 +994,7 @@ func ResolveMistake(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
rec, found := mistakeRepo.GetByID(id)
|
||||
rec, found := mistakeDAO.GetByID(id)
|
||||
if !found {
|
||||
web.Fail(c, web.NewNotFoundError("错题记录不存在"))
|
||||
return
|
||||
@@ -1013,7 +1013,7 @@ func ResolveMistake(c *gin.Context) {
|
||||
}
|
||||
wasResolved := rec.Resolved
|
||||
rec.Resolved = target
|
||||
if !mistakeRepo.Update(&rec) {
|
||||
if !mistakeDAO.Update(&rec) {
|
||||
web.Fail(c, web.NewBadRequest("更新错题状态失败"))
|
||||
return
|
||||
}
|
||||
@@ -1037,7 +1037,7 @@ func MistakePractice(c *gin.Context) {
|
||||
}
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
|
||||
recs := mistakeRepo.ListForPractice(u.ID, req.Source, req.OnlyUnresolved)
|
||||
recs := mistakeDAO.ListForPractice(u.ID, req.Source, req.OnlyUnresolved)
|
||||
if len(recs) == 0 {
|
||||
web.Fail(c, web.NewBadRequest("暂无可重练的错题"))
|
||||
return
|
||||
@@ -1050,7 +1050,7 @@ func MistakePractice(c *gin.Context) {
|
||||
ids = dedupeUints(ids)
|
||||
|
||||
// 剔除已停用/删除的题目,仅下发可作答题目
|
||||
questions := questionRepo.ListActiveByIDs(ids)
|
||||
questions := questionDAO.ListActiveByIDs(ids)
|
||||
if len(questions) == 0 {
|
||||
web.Fail(c, web.NewBadRequest("错题对应题目已失效,暂无可重练题目"))
|
||||
return
|
||||
@@ -1101,11 +1101,11 @@ func MistakePractice(c *gin.Context) {
|
||||
// touchMistakeOnPractice 错题重练判分后同步错题状态:答对置为已掌握(加积分),答错重置为未掌握。
|
||||
func touchMistakeOnPractice(userID, questionID uint, q model.Question, userAns any, correct []string, resolved bool) {
|
||||
payload := mistakePayload(userID, questionID, "", q, userAns, correct)
|
||||
flipped, touched := mistakeRepo.TouchOnPractice(payload, resolved)
|
||||
flipped, touched := mistakeDAO.TouchOnPractice(payload, resolved)
|
||||
if !touched {
|
||||
// 防御性兜底:理论上重练题目均来自错题本,这里创建一条
|
||||
payload.Source = "re_practice"
|
||||
mistakeRepo.RecordWrong(payload)
|
||||
mistakeDAO.RecordWrong(payload)
|
||||
return
|
||||
}
|
||||
// 仅在「未掌握 → 已掌握」时加分,避免反复重练刷分
|
||||
|
||||
Reference in New Issue
Block a user