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;