import React, { useState, useEffect } from 'react'; import { adminAPI } from '@/api'; interface AutomationTask { id: string; name: string; description: string; trigger_type: string; trigger_type_label: string; cron_expression: string; action_type: string; action_type_label: string; action_config: Record; event_condition: Record; is_enabled: boolean; status: string; max_retries: number; timeout_seconds: number; last_run_at: string; next_run_at: string; total_runs: number; success_runs: number; created_at: string; updated_at: string; } interface ExecLog { id: string; task_id: string; task_name: string; status: string; trigger_type: string; started_at: string; finished_at: string; duration_ms: number; result_summary: string; error_message: string; created_at: string; } interface AutomationStats { total_tasks: number; enabled_tasks: number; running_tasks: number; by_trigger: Record; recent_executions: number; recent_success: number; } const TRIGGER_CONFIG: Record = { cron: { label: '定时触发', color: '#6C63FF', bg: '#EDE9FE' }, webhook: { label: 'Webhook', color: '#059669', bg: '#D1FAE5' }, event: { label: '事件触发', color: '#D97706', bg: '#FEF3C7' }, }; const STATUS_CONFIG: Record = { idle: { label: '空闲', color: '#6B7280', bg: '#F3F4F6' }, running: { label: '运行中', color: '#3B82F6', bg: '#DBEAFE' }, success: { label: '成功', color: '#10B981', bg: '#D1FAE5' }, failed: { label: '失败', color: '#EF4444', bg: '#FEE2E2' }, }; const PAGE_SIZE = 15; const Automation: React.FC = () => { // Data state const [tasks, setTasks] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [loading, setLoading] = useState(false); const [stats, setStats] = useState(null); // Filter state const [filterTrigger, setFilterTrigger] = useState(''); const [keyword, setKeyword] = useState(''); // Modal state const [modalVisible, setModalVisible] = useState(false); const [editingTask, setEditingTask] = useState(null); const [formData, setFormData] = useState({ name: '', description: '', trigger_type: 'cron', cron_expression: '', event_condition_str: '', action_type: 'ai_pipeline', action_config_str: '{"pipeline_name": "default"}', is_enabled: true, max_retries: 3, timeout_seconds: 300, }); const [submitting, setSubmitting] = useState(false); // Execution history const [historyVisible, setHistoryVisible] = useState(false); const [historyTaskId, setHistoryTaskId] = useState(''); const [historyTaskName, setHistoryTaskName] = useState(''); const [historyLogs, setHistoryLogs] = useState([]); const [historyPage, setHistoryPage] = useState(1); const [historyTotal, setHistoryTotal] = useState(0); const [executingId, setExecutingId] = useState(null); // Toast const [toast, setToast] = useState<{ msg: string; type: 'success' | 'error' } | null>(null); const showToast = (msg: string, type: 'success' | 'error' = 'success') => { setToast({ msg, type }); setTimeout(() => setToast(null), 3000); }; // Load stats const loadStats = async () => { try { const result = await adminAPI.getAdminAutomationStats() as any; setStats(result.data || null); } catch (e) { /* ignore */ } }; // Load tasks const loadTasks = async () => { setLoading(true); try { const params: any = { page, page_size: PAGE_SIZE }; if (filterTrigger) params.trigger_type = filterTrigger; if (keyword) params.keyword = keyword; const result = await adminAPI.getAdminAutomationTasks(params) as any; const data = result.data || result; setTasks(data.items || []); setTotal(data.total || 0); } catch (e: any) { showToast('加载失败: ' + (e.message || e), 'error'); } finally { setLoading(false); } }; useEffect(() => { loadStats(); }, []); useEffect(() => { loadTasks(); }, [page, filterTrigger, keyword]); // Open edit modal (tasks are created by AI in chat; admin only edits/enables existing ones) const openEditModal = async (task: AutomationTask) => { try { const result = await adminAPI.getAdminAutomationTask(task.id) as any; const detail = result.data; setEditingTask(task); setFormData({ name: detail.name || '', description: detail.description || '', trigger_type: detail.trigger_type || 'cron', cron_expression: detail.cron_expression || '', event_condition_str: detail.event_condition ? JSON.stringify(detail.event_condition, null, 2) : '', action_type: detail.action_type || 'ai_pipeline', action_config_str: detail.action_config ? JSON.stringify(detail.action_config, null, 2) : '{}', is_enabled: detail.is_enabled !== false, max_retries: detail.max_retries || 3, timeout_seconds: detail.timeout_seconds || 300, }); setModalVisible(true); } catch (e: any) { showToast('获取任务详情失败', 'error'); } }; // Submit const handleSubmit = async () => { if (!formData.name.trim()) { showToast('请输入任务名称', 'error'); return; } if (formData.trigger_type === 'cron' && !formData.cron_expression.trim()) { showToast('请输入Cron表达式', 'error'); return; } let actionConfig: any = {}; if (formData.action_config_str.trim()) { try { actionConfig = JSON.parse(formData.action_config_str); } catch { showToast('操作配置JSON格式不正确', 'error'); return; } } let eventCondition: any = undefined; if (formData.event_condition_str.trim()) { try { eventCondition = JSON.parse(formData.event_condition_str); } catch { showToast('事件条件JSON格式不正确', 'error'); return; } } setSubmitting(true); try { const data: any = { name: formData.name.trim(), description: formData.description.trim(), trigger_type: formData.trigger_type, cron_expression: formData.cron_expression.trim(), action_type: formData.action_type, action_config: actionConfig, is_enabled: formData.is_enabled, max_retries: formData.max_retries, timeout_seconds: formData.timeout_seconds, }; if (eventCondition) data.event_condition = eventCondition; if (editingTask) { await adminAPI.updateAdminAutomationTask(editingTask.id, data); showToast('更新成功'); } else { await adminAPI.createAdminAutomationTask(data); showToast('创建成功'); } setModalVisible(false); loadTasks(); loadStats(); } catch (e: any) { showToast('操作失败: ' + (e.message || e), 'error'); } finally { setSubmitting(false); } }; // Delete const handleDelete = async (task: AutomationTask) => { if (!window.confirm(`确定要删除任务 "${task.name}" 吗?`)) return; try { await adminAPI.deleteAdminAutomationTask(task.id); showToast('删除成功'); loadTasks(); loadStats(); } catch (e: any) { showToast('删除失败: ' + (e.message || e), 'error'); } }; // Toggle const handleToggle = async (task: AutomationTask) => { try { await adminAPI.toggleAdminAutomationTask(task.id); showToast(task.is_enabled ? '已禁用' : '已启用'); loadTasks(); loadStats(); } catch (e: any) { showToast('操作失败: ' + (e.message || e), 'error'); } }; // Execute now const handleExecuteNow = async (task: AutomationTask) => { setExecutingId(task.id); try { const result = await adminAPI.executeAdminAutomationTask(task.id) as any; showToast(result.message || '执行完成'); loadTasks(); loadStats(); } catch (e: any) { showToast('执行失败: ' + (e.message || e), 'error'); } finally { setExecutingId(null); } }; // Show history const showHistory = async (task: AutomationTask, pageNum: number = 1) => { setHistoryTaskId(task.id); setHistoryTaskName(task.name); setHistoryPage(pageNum); setHistoryVisible(true); try { const result = await adminAPI.getAdminAutomationTaskHistory(task.id, { page: pageNum, page_size: 20 }) as any; const data = result.data || result; setHistoryLogs(data.items || []); setHistoryTotal(data.total || 0); } catch (e) { /* ignore */ } }; // Format time const formatTime = (ts: string) => { if (!ts) return '-'; try { return new Date(ts).toLocaleString('zh-CN'); } catch { return ts; } }; // Format duration const formatDuration = (ms: number) => { if (!ms) return '-'; if (ms < 1000) return `${ms}ms`; return `${(ms / 1000).toFixed(1)}s`; }; const labelStyle: React.CSSProperties = { display: 'block', marginBottom: '4px', fontSize: '13px', color: '#374151', fontWeight: 500 }; const inputStyle: React.CSSProperties = { width: '100%', padding: '8px 12px', borderRadius: '6px', border: '1px solid #D1D5DB', fontSize: '14px', boxSizing: 'border-box' }; const btnStyle = (bg: string, disabled = false): React.CSSProperties => ({ padding: '8px 20px', borderRadius: '6px', border: 'none', background: bg, cursor: disabled ? 'not-allowed' : 'pointer', fontSize: '14px', color: '#fff', opacity: disabled ? 0.7 : 1, }); return (

自动化任务调度

任务由 AI 在聊天中自动创建,此处仅支持查看、编辑与启停
{/* Stats Cards */} {stats && (
{stats.total_tasks}
总任务数
{stats.enabled_tasks}
已启用
{stats.running_tasks}
运行中
{stats.recent_executions}
24小时执行
{stats.recent_success}
24小时成功
)} {/* Filter Bar */}
{ setKeyword(e.target.value); setPage(1); }} style={{ padding: '8px 12px', borderRadius: '6px', border: '1px solid #D1D5DB', fontSize: '14px', flex: 1, maxWidth: '400px' }} />
{/* Table */}
{loading ? ( ) : tasks.length === 0 ? ( ) : ( tasks.map(task => ( )) )}
任务名称 触发方式 操作类型 状态 上次执行 下次执行 成功率 操作
加载中...
暂无自动化任务
{task.name}
{task.description &&
{task.description}
}
{(TRIGGER_CONFIG[task.trigger_type] || {}).label || task.trigger_type} {task.cron_expression &&
{task.cron_expression}
}
{task.action_type_label} {(STATUS_CONFIG[task.status] || {}).label || task.status} {formatTime(task.last_run_at)} {formatTime(task.next_run_at)} {task.total_runs > 0 ? = 0.9 ? '#10B981' : '#D97706' }}> {Math.round(task.success_runs / task.total_runs * 100)}% ({task.success_runs}/{task.total_runs}) : -}
{/* Pagination */} {total > PAGE_SIZE && (
第 {page} / {Math.ceil(total / PAGE_SIZE)} 页 (共 {total} 条)
)} {/* Create/Edit Modal */} {modalVisible && (

编辑任务

setFormData({ ...formData, name: e.target.value })} style={inputStyle} placeholder="如: 每日报表生成" />
setFormData({ ...formData, description: e.target.value })} style={inputStyle} placeholder="可选描述" />
{formData.trigger_type === 'cron' && (
setFormData({ ...formData, cron_expression: e.target.value })} style={{ ...inputStyle, fontFamily: 'monospace' }} placeholder="*/5 * * * * (每5分钟)" />
格式: 分 时 日 月 周 | 示例: 0 9 * * * (每天9点)
)} {formData.trigger_type !== 'cron' && (