/* Copyright 2025-2026 linux.do Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package model import ( "context" "fmt" "time" "github.com/linux-do/credit/internal/db" "github.com/linux-do/credit/internal/db/idgen" "gorm.io/gorm" ) // TaskExecutionStatus 任务执行状态 type TaskExecutionStatus string const ( TaskExecutionStatusPending TaskExecutionStatus = "pending" TaskExecutionStatusRunning TaskExecutionStatus = "running" TaskExecutionStatusSucceeded TaskExecutionStatus = "succeeded" TaskExecutionStatusFailed TaskExecutionStatus = "failed" ) // TaskExecution 任务执行记录 type TaskExecution struct { ID uint64 `json:"id,string" gorm:"primaryKey"` TaskID string `json:"task_id" gorm:"size:128;uniqueIndex;not null"` TaskType string `json:"task_type" gorm:"size:64;index;not null"` TaskName string `json:"task_name" gorm:"size:128"` Status TaskExecutionStatus `json:"status" gorm:"size:32;index;not null"` Retryable bool `json:"retryable" gorm:"not null;default:false"` MaxRetry int `json:"max_retry" gorm:"not null;default:0"` RetryCount int `json:"retry_count" gorm:"not null;default:0"` Log string `json:"log" gorm:"type:text"` ErrorMessage string `json:"error_message" gorm:"type:text"` Result string `json:"result" gorm:"type:text"` StartedAt *time.Time `json:"started_at" gorm:"index"` FinishedAt *time.Time `json:"finished_at"` Duration int64 `json:"duration" gorm:"comment:耗时毫秒"` Payload string `json:"payload" gorm:"type:text"` TriggeredBy string `json:"triggered_by" gorm:"size:32;not null;default:system"` CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;index"` UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"` } // TableName 表名 func (TaskExecution) TableName() string { return "task_executions" } // CreateTaskExecution 创建任务执行记录 func CreateTaskExecution(ctx context.Context, execution *TaskExecution) error { execution.ID = idgen.NextUint64ID() return db.DB(ctx).Create(execution).Error } // UpdateTaskExecution 更新任务执行记录 func UpdateTaskExecution(ctx context.Context, execution *TaskExecution) error { return db.DB(ctx).Save(execution).Error } // GetTaskExecutionByTaskID 根据 TaskID 获取执行记录 func GetTaskExecutionByTaskID(ctx context.Context, taskID string) (*TaskExecution, error) { var execution TaskExecution if err := db.DB(ctx).Where("task_id = ?", taskID).First(&execution).Error; err != nil { return nil, err } return &execution, nil } // GetTaskExecutionByID 根据 ID 获取执行记录 func GetTaskExecutionByID(ctx context.Context, id uint64) (*TaskExecution, error) { var execution TaskExecution if err := db.DB(ctx).Where("id = ?", id).First(&execution).Error; err != nil { return nil, err } return &execution, nil } // AppendTaskExecutionLog 追加日志到执行记录 func AppendTaskExecutionLog(ctx context.Context, taskID string, logLine string) error { now := time.Now().Format("15:04:05") line := fmt.Sprintf("[%s] %s\n", now, logLine) return db.DB(ctx).Model(&TaskExecution{}). Where("task_id = ?", taskID). Update("log", gorm.Expr("COALESCE(log, '') || ?", line)).Error } // ListTaskExecutionsRequest 查询任务执行记录列表请求 type ListTaskExecutionsRequest struct { Status string `form:"status"` TaskType string `form:"task_type"` Page int `form:"page"` PageSize int `form:"page_size"` } // ListTaskExecutions 分页查询任务执行记录 func ListTaskExecutions(ctx context.Context, req ListTaskExecutionsRequest) ([]TaskExecution, int64, error) { if req.Page <= 0 { req.Page = 1 } if req.PageSize <= 0 { req.PageSize = 20 } query := db.DB(ctx).Model(&TaskExecution{}) if req.Status != "" { query = query.Where("status = ?", req.Status) } if req.TaskType != "" { query = query.Where("task_type = ?", req.TaskType) } var total int64 if err := query.Count(&total).Error; err != nil { return nil, 0, err } var executions []TaskExecution offset := (req.Page - 1) * req.PageSize if err := query.Order("id DESC").Offset(offset).Limit(req.PageSize).Find(&executions).Error; err != nil { return nil, 0, err } return executions, total, nil }