| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- package handler
- import (
- "net/http"
- "strings"
- "dbview/service/internal/common/response"
- "dbview/service/internal/modules/mcp_ai/api"
- "dbview/service/internal/modules/mcp_ai/service"
- "github.com/gin-gonic/gin"
- )
- // Handler 提供 AI 聊天接口的 HTTP 处理器。
- type Handler struct {
- svc *service.AIService
- }
- // NewHandler 创建 Handler。
- func NewHandler(svc *service.AIService) *Handler {
- return &Handler{svc: svc}
- }
- // Chat 提供 POST /ai/chat 接口。
- // 支持三种模式:
- // - sync(默认):同步返回完整文本。
- // - stream:SSE 流式返回(同一路径,header 设为 text/event-stream)。
- // - async:创建后台任务立即返回 task_id/request_id。
- func (h *Handler) Chat(c *gin.Context) {
- var req api.ChatRequest
- if err := c.ShouldBindJSON(&req); err != nil {
- c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
- return
- }
- mode := strings.ToLower(strings.TrimSpace(req.Mode))
- if mode == "" {
- mode = "sync"
- }
- ctx := c.Request.Context()
- switch mode {
- case "async":
- taskObj, requestID, err := h.svc.StartChatTask(ctx, req.SessionID, req.Prompt, req.System, req.Model, req.ConfigID, req.RequestID, req.TimeoutMs)
- if err != nil {
- c.JSON(http.StatusBadRequest, response.Response{Code: 2, Msg: "创建任务失败", Error: err.Error()})
- return
- }
- c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.ChatResponse{Mode: "async", TaskID: taskObj.ID, RequestID: requestID}})
- return
- case "stream":
- c.Writer.Header().Set("Content-Type", "text/event-stream")
- c.Writer.Header().Set("Cache-Control", "no-cache")
- c.Writer.Header().Set("Connection", "keep-alive")
- flusher, ok := c.Writer.(http.Flusher)
- if !ok {
- c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "AI 调用失败", Error: "stream not supported"})
- return
- }
- resultCh := make(chan struct {
- text string
- err error
- }, 1)
- go func() {
- text, err := h.svc.Chat(ctx, req.SessionID, req.Prompt, req.System, req.Model, req.ConfigID, req.RequestID, req.TimeoutMs)
- resultCh <- struct {
- text string
- err error
- }{text: text, err: err}
- }()
- c.SSEvent("start", gin.H{"request_id": req.RequestID})
- flusher.Flush()
- select {
- case <-ctx.Done():
- c.SSEvent("error", gin.H{"error": ctx.Err().Error()})
- flusher.Flush()
- return
- case res := <-resultCh:
- if res.err != nil {
- c.SSEvent("error", gin.H{"error": res.err.Error()})
- flusher.Flush()
- return
- }
- c.SSEvent("message", gin.H{"text": res.text})
- flusher.Flush()
- c.SSEvent("done", gin.H{"request_id": req.RequestID})
- flusher.Flush()
- }
- return
- default:
- text, err := h.svc.Chat(ctx, req.SessionID, req.Prompt, req.System, req.Model, req.ConfigID, req.RequestID, req.TimeoutMs)
- if err != nil {
- c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "AI 调用失败", Error: err.Error()})
- return
- }
- c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.ChatResponse{Mode: "sync", Text: text, RequestID: req.RequestID}})
- }
- }
- // CancelChat 提供 POST /ai/chat/cancel 接口,取消指定聊天请求。
- func (h *Handler) CancelChat(c *gin.Context) {
- var req api.CancelChatRequest
- if err := c.ShouldBindJSON(&req); err != nil {
- c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
- return
- }
- if req.RequestID == "" && req.SessionID == "" {
- c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: "request_id 或 session_id 必填其一"})
- return
- }
- if err := h.svc.CancelChat(req.RequestID, req.SessionID); err != nil {
- c.JSON(http.StatusBadRequest, response.Response{Code: 2, Msg: "取消失败", Error: err.Error()})
- return
- }
- c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok"})
- }
- // History 提供 POST /ai/history 接口,查询指定 session 的消息历史,按时间正序返回。
- func (h *Handler) History(c *gin.Context) {
- var req api.HistoryRequest
- if err := c.ShouldBindJSON(&req); err != nil {
- c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
- return
- }
- msgs, err := h.svc.ListHistory(c.Request.Context(), req.SessionID, req.Limit, req.Offset)
- if err != nil {
- c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "获取历史失败", Error: err.Error()})
- return
- }
- c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.HistoryResponse{Messages: msgs}})
- }
- // DeleteHistory 提供 POST /ai/history/delete 接口,删除指定 session 的全部历史。
- func (h *Handler) DeleteHistory(c *gin.Context) {
- var req api.DeleteHistoryRequest
- if err := c.ShouldBindJSON(&req); err != nil {
- c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
- return
- }
- if err := h.svc.DeleteHistory(c.Request.Context(), req.SessionID); err != nil {
- c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "删除历史失败", Error: err.Error()})
- return
- }
- c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok"})
- }
- // GetConfig 提供 POST /ai/config/get 接口,返回当前 AI 配置。
- func (h *Handler) GetConfig(c *gin.Context) {
- cfg, err := h.svc.GetConfig(c.Request.Context())
- if err != nil {
- c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "获取 AI 配置失败", Error: err.Error()})
- return
- }
- c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.ConfigResponse{AISettings: *cfg}})
- }
- // ListConfigs 提供 POST /ai/config/list 接口,返回全部 AI 配置记录。
- func (h *Handler) ListConfigs(c *gin.Context) {
- items, err := h.svc.ListConfigs(c.Request.Context())
- if err != nil {
- c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "获取 AI 配置列表失败", Error: err.Error()})
- return
- }
- c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.ConfigListResponse{Items: items}})
- }
- // UpdateConfig 提供 POST /ai/config/update 接口,允许用户覆盖 AI 配置。
- func (h *Handler) UpdateConfig(c *gin.Context) {
- var req api.UpdateConfigRequest
- if err := c.ShouldBindJSON(&req); err != nil {
- c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
- return
- }
- updated, err := h.svc.UpdateConfig(c.Request.Context(), req.AISettings)
- if err != nil {
- c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "更新 AI 配置失败", Error: err.Error()})
- return
- }
- c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.ConfigResponse{AISettings: *updated}})
- }
- // CreateConfig 提供 POST /ai/config/create 接口,创建/覆盖 AI 配置。
- func (h *Handler) CreateConfig(c *gin.Context) {
- var req api.CreateConfigRequest
- if err := c.ShouldBindJSON(&req); err != nil {
- c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
- return
- }
- created, err := h.svc.CreateConfig(c.Request.Context(), req.AISettings)
- if err != nil {
- c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "创建 AI 配置失败", Error: err.Error()})
- return
- }
- c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok", Data: api.ConfigResponse{AISettings: *created}})
- }
- // DeleteConfig 提供 POST /ai/config/delete 接口,删除指定 AI 配置(默认 default)。
- func (h *Handler) DeleteConfig(c *gin.Context) {
- var req api.DeleteConfigRequest
- if err := c.ShouldBindJSON(&req); err != nil {
- c.JSON(http.StatusBadRequest, response.Response{Code: 1, Msg: "参数错误", Error: err.Error()})
- return
- }
- if err := h.svc.DeleteConfig(c.Request.Context(), req.ID); err != nil {
- c.JSON(http.StatusInternalServerError, response.Response{Code: 2, Msg: "删除 AI 配置失败", Error: err.Error()})
- return
- }
- c.JSON(http.StatusOK, response.Response{Code: 0, Msg: "ok"})
- }
|