package plant import ( "sundynix-go/global" "sundynix-go/model/commom/response" "sundynix-go/utils/auth" "github.com/gin-gonic/gin" "go.uber.org/zap" ) type AiChatApi struct{} // ChatStreamPlant 接收用户输入,基于植物百科 RAG 产生流式 SSE 回答 // @Summary 植物助手聊天(SSE 流式输出) // @Tags Plant-AiChat // @Param query query string true "用户提问内容" // @Produce text/event-stream // @Router /plant/chat/stream [get] func (a *AiChatApi) ChatStreamPlant(c *gin.Context) { query := c.Query("query") if query == "" { response.FailWithMsg("参数 query 不能为空", c) return } // SSE Headers(微信小程序通过 enableChunked: true 配合实现打字机效果) w := c.Writer header := w.Header() header.Set("Content-Type", "text/event-stream") header.Set("Cache-Control", "no-cache") header.Set("Connection", "keep-alive") header.Set("Transfer-Encoding", "chunked") w.WriteHeader(200) var fullAnswer string err := aiRagService.PlantChatStreamRAG(c.Request.Context(), query, func(chunk string) error { fullAnswer += chunk _, writeErr := w.WriteString("data: " + chunk + "\n\n") w.Flush() return writeErr }) if err != nil { global.Logger.Error("PlantChatStreamRAG error", zap.Error(err)) _, _ = w.WriteString("data: [ERROR]" + err.Error() + "\n\n") w.Flush() } else { // 流结束标志 _, _ = w.WriteString("data: [DONE]\n\n") w.Flush() } // 异步保存问答历史 if fullAnswer != "" { userId := auth.GetUserId(c) go func() { if saveErr := chatHistoryService.SaveHistory(userId, query, fullAnswer); saveErr != nil { global.Logger.Error("Save chat history failed", zap.Error(saveErr)) } }() } } // SyncWikiToQdrant 手动触发全量植物百科同步到 Qdrant // @Summary 同步植物百科数据到 Qdrant 向量库 // @Tags System-SysAiConfig // @Produce json // @Success 200 {object} response.Response // @Router /plant/chat/sync [post] func (a *AiChatApi) SyncWikiToQdrant(c *gin.Context) { if err := aiRagService.SyncWikiToQdrant(); err != nil { global.Logger.Error("SyncWikiToQdrant error", zap.Error(err)) response.FailWithMsg("同步失败: "+err.Error(), c) return } response.OkWithMsg("同步成功", c) } // GetChatHistory 获取AI问答历史列表 // @Tags Plant-AiChat // @Param current query int false "页码" // @Param pageSize query int false "每页条数" // @Router /plant/chat/history [get] func (a *AiChatApi) GetChatHistory(c *gin.Context) { userId := auth.GetUserId(c) current := 1 pageSize := 20 if v, ok := c.GetQuery("current"); ok { if n := parseInt(v); n > 0 { current = n } } if v, ok := c.GetQuery("pageSize"); ok { if n := parseInt(v); n > 0 { pageSize = n } } list, total, err := chatHistoryService.GetHistoryList(userId, current, pageSize) if err != nil { response.FailWithMsg("查询失败", c) return } response.OkWithData(map[string]interface{}{ "list": list, "total": total, }, c) } // DeleteChatHistory 删除单条历史 // @Tags Plant-AiChat // @Router /plant/chat/history/delete [post] func (a *AiChatApi) DeleteChatHistory(c *gin.Context) { var req struct { Id string `json:"id"` } if err := c.ShouldBindJSON(&req); err != nil || req.Id == "" { response.FailWithMsg("参数错误", c) return } userId := auth.GetUserId(c) if err := chatHistoryService.DeleteHistory(userId, req.Id); err != nil { response.FailWithMsg("删除失败", c) return } response.OkWithMsg("删除成功", c) } // ClearChatHistory 清空所有历史 // @Tags Plant-AiChat // @Router /plant/chat/history/clear [post] func (a *AiChatApi) ClearChatHistory(c *gin.Context) { userId := auth.GetUserId(c) if err := chatHistoryService.ClearHistory(userId); err != nil { response.FailWithMsg("清空失败", c) return } response.OkWithMsg("已清空", c) } func parseInt(s string) int { n := 0 for _, c := range s { if c >= '0' && c <= '9' { n = n*10 + int(c-'0') } } return n }