package api import ( "encoding/json" "github.com/gin-gonic/gin" "eai_agentplatform/backend/internal/middleware" "eai_agentplatform/backend/internal/model" "eai_agentplatform/backend/internal/store" "eai_agentplatform/backend/internal/web" ) // 本文件用到的仓库:questionRepo/examRecordRepo 声明在 exam.go,learningRepo 声明在 learning.go。 // domainLabels 能力雷达维度(正式考试按域聚合的掌握度)。 var domainLabels = map[string]string{ "company": "公司认知", "product": "产品知识", "sales": "销售能力", } // domainOrder 雷达图固定排序。 var domainOrder = []string{"company", "product", "sales"} // detailWrap 正式考试答题明细(与 ExamSubmit 持久化的 detail_json 结构对齐)。 type detailWrap struct { Questions []struct { QuestionID uint `json:"question_id"` IsCorrect bool `json:"is_correct"` } `json:"questions"` } // MyProfile GET /api/my/profile —— 学员学习档案:能力雷达 + 学习统计 + 成绩趋势 + 积分。 func MyProfile(c *gin.Context) { u := middleware.CurrentUser(c) if u == nil { web.Fail(c, web.NewAuthError("未登录")) return } // 学习进度(公司/产品/课程浏览) lps := learningRepo.ListByUser(u.ID) companyViewed := false productViewed, courseViewed := 0, 0 for _, lp := range lps { switch lp.ItemType { case "company": companyViewed = true case "product": productViewed++ case "course": courseViewed++ } } // 错题统计 mistakeTotal := mistakeRepo.CountByUser(u.ID) mistakeResolved := mistakeRepo.CountResolvedByUser(u.ID) // 自测次数(自测不落 exam_record,改由积分流水统计) var selfTestCount int64 store.DB.Model(&model.PointEvent{}).Where("user_id = ? AND event_type = ?", u.ID, "self_test").Count(&selfTestCount) // 正式考试记录 + 按域能力聚合(趋势图要按时间正序) recs := examRecordRepo.ListByUserChronological(u.ID) // 题目 → 域映射(用于从答题明细反推各域掌握度) qDomain := questionRepo.DomainMap() type domainAgg struct { Correct int `json:"correct"` Total int `json:"total"` } perDomain := map[string]*domainAgg{} formalPassed := 0 trend := make([]gin.H, 0, len(recs)) for _, r := range recs { if r.Passed { formalPassed++ } trend = append(trend, gin.H{ "exam_name": r.ExamName, "score": r.Score, "total_score": r.TotalScore, "passed": r.Passed, "submitted_at": r.SubmittedAt, }) var dw detailWrap if err := json.Unmarshal([]byte(r.DetailJSON), &dw); err != nil { continue } for _, qd := range dw.Questions { domain := qDomain[qd.QuestionID] if domain == "" { continue } a := perDomain[domain] if a == nil { a = &domainAgg{} perDomain[domain] = a } a.Total++ if qd.IsCorrect { a.Correct++ } } } domains := make([]gin.H, 0, len(domainOrder)) for _, d := range domainOrder { a := perDomain[d] if a == nil { a = &domainAgg{} } mastery := 0.0 if a.Total > 0 { mastery = round1(float64(a.Correct) * 100 / float64(a.Total)) } domains = append(domains, gin.H{ "domain": d, "label": domainLabels[d], "correct": a.Correct, "total": a.Total, "mastery": mastery, }) } // 薄弱点:有作答记录中掌握度最低的域 weakDomain := "" weakMastery := 100.0 for _, d := range domains { if d["total"].(int) > 0 && d["mastery"].(float64) < weakMastery { weakMastery = d["mastery"].(float64) weakDomain = d["domain"].(string) } } web.OK(c, gin.H{ "learning_points": u.LearningPoints, "stats": gin.H{ "company_viewed": companyViewed, "product_viewed": productViewed, "course_viewed": courseViewed, "self_test_count": selfTestCount, "formal_count": len(recs), "formal_passed": formalPassed, "mistake_count": mistakeTotal, "mistake_resolved": mistakeResolved, }, "domains": domains, "weak_domain": weakDomain, "recent_scores": trend, }) }