package httpapi import ( "net/http" "strings" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" "github.com/google/uuid" ) // listAdminTasks godoc // @Summary 管理员列出任务 // @Description 跨租户分页查询任务;请求、结果和执行快照中的常见敏感字段默认脱敏。 // @Tags admin-tasks // @Produce json // @Security BearerAuth // @Param q query string false "关键词,匹配任务、用户、租户、模型、平台和 API Key" // @Param tenantId query string false "网关租户 ID" // @Param userId query string false "网关用户 ID" // @Param userGroupId query string false "用户组 ID" // @Param status query string false "任务状态" // @Param platformId query string false "执行平台 ID" // @Param model query string false "调用或实际模型名称" // @Param modelType query string false "模型类型" // @Param runMode query string false "运行模式" // @Param billingStatus query string false "计费状态" // @Param apiKey query string false "API Key ID、名称或前缀" // @Param createdFrom query string false "创建时间起点,支持 RFC3339 或日期格式" // @Param createdTo query string false "创建时间终点,支持 RFC3339 或日期格式" // @Param page query int false "页码" default(1) // @Param pageSize query int false "每页数量" default(50) // @Success 200 {object} AdminTaskListResponse // @Failure 400 {object} ErrorEnvelope // @Failure 401 {object} ErrorEnvelope // @Failure 403 {object} ErrorEnvelope // @Failure 500 {object} ErrorEnvelope // @Router /api/admin/tasks [get] func (s *Server) listAdminTasks(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() page, err := positiveQueryInt(query.Get("page"), 1) if err != nil { writeError(w, http.StatusBadRequest, "invalid page") return } pageSize, err := positiveQueryInt(query.Get("pageSize"), 50) if err != nil { writeError(w, http.StatusBadRequest, "invalid pageSize") return } createdFrom, err := parseTaskListTime(query.Get("createdFrom"), query.Get("from")) if err != nil { writeError(w, http.StatusBadRequest, "invalid createdFrom") return } createdTo, err := parseTaskListTime(query.Get("createdTo"), query.Get("to")) if err != nil { writeError(w, http.StatusBadRequest, "invalid createdTo") return } for name, value := range map[string]string{ "tenantId": query.Get("tenantId"), "userId": query.Get("userId"), "userGroupId": query.Get("userGroupId"), "platformId": query.Get("platformId"), } { if value != "" && !validUUID(value) { writeError(w, http.StatusBadRequest, "invalid "+name) return } } result, err := s.store.ListAdminTasks(r.Context(), store.AdminTaskListFilter{ Query: firstNonEmpty(query.Get("q"), query.Get("query")), GatewayTenant: query.Get("tenantId"), GatewayUser: query.Get("userId"), UserGroup: query.Get("userGroupId"), Status: query.Get("status"), Platform: query.Get("platformId"), Model: query.Get("model"), ModelType: query.Get("modelType"), RunMode: query.Get("runMode"), BillingStatus: query.Get("billingStatus"), APIKey: query.Get("apiKey"), CreatedFrom: createdFrom, CreatedTo: createdTo, Page: page, PageSize: pageSize, }) if err != nil { s.logger.Error("list admin tasks failed", "error", err) writeError(w, http.StatusInternalServerError, "list admin tasks failed") return } for index := range result.Items { result.Items[index] = store.MaskAdminGatewayTask(result.Items[index]) } writeJSON(w, http.StatusOK, map[string]any{ "items": result.Items, "total": result.Total, "page": result.Page, "pageSize": result.PageSize, }) } // getAdminTask godoc // @Summary 管理员获取任务详情 // @Description 返回任意租户的任务详情;默认脱敏,sensitive=full 时返回完整存储内容。 // @Tags admin-tasks // @Produce json // @Security BearerAuth // @Param taskID path string true "任务 ID" // @Param sensitive query string false "敏感字段模式" Enums(masked,full) default(masked) // @Success 200 {object} store.AdminGatewayTask // @Failure 400 {object} ErrorEnvelope // @Failure 401 {object} ErrorEnvelope // @Failure 403 {object} ErrorEnvelope // @Failure 404 {object} ErrorEnvelope // @Failure 500 {object} ErrorEnvelope // @Router /api/admin/tasks/{taskID} [get] func (s *Server) getAdminTask(w http.ResponseWriter, r *http.Request) { taskID := r.PathValue("taskID") if !validUUID(taskID) { writeError(w, http.StatusBadRequest, "invalid taskID") return } full, ok := parseAdminTaskSensitiveMode(r.URL.Query().Get("sensitive")) if !ok { writeError(w, http.StatusBadRequest, "invalid sensitive mode") return } task, err := s.store.GetAdminTask(r.Context(), taskID) if err != nil { if store.IsNotFound(err) { writeError(w, http.StatusNotFound, "task not found") return } s.logger.Error("get admin task failed", "taskID", taskID, "error", err) writeError(w, http.StatusInternalServerError, "get admin task failed") return } task.GatewayTask, err = s.hydrateTaskResult(r.Context(), task.GatewayTask) if err != nil { writeStoredBinaryResultError(w, err) return } if !full { task = store.MaskAdminGatewayTask(task) } writeJSON(w, http.StatusOK, task) } // adminTaskParamPreprocessing godoc // @Summary 管理员获取任务参数预处理日志 // @Description 返回任意租户任务的参数改写、校验或模板处理日志;默认脱敏。 // @Tags admin-tasks // @Produce json // @Security BearerAuth // @Param taskID path string true "任务 ID" // @Param sensitive query string false "敏感字段模式" Enums(masked,full) default(masked) // @Success 200 {object} TaskParamPreprocessingLogListResponse // @Failure 400 {object} ErrorEnvelope // @Failure 401 {object} ErrorEnvelope // @Failure 403 {object} ErrorEnvelope // @Failure 404 {object} ErrorEnvelope // @Failure 500 {object} ErrorEnvelope // @Router /api/admin/tasks/{taskID}/param-preprocessing [get] func (s *Server) adminTaskParamPreprocessing(w http.ResponseWriter, r *http.Request) { taskID := r.PathValue("taskID") if !validUUID(taskID) { writeError(w, http.StatusBadRequest, "invalid taskID") return } full, ok := parseAdminTaskSensitiveMode(r.URL.Query().Get("sensitive")) if !ok { writeError(w, http.StatusBadRequest, "invalid sensitive mode") return } if _, err := s.store.GetAdminTask(r.Context(), taskID); err != nil { if store.IsNotFound(err) { writeError(w, http.StatusNotFound, "task not found") return } s.logger.Error("get admin task failed", "taskID", taskID, "error", err) writeError(w, http.StatusInternalServerError, "get admin task failed") return } items, err := s.store.ListTaskParamPreprocessingLogs(r.Context(), taskID) if err != nil { s.logger.Error("list admin task parameter preprocessing logs failed", "taskID", taskID, "error", err) writeError(w, http.StatusInternalServerError, "list admin task parameter preprocessing logs failed") return } if !full { items = store.MaskTaskParamPreprocessingLogs(items) } writeJSON(w, http.StatusOK, map[string]any{"items": items}) } func parseAdminTaskSensitiveMode(value string) (bool, bool) { switch strings.ToLower(strings.TrimSpace(value)) { case "", "masked": return false, true case "full": return true, true default: return false, false } } func validUUID(value string) bool { _, err := uuid.Parse(strings.TrimSpace(value)) return err == nil }