package user import ( "io" "strconv" "time" "github.com/flipped-aurora/gin-vue-admin/server/global" "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" petRequest "github.com/flipped-aurora/gin-vue-admin/server/model/pet/request" petResponse "github.com/flipped-aurora/gin-vue-admin/server/model/pet/response" "github.com/flipped-aurora/gin-vue-admin/server/service" "github.com/flipped-aurora/gin-vue-admin/server/utils" "github.com/gin-gonic/gin" "go.uber.org/zap" ) type PetAssistantApi struct{} var petChatService = service.ServiceGroupApp.PetServiceGroup.PetChatService // StreamAskPetAssistant 向宠物助手流式提问 // @Tags PetAssistant // @Summary 向宠物助手流式提问接口 // @Security ApiKeyAuth // @Accept application/json // @Produce text/event-stream // @Param data body petRequest.ChatRequest true "宠物助手流式提问请求" // @Success 200 {string} string "流式响应" // @Router /api/v1/pet/user/assistant/stream-ask [post] func (p *PetAssistantApi) StreamAskPetAssistant(ctx *gin.Context) { // 创建业务用Context businessCtx := ctx.Request.Context() // 获取用户ID userId := utils.GetAppUserID(ctx) if userId == 0 { global.GVA_LOG.Error("获取用户ID失败") response.FailWithMessage("用户认证失败", ctx) return } // 绑定请求参数 var req petRequest.ChatRequest if err := ctx.ShouldBindJSON(&req); err != nil { global.GVA_LOG.Error("参数绑定失败", zap.Error(err)) response.FailWithMessage("参数错误: "+err.Error(), ctx) return } // 参数验证 if req.Message == "" { response.FailWithMessage("消息内容不能为空", ctx) return } // 强制设置为流式响应 req.Stream = true // 设置默认参数 if req.Temperature <= 0 { req.Temperature = 0.7 } if req.MaxTokens <= 0 { req.MaxTokens = 1000 } // 设置SSE响应头 ctx.Header("Content-Type", "text/event-stream") ctx.Header("Cache-Control", "no-cache") ctx.Header("Connection", "keep-alive") ctx.Header("Access-Control-Allow-Origin", "*") ctx.Header("Access-Control-Allow-Headers", "Cache-Control") // 创建事件通道 eventChan := make(chan petResponse.StreamEvent, 100) defer close(eventChan) // 启动流式聊天 go func() { if err := petChatService.StreamChat(businessCtx, userId, req, eventChan); err != nil { global.GVA_LOG.Error("流式聊天失败", zap.Error(err), zap.Uint("userId", userId)) eventChan <- petResponse.StreamEvent{ Event: "error", Data: map[string]interface{}{ "error": "流式聊天失败: " + err.Error(), }, } } }() // 发送流式数据 ctx.Stream(func(w io.Writer) bool { select { case event, ok := <-eventChan: if !ok { return false } switch event.Event { case "message": // 发送消息事件 ctx.SSEvent("message", event.Data) case "error": // 发送错误事件 ctx.SSEvent("error", event.Data) return false case "done": // 发送完成事件 ctx.SSEvent("done", event.Data) return false } return true case <-time.After(30 * time.Second): // 超时处理 ctx.SSEvent("error", map[string]interface{}{ "error": "流式响应超时", }) return false } }) } // GetAssistantHistory 获取宠物助手对话历史 // @Tags PetAssistant // @Summary 获取宠物助手对话历史记录(简化版本,只返回必要字段) // @Security ApiKeyAuth // @Accept application/json // @Produce application/json // @Param sessionId query string false "会话ID" // @Param page query int false "页码" default(1) // @Param pageSize query int false "每页数量" default(20) // @Success 200 {object} response.Response{data=[]petResponse.ChatHistoryItem,msg=string} "获取成功" // @Router /api/v1/pet/user/assistant/history [get] func (p *PetAssistantApi) GetAssistantHistory(ctx *gin.Context) { // 创建业务用Context businessCtx := ctx.Request.Context() // 获取用户ID userId := utils.GetAppUserID(ctx) if userId == 0 { global.GVA_LOG.Error("获取用户ID失败") response.FailWithMessage("用户认证失败", ctx) return } // 获取查询参数 sessionId := ctx.Query("sessionId") pageStr := ctx.DefaultQuery("page", "1") pageSizeStr := ctx.DefaultQuery("pageSize", "20") page, err := strconv.Atoi(pageStr) if err != nil || page < 1 { page = 1 } pageSize, err := strconv.Atoi(pageSizeStr) if err != nil || pageSize < 1 || pageSize > 100 { pageSize = 20 } // 计算limit limit := pageSize // 调用服务层 conversations, err := petChatService.GetChatHistory(businessCtx, userId, sessionId, limit) if err != nil { global.GVA_LOG.Error("获取聊天历史失败", zap.Error(err), zap.Uint("userId", userId)) response.FailWithMessage("获取聊天历史失败: "+err.Error(), ctx) return } response.OkWithDetailed(conversations, "获取成功", ctx) } // ClearAssistantHistory 清空宠物助手对话历史 // @Tags PetAssistant // @Summary 清空宠物助手对话历史记录 // @Security ApiKeyAuth // @Accept application/json // @Produce application/json // @Param sessionId query string false "会话ID,不传则清空所有会话" // @Success 200 {object} response.Response{msg=string} "清空成功" // @Router /api/v1/pet/user/assistant/clear-history [delete] func (p *PetAssistantApi) ClearAssistantHistory(ctx *gin.Context) { // 创建业务用Context businessCtx := ctx.Request.Context() // 获取用户ID userId := utils.GetAppUserID(ctx) if userId == 0 { global.GVA_LOG.Error("获取用户ID失败") response.FailWithMessage("用户认证失败", ctx) return } // 获取会话ID(可选) sessionId := ctx.Query("sessionId") // 调用服务层 err := petChatService.ClearChatHistory(businessCtx, userId, sessionId) if err != nil { global.GVA_LOG.Error("清空聊天历史失败", zap.Error(err), zap.Uint("userId", userId)) response.FailWithMessage("清空聊天历史失败: "+err.Error(), ctx) return } if sessionId != "" { response.OkWithMessage("指定会话历史清空成功", ctx) } else { response.OkWithMessage("所有聊天历史清空成功", ctx) } } // GetAssistantSessions 获取宠物助手会话列表 // @Tags PetAssistant // @Summary 获取用户的宠物助手会话列表 // @Security ApiKeyAuth // @Accept application/json // @Produce application/json // @Success 200 {object} response.Response{data=[]map[string]interface{},msg=string} "获取成功" // @Router /api/v1/pet/user/assistant/sessions [get] func (p *PetAssistantApi) GetAssistantSessions(ctx *gin.Context) { // 创建业务用Context businessCtx := ctx.Request.Context() // 获取用户ID userId := utils.GetAppUserID(ctx) if userId == 0 { global.GVA_LOG.Error("获取用户ID失败") response.FailWithMessage("用户认证失败", ctx) return } // 调用服务层 sessions, err := petChatService.GetChatSessions(businessCtx, userId) if err != nil { global.GVA_LOG.Error("获取会话列表失败", zap.Error(err), zap.Uint("userId", userId)) response.FailWithMessage("获取会话列表失败: "+err.Error(), ctx) return } response.OkWithDetailed(sessions, "获取成功", ctx) }