refactor: 优化代码结构和类型定义

feat(types): 添加express.d.ts类型引用
style: 格式化express.d.ts中的接口定义
refactor: 移除未使用的AntFC类型导入
chore: 删除自动生成的.umi-production文件
feat: 添加店铺管理相关表和初始化脚本
docs: 更新安全规则和交互指南文档
refactor: 统一使用FC类型替代React.FC
perf: 优化图表组件导入方式
style: 添加.prettierrc配置文件
refactor: 调整组件导入顺序和结构
feat: 添加平台库存管理路由
fix: 修复订单同步时的库存检查逻辑
docs: 更新RBAC设计和租户管理文档
refactor: 优化部门控制器代码
This commit is contained in:
2026-03-30 01:20:57 +08:00
parent d327706087
commit 1b14947e7b
106 changed files with 11251 additions and 38565 deletions

View File

@@ -0,0 +1,553 @@
/**
* [FE-MON004] 系统状态监控页面
* @description 展示系统健康状态、服务状态、性能指标、告警信息
* @version 1.0
*/
import { useState, useEffect, useCallback, FC } from '@/imports';
import {
Card,
Row,
Col,
Statistic,
Table,
Tag,
Alert,
Progress,
Badge,
Button,
Space,
Typography,
Tooltip,
Spin,
Descriptions,
Timeline,
Tabs,
} from 'antd';
import {
CheckCircleOutlined,
CloseCircleOutlined,
WarningOutlined,
SyncOutlined,
ReloadOutlined,
ServerOutlined,
DatabaseOutlined,
CloudServerOutlined,
AlertOutlined,
DashboardOutlined,
} from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import {
systemStatusDataSource,
MonitoringDashboardData,
ServiceStatus,
AlertInfo,
PerformanceDataPoint,
} from '@/services/systemStatusDataSource';
const { Title, Text } = Typography;
const { TabPane } = Tabs;
const SystemMonitoringPage: FC = () => {
const [loading, setLoading] = useState(true);
const [data, setData] = useState<MonitoringDashboardData | null>(null);
const [refreshing, setRefreshing] = useState(false);
const [autoRefresh, setAutoRefresh] = useState(true);
const loadData = useCallback(async () => {
try {
const result = await systemStatusDataSource.getDashboardData();
setData(result);
} catch (error) {
console.error('Failed to load monitoring data:', error);
} finally {
setLoading(false);
setRefreshing(false);
}
}, []);
useEffect(() => {
loadData();
let unsubscribe: (() => void) | null = null;
if (autoRefresh) {
unsubscribe = systemStatusDataSource.subscribeToUpdates((newData) => {
setData(newData);
});
}
return () => {
if (unsubscribe) {
unsubscribe();
}
};
}, [loadData, autoRefresh]);
const handleRefresh = () => {
setRefreshing(true);
loadData();
};
const handleAcknowledgeAlert = async (alertId: string) => {
await systemStatusDataSource.acknowledgeAlert(alertId);
loadData();
};
const handleRestartService = async (serviceId: string) => {
try {
const result = await systemStatusDataSource.restartService(serviceId);
if (result.success) {
loadData();
}
} catch (error) {
console.error('Failed to restart service:', error);
}
};
const getStatusBadge = (status: string) => {
switch (status) {
case 'healthy':
case 'up':
case 'running':
return <Badge status="success" text="正常" />;
case 'degraded':
return <Badge status="warning" text="降级" />;
case 'unhealthy':
case 'down':
case 'error':
return <Badge status="error" text="异常" />;
case 'stopped':
return <Badge status="default" text="停止" />;
case 'pending':
return <Badge status="processing" text="启动中" />;
default:
return <Badge status="default" text={status} />;
}
};
const getStatusIcon = (status: string) => {
switch (status) {
case 'healthy':
case 'up':
case 'running':
return <CheckCircleOutlined style={{ color: '#52c41a', fontSize: 24 }} />;
case 'degraded':
return <WarningOutlined style={{ color: '#faad14', fontSize: 24 }} />;
case 'unhealthy':
case 'down':
case 'error':
return <CloseCircleOutlined style={{ color: '#ff4d4f', fontSize: 24 }} />;
default:
return <SyncOutlined spin style={{ color: '#1890ff', fontSize: 24 }} />;
}
};
const serviceColumns: ColumnsType<ServiceStatus> = [
{
title: '服务名称',
dataIndex: 'name',
key: 'name',
render: (name: string, record) => (
<Space>
<CloudServerOutlined />
<span>{name}</span>
{record.version && <Tag color="blue">v{record.version}</Tag>}
</Space>
),
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 120,
render: (status: string) => getStatusBadge(status),
},
{
title: '运行时间',
dataIndex: 'uptime',
key: 'uptime',
width: 120,
render: (uptime: number) => formatUptime(uptime),
},
{
title: 'CPU',
dataIndex: 'cpu',
key: 'cpu',
width: 120,
render: (cpu: number) => (
<Progress
percent={Math.round(cpu)}
size="small"
status={cpu > 80 ? 'exception' : 'normal'}
format={(percent) => `${percent}%`}
/>
),
},
{
title: '内存',
dataIndex: 'memory',
key: 'memory',
width: 120,
render: (memory: number) => (
<Progress
percent={Math.round(memory)}
size="small"
status={memory > 80 ? 'exception' : 'normal'}
format={(percent) => `${percent}%`}
/>
),
},
{
title: '最后心跳',
dataIndex: 'lastHeartbeat',
key: 'lastHeartbeat',
width: 180,
render: (time: string) => new Date(time).toLocaleString(),
},
{
title: '操作',
key: 'action',
width: 100,
render: (_: unknown, record: ServiceStatus) => (
<Button
type="link"
size="small"
onClick={() => handleRestartService(record.id)}
disabled={record.status !== 'running'}
>
</Button>
),
},
];
const alertColumns: ColumnsType<AlertInfo> = [
{
title: '类型',
dataIndex: 'type',
key: 'type',
width: 80,
render: (type: string) => {
const colors: Record<string, string> = {
error: 'red',
warning: 'orange',
info: 'blue',
};
const icons: Record<string, React.ReactNode> = {
error: <CloseCircleOutlined />,
warning: <WarningOutlined />,
info: <AlertOutlined />,
};
return (
<Tag color={colors[type]} icon={icons[type]}>
{type.toUpperCase()}
</Tag>
);
},
},
{
title: '消息',
dataIndex: 'message',
key: 'message',
},
{
title: '来源',
dataIndex: 'source',
key: 'source',
width: 120,
},
{
title: '时间',
dataIndex: 'timestamp',
key: 'timestamp',
width: 180,
render: (time: string) => new Date(time).toLocaleString(),
},
{
title: '状态',
dataIndex: 'acknowledged',
key: 'acknowledged',
width: 100,
render: (acknowledged: boolean) => (
<Tag color={acknowledged ? 'green' : 'orange'}>
{acknowledged ? '已确认' : '待处理'}
</Tag>
),
},
{
title: '操作',
key: 'action',
width: 80,
render: (_: unknown, record: AlertInfo) => (
<Button
type="link"
size="small"
onClick={() => handleAcknowledgeAlert(record.id)}
disabled={record.acknowledged}
>
</Button>
),
},
];
if (loading) {
return (
<div style={{ textAlign: 'center', padding: '100px 0' }}>
<Spin size="large" tip="加载系统状态..." />
</div>
);
}
if (!data) {
return (
<Alert
message="无法加载系统状态"
description="请检查后端服务是否正常运行"
type="error"
showIcon
/>
);
}
return (
<div>
<Row gutter={[16, 16]}>
<Col span={24}>
<Card>
<Row justify="space-between" align="middle">
<Col>
<Space>
<DashboardOutlined style={{ fontSize: 24 }} />
<Title level={4} style={{ margin: 0 }}></Title>
</Space>
</Col>
<Col>
<Space>
<Button
icon={<SyncOutlined spin={refreshing} />}
onClick={handleRefresh}
loading={refreshing}
>
</Button>
<Button
type={autoRefresh ? 'primary' : 'default'}
icon={<ReloadOutlined />}
onClick={() => setAutoRefresh(!autoRefresh)}
>
{autoRefresh ? '停止自动刷新' : '开启自动刷新'}
</Button>
</Space>
</Col>
</Row>
</Card>
</Col>
</Row>
<Row gutter={[16, 16]} style={{ marginTop: 16 }}>
<Col span={6}>
<Card>
<Statistic
title="系统状态"
value={data.health.status === 'healthy' ? '正常' : data.health.status === 'degraded' ? '降级' : '异常'}
prefix={getStatusIcon(data.health.status)}
valueStyle={{
color: data.health.status === 'healthy' ? '#52c41a' :
data.health.status === 'degraded' ? '#faad14' : '#ff4d4f'
}}
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="CPU 使用率"
value={Math.round(data.health.metrics.cpu)}
suffix="%"
prefix={<ServerOutlined />}
valueStyle={{
color: data.health.metrics.cpu > 80 ? '#ff4d4f' :
data.health.metrics.cpu > 60 ? '#faad14' : '#52c41a'
}}
/>
<Progress
percent={Math.round(data.health.metrics.cpu)}
showInfo={false}
status={data.health.metrics.cpu > 80 ? 'exception' : 'normal'}
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="内存使用率"
value={Math.round(data.health.metrics.memory)}
suffix="%"
prefix={<DatabaseOutlined />}
valueStyle={{
color: data.health.metrics.memory > 80 ? '#ff4d4f' :
data.health.metrics.memory > 60 ? '#faad14' : '#52c41a'
}}
/>
<Progress
percent={Math.round(data.health.metrics.memory)}
showInfo={false}
status={data.health.metrics.memory > 80 ? 'exception' : 'normal'}
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="活跃连接"
value={data.activeConnections}
prefix={<CloudServerOutlined />}
/>
<div style={{ marginTop: 8 }}>
<Text type="secondary">: {data.queueSize}</Text>
</div>
</Card>
</Col>
</Row>
<Row gutter={[16, 16]} style={{ marginTop: 16 }}>
<Col span={6}>
<Card>
<Statistic
title="请求速率"
value={Math.round(data.health.metrics.requestRate)}
suffix="req/s"
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="平均响应时间"
value={Math.round(data.health.metrics.avgResponseTime)}
suffix="ms"
valueStyle={{
color: data.health.metrics.avgResponseTime > 500 ? '#ff4d4f' :
data.health.metrics.avgResponseTime > 200 ? '#faad14' : '#52c41a'
}}
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="错误率"
value={data.health.metrics.errorRate.toFixed(2)}
suffix="%"
valueStyle={{
color: data.health.metrics.errorRate > 5 ? '#ff4d4f' :
data.health.metrics.errorRate > 1 ? '#faad14' : '#52c41a'
}}
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="缓存命中率"
value={(data.cacheHitRate * 100).toFixed(1)}
suffix="%"
/>
</Card>
</Col>
</Row>
<Row gutter={[16, 16]} style={{ marginTop: 16 }}>
<Col span={24}>
<Card title="组件状态">
<Row gutter={[16, 16]}>
{Object.entries(data.health.components).map(([name, component]) => (
<Col span={4} key={name}>
<Tooltip title={`响应时间: ${component.responseTime}ms`}>
<Card size="small">
<div style={{ textAlign: 'center' }}>
{getStatusIcon(component.status)}
<div style={{ marginTop: 8 }}>
<Text strong>{name.toUpperCase()}</Text>
</div>
<div style={{ marginTop: 4 }}>
{getStatusBadge(component.status)}
</div>
</div>
</Card>
</Tooltip>
</Col>
))}
</Row>
</Card>
</Col>
</Row>
<Row gutter={[16, 16]} style={{ marginTop: 16 }}>
<Col span={24}>
<Card title="服务状态">
<Table
columns={serviceColumns}
dataSource={data.services}
rowKey="id"
pagination={false}
size="small"
/>
</Card>
</Col>
</Row>
<Row gutter={[16, 16]} style={{ marginTop: 16 }}>
<Col span={24}>
<Card title="告警信息">
<Table
columns={alertColumns}
dataSource={data.alerts}
rowKey="id"
pagination={{ pageSize: 5 }}
size="small"
/>
</Card>
</Col>
</Row>
<Row gutter={[16, 16]} style={{ marginTop: 16 }}>
<Col span={24}>
<Card title="系统信息">
<Descriptions column={4}>
<Descriptions.Item label="系统运行时间">
{formatUptime(data.health.metrics.uptime)}
</Descriptions.Item>
<Descriptions.Item label="磁盘使用率">
{Math.round(data.health.metrics.disk)}%
</Descriptions.Item>
<Descriptions.Item label="最后更新">
{new Date(data.health.timestamp).toLocaleString()}
</Descriptions.Item>
<Descriptions.Item label="数据刷新">
{autoRefresh ? '自动 (5秒)' : '手动'}
</Descriptions.Item>
</Descriptions>
</Card>
</Col>
</Row>
</div>
);
};
function formatUptime(seconds: number): string {
const days = Math.floor(seconds / 86400);
const hours = Math.floor((seconds % 86400) / 3600);
const mins = Math.floor((seconds % 3600) / 60);
if (days > 0) {
return `${days}${hours}小时`;
}
if (hours > 0) {
return `${hours}小时 ${mins}分钟`;
}
return `${mins}分钟`;
}
export default SystemMonitoringPage;

