package logic import ( "context" "encoding/json" "sort" "time" "healthapi/internal/model" "healthapi/internal/svc" "healthapi/internal/types" "healthapi/pkg/errorx" "github.com/zeromicro/go-zero/core/logx" ) type SubmitAssessmentLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewSubmitAssessmentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SubmitAssessmentLogic { return &SubmitAssessmentLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } type constitutionScore struct { Type string `json:"type"` Name string `json:"name"` Score float64 `json:"score"` Description string `json:"description"` } func (l *SubmitAssessmentLogic) SubmitAssessment(req *types.SubmitAssessmentReq) (resp *types.AssessmentResult, err error) { userID, err := GetUserIDFromCtx(l.ctx) if err != nil { return nil, errorx.ErrUnauthorized } // 获取所有问题 var questions []model.QuestionBank l.svcCtx.DB.Find(&questions) if len(questions) == 0 { return nil, errorx.NewCodeError(errorx.CodeServerError, "问卷题库为空") } // 构建问题ID到体质类型的映射 questionTypeMap := make(map[uint]string) typeQuestionCount := make(map[string]int) for _, q := range questions { questionTypeMap[q.ID] = q.ConstitutionType typeQuestionCount[q.ConstitutionType]++ } // 计算各体质原始分 typeScores := make(map[string]int) for _, answer := range req.Answers { if cType, ok := questionTypeMap[uint(answer.QuestionID)]; ok { typeScores[cType] += answer.Score } } // 计算转化分 allScores := make([]constitutionScore, 0) for cType, rawScore := range typeScores { questionCount := typeQuestionCount[cType] if questionCount == 0 { continue } transformedScore := float64(rawScore-questionCount) / float64(questionCount*4) * 100 if transformedScore < 0 { transformedScore = 0 } if transformedScore > 100 { transformedScore = 100 } allScores = append(allScores, constitutionScore{ Type: cType, Name: model.ConstitutionNames[cType], Score: transformedScore, Description: model.ConstitutionDescriptions[cType], }) } // 按分数排序 sort.Slice(allScores, func(i, j int) bool { return allScores[i].Score > allScores[j].Score }) // 判定主要体质和次要体质 var primary constitutionScore var secondary []constitutionScore pingheScore := float64(0) otherMax := float64(0) for _, score := range allScores { if score.Type == model.ConstitutionPinghe { pingheScore = score.Score } else if score.Score > otherMax { otherMax = score.Score } } if pingheScore >= 60 && otherMax < 30 { for _, score := range allScores { if score.Type == model.ConstitutionPinghe { primary = score break } } } else { for _, score := range allScores { if score.Type == model.ConstitutionPinghe { continue } if primary.Type == "" && score.Score >= 40 { primary = score } else if score.Score >= 30 { secondary = append(secondary, score) } } if primary.Type == "" { for _, score := range allScores { if score.Type != model.ConstitutionPinghe { primary = score break } } } } // 获取调养建议 recommendations := make(map[string]string) if primary.Type != "" { if recs, ok := model.ConstitutionRecommendations[primary.Type]; ok { for k, v := range recs { recommendations[k] = v } } } // 保存评估结果 scoresJSON, _ := json.Marshal(allScores) secondaryTypes := make([]string, len(secondary)) for i, s := range secondary { secondaryTypes[i] = s.Type } secondaryJSON, _ := json.Marshal(secondaryTypes) recsJSON, _ := json.Marshal(recommendations) assessment := model.ConstitutionAssessment{ UserID: userID, AssessedAt: time.Now(), Scores: string(scoresJSON), PrimaryConstitution: primary.Type, SecondaryConstitutions: string(secondaryJSON), Recommendations: string(recsJSON), } if err := l.svcCtx.DB.Create(&assessment).Error; err != nil { return nil, errorx.ErrServerError } // 保存答案记录 answers := make([]model.AssessmentAnswer, len(req.Answers)) for i, a := range req.Answers { answers[i] = model.AssessmentAnswer{ AssessmentID: assessment.ID, QuestionID: uint(a.QuestionID), Score: a.Score, } } l.svcCtx.DB.Create(&answers) // 构建响应 // 转换 allScores 为 map[string]float64 scoresMap := make(map[string]float64) for _, s := range allScores { scoresMap[s.Type] = s.Score } return &types.AssessmentResult{ ID: uint(assessment.ID), AssessedAt: assessment.AssessedAt.Format(time.RFC3339), Scores: scoresMap, PrimaryConstitution: primary.Type, SecondaryConstitutions: secondaryTypes, Recommendations: recommendations, }, nil }