Files
hpc/internal/handler/job.go
dailz b90942de77 fix(task): prevent duplicate Slurm job submission on backend restart
RecoverStuckTasks now skips tasks that already have a slurm_job_id,
and ProcessTask adds a guard before the submitting step to prevent
re-submission even if a task is incorrectly re-enqueued.

Also deprecates POST /api/v1/jobs/submit endpoint (replaced by POST /tasks)
and comments out related handlers and tests.
2026-04-21 10:57:38 +08:00

135 lines
3.9 KiB
Go

package handler
import (
"net/http"
"gcy_hpc_server/internal/model"
"gcy_hpc_server/internal/server"
"gcy_hpc_server/internal/service"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// JobHandler handles HTTP requests for job operations.
type JobHandler struct {
jobSvc *service.JobService
logger *zap.Logger
}
// NewJobHandler creates a new JobHandler with the given JobService.
func NewJobHandler(jobSvc *service.JobService, logger *zap.Logger) *JobHandler {
return &JobHandler{jobSvc: jobSvc, logger: logger}
}
// [已弃用] SubmitJob 已被 POST /tasks 取代。
// 保留方法体以防需要回滚。
// SubmitJob handles POST /api/v1/jobs/submit.
// func (h *JobHandler) SubmitJob(c *gin.Context) {
// var req model.SubmitJobRequest
// if err := c.ShouldBindJSON(&req); err != nil {
// h.logger.Warn("bad request", zap.String("method", "SubmitJob"), zap.String("error", "invalid request body"))
// server.BadRequest(c, "invalid request body")
// return
// }
// if req.Script == "" {
// h.logger.Warn("bad request", zap.String("method", "SubmitJob"), zap.String("error", "script is required"))
// server.BadRequest(c, "script is required")
// return
// }
//
// resp, err := h.jobSvc.SubmitJob(c.Request.Context(), &req)
// if err != nil {
// h.logger.Error("handler error", zap.String("method", "SubmitJob"), zap.Int("status", http.StatusBadGateway), zap.Error(err))
// server.ErrorWithStatus(c, http.StatusBadGateway, "slurm error: "+err.Error())
// return
// }
//
// server.Created(c, resp)
// }
// GetJobs handles GET /api/v1/jobs with pagination.
func (h *JobHandler) GetJobs(c *gin.Context) {
var query model.JobListQuery
if err := c.ShouldBindQuery(&query); err != nil {
h.logger.Warn("bad request", zap.String("method", "GetJobs"), zap.String("error", "invalid query params"))
server.BadRequest(c, "invalid query params")
return
}
if query.Page < 1 {
query.Page = 1
}
if query.PageSize < 1 {
query.PageSize = 20
}
resp, err := h.jobSvc.GetJobs(c.Request.Context(), &query)
if err != nil {
h.logger.Error("handler error", zap.String("method", "GetJobs"), zap.Int("status", http.StatusInternalServerError), zap.Error(err))
server.InternalError(c, err.Error())
return
}
server.OK(c, resp)
}
// GetJob handles GET /api/v1/jobs/:id.
func (h *JobHandler) GetJob(c *gin.Context) {
jobID := c.Param("id")
resp, err := h.jobSvc.GetJob(c.Request.Context(), jobID)
if err != nil {
h.logger.Error("handler error", zap.String("method", "GetJob"), zap.Int("status", http.StatusInternalServerError), zap.Error(err))
server.InternalError(c, err.Error())
return
}
if resp == nil {
h.logger.Warn("bad request", zap.String("method", "GetJob"), zap.String("error", "job not found"))
server.NotFound(c, "job not found")
return
}
server.OK(c, resp)
}
// CancelJob handles DELETE /api/v1/jobs/:id.
func (h *JobHandler) CancelJob(c *gin.Context) {
jobID := c.Param("id")
err := h.jobSvc.CancelJob(c.Request.Context(), jobID)
if err != nil {
h.logger.Error("handler error", zap.String("method", "CancelJob"), zap.Int("status", http.StatusBadGateway), zap.Error(err))
server.ErrorWithStatus(c, http.StatusBadGateway, err.Error())
return
}
server.OK(c, gin.H{"message": "job cancelled"})
}
// GetJobHistory handles GET /api/v1/jobs/history.
func (h *JobHandler) GetJobHistory(c *gin.Context) {
var query model.JobHistoryQuery
if err := c.ShouldBindQuery(&query); err != nil {
h.logger.Warn("bad request", zap.String("method", "GetJobHistory"), zap.String("error", "invalid query params"))
server.BadRequest(c, "invalid query params")
return
}
if query.Page < 1 {
query.Page = 1
}
if query.PageSize < 1 {
query.PageSize = 20
}
resp, err := h.jobSvc.GetJobHistory(c.Request.Context(), &query)
if err != nil {
h.logger.Error("handler error", zap.String("method", "GetJobHistory"), zap.Int("status", http.StatusInternalServerError), zap.Error(err))
server.InternalError(c, err.Error())
return
}
server.OK(c, resp)
}