mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 22:06:38 +08:00
fb6a3edb89
- Physically purged all legacy internal/ packages, centralized pkg/model/ and pkg/repository/ - Migrated domain models and database repositories into self-contained owner plugins (user, auth, message_gateway, admin, upload, risk_control) - Decoupled cross-plugin interactions via pure core/contracts and typed EventBus - Ensured 100% test coverage pass, zero data races (-race clean), and 0 lint issues in make code-check
155 lines
5.4 KiB
Go
155 lines
5.4 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package task
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
db "github.com/Rain-kl/Wavelet/pkg/persistence"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// TaskExecutionStatus 任务执行状态
|
|
//
|
|
//nolint:revive
|
|
type TaskExecutionStatus string
|
|
|
|
// Task execution status constants.
|
|
const (
|
|
TaskExecutionStatusPending TaskExecutionStatus = "pending"
|
|
TaskExecutionStatusRunning TaskExecutionStatus = "running"
|
|
TaskExecutionStatusSucceeded TaskExecutionStatus = "succeeded"
|
|
TaskExecutionStatusFailed TaskExecutionStatus = "failed"
|
|
)
|
|
|
|
// TaskExecution 任务执行记录
|
|
//
|
|
//nolint:revive
|
|
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 "w_task_executions"
|
|
}
|
|
|
|
// CreateTaskExecution 创建任务执行记录
|
|
func CreateTaskExecution(ctx context.Context, exec *TaskExecution) error {
|
|
return db.DB(ctx).Create(exec).Error
|
|
}
|
|
|
|
// GetTaskExecutionByTaskID 根据 TaskID 查询执行记录
|
|
func GetTaskExecutionByTaskID(ctx context.Context, taskID string) (*TaskExecution, error) {
|
|
var exec TaskExecution
|
|
if err := db.DB(ctx).Where("task_id = ?", taskID).First(&exec).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
_ = loadTaskExecutionLog(ctx, &exec)
|
|
return &exec, nil
|
|
}
|
|
|
|
// GetTaskExecutionByID 根据主键 ID 查询执行记录
|
|
func GetTaskExecutionByID(ctx context.Context, id uint64) (*TaskExecution, error) {
|
|
var exec TaskExecution
|
|
if err := db.DB(ctx).Where("id = ?", id).First(&exec).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
_ = loadTaskExecutionLog(ctx, &exec)
|
|
return &exec, nil
|
|
}
|
|
|
|
// GetLatestTaskExecutionByTaskType 获取指定任务类型的最新执行记录
|
|
func GetLatestTaskExecutionByTaskType(ctx context.Context, taskType string) (*TaskExecution, bool, error) {
|
|
var exec TaskExecution
|
|
err := db.DB(ctx).Where("task_type = ?", taskType).Order("id DESC").First(&exec).Error
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, false, nil
|
|
}
|
|
return nil, false, err
|
|
}
|
|
return &exec, true, nil
|
|
}
|
|
|
|
// TaskExecutionCleanupStats describes task execution log cleanup results.
|
|
//
|
|
//nolint:revive
|
|
type TaskExecutionCleanupStats struct {
|
|
HighFrequencyDeleted int64
|
|
LowFrequencyDeleted int64
|
|
}
|
|
|
|
// CleanupTaskExecutionLogs removes finished task execution logs according to frequency-based retention.
|
|
func CleanupTaskExecutionLogs(ctx context.Context, now time.Time) (TaskExecutionCleanupStats, error) {
|
|
const (
|
|
frequencyWindowDays = 30
|
|
highFrequencyThreshold = frequencyWindowDays
|
|
)
|
|
|
|
frequencyWindowStart := now.AddDate(0, 0, -frequencyWindowDays)
|
|
highFrequencyCutoff := now.AddDate(0, 0, -3)
|
|
lowFrequencyCutoff := now.AddDate(0, 0, -30)
|
|
terminalStatuses := []TaskExecutionStatus{TaskExecutionStatusSucceeded, TaskExecutionStatusFailed}
|
|
|
|
var highFrequencyTaskTypes []string
|
|
if err := db.DB(ctx).
|
|
Model(&TaskExecution{}).
|
|
Select("task_type").
|
|
Where("created_at >= ?", frequencyWindowStart).
|
|
Group("task_type").
|
|
Having("COUNT(*) > ?", highFrequencyThreshold).
|
|
Pluck("task_type", &highFrequencyTaskTypes).Error; err != nil {
|
|
return TaskExecutionCleanupStats{}, err
|
|
}
|
|
|
|
var highFrequencyDeleted int64
|
|
if len(highFrequencyTaskTypes) > 0 {
|
|
highFrequencyResult := db.DB(ctx).
|
|
Where("status IN ?", terminalStatuses).
|
|
Where("created_at < ?", highFrequencyCutoff).
|
|
Where("task_type IN ?", highFrequencyTaskTypes).
|
|
Delete(&TaskExecution{})
|
|
if highFrequencyResult.Error != nil {
|
|
return TaskExecutionCleanupStats{}, highFrequencyResult.Error
|
|
}
|
|
highFrequencyDeleted = highFrequencyResult.RowsAffected
|
|
}
|
|
|
|
lowFrequencyQuery := db.DB(ctx).
|
|
Where("status IN ?", terminalStatuses).
|
|
Where("created_at < ?", lowFrequencyCutoff)
|
|
if len(highFrequencyTaskTypes) > 0 {
|
|
lowFrequencyQuery = lowFrequencyQuery.Where("task_type NOT IN ?", highFrequencyTaskTypes)
|
|
}
|
|
lowFrequencyResult := lowFrequencyQuery.Delete(&TaskExecution{})
|
|
if lowFrequencyResult.Error != nil {
|
|
return TaskExecutionCleanupStats{}, lowFrequencyResult.Error
|
|
}
|
|
|
|
return TaskExecutionCleanupStats{
|
|
HighFrequencyDeleted: highFrequencyDeleted,
|
|
LowFrequencyDeleted: lowFrequencyResult.RowsAffected,
|
|
}, nil
|
|
}
|