You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
4.8 KiB
194 lines
4.8 KiB
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
|
|
}
|
|
|