package handler import ( "encoding/json" "errors" "net/http" "time" "github.com/enterprise-ai-platform/server/internal/middleware" "github.com/enterprise-ai-platform/server/internal/response" "github.com/enterprise-ai-platform/server/internal/service/research" "github.com/go-chi/chi/v5" ) // ResearchHandler 深度研究任务的 HTTP 入口(薄层:仅解析参数与组织响应)。 type ResearchHandler struct { svc *research.Service } func NewResearchHandler(svc *research.Service) *ResearchHandler { return &ResearchHandler{svc: svc} } type createResearchRequest struct { Topic string `json:"topic"` AppID string `json:"app_id,omitempty"` Config map[string]any `json:"config"` } type researchTaskResponse struct { TaskID string `json:"task_id"` Topic string `json:"topic"` Status string `json:"status"` Progress int `json:"progress"` StatusMessage *string `json:"status_message,omitempty"` ErrorMessage *string `json:"error_message,omitempty"` Report *string `json:"report,omitempty"` Sources json.RawMessage `json:"sources,omitempty"` TokensUsed int `json:"tokens_used"` CreatedAt string `json:"created_at"` } func toResearchResponse(t *research.Task) researchTaskResponse { resp := researchTaskResponse{ TaskID: t.ID, Topic: t.Topic, Status: t.Status, Progress: t.Progress, StatusMessage: t.StatusMessage, ErrorMessage: t.ErrorMessage, Report: t.Report, TokensUsed: t.TokensUsed, CreatedAt: t.CreatedAt.Format(time.RFC3339), } if len(t.Sources) > 0 { resp.Sources = json.RawMessage(t.Sources) } return resp } // CreateTask 创建深度研究任务。 func (h *ResearchHandler) CreateTask(w http.ResponseWriter, r *http.Request) { userID := middleware.GetUserID(r.Context()) var req createResearchRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { response.BadRequest(w, "无效的请求格式") return } in := research.CreateInput{UserID: userID.String(), Topic: req.Topic, Config: req.Config} if req.AppID != "" { in.AppID = &req.AppID } id, err := h.svc.Create(r.Context(), in) if err != nil { if errors.Is(err, research.ErrEmptyTopic) { response.BadRequest(w, "研究题目不能为空") return } response.InternalError(w, "创建任务失败") return } response.JSON(w, http.StatusCreated, map[string]string{"task_id": id, "status": "pending"}) } // GetTaskStatus 查询任务状态/结果。 func (h *ResearchHandler) GetTaskStatus(w http.ResponseWriter, r *http.Request) { userID := middleware.GetUserID(r.Context()) taskID := chi.URLParam(r, "taskId") t, err := h.svc.Status(r.Context(), userID.String(), taskID) if err != nil { response.NotFound(w, "任务不存在") return } response.JSON(w, http.StatusOK, toResearchResponse(t)) } // ListTasks 列出当前用户的研究任务。 func (h *ResearchHandler) ListTasks(w http.ResponseWriter, r *http.Request) { userID := middleware.GetUserID(r.Context()) tasks, err := h.svc.List(r.Context(), userID.String()) if err != nil { response.InternalError(w, "查询失败") return } out := make([]researchTaskResponse, 0, len(tasks)) for i := range tasks { out = append(out, toResearchResponse(&tasks[i])) } response.JSON(w, http.StatusOK, out) } // CancelTask 取消进行中的研究任务。 func (h *ResearchHandler) CancelTask(w http.ResponseWriter, r *http.Request) { userID := middleware.GetUserID(r.Context()) taskID := chi.URLParam(r, "taskId") if err := h.svc.Cancel(r.Context(), userID.String(), taskID); err != nil { if errors.Is(err, research.ErrNotFound) { response.NotFound(w, "任务不存在或无法取消") return } response.InternalError(w, "取消失败") return } response.JSON(w, http.StatusOK, map[string]string{"message": "已取消"}) }