feat: 添加汇率服务和缓存服务,优化数据源和日志服务
refactor: 重构数据源工厂和类型定义,提升代码可维护性 fix: 修复类型转换和状态机文档中的错误 docs: 更新服务架构文档,添加新的服务闭环流程 test: 添加汇率服务单元测试 chore: 清理无用代码和注释,优化代码结构
This commit is contained in:
260
dashboard/src/pages/AutoExecution/components/EvolutionStage.tsx
Normal file
260
dashboard/src/pages/AutoExecution/components/EvolutionStage.tsx
Normal 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;
|
||||
@@ -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;
|
||||
1
dashboard/src/pages/AutoExecution/index.ts
Normal file
1
dashboard/src/pages/AutoExecution/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './index.tsx';
|
||||
422
dashboard/src/pages/AutoExecution/index.tsx
Normal file
422
dashboard/src/pages/AutoExecution/index.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user