package service import ( "context" "errors" "fmt" "time" "gorm.io/gorm" "go-admin/app/jobs/models" ) var ( ErrExecutionLogInvalidRequest = errors.New("invalid execution log request") ErrExecutionLogJobNotFound = errors.New("scheduled job not found") ) type ExecutionLogListRequest struct { Page int PageSize int Status string StartedFrom *time.Time StartedTo *time.Time } type ExecutionLogJob struct { JobID int `json:"jobId"` JobName string `json:"jobName"` InvokeTarget string `json:"invokeTarget"` Deleted bool `json:"deleted"` } type ExecutionLogListResponse struct { Job ExecutionLogJob `json:"job"` Items []models.SysJobExecutionLog `json:"items"` Total int64 `json:"total"` Page int `json:"page"` PageSize int `json:"pageSize"` } type ExecutionLogService struct{ db *gorm.DB } func NewExecutionLogService(db *gorm.DB) *ExecutionLogService { return &ExecutionLogService{db: db} } func (service *ExecutionLogService) List(ctx context.Context, jobID int, request ExecutionLogListRequest) (ExecutionLogListResponse, error) { if service.db == nil || jobID < 1 { return ExecutionLogListResponse{}, fmt.Errorf("%w: jobId 无效", ErrExecutionLogInvalidRequest) } if request.Page < 1 { request.Page = 1 } if request.PageSize < 1 { request.PageSize = 20 } if request.PageSize > 100 { return ExecutionLogListResponse{}, fmt.Errorf("%w: pageSize 必须是 1 到 100 的整数", ErrExecutionLogInvalidRequest) } if request.Status != "" && !validExecutionStatus(request.Status) { return ExecutionLogListResponse{}, fmt.Errorf("%w: status 无效", ErrExecutionLogInvalidRequest) } if request.StartedFrom != nil && request.StartedTo != nil && request.StartedFrom.After(*request.StartedTo) { return ExecutionLogListResponse{}, fmt.Errorf("%w: 开始时间范围无效", ErrExecutionLogInvalidRequest) } var job models.SysJob if err := service.db.WithContext(ctx).Unscoped().First(&job, jobID).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ExecutionLogListResponse{}, ErrExecutionLogJobNotFound } return ExecutionLogListResponse{}, err } query := service.db.WithContext(ctx).Model(&models.SysJobExecutionLog{}).Where("job_id = ?", jobID) if request.Status != "" { query = query.Where("status = ?", request.Status) } if request.StartedFrom != nil { query = query.Where("started_at >= ?", request.StartedFrom.UTC()) } if request.StartedTo != nil { query = query.Where("started_at <= ?", request.StartedTo.UTC()) } var total int64 if err := query.Count(&total).Error; err != nil { return ExecutionLogListResponse{}, err } items := make([]models.SysJobExecutionLog, 0, request.PageSize) if err := query.Order("started_at DESC, id DESC").Offset((request.Page - 1) * request.PageSize).Limit(request.PageSize).Find(&items).Error; err != nil { return ExecutionLogListResponse{}, err } return ExecutionLogListResponse{ Job: ExecutionLogJob{JobID: job.JobId, JobName: job.JobName, InvokeTarget: job.InvokeTarget, Deleted: job.DeletedAt.Valid}, Items: items, Total: total, Page: request.Page, PageSize: request.PageSize, }, nil } func validExecutionStatus(status string) bool { switch status { case models.JobExecutionRunning, models.JobExecutionSucceeded, models.JobExecutionFailed, models.JobExecutionInterrupted: return true default: return false } }