improve(task):定时任务优化,显示中文名称
This commit is contained in:
1 parent
84ea7543b1
commit
ab463ede83
8 files changed
+57
-67
No files matched your search
@@ -10,6 +10,7 @@ type CronJobListReq struct {
|
||||
|
||||
type CronJobItem struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Schedule string `json:"schedule"`
|
||||
IsRunning bool `json:"is_running"`
|
||||
LastStatus string `json:"last_status"`
|
||||
|
||||
+21
-21
@@ -315,57 +315,57 @@ func printBanner() {
|
||||
// registerCronJobs 集中注册所有定时任务,避免散落在主启动流程中。
|
||||
// 新增 cron 任务时在此追加一项即可;任务名(用于分布式锁 key)需保持唯一且稳定。
|
||||
func registerCronJobs(cs *common.CronScheduler) {
|
||||
cs.Register("ops_system_collector", "* * * * *", func(ctx context.Context) error {
|
||||
cs.Register("ops_system_collector", "系统指标采集", "* * * * *", func(ctx context.Context) error {
|
||||
return monitor.CollectSystemMetrics(ctx)
|
||||
})
|
||||
cs.Register("ops_alert_detector", "* * * * *", func(ctx context.Context) error {
|
||||
cs.Register("ops_alert_detector", "告警检测", "* * * * *", func(ctx context.Context) error {
|
||||
return monitor.RunAlertDetection(ctx)
|
||||
})
|
||||
cs.Register("ops_metrics_cleanup", "0 3 * * *", func(ctx context.Context) error {
|
||||
cs.Register("ops_metrics_cleanup", "指标数据清理", "0 3 * * *", func(ctx context.Context) error {
|
||||
return monitor.CleanupOldMetrics(ctx)
|
||||
})
|
||||
cs.Register("partition_ensure", "0 2 * * *", func(ctx context.Context) error {
|
||||
cs.Register("partition_ensure", "分区维护", "0 2 * * *", func(ctx context.Context) error {
|
||||
return common.EnsurePartitions(ctx)
|
||||
})
|
||||
cs.Register("health_snapshot", "*/5 * * * *", func(ctx context.Context) error {
|
||||
cs.Register("health_snapshot", "渠道健康快照", "*/5 * * * *", func(ctx context.Context) error {
|
||||
return task.SnapshotHealthScores(ctx)
|
||||
})
|
||||
cs.Register("channel_auto_test", "*/5 * * * *", func(ctx context.Context) error {
|
||||
cs.Register("channel_auto_test", "渠道自动测试", "*/5 * * * *", func(ctx context.Context) error {
|
||||
if common.Config().GetBool(ctx, "channel_auto_test_enabled") {
|
||||
task.AutoTestChannels(ctx)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
cs.Register("model_sunset_check", "0 0 * * *", func(ctx context.Context) error {
|
||||
cs.Register("model_sunset_check", "模型下线检查", "0 0 * * *", func(ctx context.Context) error {
|
||||
return task.CheckModelSunset(ctx)
|
||||
})
|
||||
cs.Register("data_cleanup", "0 3 * * *", func(ctx context.Context) error {
|
||||
cs.Register("data_cleanup", "过期数据清理", "0 3 * * *", func(ctx context.Context) error {
|
||||
return admin.CleanupExpiredData(ctx)
|
||||
})
|
||||
cs.Register("export_file_cleanup", "0 4 * * *", func(ctx context.Context) error {
|
||||
cs.Register("export_file_cleanup", "导出文件清理", "0 4 * * *", func(ctx context.Context) error {
|
||||
return admin.CleanupExpiredExportFiles(ctx)
|
||||
})
|
||||
cs.Register("file_retention_check", "0 5 * * *", func(ctx context.Context) error {
|
||||
cs.Register("file_retention_check", "文件保留检查", "0 5 * * *", func(ctx context.Context) error {
|
||||
return admin.CheckFileRetention(ctx)
|
||||
})
|
||||
cs.Register("task_timeout_check", "*/10 * * * *", func(ctx context.Context) error {
|
||||
cs.Register("task_timeout_check", "任务超时检查", "*/10 * * * *", func(ctx context.Context) error {
|
||||
return admin.MarkStuckTasksFailed(ctx)
|
||||
})
|
||||
cs.Register("task_executor", "*/1 * * * *", func(ctx context.Context) error {
|
||||
cs.Register("task_executor", "异步任务执行", "*/1 * * * *", func(ctx context.Context) error {
|
||||
task.RunPendingTasks(ctx)
|
||||
return nil
|
||||
})
|
||||
cs.Register("project_budget_check", "*/5 * * * *", func(ctx context.Context) error {
|
||||
cs.Register("project_budget_check", "项目预算检查", "*/5 * * * *", func(ctx context.Context) error {
|
||||
return tenant.CheckBudgetExhausted(ctx)
|
||||
})
|
||||
cs.Register("usage_log_cleanup", "0 3 * * *", func(ctx context.Context) error {
|
||||
cs.Register("usage_log_cleanup", "用量日志清理", "0 3 * * *", func(ctx context.Context) error {
|
||||
retentionDays := common.Config().GetInt(ctx, "usage_log_retention_days")
|
||||
if retentionDays == 0 {
|
||||
retentionDays = 90
|
||||
}
|
||||
return task.ScheduleAutoCleanup(ctx, retentionDays)
|
||||
})
|
||||
cs.Register("usage_daily_aggregate", "0 1 * * *", func(ctx context.Context) error {
|
||||
cs.Register("usage_daily_aggregate", "用量日报表聚合", "0 1 * * *", func(ctx context.Context) error {
|
||||
// 用量日维度聚合:将 bil_usage_logs 聚合进 bil_usage_daily,供流量桑基图与趋势分析。
|
||||
// 自愈:每次重算最近 3 个完整天,覆盖短暂宕机;ON CONFLICT 保证幂等。
|
||||
// end 取今天 00:00(开区间),永不聚合当天进行中的数据。
|
||||
@@ -373,30 +373,30 @@ func registerCronJobs(cs *common.CronScheduler) {
|
||||
start := time.Now().AddDate(0, 0, -3).Format("2006-01-02")
|
||||
return task.AggregateUsageRange(ctx, start, end)
|
||||
})
|
||||
cs.Register("oauth_token_refresh", "*/10 * * * *", func(ctx context.Context) error {
|
||||
cs.Register("oauth_token_refresh", "OAuth 令牌刷新", "*/10 * * * *", func(ctx context.Context) error {
|
||||
return task.RefreshExpiringOAuthTokens(ctx)
|
||||
})
|
||||
cs.Register("prededuct_orphan_cleanup", "*/2 * * * *", func(ctx context.Context) error {
|
||||
cs.Register("prededuct_orphan_cleanup", "预扣孤儿清理", "*/2 * * * *", func(ctx context.Context) error {
|
||||
billing.CleanExpiredPreDeducts(ctx)
|
||||
return nil
|
||||
})
|
||||
cs.Register("prededuct_tracks_cleanup", "0 4 * * *", func(ctx context.Context) error {
|
||||
cs.Register("prededuct_tracks_cleanup", "预扣轨迹清理", "0 4 * * *", func(ctx context.Context) error {
|
||||
billing.CleanSettledPreDeductTracks(ctx)
|
||||
return nil
|
||||
})
|
||||
cs.Register("billing_daily_reconciliation", "20 5 * * *", func(ctx context.Context) error {
|
||||
cs.Register("billing_daily_reconciliation", "计费日对账", "20 5 * * *", func(ctx context.Context) error {
|
||||
// 日对账:聚合对账(bil_records vs bil_transactions)+ 交叉对账(usage_logs 反连接
|
||||
// 发现漏结算免单请求)+ 冻结余额一致性校验,结果经日志告警
|
||||
_, err := billing.RunDailyReconciliation(ctx)
|
||||
return err
|
||||
})
|
||||
cs.Register("update_check", "0 */6 * * *", func(ctx context.Context) error {
|
||||
cs.Register("update_check", "系统更新检查", "0 */6 * * *", func(ctx context.Context) error {
|
||||
if common.Config().GetBool(ctx, "update_auto_check_enabled") {
|
||||
return update.BackgroundCheck(ctx)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
cs.Register("order_expiration", "*/5 * * * *", func(ctx context.Context) error {
|
||||
cs.Register("order_expiration", "订单过期处理", "*/5 * * * *", func(ctx context.Context) error {
|
||||
return task.ExpirePendingOrders(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -40,9 +40,10 @@ func (s *sAdmin) CronJobList(ctx context.Context, _ *v1.CronJobListReq) (*v1.Cro
|
||||
items := make([]v1.CronJobItem, 0, len(jobs))
|
||||
for _, j := range jobs {
|
||||
item := v1.CronJobItem{
|
||||
Name: j.Name,
|
||||
Schedule: j.Schedule,
|
||||
IsRunning: j.IsRunning,
|
||||
Name: j.Name,
|
||||
DisplayName: j.DisplayName,
|
||||
Schedule: j.Schedule,
|
||||
IsRunning: j.IsRunning,
|
||||
}
|
||||
|
||||
if data, ok := jobMap[j.Name]; ok {
|
||||
|
||||
@@ -14,16 +14,18 @@ import (
|
||||
|
||||
// CronJob defines a scheduled job.
|
||||
type CronJob struct {
|
||||
Name string
|
||||
Schedule string // cron expression, e.g. "0 0 * * *" for daily at midnight
|
||||
Handler func(ctx context.Context) error
|
||||
Name string
|
||||
DisplayName string // 中文展示名,供管理后台列表展示;空则前端回退到 Name
|
||||
Schedule string // cron expression, e.g. "0 0 * * *" for daily at midnight
|
||||
Handler func(ctx context.Context) error
|
||||
}
|
||||
|
||||
// JobInfo contains info about a registered job for listing.
|
||||
type JobInfo struct {
|
||||
Name string `json:"name"`
|
||||
Schedule string `json:"schedule"`
|
||||
IsRunning bool `json:"is_running"`
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Schedule string `json:"schedule"`
|
||||
IsRunning bool `json:"is_running"`
|
||||
}
|
||||
|
||||
// CronScheduler manages scheduled tasks with distributed locking.
|
||||
@@ -64,13 +66,16 @@ func NewCronScheduler() *CronScheduler {
|
||||
}
|
||||
|
||||
// Register adds a scheduled job.
|
||||
func (cs *CronScheduler) Register(name, schedule string, handler func(ctx context.Context) error) {
|
||||
// name 为全局唯一标识(用作分布式锁 key 和 sys_cron_jobs 主键,必须稳定,禁止中文);
|
||||
// displayName 为管理后台展示用的中文名,与 name 就近声明,避免散落到前端映射表。
|
||||
func (cs *CronScheduler) Register(name, displayName, schedule string, handler func(ctx context.Context) error) {
|
||||
cs.mu.Lock()
|
||||
defer cs.mu.Unlock()
|
||||
cs.jobs = append(cs.jobs, &CronJob{
|
||||
Name: name,
|
||||
Schedule: schedule,
|
||||
Handler: handler,
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
Schedule: schedule,
|
||||
Handler: handler,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -85,9 +90,10 @@ func (cs *CronScheduler) ListJobs() []JobInfo {
|
||||
running := cs.running[j.Name]
|
||||
cs.runMu.Unlock()
|
||||
jobs = append(jobs, JobInfo{
|
||||
Name: j.Name,
|
||||
Schedule: j.Schedule,
|
||||
IsRunning: running,
|
||||
Name: j.Name,
|
||||
DisplayName: j.DisplayName,
|
||||
Schedule: j.Schedule,
|
||||
IsRunning: running,
|
||||
})
|
||||
}
|
||||
return jobs
|
||||
|
||||
@@ -67,7 +67,7 @@ func startPlugin(ctx context.Context, entry *PluginEntry) error {
|
||||
for _, job := range c.CronJobs() {
|
||||
jobFunc := job.Handler
|
||||
jobName := "plugin_" + job.Name
|
||||
cs.Register(jobName, job.CronExpr, func(ctx context.Context) error {
|
||||
cs.Register(jobName, job.DisplayName, job.CronExpr, func(ctx context.Context) error {
|
||||
jobFunc(ctx)
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -71,10 +71,11 @@ type HookResult struct {
|
||||
|
||||
// CronJobDef 定时任务定义。
|
||||
type CronJobDef struct {
|
||||
Name string // 任务名(全局唯一)
|
||||
CronExpr string // cron 表达式
|
||||
Handler func(ctx context.Context) // 任务处理函数
|
||||
Singleton bool // 是否单例执行
|
||||
Name string // 任务名(全局唯一,注册时自动加 plugin_ 前缀)
|
||||
DisplayName string // 中文展示名,供管理后台列表展示
|
||||
CronExpr string // cron 表达式
|
||||
Handler func(ctx context.Context) // 任务处理函数
|
||||
Singleton bool // 是否单例执行
|
||||
}
|
||||
|
||||
// ConfigFieldDef 配置字段定义。
|
||||
|
||||
@@ -99,10 +99,11 @@ func (p *examplePlugin) Routes(ctx context.Context, server *ghttp.Server) error
|
||||
func (p *examplePlugin) CronJobs() []plugin.CronJobDef {
|
||||
return []plugin.CronJobDef{
|
||||
{
|
||||
Name: "example_daily_cleanup",
|
||||
CronExpr: "0 3 * * *",
|
||||
Handler: p.dailyCleanup,
|
||||
Singleton: true,
|
||||
Name: "example_daily_cleanup",
|
||||
DisplayName: "示例插件数据清理",
|
||||
CronExpr: "0 3 * * *",
|
||||
Handler: p.dailyCleanup,
|
||||
Singleton: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,26 +15,6 @@ const execLoading = ref<Record<string, boolean>>({})
|
||||
const execData = ref<Record<string, any[]>>({})
|
||||
const execPagination = reactive<Record<string, { current: number; total: number }>>({})
|
||||
|
||||
const jobNameMap: Record<string, string> = {
|
||||
ops_system_collector: '系统指标采集',
|
||||
ops_alert_detector: '告警检测',
|
||||
ops_metrics_cleanup: '指标数据清理',
|
||||
partition_ensure: '分区维护',
|
||||
health_snapshot: '渠道健康快照',
|
||||
channel_auto_test: '渠道自动测试',
|
||||
model_sunset_check: '模型下线检查',
|
||||
data_cleanup: '过期数据清理',
|
||||
export_file_cleanup: '导出文件清理',
|
||||
file_retention_check: '文件保留检查',
|
||||
task_timeout_check: '任务超时检查',
|
||||
task_executor: '异步任务执行',
|
||||
usage_log_cleanup: '用量日志清理',
|
||||
oauth_token_refresh: 'OAuth 令牌刷新',
|
||||
cron_execution_cleanup: '执行记录清理',
|
||||
prededuct_orphan_cleanup: '预扣孤儿清理',
|
||||
plugin_example_daily_cleanup: '插件示例数据清理',
|
||||
}
|
||||
|
||||
function formatDuration(ms: number): string {
|
||||
if (ms < 1000) return `${ms}ms`
|
||||
return `${(ms / 1000).toFixed(1)}s`
|
||||
@@ -46,7 +26,7 @@ const columns: TableColumnData[] = [
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
render({ record }: { record: any }) {
|
||||
return h('span', jobNameMap[record.name] || record.name)
|
||||
return h('span', record.display_name || record.name)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user