healthapp
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.
 
 
 
 
 
 

138 lines
4.1 KiB

package logic
import (
"context"
"healthapi/internal/model"
"healthapi/internal/svc"
"healthapi/internal/types"
"healthapi/pkg/ai"
"healthapi/pkg/errorx"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type SendMessageLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewSendMessageLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendMessageLogic {
return &SendMessageLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *SendMessageLogic) SendMessage(req *types.SendMessageReq) (resp *types.MessageResp, err error) {
userID, err := GetUserIDFromCtx(l.ctx)
if err != nil {
return nil, errorx.ErrUnauthorized
}
// 验证对话属于该用户
var conversation model.Conversation
if err := l.svcCtx.DB.Where("id = ? AND user_id = ?", req.Id, userID).First(&conversation).Error; err != nil {
return nil, errorx.ErrNotFound
}
// 保存用户消息
userMessage := model.Message{
ConversationID: conversation.ID,
Role: model.RoleUser,
Content: req.Content,
}
if err := l.svcCtx.DB.Create(&userMessage).Error; err != nil {
return nil, errorx.ErrServerError
}
// 获取历史消息构建上下文
var historyMessages []model.Message
l.svcCtx.DB.Where("conversation_id = ?", conversation.ID).
Order("created_at DESC").
Limit(l.svcCtx.Config.AI.MaxHistoryMessages).
Find(&historyMessages)
// 获取用户健康档案构建系统提示
systemPrompt := buildSystemPrompt(l.svcCtx.DB, userID)
// 构建 AI 消息
aiMessages := []ai.Message{{Role: "system", Content: systemPrompt}}
for i := len(historyMessages) - 1; i >= 0; i-- {
aiMessages = append(aiMessages, ai.Message{
Role: historyMessages[i].Role,
Content: historyMessages[i].Content,
})
}
// 调用 AI 服务
aiResponse, err := l.svcCtx.AIClient.Chat(l.ctx, aiMessages)
if err != nil {
l.Logger.Errorf("AI service error: %v", err)
aiResponse = "抱歉,AI 服务暂时不可用,请稍后再试。"
}
// 保存 AI 回复
assistantMessage := model.Message{
ConversationID: conversation.ID,
Role: model.RoleAssistant,
Content: aiResponse,
}
if err := l.svcCtx.DB.Create(&assistantMessage).Error; err != nil {
return nil, errorx.ErrServerError
}
// 更新对话标题
if conversation.Title == "新对话" {
title := req.Content
if len(title) > 50 {
title = title[:50] + "..."
}
l.svcCtx.DB.Model(&conversation).Update("title", title)
}
return &types.MessageResp{
UserMessage: types.Message{
ID: uint(userMessage.ID),
ConversationID: userMessage.ConversationID,
Role: userMessage.Role,
Content: userMessage.Content,
CreatedAt: userMessage.CreatedAt.Format("2006-01-02 15:04:05"),
},
AssistantMessage: types.Message{
ID: uint(assistantMessage.ID),
ConversationID: assistantMessage.ConversationID,
Role: assistantMessage.Role,
Content: assistantMessage.Content,
CreatedAt: assistantMessage.CreatedAt.Format("2006-01-02 15:04:05"),
},
}, nil
}
// buildSystemPrompt 构建系统提示(包含用户健康档案)
func buildSystemPrompt(db *gorm.DB, userID uint) string {
basePrompt := `你是一位专业的中医健康顾问,名叫"健康小助手"。你的职责是:
1. 根据用户的健康档案和体质特点,提供个性化的健康建议
2. 解答用户关于中医养生、体质调理的问题
3. 推荐适合用户体质的饮食、运动和生活方式
4. 在必要时提醒用户寻求专业医疗帮助
请注意:
- 你的建议仅供参考,不能替代专业医疗诊断
- 回答要简洁明了,通俗易懂
- 适当使用中医术语,但要给出解释
- 保持友好、专业的语气`
// 获取用户体质信息
var assessment model.ConstitutionAssessment
if err := db.Where("user_id = ?", userID).Order("assessed_at DESC").First(&assessment).Error; err == nil {
if assessment.PrimaryConstitution != "" {
basePrompt += "\n\n用户体质类型: " + model.ConstitutionNames[assessment.PrimaryConstitution]
}
}
return basePrompt
}