package handler import ( "context" "strings" "gcy_hpc_server/internal/model" "gcy_hpc_server/internal/server" "github.com/gin-gonic/gin" "go.uber.org/zap" ) type taskServiceProvider interface { SubmitAsync(ctx context.Context, req *model.CreateTaskRequest) (int64, error) ListTasks(ctx context.Context, query *model.TaskListQuery) ([]model.Task, int64, error) } type TaskHandler struct { svc taskServiceProvider logger *zap.Logger } func NewTaskHandler(svc taskServiceProvider, logger *zap.Logger) *TaskHandler { return &TaskHandler{svc: svc, logger: logger} } func (h *TaskHandler) CreateTask(c *gin.Context) { var req model.CreateTaskRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Warn("invalid request body for create task", zap.Error(err)) server.BadRequest(c, err.Error()) return } taskID, err := h.svc.SubmitAsync(c.Request.Context(), &req) if err != nil { errStr := err.Error() if strings.Contains(errStr, "not found") { h.logger.Warn("task submit target not found", zap.Error(err)) server.NotFound(c, errStr) return } if strings.Contains(errStr, "exceeds limit") || strings.Contains(errStr, "validation") { h.logger.Warn("task submit validation failed", zap.Error(err)) server.BadRequest(c, errStr) return } h.logger.Error("failed to create task", zap.Error(err)) server.InternalError(c, errStr) return } h.logger.Info("task created", zap.Int64("id", taskID)) server.Created(c, gin.H{"id": taskID}) } func (h *TaskHandler) ListTasks(c *gin.Context) { var query model.TaskListQuery _ = c.ShouldBindQuery(&query) if query.Page < 1 { query.Page = 1 } if query.PageSize < 1 || query.PageSize > 100 { query.PageSize = 10 } tasks, total, err := h.svc.ListTasks(c.Request.Context(), &query) if err != nil { h.logger.Error("failed to list tasks", zap.Error(err)) server.InternalError(c, err.Error()) return } responses := make([]model.TaskResponse, 0, len(tasks)) for i := range tasks { responses = append(responses, model.TaskResponse{ ID: tasks[i].ID, TaskName: tasks[i].TaskName, AppID: tasks[i].AppID, AppName: tasks[i].AppName, Status: tasks[i].Status, CurrentStep: tasks[i].CurrentStep, RetryCount: tasks[i].RetryCount, SlurmJobID: tasks[i].SlurmJobID, WorkDir: tasks[i].WorkDir, ErrorMessage: tasks[i].ErrorMessage, CreatedAt: tasks[i].CreatedAt, UpdatedAt: tasks[i].UpdatedAt, Partition: tasks[i].Partition, Cpus: tasks[i].Cpus, MemoryPerNode: tasks[i].MemoryPerNode, MemoryPerCpu: tasks[i].MemoryPerCpu, TimeLimit: tasks[i].TimeLimit, QOS: tasks[i].QOS, JobName: tasks[i].JobName, Nodes: tasks[i].Nodes, Tasks: tasks[i].Tasks, CpusPerTask: tasks[i].CpusPerTask, Constraints: tasks[i].Constraints, Reservation: tasks[i].Reservation, Account: tasks[i].Account, Nice: tasks[i].Nice, MailType: tasks[i].MailType, MailUser: tasks[i].MailUser, StandardOutput: tasks[i].StandardOutput, StandardError: tasks[i].StandardError, StandardInput: tasks[i].StandardInput, RequiredNodes: tasks[i].RequiredNodes, ExcludedNodes: tasks[i].ExcludedNodes, BeginTime: tasks[i].BeginTime, Deadline: tasks[i].Deadline, Array: tasks[i].Array, Dependency: tasks[i].Dependency, Requeue: tasks[i].Requeue, KillOnNodeFail: tasks[i].KillOnNodeFail, }) } server.OK(c, model.TaskListResponse{ Items: responses, Total: total, }) }