handler.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. package handler
  2. import (
  3. "net/http"
  4. "strings"
  5. "dbview/service/internal/common/response"
  6. "dbview/service/internal/modules/mcp_ai/api"
  7. "dbview/service/internal/modules/mcp_ai/service"
  8. "github.com/gin-gonic/gin"
  9. )
  10. // Handler 提供 AI 聊天接口的 HTTP 处理器。
  11. type Handler struct {
  12. svc *service.AIService
  13. }
  14. // NewHandler 创建 Handler。
  15. func NewHandler(svc *service.AIService) *Handler {
  16. return &Handler{svc: svc}
  17. }
  18. // Chat 提供 POST /ai/chat 接口。
  19. // 支持三种模式:
  20. // - sync(默认):同步返回完整文本。
  21. // - stream:SSE 流式返回(同一路径,header 设为 text/event-stream)。
  22. // - async:创建后台任务立即返回 task_id/request_id。
  23. func (h *Handler) Chat(c *gin.Context) {
  24. var req api.ChatRequest
  25. if err := c.ShouldBindJSON(&req); err != nil {
  26. c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
  27. return
  28. }
  29. mode := strings.ToLower(strings.TrimSpace(req.Mode))
  30. if mode == "" {
  31. mode = "sync"
  32. }
  33. ctx := c.Request.Context()
  34. switch mode {
  35. case "async":
  36. taskObj, requestID, err := h.svc.StartChatTask(ctx, req.SessionID, req.Prompt, req.System, req.Model, req.ConfigID, req.RequestID, req.TimeoutMs)
  37. if err != nil {
  38. c.JSON(http.StatusBadRequest, response.Response{Code: 2, Msg: "创建任务失败", Error: err.Error()})
  39. return
  40. }
  41. c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.ChatResponse{Mode: "async", TaskID: taskObj.ID, RequestID: requestID}})
  42. return
  43. case "stream":
  44. c.Writer.Header().Set("Content-Type", "text/event-stream")
  45. c.Writer.Header().Set("Cache-Control", "no-cache")
  46. c.Writer.Header().Set("Connection", "keep-alive")
  47. flusher, ok := c.Writer.(http.Flusher)
  48. if !ok {
  49. c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "AI 调用失败", Error: "stream not supported"})
  50. return
  51. }
  52. resultCh := make(chan struct {
  53. text string
  54. err error
  55. }, 1)
  56. go func() {
  57. text, err := h.svc.Chat(ctx, req.SessionID, req.Prompt, req.System, req.Model, req.ConfigID, req.RequestID, req.TimeoutMs)
  58. resultCh <- struct {
  59. text string
  60. err error
  61. }{text: text, err: err}
  62. }()
  63. c.SSEvent("start", gin.H{"request_id": req.RequestID})
  64. flusher.Flush()
  65. select {
  66. case <-ctx.Done():
  67. c.SSEvent("error", gin.H{"error": ctx.Err().Error()})
  68. flusher.Flush()
  69. return
  70. case res := <-resultCh:
  71. if res.err != nil {
  72. c.SSEvent("error", gin.H{"error": res.err.Error()})
  73. flusher.Flush()
  74. return
  75. }
  76. c.SSEvent("message", gin.H{"text": res.text})
  77. flusher.Flush()
  78. c.SSEvent("done", gin.H{"request_id": req.RequestID})
  79. flusher.Flush()
  80. }
  81. return
  82. default:
  83. text, err := h.svc.Chat(ctx, req.SessionID, req.Prompt, req.System, req.Model, req.ConfigID, req.RequestID, req.TimeoutMs)
  84. if err != nil {
  85. c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "AI 调用失败", Error: err.Error()})
  86. return
  87. }
  88. c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.ChatResponse{Mode: "sync", Text: text, RequestID: req.RequestID}})
  89. }
  90. }
  91. // CancelChat 提供 POST /ai/chat/cancel 接口,取消指定聊天请求。
  92. func (h *Handler) CancelChat(c *gin.Context) {
  93. var req api.CancelChatRequest
  94. if err := c.ShouldBindJSON(&req); err != nil {
  95. c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
  96. return
  97. }
  98. if req.RequestID == "" && req.SessionID == "" {
  99. c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: "request_id 或 session_id 必填其一"})
  100. return
  101. }
  102. if err := h.svc.CancelChat(req.RequestID, req.SessionID); err != nil {
  103. c.JSON(http.StatusBadRequest, response.Response{Code: 2, Msg: "取消失败", Error: err.Error()})
  104. return
  105. }
  106. c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok"})
  107. }
  108. // History 提供 POST /ai/history 接口,查询指定 session 的消息历史,按时间正序返回。
  109. func (h *Handler) History(c *gin.Context) {
  110. var req api.HistoryRequest
  111. if err := c.ShouldBindJSON(&req); err != nil {
  112. c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
  113. return
  114. }
  115. msgs, err := h.svc.ListHistory(c.Request.Context(), req.SessionID, req.Limit, req.Offset)
  116. if err != nil {
  117. c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "获取历史失败", Error: err.Error()})
  118. return
  119. }
  120. c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.HistoryResponse{Messages: msgs}})
  121. }
  122. // DeleteHistory 提供 POST /ai/history/delete 接口,删除指定 session 的全部历史。
  123. func (h *Handler) DeleteHistory(c *gin.Context) {
  124. var req api.DeleteHistoryRequest
  125. if err := c.ShouldBindJSON(&req); err != nil {
  126. c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
  127. return
  128. }
  129. if err := h.svc.DeleteHistory(c.Request.Context(), req.SessionID); err != nil {
  130. c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "删除历史失败", Error: err.Error()})
  131. return
  132. }
  133. c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok"})
  134. }
  135. // GetConfig 提供 POST /ai/config/get 接口,返回当前 AI 配置。
  136. func (h *Handler) GetConfig(c *gin.Context) {
  137. cfg, err := h.svc.GetConfig(c.Request.Context())
  138. if err != nil {
  139. c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "获取 AI 配置失败", Error: err.Error()})
  140. return
  141. }
  142. c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.ConfigResponse{AISettings: *cfg}})
  143. }
  144. // ListConfigs 提供 POST /ai/config/list 接口,返回全部 AI 配置记录。
  145. func (h *Handler) ListConfigs(c *gin.Context) {
  146. items, err := h.svc.ListConfigs(c.Request.Context())
  147. if err != nil {
  148. c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "获取 AI 配置列表失败", Error: err.Error()})
  149. return
  150. }
  151. c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.ConfigListResponse{Items: items}})
  152. }
  153. // UpdateConfig 提供 POST /ai/config/update 接口,允许用户覆盖 AI 配置。
  154. func (h *Handler) UpdateConfig(c *gin.Context) {
  155. var req api.UpdateConfigRequest
  156. if err := c.ShouldBindJSON(&req); err != nil {
  157. c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
  158. return
  159. }
  160. updated, err := h.svc.UpdateConfig(c.Request.Context(), req.AISettings)
  161. if err != nil {
  162. c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "更新 AI 配置失败", Error: err.Error()})
  163. return
  164. }
  165. c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.ConfigResponse{AISettings: *updated}})
  166. }
  167. // CreateConfig 提供 POST /ai/config/create 接口,创建/覆盖 AI 配置。
  168. func (h *Handler) CreateConfig(c *gin.Context) {
  169. var req api.CreateConfigRequest
  170. if err := c.ShouldBindJSON(&req); err != nil {
  171. c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
  172. return
  173. }
  174. created, err := h.svc.CreateConfig(c.Request.Context(), req.AISettings)
  175. if err != nil {
  176. c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "创建 AI 配置失败", Error: err.Error()})
  177. return
  178. }
  179. c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.ConfigResponse{AISettings: *created}})
  180. }
  181. // DeleteConfig 提供 POST /ai/config/delete 接口,删除指定 AI 配置(默认 default)。
  182. func (h *Handler) DeleteConfig(c *gin.Context) {
  183. var req api.DeleteConfigRequest
  184. if err := c.ShouldBindJSON(&req); err != nil {
  185. c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
  186. return
  187. }
  188. if err := h.svc.DeleteConfig(c.Request.Context(), req.ID); err != nil {
  189. c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "删除 AI 配置失败", Error: err.Error()})
  190. return
  191. }
  192. c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok"})
  193. }