- 新端点 /ai/v1/audio/speech(xai.grok-tts)、/rerank(cohere.rerank-v4)、/moderations(OCI Guardrails) - Responses 放行 code_interpreter 与远程 mcp 工具,web_search/x_search 解除仅非流式限制 - 模型能力映射扩展:TEXT_RERANK→RERANK、TEXT_TO_AUDIO→TTS - AI 网关文档独立 docs/ai-gateway.md,字段兼容矩阵只列支持项;README 精简引用 - swagger 修缺:135 处响应注解具体化,RawMessage/联合类型统一渲染 AnyJSON,overrides 迁至 docs/.swaggo - CHANGELOG 0.4.0,版本段不再记日期;DASH_VERSION v0.4.0
182 lines
4.5 KiB
Go
182 lines
4.5 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
_ "oci-portal/internal/model" // swagger 注解引用
|
|
"oci-portal/internal/service"
|
|
)
|
|
|
|
// taskHandler 处理后台任务(定时测活、抢机)相关请求。
|
|
type taskHandler struct {
|
|
svc *service.TaskService
|
|
}
|
|
|
|
type createTaskRequest struct {
|
|
Name string `json:"name" binding:"required"`
|
|
Type string `json:"type" binding:"required"`
|
|
CronExpr string `json:"cronExpr" binding:"required"`
|
|
Payload json.RawMessage `json:"payload"`
|
|
}
|
|
|
|
type updateTaskRequest struct {
|
|
Name *string `json:"name"`
|
|
CronExpr *string `json:"cronExpr"`
|
|
Payload json.RawMessage `json:"payload"`
|
|
Status *string `json:"status"`
|
|
}
|
|
|
|
// @Summary 创建任务
|
|
// @Tags 任务与日志回传
|
|
// @Param body body createTaskRequest true "任务定义(类型/cron/payload)"
|
|
// @Success 201 {object} model.Task
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/tasks [post]
|
|
func (h *taskHandler) create(c *gin.Context) {
|
|
var req createTaskRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
task, err := h.svc.CreateTask(c.Request.Context(), service.CreateTaskInput{
|
|
Name: req.Name,
|
|
Type: req.Type,
|
|
CronExpr: req.CronExpr,
|
|
Payload: req.Payload,
|
|
})
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, task)
|
|
}
|
|
|
|
// @Summary 任务列表
|
|
// @Tags 任务与日志回传
|
|
// @Success 200 {array} model.Task
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/tasks [get]
|
|
func (h *taskHandler) list(c *gin.Context) {
|
|
tasks, err := h.svc.ListTasks(c.Request.Context())
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, tasks)
|
|
}
|
|
|
|
// @Summary 任务详情
|
|
// @Tags 任务与日志回传
|
|
// @Param id path int true "任务 ID"
|
|
// @Success 200 {object} model.Task
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/tasks/{id} [get]
|
|
func (h *taskHandler) get(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
task, err := h.svc.GetTask(c.Request.Context(), id)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, task)
|
|
}
|
|
|
|
// @Summary 更新任务
|
|
// @Tags 任务与日志回传
|
|
// @Param id path int true "任务 ID"
|
|
// @Param body body updateTaskRequest true "可更新字段"
|
|
// @Success 200 {object} model.Task
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/tasks/{id} [put]
|
|
func (h *taskHandler) update(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req updateTaskRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
task, err := h.svc.UpdateTask(c.Request.Context(), id, service.UpdateTaskInput{
|
|
Name: req.Name,
|
|
CronExpr: req.CronExpr,
|
|
Payload: req.Payload,
|
|
Status: req.Status,
|
|
})
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, task)
|
|
}
|
|
|
|
// @Summary 删除任务
|
|
// @Tags 任务与日志回传
|
|
// @Param id path int true "任务 ID"
|
|
// @Success 204 "无内容"
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/tasks/{id} [delete]
|
|
func (h *taskHandler) remove(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := h.svc.DeleteTask(c.Request.Context(), id); err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
// @Summary 任务执行日志
|
|
// @Tags 任务与日志回传
|
|
// @Param id path int true "配置 ID"
|
|
// @Success 200 {array} model.TaskLog
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/tasks/{id}/logs [get]
|
|
func (h *taskHandler) logs(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
|
logs, err := h.svc.TaskLogs(c.Request.Context(), id, limit)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, logs)
|
|
}
|
|
|
|
// @Summary 立即执行任务(异步触发,结果经任务日志轮询获取)
|
|
// @Tags 任务与日志回传
|
|
// @Param id path int true "配置 ID"
|
|
// @Success 202 {object} triggerResponse
|
|
// @Failure 409 {object} errorResponse "任务正在执行中"
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/tasks/{id}/run [post]
|
|
func (h *taskHandler) run(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := h.svc.TriggerTask(c.Request.Context(), id); err != nil {
|
|
if errors.Is(err, service.ErrTaskRunning) {
|
|
c.JSON(http.StatusConflict, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusAccepted, gin.H{"triggered": true})
|
|
}
|