import { useState } from 'react'; import { Clock, Plus, Play, Pause, Trash2, AlertTriangle, Calendar, Zap, Database, ChevronDown, ChevronUp, X, Save, Eye } from 'lucide-react'; import { Card, CardContent } from '@/components/ui/card'; import { Switch } from '@/components/ui/switch'; type TaskFrequency = 'once' | 'hourly' | 'daily' | 'weekly' | 'monthly'; type TaskStatus = 'running' | 'paused' | 'completed' | 'failed'; type AutoTask = { id: string; name: string; description: string; enabled: boolean; frequency: TaskFrequency; cron: string; nextRun: string; lastRun: string; lastStatus: 'success' | 'failed' | 'none'; dbType: string; instance: string; sql: string; status: TaskStatus; notifyOnFail: boolean; createdBy: string; }; const frequencyLabels: Record = { once: '一次性', hourly: '每小时', daily: '每天', weekly: '每周', monthly: '每月', }; const initialTasks: AutoTask[] = [ { id: 'task-1', name: '每日数据归档', description: '将 30 天前的日志数据归档到历史表', enabled: true, frequency: 'daily', cron: '0 2 * * *', nextRun: '2024-01-16 02:00', lastRun: '2024-01-15 02:00', lastStatus: 'success', dbType: 'mysql', instance: '生产主库', sql: 'INSERT INTO logs_archive SELECT * FROM system_logs WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);\nDELETE FROM system_logs WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);', status: 'running', notifyOnFail: true, createdBy: 'admin' }, { id: 'task-2', name: '每周统计报表', description: '生成周活跃用户统计报表', enabled: true, frequency: 'weekly', cron: '0 6 * * 1', nextRun: '2024-01-22 06:00', lastRun: '2024-01-08 06:00', lastStatus: 'success', dbType: 'mysql', instance: '生产主库', sql: 'INSERT INTO report_weekly_active (week_start, week_end, active_users, new_users)\nSELECT \n DATE_SUB(NOW(), INTERVAL 7 DAY) as week_start,\n NOW() as week_end,\n COUNT(DISTINCT user_id) as active_users,\n COUNT(DISTINCT CASE WHEN created_at > DATE_SUB(NOW(), INTERVAL 7 DAY) THEN user_id END) as new_users\nFROM user_activity\nWHERE created_at > DATE_SUB(NOW(), INTERVAL 7 DAY);', status: 'running', notifyOnFail: true, createdBy: 'admin' }, { id: 'task-3', name: '索引健康检查', description: '检查慢查询并建议优化索引', enabled: true, frequency: 'daily', cron: '0 3 * * *', nextRun: '2024-01-16 03:00', lastRun: '2024-01-15 03:00', lastStatus: 'failed', dbType: 'mysql', instance: '生产主库', sql: 'SELECT \n table_schema,\n table_name,\n index_name,\n cardinality,\n table_rows,\n ROUND(cardinality / table_rows * 100, 2) as selectivity\nFROM information_schema.statistics\nWHERE table_schema NOT IN ("information_schema", "mysql", "performance_schema")\n AND index_name != "PRIMARY"\n AND cardinality / table_rows < 0.1\nORDER BY table_rows DESC\nLIMIT 100;', status: 'paused', notifyOnFail: true, createdBy: 'zhangsan' }, { id: 'task-4', name: 'Redis 键过期清理', description: '清理 Redis 中已过期的会话键', enabled: false, frequency: 'hourly', cron: '0 * * * *', nextRun: '-', lastRun: '2024-01-14 12:00', lastStatus: 'success', dbType: 'redis', instance: 'Redis集群-01', sql: 'EVAL "local keys = redis.call(\'keys\', \'session:*\')\nfor i=1,#keys,5000 do\n redis.call(\'del\', unpack(keys, i, math.min(i+4999, #keys)))\nend\nreturn #keys" 0', status: 'paused', notifyOnFail: false, createdBy: 'lisi' }, { id: 'task-5', name: '月度数据备份', description: '每月全量备份核心数据表', enabled: true, frequency: 'monthly', cron: '0 1 1 * *', nextRun: '2024-02-01 01:00', lastRun: '2024-01-01 01:00', lastStatus: 'success', dbType: 'mysql', instance: '生产主库', sql: '-- 调用备份脚本\nBACKUP DATABASE user_db, order_db TO S3://backups/monthly/;', status: 'running', notifyOnFail: true, createdBy: 'admin' }, ]; export default function AutoTaskPage() { const [tasks, setTasks] = useState(initialTasks); const [showCreate, setShowCreate] = useState(false); const [expandedId, setExpandedId] = useState(null); const [previewTask, setPreviewTask] = useState(null); // 表单状态 const [formName, setFormName] = useState(''); const [formDesc, setFormDesc] = useState(''); const [formFreq, setFormFreq] = useState('daily'); const [formCron, setFormCron] = useState('0 2 * * *'); const [formDbType, setFormDbType] = useState('mysql'); const [formInstance, setFormInstance] = useState('生产主库'); const [formSql, setFormSql] = useState(''); const [formNotify, setFormNotify] = useState(true); const toggleTask = (id: string) => { setTasks(prev => prev.map(t => t.id === id ? { ...t, enabled: !t.enabled, status: !t.enabled ? 'running' : 'paused' } : t)); }; const deleteTask = (id: string) => { setTasks(prev => prev.filter(t => t.id !== id)); }; const openCreate = () => { setFormName(''); setFormDesc(''); setFormFreq('daily'); setFormCron('0 2 * * *'); setFormDbType('mysql'); setFormInstance('生产主库'); setFormSql(''); setFormNotify(true); setShowCreate(true); }; const saveTask = () => { if (!formName.trim() || !formSql.trim()) return; const newTask: AutoTask = { id: `task-${Date.now()}`, name: formName, description: formDesc, enabled: true, frequency: formFreq, cron: formCron, nextRun: '即将计算...', lastRun: '-', lastStatus: 'none', dbType: formDbType, instance: formInstance, sql: formSql, status: 'running', notifyOnFail: formNotify, createdBy: 'admin', }; setTasks(prev => [...prev, newTask]); setShowCreate(false); }; const statusColors: Record = { running: 'bg-green-500/15 text-green-400', paused: 'bg-yellow-500/15 text-yellow-400', completed: 'bg-blue-500/15 text-blue-400', failed: 'bg-red-500/15 text-red-400', }; return (

自动任务

配置定时执行的 SQL 任务,支持 Cron 表达式

{[ { label: '任务总数', value: tasks.length, icon: Clock, color: 'text-blue-400', bg: 'bg-blue-400/10' }, { label: '运行中', value: tasks.filter(t => t.status === 'running').length, icon: Play, color: 'text-green-400', bg: 'bg-green-400/10' }, { label: '已暂停', value: tasks.filter(t => t.status === 'paused').length, icon: Pause, color: 'text-yellow-400', bg: 'bg-yellow-400/10' }, { label: '上次失败', value: tasks.filter(t => t.lastStatus === 'failed').length, icon: AlertTriangle, color: 'text-red-400', bg: 'bg-red-400/10' }, ].map((s, i) => (
{s.value}
{s.label}
))}
{tasks.map(task => { const isExpanded = expandedId === task.id; return (

{task.name}

{task.status === 'running' ? '运行中' : task.status === 'paused' ? '已暂停' : task.status === 'failed' ? '失败' : '完成'} {frequencyLabels[task.frequency]} {task.cron}

{task.description}

{task.instance} ({task.dbType.toUpperCase()}) 下次执行: {task.nextRun} 上次执行: {task.lastRun} 创建者: {task.createdBy}
{isExpanded && (
SQL 语句
{task.sql}
)}
); })}
{/* 预览弹窗 */} {previewTask && (
setPreviewTask(null)}>
e.stopPropagation()}>

{previewTask.name}

执行频率: {frequencyLabels[previewTask.frequency]} ({previewTask.cron})
数据库: {previewTask.instance} ({previewTask.dbType.toUpperCase()})
下次执行: {previewTask.nextRun}
上次状态: {previewTask.lastStatus === 'success' ? '成功' : previewTask.lastStatus === 'failed' ? '失败' : '未执行'}
SQL 语句
{previewTask.sql}
)} {/* 创建弹窗 */} {showCreate && (
setShowCreate(false)}>
e.stopPropagation()}>

新建自动任务

setFormName(e.target.value)} className="w-full px-3 py-2 bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-500" placeholder="例如:每日数据归档" />