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
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
|
|
}
|
|
|