feat: 添加汇率服务和缓存服务,优化数据源和日志服务

refactor: 重构数据源工厂和类型定义,提升代码可维护性

fix: 修复类型转换和状态机文档中的错误

docs: 更新服务架构文档,添加新的服务闭环流程

test: 添加汇率服务单元测试

chore: 清理无用代码和注释,优化代码结构
This commit is contained in:
2026-03-19 14:19:01 +08:00
parent 0dac26d781
commit aa2cf560c6
120 changed files with 33383 additions and 4347 deletions

View File

@@ -0,0 +1,260 @@
/**
* [FE-AIAUTO-003] 演进阶段展示组件
* 展示自动化等级的演进阶段和能力说明
*/
import React from 'react';
import {
Card,
Row,
Col,
Steps,
Tag,
Typography,
Space,
Badge,
Tooltip,
Descriptions,
Progress,
} from 'antd';
import {
RobotOutlined,
UserOutlined,
ThunderboltOutlined,
SafetyOutlined,
CheckCircleOutlined,
LockOutlined,
} from '@ant-design/icons';
import { AutomationLevel, LEVEL_CAPABILITIES, RiskLevel } from '@/services/autoExecutionDataSource';
const { Title, Text, Paragraph } = Typography;
const RISK_LEVEL_COLORS: Record<RiskLevel, string> = {
LOW: 'green',
MEDIUM: 'orange',
HIGH: 'red',
CRITICAL: 'purple',
};
const LEVEL_ICONS: Record<AutomationLevel, React.ReactNode> = {
L1: <UserOutlined />,
L2: <RobotOutlined />,
L3: <ThunderboltOutlined />,
L4: <SafetyOutlined />,
};
const LEVEL_DESCRIPTIONS: Record<AutomationLevel, {
title: string;
subtitle: string;
features: string[];
requirements: string[];
}> = {
L1: {
title: '建议模式',
subtitle: '所有操作需人工确认',
features: [
'AI仅提供建议',
'所有决策需人工审核',
'适合新模块或高风险场景',
'完整的操作日志记录',
],
requirements: [
'无需前置条件',
'默认初始等级',
],
},
L2: {
title: '辅助模式',
subtitle: '低风险操作可自动执行',
features: [
'低风险操作自动执行',
'中高风险需人工审核',
'支持金额限制',
'自动回滚机制',
],
requirements: [
'执行次数 ≥ 100次',
'成功率 ≥ 95%',
'平均置信度 ≥ 80%',
],
},
L3: {
title: '自动模式',
subtitle: '大部分操作可自动执行',
features: [
'大部分操作自动执行',
'仅高风险需审核',
'支持更高金额限制',
'智能异常检测',
],
requirements: [
'L2等级运行 ≥ 7天',
'执行次数 ≥ 500次',
'成功率 ≥ 97%',
],
},
L4: {
title: '全自主模式',
subtitle: '完全自动化运行',
features: [
'全自动化执行',
'包含关键操作',
'实时监控告警',
'智能风险控制',
],
requirements: [
'L3等级运行 ≥ 30天',
'执行次数 ≥ 2000次',
'成功率 ≥ 99%',
'管理员审批',
],
},
};
interface EvolutionStageProps {
currentLevel?: AutomationLevel;
onLevelClick?: (level: AutomationLevel) => void;
}
const EvolutionStage: React.FC<EvolutionStageProps> = ({
currentLevel = 'L1',
onLevelClick,
}) => {
const levels: AutomationLevel[] = ['L1', 'L2', 'L3', 'L4'];
const currentIndex = levels.indexOf(currentLevel);
return (
<Card title="自动化等级演进路线" className="evolution-stage-card">
<Steps
current={currentIndex}
size="small"
items={levels.map((level, index) => {
const capability = LEVEL_CAPABILITIES[level];
const desc = LEVEL_DESCRIPTIONS[level];
const isActive = index <= currentIndex;
const isLocked = index > currentIndex + 1;
return {
title: (
<Space>
<Tag color={isActive ? 'processing' : 'default'}>{level}</Tag>
{isLocked && <LockOutlined style={{ color: '#999' }} />}
</Space>
),
description: desc.title,
icon: LEVEL_ICONS[level],
status: isActive ? 'finish' : 'wait',
};
})}
/>
<Row gutter={[16, 16]} style={{ marginTop: 24 }}>
{levels.map((level, index) => {
const capability = LEVEL_CAPABILITIES[level];
const desc = LEVEL_DESCRIPTIONS[level];
const isActive = index <= currentIndex;
const isCurrent = index === currentIndex;
return (
<Col span={6} key={level}>
<Card
size="small"
hoverable={isActive}
onClick={() => isActive && onLevelClick?.(level)}
style={{
opacity: isActive ? 1 : 0.6,
border: isCurrent ? '2px solid #1890ff' : undefined,
}}
>
<Space direction="vertical" style={{ width: '100%' }}>
<Space>
{LEVEL_ICONS[level]}
<Text strong>{level}</Text>
<Tag color={isCurrent ? 'blue' : 'default'}>{desc.title}</Tag>
</Space>
<Text type="secondary" style={{ fontSize: 12 }}>
{desc.subtitle}
</Text>
<Descriptions column={1} size="small">
<Descriptions.Item label="最大风险等级">
<Tag color={RISK_LEVEL_COLORS[capability.max_risk_level]}>
{capability.max_risk_level}
</Tag>
</Descriptions.Item>
<Descriptions.Item label="金额倍数">
{capability.max_amount_multiplier}x
</Descriptions.Item>
<Descriptions.Item label="需审批">
{capability.requires_approval ? (
<CheckCircleOutlined style={{ color: '#52c41a' }} />
) : (
<Text type="secondary"></Text>
)}
</Descriptions.Item>
</Descriptions>
{index > 0 && (
<>
<Text type="secondary" style={{ fontSize: 11 }}>:</Text>
<ul style={{ margin: 0, paddingLeft: 16, fontSize: 11 }}>
{desc.requirements.map((req, i) => (
<li key={i}>{req}</li>
))}
</ul>
</>
)}
</Space>
</Card>
</Col>
);
})}
</Row>
<Card size="small" style={{ marginTop: 16, background: '#fafafa' }}>
<Title level={5}></Title>
<Row gutter={16}>
<Col span={6}>
<Text strong></Text>
<Progress
percent={(currentIndex + 1) * 25}
format={() => `${['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'][currentIndex]}`}
strokeColor={{
'0%': '#52c41a',
'50%': '#faad14',
'100%': '#f5222d',
}}
/>
</Col>
<Col span={6}>
<Text strong></Text>
<Progress
percent={(currentIndex + 1) * 25}
format={() => `${(currentIndex + 1) * 25}%`}
strokeColor="#1890ff"
/>
</Col>
<Col span={6}>
<Text strong></Text>
<Progress
percent={Math.min(LEVEL_CAPABILITIES[currentLevel].max_amount_multiplier * 20, 100)}
format={() => `${LEVEL_CAPABILITIES[currentLevel].max_amount_multiplier}x`}
strokeColor="#722ed1"
/>
</Col>
<Col span={6}>
<Text strong></Text>
<Progress
percent={100 - (currentIndex * 30)}
format={() => currentIndex === 0 ? '100%' : currentIndex === 1 ? '70%' : currentIndex === 2 ? '40%' : '10%'}
strokeColor="#13c2c2"
/>
</Col>
</Row>
</Card>
</Card>
);
};
export default EvolutionStage;

