package job import ( "fmt" "path/filepath" "hyapp-admin-server/internal/config" "hyapp-admin-server/internal/middleware" "hyapp-admin-server/internal/model" "hyapp-admin-server/internal/modules/shared" "hyapp-admin-server/internal/repository" "hyapp-admin-server/internal/response" "github.com/gin-gonic/gin" ) type Handler struct { service *JobService audit shared.OperationLogger } func New(store *repository.Store, cfg config.Config, audit shared.OperationLogger) *Handler { return &Handler{service: NewService(store, cfg), audit: audit} } func (h *Handler) ListJobs(c *gin.Context) { options := shared.ListOptions(c) jobs, total, err := h.service.ListJobs(options) if err != nil { response.ServerError(c, "获取任务失败") return } response.OK(c, response.Page{Items: jobs, Page: options.Page, PageSize: options.PageSize, Total: total}) } func (h *Handler) GetJob(c *gin.Context) { id, ok := shared.ParseID(c, "id") if !ok { return } job, err := h.service.GetJob(id) if err != nil { response.BadRequest(c, "任务不存在") return } if !canReadJob(c, job) { response.Forbidden(c, "没有查看该任务的权限") return } response.OK(c, job) } func (h *Handler) CreateUserExportJob(c *gin.Context) { job, err := h.service.CreateUserExportJob(shared.ActorFromContext(c), shared.ListOptions(c)) if err != nil { response.ServerError(c, "创建导出任务失败") return } shared.OperationLog(c, h.audit, "create-user-export-job", "admin_jobs", "success", fmt.Sprintf("job_id=%d", job.ID)) response.OK(c, job) } func (h *Handler) CancelJob(c *gin.Context) { id, ok := shared.ParseID(c, "id") if !ok { return } if err := h.service.CancelJob(id); err != nil { response.BadRequest(c, err.Error()) return } shared.OperationLog(c, h.audit, "cancel-job", "admin_jobs", "success", fmt.Sprintf("job_id=%d", id)) response.OK(c, gin.H{"canceled": true}) } func (h *Handler) DownloadJobArtifact(c *gin.Context) { id, ok := shared.ParseID(c, "id") if !ok { return } job, err := h.service.GetJob(id) if err != nil { response.BadRequest(c, "任务不存在") return } if !canReadJob(c, job) { response.Forbidden(c, "没有下载该任务的权限") return } _, artifactPath, err := h.service.JobArtifact(id) if err != nil { response.BadRequest(c, err.Error()) return } shared.OperationLog(c, h.audit, "download-job-artifact", "admin_jobs", "success", fmt.Sprintf("job_id=%d", id)) c.Header("Content-Disposition", "attachment; filename="+filepath.Base(artifactPath)) c.File(artifactPath) } func canReadJob(c *gin.Context, job *model.AdminJob) bool { if job == nil { return false } if middleware.HasAnyPermission(c, "job:view", "country:update") { return true } // app-user:export 只允许读取本人创建的用户导出,不能借任务 ID 查看其他后台任务或他人的 CSV。 return middleware.HasPermission(c, "app-user:export") && job.Type == "app-user-export" && job.CreatedBy == middleware.CurrentUserID(c) }