package handler import ( "fmt" "net/http" "strconv" "time" "kra/internal/server/httpx" "kra/internal/service" "kra/internal/service/dto" "kra/internal/worker" "github.com/gin-gonic/gin" ) type Task struct { service *service.TaskService scheduler *worker.TaskScheduler } func NewTask(service *service.TaskService, scheduler *worker.TaskScheduler) *Task { return &Task{service: service, scheduler: scheduler} } func (h *Task) Create(c *gin.Context) { var req dto.TaskRequest if c.ShouldBindJSON(&req) != nil { httpx.Fail(c, "参数错误") return } id, err := h.service.CreateRequest(c.Request.Context(), &req) if err != nil { httpx.Fail(c, "创建失败:"+err.Error()) return } if err = h.scheduler.ScheduleID(c.Request.Context(), id); err != nil { httpx.Fail(c, "调度失败:"+err.Error()) return } httpx.Write(c, httpx.CodeSuccess, gin.H{}, "创建成功") } func (h *Task) Update(c *gin.Context) { var req dto.TaskRequest if c.ShouldBindJSON(&req) != nil { httpx.Fail(c, "参数错误") return } if err := h.service.UpdateRequest(c.Request.Context(), &req); err != nil { httpx.Fail(c, "更新失败:"+err.Error()) return } if err := h.scheduler.ScheduleID(c.Request.Context(), req.ID); err != nil { httpx.Fail(c, "调度失败:"+err.Error()) return } httpx.Write(c, httpx.CodeSuccess, gin.H{}, "更新成功") } func (h *Task) Delete(c *gin.Context) { var req dto.DeleteTaskRequest if c.ShouldBindJSON(&req) != nil || req.ID == 0 { httpx.Fail(c, "参数错误") return } h.scheduler.Remove(req.ID) if err := h.service.Delete(c.Request.Context(), req.ID); err != nil { httpx.Fail(c, "删除失败") return } httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功") } func (h *Task) Toggle(c *gin.Context) { var req dto.ToggleTaskRequest if c.ShouldBindJSON(&req) != nil { httpx.Fail(c, "参数错误") return } if err := h.service.Toggle(c.Request.Context(), req.ID, req.Enabled); err != nil { httpx.Fail(c, "操作失败: "+err.Error()) return } if err := h.scheduler.ScheduleID(c.Request.Context(), req.ID); err != nil { httpx.Fail(c, "操作失败: "+err.Error()) return } httpx.Write(c, httpx.CodeSuccess, gin.H{}, "操作成功") } func (h *Task) Trigger(c *gin.Context) { var req dto.DeleteTaskRequest if c.ShouldBindJSON(&req) != nil { httpx.Fail(c, "参数错误") return } if err := h.scheduler.TriggerID(c.Request.Context(), req.ID); err != nil { httpx.Fail(c, "触发失败: "+err.Error()) return } httpx.Write(c, httpx.CodeSuccess, gin.H{}, "已触发, 执行结果见执行日志") } func (h *Task) List(c *gin.Context) { page, _ := strconv.Atoi(c.Query("page")) size, _ := strconv.Atoi(c.Query("pageSize")) var enabled *bool if raw := c.Query("enabled"); raw != "" { value, _ := strconv.ParseBool(raw) enabled = &value } items, total, err := h.service.ListRequest(c.Request.Context(), page, size, c.Query("name"), c.Query("executorType"), enabled, h.scheduler.NextRuns()) if err != nil { httpx.Fail(c, "获取失败") return } httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: page, PageSize: size}, "获取成功") } func (h *Task) Logs(c *gin.Context) { page, _ := strconv.Atoi(c.Query("page")) size, _ := strconv.Atoi(c.Query("pageSize")) taskID, _ := strconv.ParseUint(c.Query("taskId"), 10, 64) items, total, err := h.service.Logs(c.Request.Context(), page, size, uint(taskID), c.Query("status")) if err != nil { httpx.Fail(c, "获取失败") return } httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: page, PageSize: size}, "获取成功") } func (h *Task) Methods(c *gin.Context) { httpx.Write(c, httpx.CodeSuccess, gin.H{"methods": h.service.RegisteredMethods()}, "获取成功") } func (h *Task) AlertStream(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, ok := c.Writer.(http.Flusher) if !ok { httpx.Fail(c, "SSE 不受支持") return } // Remove the normal server deadline for long-lived SSE connections. _ = http.NewResponseController(c.Writer).SetWriteDeadline(time.Time{}) events := h.scheduler.Subscribe() defer h.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 } } }