View File

@@ -0,0 +1,337 @@
/**
* [FE-AIAUTO-002] 阈值配置表单组件
* 用于配置自动执行的置信度阈值和风险限制
*/
import React from 'react';
import {
Form,
InputNumber,
Select,
Switch,
Divider,
Row,
Col,
Card,
Slider,
Space,
Tag,
Alert,
} from 'antd';
import {
ThunderboltOutlined,
SafetyOutlined,
ClockCircleOutlined,
BellOutlined,
} from '@ant-design/icons';
import { AutoExecutionConfig, RiskLevel, LEVEL_CAPABILITIES } from '@/services/autoExecutionDataSource';
const { Option } = Select;
interface ThresholdConfigFormProps {
config: AutoExecutionConfig;
onSubmit: (values: Partial<AutoExecutionConfig>) => void;
onCancel: () => void;
}
const ThresholdConfigForm: React.FC<ThresholdConfigFormProps> = ({
config,
onSubmit,
onCancel,
}) => {
const [form] = Form.useForm();
const handleSubmit = (values: any) => {
const thresholds = {
auto_execute: values.auto_execute / 100,
pending_review: values.pending_review / 100,
auto_reject: values.auto_reject / 100,
};
const riskLimits = {
max_amount: values.max_amount,
max_quantity: values.max_quantity,
allowed_risk_levels: values.allowed_risk_levels,
};
const timeRestrictions = {
allowed_hours: values.allowed_hours || Array.from({ length: 24 }, (_, i) => i),
excluded_dates: config.time_restrictions.excluded_dates,
};
const rollbackConfig = {
enabled: values.rollback_enabled,
max_attempts: values.max_attempts,
cooldown_minutes: values.cooldown_minutes,
};
const notificationConfig = {
on_execute: values.notify_execute,
on_fail: values.notify_fail,
on_rollback: values.notify_rollback,
channels: values.notification_channels,
};
onSubmit({
confidence_thresholds: thresholds,
risk_limits: riskLimits,
time_restrictions: timeRestrictions,
rollback_config: rollbackConfig,
notification_config: notificationConfig,
});
};
const levelCapability = LEVEL_CAPABILITIES[config.automation_level];
const maxAmountLimit = config.risk_limits.max_amount * levelCapability.max_amount_multiplier;
return (
<Form
form={form}
layout="vertical"
initialValues={{
auto_execute: config.confidence_thresholds.auto_execute * 100,
pending_review: config.confidence_thresholds.pending_review * 100,
auto_reject: config.confidence_thresholds.auto_reject * 100,
max_amount: config.risk_limits.max_amount,
max_quantity: config.risk_limits.max_quantity,
allowed_risk_levels: config.risk_limits.allowed_risk_levels,
allowed_hours: config.time_restrictions.allowed_hours,
rollback_enabled: config.rollback_config.enabled,
max_attempts: config.rollback_config.max_attempts,
cooldown_minutes: config.rollback_config.cooldown_minutes,
notify_execute: config.notification_config.on_execute,
notify_fail: config.notification_config.on_fail,
notify_rollback: config.notification_config.on_rollback,
notification_channels: config.notification_config.channels,
}}
onFinish={handleSubmit}
>
<Alert
message={`当前等级: ${config.automation_level} - ${levelCapability.description}`}
type="info"
showIcon
style={{ marginBottom: 16 }}
/>
<Card
title={<><ThunderboltOutlined /> </>}
size="small"
style={{ marginBottom: 16 }}
>
<Row gutter={16}>
<Col span={8}>
<Form.Item
name="auto_execute"
label="自动执行阈值 (%)"
tooltip="置信度达到此阈值时自动执行"
>
<Slider
min={50}
max={100}
marks={{ 50: '50%', 75: '75%', 100: '100%' }}
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="pending_review"
label="待审核阈值 (%)"
tooltip="置信度在此区间需要人工审核"
>
<Slider
min={30}
max={90}
marks={{ 30: '30%', 60: '60%', 90: '90%' }}
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="auto_reject"
label="自动拒绝阈值 (%)"
tooltip="置信度低于此阈值自动拒绝"
>
<Slider
min={10}
max={70}
marks={{ 10: '10%', 40: '40%', 70: '70%' }}
/>
</Form.Item>
</Col>
</Row>
</Card>
<Card
title={<><SafetyOutlined /> </>}
size="small"
style={{ marginBottom: 16 }}
>
<Row gutter={16}>
<Col span={8}>
<Form.Item
name="max_amount"
label="最大金额限制"
tooltip={`当前等级最大允许: ${maxAmountLimit}`}
>
<InputNumber
style={{ width: '100%' }}
min={0}
max={maxAmountLimit}
prefix="¥"
formatter={(value) => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="max_quantity"
label="最大数量限制"
>
<InputNumber
style={{ width: '100%' }}
min={1}
max={10000}
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="allowed_risk_levels"
label="允许的风险等级"
>
<Select mode="multiple" style={{ width: '100%' }}>
<Option value="LOW">
<Tag color="green">LOW</Tag>
</Option>
<Option value="MEDIUM">
<Tag color="orange">MEDIUM</Tag>
</Option>
<Option value="HIGH" disabled={levelCapability.max_risk_level === 'LOW'}>
<Tag color="red">HIGH</Tag>
</Option>
<Option value="CRITICAL" disabled={levelCapability.max_risk_level !== 'CRITICAL'}>
<Tag color="purple">CRITICAL</Tag>
</Option>
</Select>
</Form.Item>
</Col>
</Row>
</Card>
<Card
title={<><ClockCircleOutlined /> </>}
size="small"
style={{ marginBottom: 16 }}
>
<Form.Item
name="allowed_hours"
label="允许执行的时间段"
tooltip="选择允许自动执行的小时0-23"
>
<Select mode="multiple" style={{ width: '100%' }}>
{Array.from({ length: 24 }, (_, i) => (
<Option key={i} value={i}>
{i.toString().padStart(2, '0')}:00
</Option>
))}
</Select>
</Form.Item>
</Card>
<Card
title={<><ThunderboltOutlined /> </>}
size="small"
style={{ marginBottom: 16 }}
>
<Row gutter={16}>
<Col span={6}>
<Form.Item
name="rollback_enabled"
label="启用自动回滚"
valuePropName="checked"
>
<Switch />
</Form.Item>
</Col>
<Col span={9}>
<Form.Item
name="max_attempts"
label="最大重试次数"
>
<InputNumber min={1} max={10} style={{ width: '100%' }} />
</Form.Item>
</Col>
<Col span={9}>
<Form.Item
name="cooldown_minutes"
label="冷却时间(分钟)"
>
<InputNumber min={1} max={1440} style={{ width: '100%' }} />
</Form.Item>
</Col>
</Row>
</Card>
<Card
title={<><BellOutlined /> </>}
size="small"
style={{ marginBottom: 16 }}
>
<Row gutter={16}>
<Col span={6}>
<Form.Item
name="notify_execute"
label="执行时通知"
valuePropName="checked"
>
<Switch />
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
name="notify_fail"
label="失败时通知"
valuePropName="checked"
>
<Switch />
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
name="notify_rollback"
label="回滚时通知"
valuePropName="checked"
>
<Switch />
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
name="notification_channels"
label="通知渠道"
>
<Select mode="multiple" style={{ width: '100%' }}>
<Option value="EMAIL"></Option>
<Option value="SMS"></Option>
<Option value="WEBHOOK">Webhook</Option>
</Select>
</Form.Item>
</Col>
</Row>
</Card>
<Divider />
<Space style={{ width: '100%', justifyContent: 'flex-end' }}>
<button type="button" onClick={onCancel} className="ant-btn">
</button>
<button type="submit" className="ant-btn ant-btn-primary">
</button>
</Space>
</Form>
);
};
export default ThresholdConfigForm;

View File

@@ -0,0 +1 @@
export { default } from './index.tsx';

View File

@@ -0,0 +1,422 @@
/**
* [FE-AIAUTO-001] 自动化配置页面
* 管理AI决策自动执行的配置、阈值和等级
*/
import React, { useState, useEffect } from 'react';
import {
Card,
Table,
Button,
Space,
Tag,
Row,
Col,
Statistic,
Modal,
Form,
Input,
InputNumber,
Select,
Switch,
Tabs,
Tooltip,
Progress,
Badge,
message,
Descriptions,
Divider,
Alert,
Timeline,
Popconfirm,
} from 'antd';
import {
SettingOutlined,
ThunderboltOutlined,
SafetyOutlined,
ClockCircleOutlined,
CheckCircleOutlined,
CloseCircleOutlined,
ExclamationCircleOutlined,
RiseOutlined,
FallOutlined,
HistoryOutlined,
InfoCircleOutlined,
SyncOutlined,
} from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import {
autoExecutionDataSource,
AutoExecutionConfig,
LevelEvolutionRecord,
AutomationLevel,
DecisionModule,
MODULE_NAMES,
LEVEL_CAPABILITIES,
} from '@/services/autoExecutionDataSource';
import ThresholdConfigForm from './components/ThresholdConfigForm';
import EvolutionStage from './components/EvolutionStage';
const { Option } = Select;
const { TabPane } = Tabs;
const { TextArea } = Input;
const LEVEL_COLORS: Record<AutomationLevel, string> = {
L1: 'default',
L2: 'processing',
L3: 'success',
L4: 'warning',
};
const STATUS_COLORS: Record<string, string> = {
ACTIVE: 'success',
INACTIVE: 'default',
SUSPENDED: 'error',
};
const AutoExecutionConfigPage: React.FC = () => {
const [configs, setConfigs] = useState<AutoExecutionConfig[]>([]);
const [evolutionHistory, setEvolutionHistory] = useState<LevelEvolutionRecord[]>([]);
const [loading, setLoading] = useState(false);
const [selectedConfig, setSelectedConfig] = useState<AutoExecutionConfig | null>(null);
const [configModalVisible, setConfigModalVisible] = useState(false);
const [levelModalVisible, setLevelModalVisible] = useState(false);
const [levelAction, setLevelAction] = useState<'upgrade' | 'downgrade'>('upgrade');
const [levelForm] = Form.useForm();
useEffect(() => {
fetchConfigs();
fetchHistory();
}, []);
const fetchConfigs = async () => {
setLoading(true);
try {
const data = await autoExecutionDataSource.fetchConfigs('tenant-001');
setConfigs(data);
} catch (error) {
message.error('获取配置失败');
} finally {
setLoading(false);
}
};
const fetchHistory = async () => {
try {
const data = await autoExecutionDataSource.fetchEvolutionHistory('tenant-001');
setEvolutionHistory(data);
} catch (error) {
console.error('获取演进历史失败', error);
}
};
const handleUpdateConfig = async (values: Partial<AutoExecutionConfig>) => {
if (!selectedConfig) return;
try {
await autoExecutionDataSource.updateConfig(selectedConfig.id, values);
message.success('配置更新成功');
setConfigModalVisible(false);
fetchConfigs();
} catch (error) {
message.error('配置更新失败');
}
};
const handleLevelChange = async (values: { reason: string }) => {
if (!selectedConfig) return;
try {
const targetLevel = levelAction === 'upgrade'
? getNextLevel(selectedConfig.automation_level)
: getPrevLevel(selectedConfig.automation_level);
if (levelAction === 'upgrade') {
await autoExecutionDataSource.upgradeLevel(
selectedConfig.tenant_id,
selectedConfig.shop_id,
selectedConfig.module,
targetLevel,
values.reason
);
message.success(`等级已升级至 ${targetLevel}`);
} else {
await autoExecutionDataSource.downgradeLevel(
selectedConfig.tenant_id,
selectedConfig.shop_id,
selectedConfig.module,
targetLevel,
values.reason
);
message.success(`等级已降级至 ${targetLevel}`);
}
setLevelModalVisible(false);
levelForm.resetFields();
fetchConfigs();
fetchHistory();
} catch (error: any) {
message.error(error.message || '等级变更失败');
}
};
const getNextLevel = (current: AutomationLevel): AutomationLevel => {
const levels: AutomationLevel[] = ['L1', 'L2', 'L3', 'L4'];
const index = levels.indexOf(current);
return levels[Math.min(index + 1, 3)];
};
const getPrevLevel = (current: AutomationLevel): AutomationLevel => {
const levels: AutomationLevel[] = ['L1', 'L2', 'L3', 'L4'];
const index = levels.indexOf(current);
return levels[Math.max(index - 1, 0)];
};
const canUpgrade = (config: AutoExecutionConfig): boolean => {
if (config.automation_level === 'L4') return false;
const stats = config.statistics;
if (stats.total_executions < 100) return false;
const successRate = stats.success_count / stats.total_executions;
return successRate >= 0.95;
};
const columns: ColumnsType<AutoExecutionConfig> = [
{
title: '模块',
dataIndex: 'module',
key: 'module',
render: (module: DecisionModule) => (
<Tag color="blue">{MODULE_NAMES[module]}</Tag>
),
},
{
title: '自动化等级',
dataIndex: 'automation_level',
key: 'automation_level',
render: (level: AutomationLevel) => (
<Tag color={LEVEL_COLORS[level]}>{level}</Tag>
),
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: string) => (
<Badge status={STATUS_COLORS[status] as any} text={status} />
),
},
{
title: '置信度阈值',
key: 'thresholds',
render: (_, record) => (
<Space direction="vertical" size="small">
<span>: {(record.confidence_thresholds.auto_execute * 100).toFixed(0)}%</span>
<span>: {(record.confidence_thresholds.pending_review * 100).toFixed(0)}%</span>
</Space>
),
},
{
title: '执行统计',
key: 'statistics',
render: (_, record) => {
const stats = record.statistics;
const successRate = stats.total_executions > 0
? ((stats.success_count / stats.total_executions) * 100).toFixed(1)
: '0';
return (
<Space direction="vertical" size="small">
<span>: {stats.total_executions} </span>
<Progress
percent={parseFloat(successRate)}
size="small"
status={parseFloat(successRate) >= 95 ? 'success' : 'normal'}
format={() => `成功率: ${successRate}%`}
/>
</Space>
);
},
},
{
title: '平均置信度',
dataIndex: ['statistics', 'avg_confidence'],
key: 'avg_confidence',
render: (confidence: number) => (
<Progress
percent={confidence * 100}
size="small"
format={(percent) => `${percent?.toFixed(1)}%`}
/>
),
},
{
title: '操作',
key: 'action',
render: (_, record) => (
<Space>
<Tooltip title="配置阈值">
<Button
type="link"
icon={<SettingOutlined />}
onClick={() => {
setSelectedConfig(record);
setConfigModalVisible(true);
}}
/>
</Tooltip>
{record.automation_level !== 'L4' && (
<Tooltip title={canUpgrade(record) ? '升级等级' : '升级条件未满足需100次执行且95%成功率)'}>
<Button
type="link"
icon={<RiseOutlined />}
disabled={!canUpgrade(record)}
onClick={() => {
setSelectedConfig(record);
setLevelAction('upgrade');
setLevelModalVisible(true);
}}
/>
</Tooltip>
)}
{record.automation_level !== 'L1' && (
<Tooltip title="降级等级">
<Button
type="link"
icon={<FallOutlined />}
danger
onClick={() => {
setSelectedConfig(record);
setLevelAction('downgrade');
setLevelModalVisible(true);
}}
/>
</Tooltip>
)}
</Space>
),
},
];
return (
<div className="auto-execution-config-page">
<Row gutter={[16, 16]}>
<Col span={24}>
<Alert
message="AI决策自动化配置"
description="管理各模块的自动化等级和执行阈值。等级越高,自动执行范围越广,但需要满足严格的成功率要求。"
type="info"
showIcon
icon={<ThunderboltOutlined />}
/>
</Col>
<Col span={24}>
<EvolutionStage />
</Col>
<Col span={24}>
<Card title="模块配置列表" extra={
<Button icon={<SyncOutlined />} onClick={fetchConfigs}></Button>
}>
<Table
columns={columns}
dataSource={configs}
rowKey="id"
loading={loading}
pagination={false}
/>
</Card>
</Col>
<Col span={24}>
<Card title="等级演进历史" extra={<HistoryOutlined />}>
{evolutionHistory.length > 0 ? (
<Timeline
items={evolutionHistory.slice(0, 10).map(record => ({
color: record.to_level > record.from_level ? 'green' : 'red',
children: (
<div>
<p>
<Tag color="blue">{MODULE_NAMES[record.module]}</Tag>
<Tag>{record.from_level}</Tag>
<RiseOutlined style={{ margin: '0 8px' }} />
<Tag color="green">{record.to_level}</Tag>
</p>
<p>: {record.reason}</p>
<p>
<small>
: {(record.metrics.success_rate * 100).toFixed(1)}% |
: {record.metrics.total_executions} |
: {record.approved_by} |
: {new Date(record.created_at).toLocaleString()}
</small>
</p>
</div>
),
}))}
/>
) : (
<div style={{ textAlign: 'center', color: '#999', padding: '20px' }}>
</div>
)}
</Card>
</Col>
</Row>
<Modal
title={`配置阈值 - ${selectedConfig ? MODULE_NAMES[selectedConfig.module] : ''}`}
open={configModalVisible}
onCancel={() => setConfigModalVisible(false)}
footer={null}
width={700}
>
{selectedConfig && (
<ThresholdConfigForm
config={selectedConfig}
onSubmit={handleUpdateConfig}
onCancel={() => setConfigModalVisible(false)}
/>
)}
</Modal>
<Modal
title={`${levelAction === 'upgrade' ? '升级' : '降级'}自动化等级`}
open={levelModalVisible}
onCancel={() => {
setLevelModalVisible(false);
levelForm.resetFields();
}}
onOk={() => levelForm.submit()}
>
{selectedConfig && (
<>
<Alert
message={`当前等级: ${selectedConfig.automation_level} → 目标等级: ${levelAction === 'upgrade' ? getNextLevel(selectedConfig.automation_level) : getPrevLevel(selectedConfig.automation_level)}`}
type={levelAction === 'upgrade' ? 'success' : 'warning'}
showIcon
style={{ marginBottom: 16 }}
/>
<Descriptions column={1} bordered size="small">
<Descriptions.Item label="模块">{MODULE_NAMES[selectedConfig.module]}</Descriptions.Item>
<Descriptions.Item label="总执行次数">{selectedConfig.statistics.total_executions}</Descriptions.Item>
<Descriptions.Item label="成功率">
{selectedConfig.statistics.total_executions > 0
? ((selectedConfig.statistics.success_count / selectedConfig.statistics.total_executions) * 100).toFixed(1) + '%'
: 'N/A'}
</Descriptions.Item>
</Descriptions>
<Divider />
<Form form={levelForm} layout="vertical" onFinish={handleLevelChange}>
<Form.Item
name="reason"
label="变更原因"
rules={[{ required: true, message: '请输入变更原因' }]}
>
<TextArea rows={3} placeholder="请说明等级变更的原因..." />
</Form.Item>
</Form>
</>
)}
</Modal>
</div>
);
};
export default AutoExecutionConfigPage;