View File

@@ -1,172 +1,5 @@
import {
useState,
useEffect,
Card,
Table,
Button,
Space,
Input,
message,
Tag,
Modal,
Form,
Select,
DatePicker,
Row,
Col,
Statistic,
ReloadOutlined,
PlusOutlined,
SearchOutlined,
Option,
RangePicker,
FC,
} from '@/imports';
import { MonitoringDataSource } from '@/services/monitoringDataSource';
import type { MonitoringItem } from '@/services/monitoringDataSource';
const MonitoringPage: FC = () => {
const [data, setData] = useState<MonitoringItem[]>([]);
const [loading, setLoading] = useState(false);
const [searchText, setSearchText] = useState('');
const [modalVisible, setModalVisible] = useState(false);
const [form] = Form.useForm();
useEffect(() => {
loadData();
}, []);
const loadData = async () => {
setLoading(true);
try {
const result = await MonitoringDataSource.list();
setData(result);
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : String(error);
message.error(`加载失败: ${errorMessage}`);
} finally {
setLoading(false);
}
};
const handleCreate = async (values: any) => {
try {
await MonitoringDataSource.create(values);
message.success('创建成功');
setModalVisible(false);
form.resetFields();
loadData();
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : String(error);
message.error(`创建失败: ${errorMessage}`);
}
};
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
width: 200,
},
{
title: '名称',
dataIndex: 'name',
key: 'name',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: string) => {
const color = status === 'ACTIVE' ? 'green' : status === 'INACTIVE' ? 'red' : 'default';
return <Tag color={color}>{status}</Tag>;
},
},
{
title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
width: 180,
},
{
title: '操作',
key: 'action',
width: 150,
render: (_: any, record: MonitoringItem) => (
<Space>
<Button type="link" size="small"></Button>
<Button type="link" size="small"></Button>
</Space>
),
},
];
return (
<div>
<Card>
<Row gutter={16} style={{ marginBottom: 16 }}>
<Col span={6}>
<Statistic title="总数" value={data.length} />
</Col>
<Col span={6}>
<Statistic
title="活跃"
value={data.filter(item => item.status === 'ACTIVE').length}
valueStyle={{ color: '#3f8600' }}
/>
</Col>
</Row>
<div style={{ marginBottom: 16 }}>
<Space>
<Input
placeholder="搜索..."
prefix={<SearchOutlined />}
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
style={{ width: 200 }}
/>
<Button type="primary" icon={<PlusOutlined />} onClick={() => setModalVisible(true)}>
</Button>
<Button icon={<ReloadOutlined />} onClick={loadData}>
</Button>
</Space>
</div>
<Table
columns={columns}
dataSource={data.filter(item =>
item.name?.toLowerCase().includes(searchText.toLowerCase())
)}
rowKey="id"
loading={loading}
pagination={{ pageSize: 20 }}
/>
</Card>
<Modal
title="新建"
open={modalVisible}
onCancel={() => setModalVisible(false)}
onOk={() => form.submit()}
>
<Form form={form} layout="vertical" onFinish={handleCreate}>
<Form.Item name="name" label="名称" rules={[{ required: true }]}>
<Input placeholder="请输入名称" />
</Form.Item>
<Form.Item name="status" label="状态" rules={[{ required: true }]}>
<Select placeholder="请选择状态">
<Option value="ACTIVE"></Option>
<Option value="INACTIVE"></Option>
</Select>
</Form.Item>
</Form>
</Modal>
</div>
);
};
export default MonitoringPage;
/**
* 监控模块入口
* @description 包含监控配置和系统状态监控页面
*/
export { default as SystemStatus } from './SystemStatus';