package server import ( "encoding/json" "fmt" "net/http" "strconv" "time" "github.com/gin-gonic/gin" "kra/internal/biz" "kra/internal/service" ) type taskRequest struct { ID uint `json:"ID"` Name string `json:"name"` Description string `json:"description"` Spec string `json:"spec"` WithSeconds bool `json:"withSeconds"` ExecutorType string `json:"executorType"` MethodName string `json:"methodName"` Params json.RawMessage `json:"params"` HTTPURL string `json:"httpUrl"` HTTPMethod string `json:"httpMethod"` HTTPHeader json.RawMessage `json:"httpHeader"` HTTPBody string `json:"httpBody"` HTTPAllowPrivate bool `json:"httpAllowPrivate"` Enabled bool `json:"enabled"` } func taskDomain(v taskRequest) *biz.TimedTask { return &biz.TimedTask{ID: v.ID, Name: v.Name, Description: v.Description, Spec: v.Spec, WithSeconds: v.WithSeconds, ExecutorType: v.ExecutorType, MethodName: v.MethodName, Params: v.Params, HTTPURL: v.HTTPURL, HTTPMethod: v.HTTPMethod, HTTPHeader: v.HTTPHeader, HTTPBody: v.HTTPBody, HTTPAllowPrivate: v.HTTPAllowPrivate, Enabled: v.Enabled} } func registerTaskRoutes(group *gin.RouterGroup, svc *service.TaskService, scheduler *TaskScheduler) { router := group.Group("/timedTask") router.POST("/createTimedTask", func(c *gin.Context) { var req taskRequest if c.ShouldBindJSON(&req) != nil { fail(c, "参数错误") return } task := taskDomain(req) if err := svc.Create(c.Request.Context(), task); err != nil { fail(c, "创建失败:"+err.Error()) return } if err := scheduler.Schedule(task); err != nil { fail(c, "调度失败:"+err.Error()) return } ok(c) }) router.PUT("/updateTimedTask", func(c *gin.Context) { var req taskRequest if c.ShouldBindJSON(&req) != nil { fail(c, "参数错误") return } task := taskDomain(req) if err := svc.Update(c.Request.Context(), task); err != nil { fail(c, "更新失败:"+err.Error()) return } if err := scheduler.Schedule(task); err != nil { fail(c, "调度失败:"+err.Error()) return } ok(c) }) router.DELETE("/deleteTimedTask", func(c *gin.Context) { id, _ := strconv.ParseUint(c.Query("ID"), 10, 64) scheduler.Remove(uint(id)) if err := svc.Delete(c.Request.Context(), uint(id)); err != nil { fail(c, "删除失败") return } ok(c) }) router.POST("/toggleTimedTask", func(c *gin.Context) { var req struct { ID uint `json:"ID"` Enabled bool `json:"enabled"` } if c.ShouldBindJSON(&req) != nil { fail(c, "参数错误") return } if err := svc.Toggle(c.Request.Context(), req.ID, req.Enabled); err != nil { fail(c, "设置失败") return } task, err := svc.Task(c.Request.Context(), req.ID) if err != nil { fail(c, "任务不存在") return } if err = scheduler.Schedule(task); err != nil { fail(c, "调度失败") return } ok(c) }) router.POST("/triggerTimedTask", func(c *gin.Context) { var req struct { ID uint `json:"ID"` } if c.ShouldBindJSON(&req) != nil { fail(c, "参数错误") return } task, err := svc.Task(c.Request.Context(), req.ID) if err != nil { fail(c, "任务不存在") return } scheduler.Trigger(task) ok(c) }) router.GET("/getTimedTaskList", func(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) size, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10")) var enabled *bool if raw := c.Query("enabled"); raw != "" { value, _ := strconv.ParseBool(raw) enabled = &value } items, total, err := svc.Tasks(c.Request.Context(), page, size, &biz.TimedTask{Name: c.Query("name"), ExecutorType: c.Query("executorType"), EnabledFilter: enabled}, scheduler.NextRuns()) if err != nil { fail(c, "获取失败") return } writeResult(c, codeSuccess, pageResult{List: items, Total: total, Page: page, PageSize: size}, "获取成功") }) router.GET("/getTimedTaskLogList", func(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) size, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10")) taskID, _ := strconv.ParseUint(c.Query("taskId"), 10, 64) items, total, err := svc.Logs(c.Request.Context(), page, size, uint(taskID), c.Query("status")) if err != nil { fail(c, "获取失败") return } writeResult(c, codeSuccess, pageResult{List: items, Total: total, Page: page, PageSize: size}, "获取成功") }) router.GET("/getRegisteredMethods", func(c *gin.Context) { writeResult(c, codeSuccess, gin.H{"methods": svc.RegisteredMethods()}, "获取成功") }) router.GET("/alertStream", func(c *gin.Context) { c.Header("Content-Type", "text/event-stream") c.Header("Cache-Control", "no-cache") c.Header("Connection", "keep-alive") c.Header("X-Accel-Buffering", "no") flusher, okFlush := c.Writer.(http.Flusher) if !okFlush { fail(c, "SSE 不受支持") return } events := scheduler.Subscribe() defer scheduler.Unsubscribe(events) ticker := time.NewTicker(20 * time.Second) defer ticker.Stop() _, _ = fmt.Fprint(c.Writer, "event: connected\ndata: {}\n\n") flusher.Flush() for { select { case event, open := <-events: if !open { return } _, _ = fmt.Fprintf(c.Writer, "event: alert\ndata: %s\n\n", event) flusher.Flush() case <-ticker.C: _, _ = fmt.Fprint(c.Writer, ": keepalive\n\n") flusher.Flush() case <-c.Request.Context().Done(): return } } }) }