Files

2289 lines
116 KiB
Markdown
Raw Permalink Normal View History

2026-06-23 10:40:10 +08:00
# 云睿资产系统 - Code Wiki
2026-06-23 10:40:10 +08:00
> 版本:2.0.0 | 更新日期:2026-05-07 | 项目地址:[源码仓库](https://code.xinmi.cloud/yunyunrui_asset
---
## 目录
1. [系统概述](#1-系统概述)
2. [整体架构](#2-整体架构)
3. [前端模块结构](#3-前端模块结构)
4. [后端模块结构](#4-后端模块结构)
5. [数据模型关系](#5-数据模型关系)
6. [API接口规范](#6-api接口规范)
7. [关键模块详解](#7-关键模块详解)
8. [依赖关系](#8-依赖关系)
9. [运行环境与配置](#9-运行环境与配置)
10. [部署指南](#10-部署指南)
11. [系统关系图](#11-系统关系图)
---
## 1. 系统概述
### 1.1 项目简介
2026-06-23 10:40:10 +08:00
云睿资产系统(YunRui Asset Management System)是一个现代化的数据中心设备全生命周期管理平台,提供机房、机柜、设备的完整管理能力,并具备3D可视化展示功能。
### 1.2 技术栈概览
| 层级 | 技术选型 | 版本 |
|------|----------|------|
| **前端框架** | React + Vite | 18.2.0 / 4.4.9 |
| **UI组件库** | Ant Design | 5.8.6 |
| **3D渲染** | Three.js + @react-three/fiber | 0.183.2 / 8.18.0 |
| **后端框架** | Express | 4.18.2 |
| **ORM框架** | Sequelize | 6.37.8 |
| **数据库** | SQLite / MySQL | 5.1.7 / 8.0+ |
| **认证** | JWT | 9.0.3 |
### 1.3 核心功能模块
| 模块 | 功能描述 | 页面入口 |
|------|----------|----------|
| **机房管理** | 多机房分类、平面图可视化 | /rooms |
| **机柜管理** | 机柜CRUD、容量统计、3D可视化 | /racks |
| **设备管理** | 设备全生命周期、批量导入导出 | /devices |
| **端口管理** | 设备端口配置、网卡绑定 | /ports |
| **线缆管理** | 机柜间线缆连接可视化追踪 | /cables |
| **网络拓扑** | ReactFlow拓扑可视化 | - |
| **工单管理** | 故障报修、维护工单全流程 | /tickets |
| **耗材管理** | 库存、领用、SN序列号追踪 | /consumables |
| **盘点管理** | 盘点计划、任务分配、盘盈设备 | /inventory |
| **数据看板** | 实时监控、趋势图表、功率监控 | / |
| **3D可视化** | Three.js机柜可视化、LOD优化 | /visualization-3d |
| **备份管理** | 本地/远程备份、定时任务 | /backup |
| **操作日志** | 审计日志、统计分析 | /operation-logs |
| **用户权限** | RBAC角色控制 | /users |
---
## 2. 整体架构
### 2.1 系统架构图
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 客户端层 (Client) │
├─────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 浏览器 │ │ 移动端 │ │ Web App │ │ PWA │ │
│ │ (Chrome) │ │ (响应式) │ │ (SPA) │ │ (可选) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ 前端层 (Frontend) │
├─────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ React 18 + Vite │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 页面组件 │ │ 3D组件 │ │ 通用组件 │ │ Hooks │ │ │
│ │ │ /pages │ │ /3d │ │/components│ │ /hooks │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Context │ │ API │ │ Utils │ │ Config │ │ │
│ │ │状态管理 │ │封装 │ │工具函数 │ │配置中心 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌───────────────┴───────────────┐
│ RESTful API │
│ /api/* │
└───────────────┬───────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ 后端层 (Backend) │
├─────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Express.js Server │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │中间件层 │ │路由层 │ │业务逻辑 │ │数据访问 │ │ │
│ │ │/middleware│ │/routes │ │/routes │ │/models │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │验证层 │ │认证授权 │ │日志审计 │ │备份恢复 │ │ │
│ │ │/validation│ │/middleware│ │/utils │ │/utils │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ 数据层 (Data Layer) │
├─────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ SQLite │ │ MySQL │ │
│ │ (开发环境) │◄────────────────►│ (生产环境) │ │
2026-06-23 10:40:10 +08:00
│ │ yunrui_management.db│ │ MySQL 8.0+ │ │
│ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.2 前端项目结构
```
frontend/
├── public/ # 静态公共资源
│ ├── assets/
│ │ └── 3d/
│ │ └── env.hdr # 3D环境贴图
│ └── png/ # 设备图标资源
│ ├── 服务器.png
│ ├── 交换机.png
│ ├── 路由器.png
│ └── 防火墙.png
├── src/ # 源代码目录
│ ├── api/ # API接口封装
│ │ ├── index.js # Axios实例、API方法
│ │ └── cache.js # 请求缓存管理
│ ├── components/ # React组件库
│ │ ├── 3d/ # 3D可视化组件
│ │ │ ├── materials/ # 3D材质组件
│ │ │ │ ├── TextureCache.js # 纹理缓存管理
│ │ │ │ ├── constants.jsx # 3D常量定义
│ │ │ │ ├── devicePanel.jsx # 设备信息面板
│ │ │ │ ├── index.jsx # 材质导出
│ │ │ │ ├── rackFrame.jsx # 机柜框架材质
│ │ │ │ └── utils.jsx # 3D工具函数
│ │ │ ├── CascadingRackPanel.jsx # 级联机柜面板
│ │ │ ├── DeviceModel.jsx # 设备3D模型
│ │ │ ├── LODManager.jsx # LOD细节层次管理
│ │ │ ├── RackModel.jsx # 机柜3D模型
│ │ │ ├── RackSelectorHeader.jsx # 机柜选择器
│ │ │ └── Scene.jsx # 3D主场景
│ │ ├── dashboard/ # 数据看板组件
│ │ │ ├── AnimatedCounter.jsx # 数字动画
│ │ │ ├── CircularProgress.jsx # 环形进度
│ │ │ ├── DeviceTrendChart.jsx # 设备趋势图
│ │ │ ├── NavigationGrid.jsx # 导航网格
│ │ │ ├── PowerGauge.jsx # 功率仪表
│ │ │ ├── QuickStats.jsx # 快速统计
│ │ │ ├── StatCard.jsx # 统计卡片
│ │ │ ├── StatusLegend.jsx # 状态图例
│ │ │ └── SystemInfo.jsx # 系统信息
│ │ ├── device/ # 设备管理组件
│ │ │ ├── BatchStatusModal.jsx # 批量状态修改
│ │ │ ├── DeviceDetailModal.jsx # 设备详情
│ │ │ ├── DeviceFormModal.jsx # 设备表单
│ │ │ ├── ExportModal.jsx # 导出弹窗
│ │ │ ├── FieldConfigModal.jsx # 字段配置
│ │ │ ├── ImportModal.jsx # 导入弹窗
│ │ │ └── ResizableTitle.jsx # 可调整标题
│ │ ├── floorplan/ # 机房平面图组件
│ │ │ ├── canvas/ # Canvas渲染
│ │ │ │ ├── CanvasConstants.js
│ │ │ │ ├── CanvasInteraction.js
│ │ │ │ ├── CanvasRenderer.js
│ │ │ │ ├── FloorPlanCanvas.jsx
│ │ │ │ └── index.js
│ │ │ ├── panels/ # 侧边面板
│ │ │ ├── toolbar/ # 工具栏
│ │ │ ├── index.js
│ │ │ └── styles.js
│ │ ├── rack/ # 机柜组件
│ │ ├── topology/ # 网络拓扑组件
│ │ │ ├── hooks/ # 拓扑Hooks
│ │ │ │ ├── useTopologyData.js
│ │ │ │ └── useTopologyLayout.js
│ │ │ └── nodes/ # 拓扑节点
│ │ ├── CableCreateModal.jsx # 线缆创建
│ │ ├── CableWizardModal.jsx # 线缆向导
│ │ ├── DeviceDetailDrawer.jsx # 设备详情抽屉
│ │ ├── ErrorBoundary.jsx # 错误边界
│ │ ├── NetworkCardPanel.jsx # 网卡面板
│ │ ├── PortManagementPanel.jsx # 端口管理面板
│ │ ├── ProtectedRoute.jsx # 路由保护
│ │ └── VirtualDeviceList.jsx # 虚拟设备列表
│ ├── config/ # 配置文件
│ │ ├── api.js # API基础配置
│ │ ├── dangerousOperationConfig.js # 危险操作配置
│ │ └── theme.js # 主题配置
│ ├── constants/ # 常量定义
│ │ └── deviceManagementConstants.js
│ ├── context/ # React Context
│ │ ├── AuthContext.jsx # 认证上下文
│ │ ├── ConfigContext.jsx # 配置上下文
│ │ ├── FloorPlanContext.jsx # 平面图上下文
│ │ └── Scene3DContext.jsx # 3D场景上下文
│ ├── hooks/ # 自定义Hooks
│ │ ├── floorplan/ # 平面图Hooks
│ │ │ ├── useFloorPlanContext.js
│ │ │ └── useFloorPlanData.js
│ │ ├── useApi.js # API请求Hook
│ │ ├── useDangerousOperation.js # 危险操作Hook
│ │ ├── useDebounce.js # 防抖Hook
│ │ ├── useDesignTokens.js # 设计令牌Hook
│ │ ├── useIdleTimeout.js # 空闲超时Hook
│ │ ├── useResponsiveLayout.js # 响应式布局Hook
│ │ ├── useSWR.js # SWR数据请求Hook
│ │ └── useSortedRacks.js # 机柜排序Hook
│ ├── pages/ # 页面组件
│ │ ├── Dashboard.jsx # 数据看板主页
│ │ ├── Login.jsx # 登录页
│ │ ├── RoomManagement.jsx # 机房管理
│ │ ├── RoomFloorPlan.jsx # 机房平面图
│ │ ├── RackManagement.jsx # 机柜管理
│ │ ├── DeviceManagement.jsx # 设备管理
│ │ ├── DeviceFieldManagement.jsx # 设备字段管理
│ │ ├── IdleDeviceManagement.jsx # 空闲设备管理
│ │ ├── PendingDeviceManagement.jsx # 待审核设备
│ │ ├── PortManagement.jsx # 端口管理
│ │ ├── CableManagement.jsx # 线缆管理
│ │ ├── TicketManagement.jsx # 工单管理
│ │ ├── TicketCategoryManagement.jsx # 工单分类
│ │ ├── TicketFieldManagement.jsx # 工单字段
│ │ ├── TicketStatistics.jsx # 工单统计
│ │ ├── ConsumableManagement.jsx # 耗材管理
│ │ ├── CategoryManagement.jsx # 耗材分类
│ │ ├── ConsumableLogs.jsx # 耗材日志
│ │ ├── ConsumableStatistics.jsx # 耗材统计
│ │ ├── InventoryManagement.jsx # 盘点管理
│ │ ├── InventoryTaskExecution.jsx # 盘点任务执行
│ │ ├── OperationLogs.jsx # 操作日志
│ │ ├── UserManagement.jsx # 用户管理
│ │ ├── SystemSettings.jsx # 系统设置
│ │ ├── BackupManagement.jsx # 备份管理
│ │ ├── AutoBackupSettings.jsx # 自动备份设置
│ │ ├── RemoteBackupSettings.jsx # 远程备份设置
│ │ ├── Rack3DVisualization.jsx # 3D可视化
│ │ └── ErrorBoundaryTest.jsx # 错误边界测试
│ ├── styles/ # 样式文件
│ │ └── deviceManagementStyles.js
│ ├── utils/ # 工具函数
│ │ ├── common.js # 通用工具
│ │ ├── crypto.js # 加密工具
│ │ ├── deviceUtils.jsx # 设备相关工具
│ │ ├── logger.js # 日志工具
│ │ └── secureStorage.js # 安全存储
│ ├── App.jsx # 应用入口
│ ├── main.jsx # 渲染入口
│ └── index.css # 全局样式
├── vite.config.mjs # Vite配置
├── package.json # 前端依赖
└── .env.example # 环境变量示例
```
### 2.3 后端项目结构
```
backend/
├── config/ # 配置模块
│ ├── index.js # 配置统一导出
│ ├── constants.js # 常量配置
│ ├── routes.js # 路由注册配置
│ ├── security.js # 安全配置
│ ├── auto-backup-settings.json # 自动备份设置
│ └── remote-backup-configs.json # 远程备份配置
├── middleware/ # 中间件
│ ├── auth.js # JWT认证中间件
│ ├── maintenance.js # 维护模式中间件
│ ├── requestLogger.js # 请求日志中间件
│ └── validation.js # 验证中间件
├── models/ # Sequelize数据模型
│ ├── index.js # 模型关联定义
│ ├── Room.js # 机房模型
│ ├── Rack.js # 机柜模型
│ ├── Device.js # 设备模型
│ ├── DeviceField.js # 设备字段模型
│ ├── DevicePort.js # 设备端口模型
│ ├── NetworkCard.js # 网卡模型
│ ├── Cable.js # 线缆模型
│ ├── Ticket.js # 工单模型
│ ├── TicketField.js # 工单字段模型
│ ├── TicketCategory.js # 工单分类模型
│ ├── TicketOperationRecord.js # 工单操作记录
│ ├── FaultCategory.js # 故障分类模型
│ ├── Consumable.js # 耗材模型
│ ├── ConsumableCategory.js # 耗材分类模型
│ ├── ConsumableRecord.js # 耗材领用记录
│ ├── ConsumableLog.js # 耗材日志模型
│ ├── ConsumableLogArchive.js # 耗材日志归档
│ ├── InventoryPlan.js # 盘点计划模型
│ ├── InventoryTask.js # 盘点任务模型
│ ├── InventoryRecord.js # 盘点记录模型
│ ├── User.js # 用户模型
│ ├── Role.js # 角色模型
│ ├── Permission.js # 权限模型
│ ├── UserRole.js # 用户角色关联
│ ├── SystemSetting.js # 系统设置模型
│ ├── PendingDevice.js # 待审核设备模型
│ ├── OperationLog.js # 操作日志模型
│ ├── BackupLog.js # 备份日志模型
│ ├── Warehouse.js # 仓库模型
│ ├── Business.js # 业务模型
│ ├── DeviceBusiness.js # 设备业务关联
│ └── ticketIndex.js # 工单索引模型
├── routes/ # API路由
│ ├── auth.js # 认证路由
│ ├── rooms.js # 机房路由
│ ├── racks.js # 机柜路由
│ ├── devices.js # 设备路由
│ ├── deviceFields.js # 设备字段路由
│ ├── devicePorts.js # 设备端口路由
│ ├── networkCards.js # 网卡路由
│ ├── cables.js # 线缆路由
│ ├── tickets.js # 工单路由
│ ├── ticketCategories.js # 工单分类路由
│ ├── ticketFields.js # 工单字段路由
│ ├── consumables.js # 耗材路由
│ ├── consumableCategories.js # 耗材分类路由
│ ├── consumableRecords.js # 耗材记录路由
│ ├── consumableImport.js # 耗材导入路由
│ ├── inventory.js # 盘点路由
│ ├── users.js # 用户路由
│ ├── roles.js # 角色路由
│ ├── systemSettings.js # 系统设置路由
│ ├── background.js # 背景配置路由
│ ├── backup.js # 备份管理路由
│ ├── statistics.js # 统计分析路由
│ ├── operationLogs.js # 操作日志路由
│ ├── idleDevices.js # 空闲设备路由
│ ├── warehouses.js # 仓库路由
│ ├── dangerousOperations.js # 危险操作路由
│ └── topology.js # 拓扑路由
├── scripts/ # 数据库脚本
│ ├── init-database.js # 数据库初始化
│ ├── migrate-all.js # 执行所有迁移
│ ├── fix-foreign-keys.js # 外键修复
│ ├── backup.js # 备份脚本
│ ├── restore.js # 恢复脚本
│ ├── create_indexes.js # 创建索引
│ ├── generate-rack-import-template.js # 生成导入模板
│ └── archive/ # 归档迁移脚本
│ ├── migrate-v2.js
│ ├── migrate-consumable-logs.js
│ ├── migrate-consumable-version.js
│ ├── migrate-consumable-log-decouple.js
│ ├── migrate-consumable-log-archive.js
│ ├── remove-consumable-log-fk.js
│ └── migrate-add-pending-status.js
├── utils/ # 工具函数
│ ├── autoBackupScheduler.js # 自动备份调度器
│ ├── backup.js # 备份工具
│ ├── remoteBackup.js # 远程备份
│ ├── remoteBackupConfig.js # 远程备份配置
│ ├── backupLog.js # 备份日志
│ ├── dangerousOperationLogger.js # 危险操作日志
│ ├── errorHandler.js # 错误处理器
│ ├── healthCheck.js # 健康检查
│ ├── idGenerator.js # ID生成器
│ ├── importJobManager.js # 导入任务管理
│ ├── logger.js # Winston日志
│ ├── maintenanceMode.js # 维护模式
│ ├── operationLogger.js # 操作日志
│ └── routeLoader.js # 路由加载器
├── validation/ # Joi验证Schema
│ ├── deviceSchema.js
│ ├── rackSchema.js
│ └── roomSchema.js
├── uploads/ # 文件上传目录
│ └── avatars/ # 用户头像
├── tests/ # 测试文件
├── temp/ # 临时文件目录
├── server.js # 服务入口
├── db.js # 数据库连接
├── initConfig.js # 初始化配置
├── initDeviceFields.js # 初始化设备字段
├── initTicketFields.js # 初始化工单字段
├── swagger.js # Swagger配置
├── swagger_docs.yaml # Swagger文档
├── swagger_index.html # Swagger页面
├── package.json # 后端依赖
├── .env.example # 环境变量示例
└── jest.config.js # Jest配置
```
---
## 3. 前端模块结构
### 3.1 组件架构
前端采用**组件化架构**,核心组件分类如下:
#### 3.1.1 页面组件 (pages/)
| 页面组件 | 文件路径 | 功能描述 |
|----------|----------|----------|
| Dashboard | pages/Dashboard.jsx | 数据看板主页,展示统计卡片、趋势图、功率仪表 |
| Login | pages/Login.jsx | 用户登录/注册页面 |
| RoomManagement | pages/RoomManagement.jsx | 机房增删改查,支持图片上传 |
| RackManagement | pages/RackManagement.jsx | 机柜管理,支持批量导入导出 |
| DeviceManagement | pages/DeviceManagement.jsx | 设备全生命周期管理 |
| PortManagement | pages/PortManagement.jsx | 设备端口配置管理 |
| CableManagement | pages/CableManagement.jsx | 线缆连接管理 |
| TicketManagement | pages/TicketManagement.jsx | 工单全流程管理 |
| ConsumableManagement | pages/ConsumableManagement.jsx | 耗材库存管理 |
| InventoryManagement | pages/InventoryManagement.jsx | 盘点计划与执行 |
| Rack3DVisualization | pages/Rack3DVisualization.jsx | Three.js 3D可视化 |
| RoomFloorPlan | pages/RoomFloorPlan.jsx | Canvas机房平面图 |
| BackupManagement | pages/BackupManagement.jsx | 数据库备份恢复 |
| UserManagement | pages/UserManagement.jsx | 用户与权限管理 |
#### 3.1.2 3D可视化组件 (components/3d/)
| 组件 | 文件 | 功能描述 |
|------|------|----------|
| Scene | Scene.jsx | Three.js主场景,管理相机、灯光、渲染器 |
| RackModel | RackModel.jsx | 机柜3D模型,支持LOD |
| DeviceModel | DeviceModel.jsx | 设备3D模型,根据类型渲染 |
| LODManager | LODManager.jsx | 细节层次管理,优化性能 |
| CascadingRackPanel | CascadingRackPanel.jsx | 级联机柜信息面板 |
| TextureCache | materials/TextureCache.js | 纹理缓存,减少加载时间 |
| devicePanel | materials/devicePanel.jsx | 设备悬停信息面板 |
#### 3.1.3 数据看板组件 (components/dashboard/)
| 组件 | 功能描述 |
|------|----------|
| StatCard | 统计卡片,展示单个指标 |
| AnimatedCounter | 数字动画组件 |
| CircularProgress | 环形进度指示器 |
| DeviceTrendChart | 设备趋势图表(ECharts) |
| PowerGauge | 功率监控仪表盘 |
| StatusLegend | 设备状态图例 |
| NavigationGrid | 快捷导航网格 |
| SystemInfo | 系统运行信息 |
### 3.2 Context状态管理
```javascript
// AuthContext - 认证状态管理
AuthContext {
user: Object, // 当前用户信息
token: String, // JWT令牌
loading: Boolean, // 加载状态
initialized: Boolean, // 初始化完成
login(), // 登录方法
register(), // 注册方法
logout(), // 登出方法
hasPermission(), // 权限检查
}
// ConfigContext - 全局配置管理
ConfigContext {
config: Object, // 系统配置
updateConfig(), // 更新配置
}
// Scene3DContext - 3D场景状态
Scene3DContext {
scene: Object, // Three.js场景
camera: Object, // 相机
selectedRack: Object, // 选中机柜
setSelectedRack(), // 设置选中
}
// FloorPlanContext - 平面图状态
FloorPlanContext {
canvas: Object, // Canvas对象
roomData: Object, // 机房数据
racks: Array, // 机柜列表
}
```
### 3.3 关键Hooks
| Hook | 文件 | 功能描述 |
|------|------|----------|
| useApi | hooks/useApi.js | 封装API请求,支持 loading/error 状态 |
| useSWR | hooks/useSWR.js | SWR数据获取与缓存 |
| useDebounce | hooks/useDebounce.js | 防抖处理 |
| useIdleTimeout | hooks/useIdleTimeout.js | 空闲超时检测 |
| useDangerousOperation | hooks/useDangerousOperation.js | 危险操作二次确认 |
| useDesignTokens | hooks/useDesignTokens.js | Ant Design主题令牌 |
| useResponsiveLayout | hooks/useResponsiveLayout.js | 响应式布局 |
| useSortedRacks | hooks/useSortedRacks.js | 机柜排序 |
### 3.4 API封装 (api/index.js)
```javascript
// Axios实例配置
const api = axios.create({
baseURL: '/api', // API基础URL
timeout: 30000, // 超时30秒
headers: { 'Content-Type': 'application/json' }
});
// 请求拦截器 - 自动添加Token
api.interceptors.request.use(config => {
const token = secureStorage.get(TOKEN_KEY);
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
// 响应拦截器 - 统一错误处理
api.interceptors.response.use(
response => response.data,
error => {
if (error.response?.status === 401) {
// Token过期,跳转登录
}
return Promise.reject(error);
}
);
// API模块导出
export const authAPI = { login, register, getProfile, ... };
export const deviceAPI = { list, get, create, update, delete, ... };
export const ticketAPI = { list, get, create, assign, process, ... };
export const backupAPI = { list, create, restore, download, ... };
// ... 更多API模块
```
---
## 4. 后端模块结构
### 4.1 服务入口 (server.js)
```javascript
// server.js - 应用入口流程
async function initializeApp() {
// 1. 数据库同步
await syncDatabase(); // 连接并同步表结构
// 2. 初始化设备字段
await initDeviceFields();
// 3. 初始化工单字段
await initTicketFields();
// 4. 初始化工单模型关联
await initTicketModels();
// 5. 同步耗材模型
await syncConsumableModels();
// 6. 同步盘点模型
await syncInventoryModels();
// 7. 初始化系统设置
await initDefaultSystemSettings();
// 8. 初始化故障分类
await initFaultCategories();
// 9. 初始化自动备份调度器
await initAutoBackupScheduler();
}
```
### 4.2 数据库连接 (db.js)
```javascript
// 支持 SQLite 和 MySQL 双数据库
const DB_TYPE = process.env.DB_TYPE || 'sqlite';
if (DB_TYPE === 'mysql') {
sequelize = new Sequelize({
dialect: 'mysql',
host: process.env.MYSQL_HOST,
port: process.env.MYSQL_PORT,
database: process.env.MYSQL_DATABASE,
username: process.env.MYSQL_USERNAME,
password: process.env.MYSQL_PASSWORD,
pool: { max: 10, min: 2, acquire: 30000, idle: 10000 }
});
} else {
sequelize = new Sequelize({
dialect: 'sqlite',
2026-06-23 10:40:10 +08:00
storage: process.env.DB_PATH || './yunrui_management.db'
});
}
```
### 4.3 中间件层
| 中间件 | 文件 | 功能描述 |
|--------|------|----------|
| authMiddleware | middleware/auth.js | JWT令牌验证、用户认证 |
| maintenanceMiddleware | middleware/maintenance.js | 维护模式拦截 |
| requestLogger | middleware/requestLogger.js | HTTP请求日志记录 |
| validationMiddleware | middleware/validation.js | Joi数据验证 |
### 4.4 路由模块 (routes/)
| 路由模块 | 基础路径 | 功能描述 |
|----------|----------|----------|
| auth | /api/auth | 登录、注册、Token管理 |
| rooms | /api/rooms | 机房CRUD |
| racks | /api/racks | 机柜CRUD |
| devices | /api/devices | 设备CRUD、批量操作 |
| deviceFields | /api/deviceFields | 设备自定义字段 |
| devicePorts | /api/device-ports | 设备端口管理 |
| networkCards | /api/network-cards | 网卡管理 |
| cables | /api/cables | 线缆连接管理 |
| tickets | /api/tickets | 工单全流程 |
| consumables | /api/consumables | 耗材管理 |
| inventory | /api/inventory | 盘点管理 |
| users | /api/users | 用户管理 |
| roles | /api/roles | 角色权限 |
| systemSettings | /api/system-settings | 系统配置 |
| backup | /api/backup | 备份恢复 |
| statistics | /api/statistics | 统计分析 |
| operationLogs | /api/operation-logs | 操作审计 |
| topology | /api/topology | 网络拓扑 |
---
## 5. 数据模型关系
### 5.1 核心模型关系图
```
┌─────────────┐
│ Room │ ──1:N──► ┌─────────┐
│ (机房) │ │ Rack │
└─────────────┘ │ (机柜) │
│ └────┬────┘
│ │
│ │ 1:N
│ ▼
│ ┌─────────┐
│ │ Device │
│ │ (设备) │
│ └────┬────┘
│ │
│ ┌───────────┼───────────┐
│ │ │ │
│ ▼ ▼ ▼
│ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ │NetworkCard│ │DevicePort│ │DeviceField│
│ │ (网卡) │ │ (端口) │ │ (字段) │
│ └────┬─────┘ └────┬─────┘ └──────────┘
│ │ │
│ │ │ N:1
│ │ ▼
│ │ ┌──────────┐
└──────────┼─────►│ Cable │
│ │ (线缆) │
│ └──────────┘
┌──────────────┐
│ Ticket │
│ (工单) │
└──────────────┘
┌──────────────┐ ┌──────────────┐
│ User │ ─N:M─► │ Role │
│ (用户) │ │ (角色) │
└──────────────┘ └──────────────┘
┌──────────────┐ ┌──────────────┐
│ Consumable │ ─N:M─► │ConsumableCat │
│ (耗材) │ │ (分类) │
└──────┬───────┘ └──────────────┘
┌──────────────┐
│ConsumableLog │
│ (日志) │
└──────────────┘
┌──────────────┐
│InventoryPlan │ ─1:N──► ┌──────────────┐
│ (盘点计划) │ │InventoryTask │
└──────────────┘ │ (盘点任务) │
└──────┬───────┘
┌──────────────┐
│InventoryRecord│
│ (盘点记录) │
└──────────────┘
```
### 5.2 主要数据模型
#### 5.2.1 机房模型 (Room)
```javascript
// backend/models/Room.js
Room.init({
roomId: { type: DataTypes.STRING, primaryKey: true }, // 机房ID
name: { type: DataTypes.STRING, allowNull: false }, // 机房名称
location: DataTypes.STRING, // 地理位置
area: DataTypes.FLOAT, // 面积(平方米)
manager: DataTypes.STRING, // 负责人
contact: DataTypes.STRING, // 联系方式
status: DataTypes.ENUM('active', 'inactive'), // 状态
description: DataTypes.TEXT, // 描述
imageUrl: DataTypes.STRING, // 机房图片
floorPlanData: DataTypes.JSON, // 平面图数据
}, {
tableName: 'rooms',
timestamps: true,
});
// 关联关系
Room.hasMany(Rack, { foreignKey: 'roomId', as: 'Racks' });
```
#### 5.2.2 机柜模型 (Rack)
```javascript
// backend/models/Rack.js
Rack.init({
rackId: { type: DataTypes.STRING, primaryKey: true },
name: { type: DataTypes.STRING, allowNull: false },
roomId: { type: DataTypes.STRING, references: { model: 'rooms' } },
totalU: { type: DataTypes.INTEGER, defaultValue: 42 }, // 总U数
usedU: { type: DataTypes.INTEGER, defaultValue: 0 }, // 已用U数
powerCapacity: { type: DataTypes.FLOAT }, // 功率容量
currentPower: { type: DataTypes.FLOAT }, // 当前功率
status: DataTypes.ENUM('active', 'maintenance', 'offline'),
positionX: DataTypes.FLOAT, // 平面图X坐标
positionY: DataTypes.FLOAT, // 平面图Y坐标
}, {
tableName: 'racks',
timestamps: true,
});
// 关联
Rack.belongsTo(Room, { foreignKey: 'roomId' });
Rack.hasMany(Device, { foreignKey: 'rackId', as: 'Devices' });
```
#### 5.2.3 设备模型 (Device)
```javascript
// backend/models/Device.js
Device.init({
deviceId: { type: DataTypes.STRING, primaryKey: true },
name: { type: DataTypes.STRING, allowNull: false },
type: DataTypes.ENUM('server', 'switch', 'router', 'storage', 'firewall', 'other'),
brand: DataTypes.STRING, // 品牌
model: DataTypes.STRING, // 型号
serialNumber: DataTypes.STRING, // 序列号
rackId: { type: DataTypes.STRING, references: { model: 'racks' } },
position: DataTypes.INTEGER, // U位
height: { type: DataTypes.INTEGER, defaultValue: 1 }, // 设备高度(U)
status: DataTypes.ENUM('purchasing', 'pending', 'running', 'maintenance', 'scrapped'),
ipAddress: DataTypes.STRING, // IP地址
macAddress: DataTypes.STRING, // MAC地址
powerConsumption: DataTypes.FLOAT, // 功率(W)
purchaseDate: DataTypes.DATE, // 采购日期
warrantyExpiry: DataTypes.DATE, // 保修到期
customFields: DataTypes.JSON, // 自定义字段
}, {
tableName: 'devices',
timestamps: true,
});
// 关联
Device.belongsTo(Rack, { foreignKey: 'rackId' });
Device.hasMany(DevicePort, { foreignKey: 'deviceId' });
Device.hasMany(NetworkCard, { foreignKey: 'deviceId' });
Device.hasMany(Cable, { foreignKey: 'sourceDeviceId' });
Device.hasMany(Cable, { foreignKey: 'targetDeviceId' });
```
#### 5.2.4 工单模型 (Ticket)
```javascript
// backend/models/Ticket.js
Ticket.init({
ticketId: { type: DataTypes.STRING, primaryKey: true },
title: { type: DataTypes.STRING, allowNull: false },
description: DataTypes.TEXT,
type: DataTypes.ENUM('fault', 'maintenance', 'change', 'other'),
priority: DataTypes.ENUM('urgent', 'high', 'medium', 'low'),
status: DataTypes.ENUM('open', 'assigned', 'processing', 'resolved', 'closed', 'reopened'),
categoryId: DataTypes.STRING, // 故障分类ID
reporterId: { type: DataTypes.STRING, references: { model: 'users' } },
assigneeId: { type: DataTypes.STRING, references: { model: 'users' } },
deviceId: { type: DataTypes.STRING, references: { model: 'devices' } },
customFields: DataTypes.JSON, // 自定义字段
rating: DataTypes.INTEGER, // 评价(1-5)
resolution: DataTypes.TEXT, // 解决方案
resolvedAt: DataTypes.DATE,
closedAt: DataTypes.DATE,
}, {
tableName: 'tickets',
timestamps: true,
});
```
#### 5.2.5 耗材模型 (Consumable)
```javascript
// backend/models/Consumable.js
Consumable.init({
consumableId: { type: DataTypes.STRING, primaryKey: true },
name: { type: DataTypes.STRING, allowNull: false },
categoryId: { type: DataTypes.STRING, references: { model: 'consumable_categories' } },
sku: DataTypes.STRING, // SKU编码
unit: DataTypes.STRING, // 单位
stock: { type: DataTypes.INTEGER, defaultValue: 0 }, // 库存
minStock: { type: DataTypes.INTEGER, defaultValue: 0 }, // 最低库存
location: DataTypes.STRING, // 存放位置
warehouseId: { type: DataTypes.STRING, references: { model: 'warehouses' } },
price: DataTypes.FLOAT, // 单价
snList: DataTypes.JSON, // SN序列号列表
version: { type: DataTypes.INTEGER, defaultValue: 0 }, // 乐观锁版本
}, {
tableName: 'consumables',
timestamps: true,
});
```
### 5.3 模型关联初始化 (models/index.js)
```javascript
// backend/models/index.js
const Room = require('./Room');
const Rack = require('./Rack');
const Device = require('./Device');
const DevicePort = require('./DevicePort');
const NetworkCard = require('./NetworkCard');
const Cable = require('./Cable');
const Ticket = require('./Ticket');
const InventoryRecord = require('./InventoryRecord');
const Warehouse = require('./Warehouse');
// 建立核心关联
Room.hasMany(Rack, { foreignKey: 'roomId', as: 'Racks' });
Rack.belongsTo(Room, { foreignKey: 'roomId' });
Rack.hasMany(Device, { foreignKey: 'rackId', as: 'Devices' });
Device.belongsTo(Rack, { foreignKey: 'rackId' });
// 设备关联端口和网卡
Device.hasMany(DevicePort, { foreignKey: 'deviceId', as: 'Ports' });
Device.hasMany(NetworkCard, { foreignKey: 'deviceId', as: 'NetworkCards' });
// 线缆关联
Device.hasMany(Cable, { foreignKey: 'sourceDeviceId', as: 'SourceCables' });
Device.hasMany(Cable, { foreignKey: 'targetDeviceId', as: 'TargetCables' });
module.exports = {
Room, Rack, Device, DevicePort, NetworkCard, Cable, Ticket,
InventoryRecord, Warehouse
};
```
---
## 6. API接口规范
### 6.1 接口基础规范
| 项目 | 规范 |
|------|------|
| 基础URL | `http://localhost:8000/api` |
| 认证方式 | JWT Bearer Token |
| Content-Type | `application/json` |
| 编码 | UTF-8 |
### 6.2 主要API模块
#### 6.2.1 设备管理API (/devices)
```javascript
// GET /api/devices - 获取设备列表
// Query: keyword, status, type, rackId, roomId, page, pageSize
// Response: { success, data: { list, total, page, pageSize } }
// GET /api/devices/all - 获取所有设备(不分页,最大50000条)
GET /api/devices/all?keyword=&status=&type=&rackId=&roomId=
// GET /api/devices/:deviceId - 获取设备详情
// Response: { success, data: device }
// POST /api/devices - 创建设备
// Body: { name, type, rackId, position, height, ... }
// PUT /api/devices/:deviceId - 更新设备
// DELETE /api/devices/:deviceId - 删除设备
// POST /api/devices/check-position - 检查U位可用性
// Body: { rackId, position, height, excludeDeviceId? }
// POST /api/devices/import-preview - 导入预览
// Body: FormData (csvFile)
// POST /api/devices/import - 执行导入
// GET /api/devices/import-template - 下载导入模板
// GET /api/devices/export - 导出设备
// Query: format(csv|xlsx), keyword, status, type, rackId, roomId
```
#### 6.2.2 工单管理API (/tickets)
```javascript
// GET /api/tickets - 获取工单列表
// Query: status, priority, categoryId, assigneeId, page, pageSize
// GET /api/tickets/:ticketId - 获取工单详情
// POST /api/tickets - 创建工单
// Body: { title, description, type, priority, categoryId, deviceId }
// PUT /api/tickets/:ticketId - 更新工单
// PUT /api/tickets/:ticketId/assign - 分配工单
// Body: { assigneeId }
// PUT /api/tickets/:ticketId/process - 处理工单
// Body: { resolution, nextStep? }
// PUT /api/tickets/:ticketId/close - 关闭工单
// PUT /api/tickets/:ticketId/reopen - 重开工单
// GET /api/tickets/:ticketId/operations - 获取操作记录
```
#### 6.2.3 备份管理API (/backup)
```javascript
// GET /api/backup/list - 获取备份列表
// POST /api/backup - 创建备份
// Body: { name?, description? }
// GET /api/backup/validate/:filename - 验证备份文件
// POST /api/backup/restore - 恢复备份
// Body: { filename, options? }
// GET /api/backup/download/:filename - 下载备份
// DELETE /api/backup/:filename - 删除备份
// POST /api/backup/upload - 上传备份文件
// Body: FormData (backup)
// GET /api/backup/info - 获取备份信息
// 自动备份
// GET /api/backup/auto/status - 获取自动备份状态
// POST /api/backup/auto/settings - 更新自动备份设置
// POST /api/backup/auto/execute - 立即执行备份
```
#### 6.2.4 统计分析API (/statistics)
```javascript
// GET /api/statistics/overview - 获取概览统计
// Response: { deviceCount, roomCount, rackCount, ticketStats }
// GET /api/statistics/devices - 设备统计
// Query: roomId?, rackId?, type?, period?
// GET /api/statistics/tickets - 工单统计
// Query: period?, categoryId?
// GET /api/statistics/consumables - 耗材统计
```
### 6.3 响应格式
```javascript
// 成功响应
{
"success": true,
"data": { ... },
"message": "操作成功"
}
// 失败响应
{
"success": false,
"message": "错误描述",
"code": "ERROR_CODE"
}
// 分页响应
{
"success": true,
"data": {
"list": [...],
"total": 100,
"page": 1,
"pageSize": 20
}
}
```
---
## 7. 关键模块详解
### 7.1 认证模块
#### 7.1.1 后端认证 (middleware/auth.js)
```javascript
// JWT认证中间件
const authMiddleware = async (req, res, next) => {
// 1. 从请求头获取Token
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({
success: false,
message: '未提供认证令牌'
});
}
// 2. 验证Token
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
// 3. 获取用户信息
const User = require('../models/User');
const user = await User.findByPk(decoded.userId, {
include: [{ model: Role, as: 'roles' }]
});
if (!user) {
return res.status(401).json({
success: false,
message: '用户不存在'
});
}
// 4. 挂载用户到请求对象
req.user = user;
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: '令牌已过期'
});
}
return res.status(401).json({
success: false,
message: '无效的认证令牌'
});
}
};
```
#### 7.1.2 前端认证 (context/AuthContext.jsx)
```javascript
// 认证Provider
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [token, setToken] = useState(null);
// 初始化 - 从本地存储恢复认证状态
useEffect(() => {
const initializeAuth = async () => {
const storedToken = await secureStorage.loadFromStorage(TOKEN_KEY);
if (storedToken) {
setToken(storedToken);
// 验证Token有效性
const response = await authAPI.getProfile();
if (response.success) {
setUser(response.data.user);
}
}
};
initializeAuth();
}, []);
const login = async (username, password) => {
const response = await authAPI.login({ username, password });
if (response.success) {
await secureStorage.set(TOKEN_KEY, response.data.token);
setToken(response.data.token);
setUser(response.data.user);
}
return response;
};
const logout = () => {
secureStorage.remove(TOKEN_KEY);
setToken(null);
setUser(null);
};
const hasPermission = (permission) => {
if (!user) return false;
const roles = user.roles || [];
if (roles.some(r => r.roleCode === 'admin')) return true;
return roles.some(r => r.roleCode === permission);
};
return (
<AuthContext.Provider value={{ user, token, login, logout, hasPermission }}>
{children}
</AuthContext.Provider>
);
};
```
### 7.2 3D可视化模块
#### 7.2.1 Three.js场景 (components/3d/Scene.jsx)
```javascript
// 3D主场景组件
const Scene = () => {
const { selectedRack, setSelectedRack } = useContext(Scene3DContext);
return (
<Canvas
camera={{ position: [0, 10, 20], fov: 50 }}
gl={{ antialias: true }}
>
{/* 灯光 */}
<ambientLight intensity={0.5} />
<directionalLight position={[10, 10, 5]} intensity={1} />
{/* 机柜模型 */}
{racks.map(rack => (
<RackModel
key={rack.rackId}
rack={rack}
isSelected={selectedRack?.rackId === rack.rackId}
onSelect={() => setSelectedRack(rack)}
/>
))}
{/* OrbitControls */}
<OrbitControls
enablePan={true}
enableZoom={true}
enableRotate={true}
minDistance={5}
maxDistance={50}
/>
</Canvas>
);
};
```
#### 7.2.2 机柜模型 (components/3d/RackModel.jsx)
```javascript
// 机柜3D模型
const RackModel = ({ rack, isSelected, onSelect }) => {
const [hovered, setHovered] = useState(false);
return (
<group position={[rack.positionX, 0, rack.positionY]}>
{/* 机柜框架 */}
<mesh
onClick={onSelect}
onPointerOver={() => setHovered(true)}
onPointerOut={() => setHovered(false)}
>
<boxGeometry args={[1, rack.totalU * 0.2, 1]} />
<meshStandardMaterial
color={isSelected ? '#1677ff' : hovered ? '#69c0ff' : '#d9d9d9'}
/>
</mesh>
{/* 设备模型 */}
{rack.devices?.map(device => (
<DeviceModel
key={device.deviceId}
device={device}
position={device.position}
/>
))}
{/* LOD优化 - 远处隐藏细节 */}
<LOD distances={[0, 10, 20]}>
<HighDetail />
<MediumDetail />
<LowDetail />
</LOD>
</group>
);
};
```
### 7.3 备份模块
#### 7.3.1 本地备份 (utils/backup.js)
```javascript
// 备份工具
const backupDatabase = async (options = {}) => {
const { DB_TYPE, dbDialect } = require('../db');
if (dbDialect === 'mysql') {
// MySQL备份
const { MYSQL_HOST, MYSQL_PORT, MYSQL_DATABASE, MYSQL_USERNAME, MYSQL_PASSWORD } = process.env;
const filename = `backup_${dayjs().format('YYYYMMDD_HHmmss')}.sql`;
const filepath = path.join(BACKUP_DIR, filename);
// mysqldump命令
const command = `mysqldump -h${MYSQL_HOST} -P${MYSQL_PORT} -u${MYSQL_USERNAME}${MYSQL_PASSWORD ? `-p${MYSQL_PASSWORD}` : ''} ${MYSQL_DATABASE} > ${filepath}`;
await exec(command);
return { filename, filepath };
} else {
// SQLite备份 - 复制数据库文件
const filename = `backup_${dayjs().format('YYYYMMDD_HHmmss')}.db`;
const filepath = path.join(BACKUP_DIR, filename);
await fs.copy(
2026-06-23 10:40:10 +08:00
process.env.DB_PATH || './yunrui_management.db',
filepath
);
return { filename, filepath };
}
};
```
#### 7.3.2 远程备份 (utils/remoteBackup.js)
```javascript
// 支持多种协议的远程备份
const uploadToRemote = async (localFile, config) => {
const { protocol, host, port, username, password, path: remotePath } = config;
switch (protocol) {
case 'ftp':
const ftpClient = require('basic-ftp');
const ftp = new ftpClient();
await ftp.access({ host, port, user: username, password });
await ftp.uploadFrom(localFile, `${remotePath}/${path.basename(localFile)}`);
await ftp.close();
break;
case 'sftp':
const sftpClient = require('ssh2-sftp-client');
const sftp = new sftpClient();
await sftp.connect({ host, port, username, password });
await sftp.put(localFile, `${remotePath}/${path.basename(localFile)}`);
await sftp.end();
break;
case 'webdav':
const { WebDAVClient } = require('webdav');
const webdav = new WebDAVClient(`http://${host}:${port}`);
await webdav.putFileContents(
`${remotePath}/${path.basename(localFile)}`,
fs.createReadStream(localFile)
);
break;
case 'smb':
const SMB2 = require('smb2');
const smb = new SMB2({ share: `\\\\${host}\\${remotePath}` });
await smb.writeFile(path.basename(localFile), fs.readFileSync(localFile));
break;
}
};
```
### 7.4 操作日志模块
#### 7.4.1 后端操作日志 (utils/operationLogger.js)
```javascript
// 操作日志记录
const logDeviceOperation = async (req, action, deviceData, userId) => {
const OperationLog = require('../models/OperationLog');
await OperationLog.create({
logId: generateId({ prefix: 'LOG' }),
userId,
module: 'device',
action,
targetId: deviceData.deviceId,
targetName: deviceData.name,
details: {
before: deviceData.before,
after: deviceData.after,
ip: req.ip,
userAgent: req.get('user-agent')
},
result: 'success'
});
};
// 生成设备变更描述
const generateDeviceDescription = (action, deviceData) => {
const descriptions = {
create: `创建设备「${deviceData.name}」`,
update: `更新设备「${deviceData.name}」信息`,
delete: `删除设备「${deviceData.name}」`,
move: `移动设备「${deviceData.name}」到新位置`,
status: `修改设备「${deviceData.name}」状态为${deviceData.status}`
};
return descriptions[action] || `${action}设备「${deviceData.name}」`;
};
```
### 7.5 设备导入模块
#### 7.5.1 导入预览验证 (routes/devices.js)
```javascript
// POST /api/devices/import-preview
router.post('/import-preview', upload.single('csvFile'), async (req, res) => {
const results = [];
const errors = [];
const warnings = [];
// 1. 解析CSV
await new Promise((resolve, reject) => {
fs.createReadStream(req.file.path)
.pipe(csv())
.on('data', (row) => results.push(row))
.on('end', resolve)
.on('error', reject);
});
// 2. 逐行验证
for (let i = 0; i < results.length; i++) {
const row = results[i];
const rowNum = i + 2; // 跳过表头
// 必填字段检查
if (!row.name) {
errors.push({ row: rowNum, field: 'name', message: '设备名称不能为空' });
}
// 格式验证
if (row.position && !/^\d+$/.test(row.position)) {
errors.push({ row: rowNum, field: 'position', message: 'U位必须是数字' });
}
// 冲突检查
if (row.rackId && row.position) {
const { available, reason } = await checkPositionAvailable(
row.rackId, row.position, row.height || 1
);
if (!available) {
warnings.push({ row: rowNum, message: reason });
}
}
}
// 3. 返回预览结果
res.json({
success: true,
data: {
total: results.length,
errors: errors.slice(0, 20), // 最多返回20条错误
warnings: warnings.slice(0, 20),
preview: results.slice(0, PREVIEW_COUNT)
}
});
});
```
---
## 8. 依赖关系
### 8.1 前端依赖 (frontend/package.json)
| 依赖 | 版本 | 用途 |
|------|------|------|
| react | ^18.2.0 | 前端框架 |
| react-dom | ^18.2.0 | DOM渲染 |
| react-router-dom | ^6.15.0 | 路由管理 |
| antd | ^5.8.6 | UI组件库 |
| @ant-design/icons | ^6.1.0 | 图标库 |
| axios | ^1.13.6 | HTTP客户端 |
| three | ^0.183.2 | 3D渲染引擎 |
| @react-three/fiber | ^8.18.0 | React Three.js集成 |
| @react-three/drei | ^9.122.0 | Three.js工具库 |
| reactflow | ^11.11.4 | 流程图/拓扑图 |
| swr | ^2.4.0 | 数据请求与缓存 |
| styled-components | ^6.3.9 | CSS-in-JS |
| framer-motion | ^12.34.0 | 动画库 |
| dayjs | ^1.11.19 | 日期处理 |
| xlsx | ^0.18.5 | Excel处理 |
| papaparse | ^5.5.3 | CSV解析 |
| dagre | ^0.8.5 | 图形布局算法 |
### 8.2 后端依赖 (backend/package.json)
| 依赖 | 版本 | 用途 |
|------|------|------|
| express | ^4.18.2 | Web框架 |
| sequelize | ^6.37.8 | ORM框架 |
| sqlite3 | ^5.1.7 | SQLite驱动 |
| mysql2 | ^3.19.1 | MySQL驱动 |
| jsonwebtoken | ^9.0.3 | JWT认证 |
| bcryptjs | ^3.0.3 | 密码加密 |
| winston | ^3.19.0 | 日志管理 |
| winston-daily-rotate-file | ^5.0.0 | 日志轮转 |
| joi | ^18.0.2 | 数据验证 |
| multer | ^2.1.1 | 文件上传 |
| express-fileupload | ^1.5.2 | 文件上传中间件 |
| cors | ^2.8.5 | 跨域资源共享 |
| node-cron | ^4.2.1 | 定时任务 |
| swagger-jsdoc | ^6.2.8 | Swagger文档生成 |
| swagger-ui-express | ^5.0.1 | Swagger UI |
| xlsx | ^0.18.5 | Excel处理 |
| csv-parser | ^3.2.0 | CSV解析 |
| csv-writer | ^1.6.0 | CSV生成 |
| basic-ftp | ^5.0.3 | FTP客户端 |
| ssh2-sftp-client | ^9.1.0 | SFTP客户端 |
| webdav | ^5.3.1 | WebDAV客户端 |
| smb2 | ^0.2.2 | SMB客户端 |
| dayjs | ^1.11.19 | 日期处理 |
| dotenv | ^17.2.3 | 环境变量 |
### 8.3 依赖关系图
```
┌─────────────────────────────────────────────────────────────────┐
│ 项目根目录 │
│ (concurrently ^9.2.1) │
└────────────────────────────┬────────────────────────────────────┘
┌─────────────────┴─────────────────┐
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ frontend/ │ │ backend/ │
│ ^2.0.0 │ │ ^2.0.0 │
├─────────────────────┤ ├─────────────────────┤
│ React 18.2.0 │ │ Express 4.18.2 │
│ ├─ react-router-dom │ │ ├─ sequelize 6.37 │
│ ├─ antd 5.8.6 │ │ │ ├─ sqlite3 │
│ │ └─ @ant-design │ │ │ └─ mysql2 │
│ ├─ three.js 0.183 │ │ ├─ jsonwebtoken │
│ │ ├─ @react-three │ │ ├─ bcryptjs │
│ │ └─ @react-three │ │ ├─ winston │
│ ├─ axios 1.13 │─────────────│ ├─ axios │
│ ├─ swr 2.4 │ 共享HTTP │ ├─ dayjs │
│ └─ styled-components│ │ └─ xlsx │
│ │ │ │
│ 开发依赖: │ │ 开发依赖: │
│ ├─ vite 4.4 │ │ ├─ nodemon │
│ ├─ eslint │ │ ├─ jest │
│ └─ vitest 4.0 │ │ └─ supertest │
└─────────────────────┘ └─────────────────────┘
```
---
## 9. 运行环境与配置
### 9.1 环境要求
| 项目 | 最低要求 | 推荐配置 |
|------|----------|----------|
| 操作系统 | Windows 10, macOS 12, Ubuntu 20.04 | 同最低 |
| Node.js | ≥14.0.0 | 20.x LTS |
| npm | ≥6.0.0 | 10.x |
| 内存 | 4GB | 8GB+ |
| 磁盘空间 | 2GB | 10GB+ |
### 9.2 环境变量配置
#### 9.2.1 前端环境变量 (frontend/.env)
```bash
# 前端环境配置
VITE_API_BASE_URL=http://localhost:8000/api
2026-06-23 10:40:10 +08:00
VITE_APP_TITLE=云睿资产管理系统
VITE_APP_VERSION=2.0.0
```
#### 9.2.2 后端环境变量 (backend/.env)
```bash
# 服务配置
NODE_ENV=development
PORT=8000
# 数据库配置
DB_TYPE=sqlite # sqlite 或 mysql
2026-06-23 10:40:10 +08:00
DB_PATH=./yunrui_management.db # SQLite数据库路径
# MySQL配置 (当DB_TYPE=mysql时)
MYSQL_HOST=localhost
MYSQL_PORT=3306
2026-06-23 10:40:10 +08:00
MYSQL_DATABASE=yunrui_management
MYSQL_USERNAME=root
MYSQL_PASSWORD=your_password
# JWT配置
JWT_SECRET=your_jwt_secret_key_here
JWT_EXPIRES_IN=7d
# 文件上传
FILE_UPLOAD_MAX_SIZE=10485760 # 10MB
# 备份配置
BACKUP_DIR=./backups
AUTO_BACKUP_ENABLED=false
AUTO_BACKUP_CRON=0 2 * * * # 每天凌晨2点
# 远程备份 (可选)
REMOTE_BACKUP_ENABLED=false
REMOTE_BACKUP_PROTOCOL=ftp
REMOTE_BACKUP_HOST=
REMOTE_BACKUP_PORT=21
REMOTE_BACKUP_USERNAME=
REMOTE_BACKUP_PASSWORD=
REMOTE_BACKUP_PATH=/backups
```
### 9.3 启动方式
#### 9.3.1 一键启动 (开发环境)
```bash
# 在项目根目录
npm start
# 效果等同于同时运行:
# - 后端: cd backend && npm run dev (端口8000)
# - 前端: cd frontend && npm run dev (端口3000)
```
#### 9.3.2 分别启动
```bash
# 终端1: 启动后端
cd backend
npm run dev
# 监听端口: http://localhost:8000
# 终端2: 启动前端
cd frontend
npm run dev
# 监听端口: http://localhost:3000
```
#### 9.3.3 生产环境部署
```bash
# 1. 构建前端
cd frontend
npm run build
# 2. 使用PM2启动后端
cd ../backend
2026-06-23 10:40:10 +08:00
pm2 start server.js --name yunrui_asset-backend
# 3. 使用Nginx反向代理
# 或使用PM2 serve提供静态文件
2026-06-23 10:40:10 +08:00
pm2 serve ./frontend/dist 3000 --name yunrui_asset-frontend
```
---
## 10. 部署指南
### 10.1 生产环境部署流程
#### 10.1.1 数据库配置
```bash
# 1. 安装MySQL 8.0+
# Ubuntu
sudo apt update
sudo apt install mysql-server
# 2. 创建数据库
mysql -u root -p
2026-06-23 10:40:10 +08:00
CREATE DATABASE yunrui_management CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'yunrui_user'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON yunrui_management.* TO 'yunrui_user'@'localhost';
FLUSH PRIVILEGES;
```
#### 10.1.2 Nginx配置
```nginx
2026-06-23 10:40:10 +08:00
# /etc/nginx/sites-available/yunrui_asset
server {
listen 80;
server_name your_domain.com;
# 前端静态文件
2026-06-23 10:40:10 +08:00
root /var/www/yunrui_asset/frontend/dist;
index index.html;
# Gzip压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# 前端路由
location / {
try_files $uri $uri/ /index.html;
}
# API代理
location /api {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# 上传文件
location /uploads {
2026-06-23 10:40:10 +08:00
alias /var/www/yunrui_asset/backend/uploads;
autoindex on;
}
# Swagger文档
location /api-docs {
proxy_pass http://127.0.0.1:8000;
}
}
# HTTPS配置 (推荐)
server {
listen 443 ssl http2;
server_name your_domain.com;
ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
# ... 同上配置
}
```
#### 10.1.3 PM2进程管理
```bash
# 安装PM2
npm install -g pm2
# 启动后端服务
cd backend
2026-06-23 10:40:10 +08:00
pm2 start server.js --name yunrui_asset-backend \
--env production \
--max-memory-restart 1G
# 配置负载均衡 (多核CPU)
2026-06-23 10:40:10 +08:00
pm2 start server.js -i max --name yunrui_asset-backend
# 保存进程列表
pm2 save
# 设置开机自启
pm2 startup
pm2 install ubuntu-upstart # Ubuntu
```
### 10.2 Docker部署 (可选)
```dockerfile
# backend/Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 8000
CMD ["node", "server.js"]
```
```yaml
# docker-compose.yml
version: '3.8'
services:
backend:
build: ./backend
ports:
- "8000:8000"
environment:
- NODE_ENV=production
- DB_TYPE=mysql
- MYSQL_HOST=db
depends_on:
- db
frontend:
build: ./frontend
ports:
- "3000:80"
depends_on:
- backend
db:
image: mysql:8.0
environment:
2026-06-23 10:40:10 +08:00
MYSQL_DATABASE: yunrui_management
MYSQL_ROOT_PASSWORD: root_password
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
```
---
## 11. 系统关系图
### 11.1 整体架构关系图
```
┌────────────────────────────────────────────────────┐
│ 用户交互层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 浏览器 │ │ 管理员 │ │ 运维人员 │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼────────────┼────────────┼────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 前端应用层 (React + Vite) │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 页面组件 (Pages) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │Dashboard│ │Devices │ │ Racks │ │Tickets │ │Backup │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ └───────┼──────────┼──────────┼──────────┼──────────┼────────────────────┘ │
│ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 通用组件 (Components) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 3D组件 │ │看板组件 │ │设备组件 │ │拓扑组件 │ │表单组件 │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ └───────┼──────────┼──────────┼──────────┼──────────┼────────────────────┘ │
│ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 状态管理与Hooks │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │AuthContext│ │ConfigCtx │ │ useApi │ │ useSWR │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
│ HTTP/REST API
┌──────────────────────────────────────────────────────────────────────────────┐
│ 后端服务层 (Express.js) │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 中间件层 (Middleware) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ 认证 │ │ 授权 │ │ 验证 │ │ 日志 │ │ 维护 │ │ │
│ │ │ Middle │ │ Middle │ │ Middle │ │ Middle │ │ Middle │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 路由层 (Routes) │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │
│ │ │ /auth │ │/devices│ │ /racks │ │/tickets│ │ /backup│ │/stats │ │ │
│ │ └────┬───┘ └────┬───┘ └────┬───┘ └────┬───┘ └────┬───┘ └────┬───┘ │ │
│ └───────┼─────────┼─────────┼─────────┼─────────┼─────────┼────────────────┘ │
│ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 业务逻辑层 (Services) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ 设备服务 │ │ 工单服务 │ │ 备份服务 │ │ 统计服务 │ │ 导入服务 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 数据访问层 (Sequelize ORM) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Device │ │ Ticket │ │Consumable│ │ User │ │ Rack │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ 数据存储层 (Database) │
│ │
│ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │
│ │ SQLite │ │ MySQL │ │
│ │ (开发环境) │◄──►│ (生产环境) │ │
2026-06-23 10:40:10 +08:00
│ │ yunrui_management.db │ │ MySQL 8.0+ │ │
│ └─────────────────────────────┘ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 数据备份层 │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ 本地备份 │ │ FTP备份 │ │ SFTP备份 │ │WebDAV备份│ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
```
### 11.2 数据模型ER图
```
┌──────────────────┐ ┌──────────────────┐
│ Room │ │ Rack │
├──────────────────┤ ├──────────────────┤
│ PK roomId │──1:N──│ FK roomId │
│ name │ │ PK rackId │
│ location │ │ name │
│ area │ │ totalU │
│ manager │ │ usedU │
│ status │ │ powerCapacity │
│ floorPlanData │ │ status │
└──────────────────┘ │ positionX │
│ │ positionY │
│ └────────┬─────────┘
│ │
│ │ 1:N
│ ▼
│ ┌──────────────────┐
│ │ Device │
│ ├──────────────────┤
│ │ PK deviceId │
│ │ FK rackId │
│ │ name │
│ │ type │
│ │ status │
│ │ position │
│ │ height │
│ │ serialNumber │
│ │ customFields │
│ └────────┬─────────┘
│ │
│ ┌──────────────┼──────────────┐
│ │ │ │
│ ▼ ▼ ▼
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ │ NetworkCard │ │ DevicePort │ │ DeviceField │
│ ├──────────────┤ ├──────────────┤ ├──────────────┤
│ │ PK nicId │ │ PK portId │ │ PK fieldId │
│ │ FK deviceId │ │ FK deviceId │ │ FK deviceId │
│ │ name │ │ FK nicId │ │ name │
│ │ type │ │ name │ │ type │
│ │ speed │ │ status │ │ value │
│ └──────────────┘ └──────────────┘ └──────────────┘
│ │ │
│ │ │ N:1
│ │ ▼
│ │ ┌──────────────┐
│ └────►│ Cable │
│ ├──────────────┤
│ │ PK cableId │
│ │ FK sourceId │
│ │ FK targetId │
│ │ type │
│ │ length │
│ │ status │
│ └──────────────┘
┌──────────────────┐ ┌──────────────────┐
│ User │ │ Role │
├──────────────────┤ ├──────────────────┤
│ PK userId │──N:M──│ PK roleId │
│ username │ │ name │
│ password │ │ code │
│ email │ │ description │
│ status │ └──────────────────┘
│ isSystem │ │
└──────────────────┘ │
│ │
│ ▼
│ ┌──────────────────┐
│ │ Permission │
│ ├──────────────────┤
│ │ PK permissionId │
│ │ FK roleId │
│ │ resource │
│ │ action │
│ └──────────────────┘
┌──────────────────┐ ┌──────────────────┐
│ Ticket │ │ TicketCategory │
├──────────────────┤ ├──────────────────┤
│ PK ticketId │──N:1──│ PK categoryId │
│ title │ │ name │
│ description │ │ priority │
│ type │ └──────────────────┘
│ priority │
│ status │ ┌──────────────────┐
│ FK reporterId │ │ FaultCategory │
│ FK assigneeId │ ├──────────────────┤
│ FK deviceId │ │ PK categoryId │
│ FK categoryId │ │ name │
│ rating │ │ priority │
│ resolution │ │ solutions │
└──────────────────┘ └──────────────────┘
┌──────────────────┐ ┌──────────────────┐
│ Consumable │ │ConsumableCategory│
├──────────────────┤ ├──────────────────┤
│ PK consumableId │──N:1──│ PK categoryId │
│ name │ │ name │
│ FK categoryId │ │ parentId │
│ sku │ │ description │
│ unit │ └──────────────────┘
│ stock │
│ minStock │ ┌──────────────────┐
│ price │ │ ConsumableLog │
│ snList │ ├──────────────────┤
│ version │ │ PK logId │
└──────────────────┘──1:N───│ FK consumableId │
│ action │
│ quantity │
│ operatorId │
│ createdAt │
└──────────────────┘
┌──────────────────┐ ┌──────────────────┐
│ InventoryPlan │ │ InventoryTask │
├──────────────────┤ ├──────────────────┤
│ PK planId │──1:N──│ PK taskId │
│ name │ │ FK planId │
│ startDate │ │ FK assigneeId │
│ endDate │ │ FK rackId │
│ status │ │ status │
│ description │ │ progress │
└──────────────────┘ └──────────────────┘
```
### 11.3 API调用流程图
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 前端组件 (React) │
│ │
│ const DeviceList = () => { │
│ const { data, error, isLoading } = useSWR('/api/devices', fetcher); │
│ // ... │
│ }; │
└────────────────────────────────┬────────────────────────────────────────┘
│ useSWR('/api/devices')
┌─────────────────────────────────────────────────────────────────────────┐
│ API层 (axios + interceptor) │
│ │
│ api.get('/devices') │
│ │ │
│ ├──► 请求拦截器 │
│ │ ├─ 添加 Authorization: Bearer {token} │
│ │ └─ 添加 Content-Type: application/json │
│ │ │
│ ▼ │
│ └──► 发送HTTP请求到 /api/devices │
└────────────────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Express路由 (routes/devices.js) │
│ │
│ router.get('/devices', async (req, res) => { │
│ │ │
│ ▼ │
│ ├─► 中间件链: auth → validation → logging │
│ │ │
│ ▼ │
│ └─► 控制器函数: getDevices(req, res) │
│ │ │
│ ▼ │
│ ├─► 参数验证 (Joi schema) │
│ │ │
│ ▼ │
│ ├─► 调用Service层 │
│ │ │
│ ▼ │
│ └─► 返回JSON响应 │
└────────────────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Service层 (业务逻辑) │
│ │
│ async function getDevices(params) { │
│ │ │
│ ▼ │
│ ├─► 构建查询条件 (Sequelize Op) │
│ │ { where: { status, type, ... } } │
│ │ │
│ ▼ │
│ ├─► 分页处理 │
│ │ { limit, offset } │
│ │ │
│ ▼ │
│ └─► 调用DAO层 │
└────────────────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ DAO层 (Sequelize ORM) │
│ │
│ Device.findAndCountAll({ │
│ where: conditions, │
│ include: [Room, Rack, NetworkCard, ...], │
│ limit, │
│ offset, │
│ order: [['createdAt', 'DESC']] │
│ }) │
│ │ │
│ ▼ │
│ └─► 执行SQL查询 │
└────────────────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ 数据库 (SQLite / MySQL) │
│ │
│ SELECT * FROM devices │
│ JOIN racks ON devices.rackId = racks.rackId │
│ JOIN rooms ON racks.roomId = rooms.roomId │
│ WHERE ... │
│ ORDER BY createdAt DESC │
│ LIMIT 20 OFFSET 0 │
└─────────────────────────────────────────────────────────────────────────┘
```
### 11.4 用户权限控制流程
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 用户权限控制流程 │
│ │
│ ┌─────────────┐ │
│ │ 用户登录 │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ JWT Token 生成 │ │
│ │ jwt.sign({ userId, roles: [...] }, secret, { expiresIn: '7d' }) │ │
│ └──────┬────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 请求API资源 │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Auth Middleware │ │
│ │ 1. 提取 Authorization Header │ │
│ │ 2. 验证 JWT Token │ │
│ │ 3. 从数据库加载用户信息和角色 │ │
│ │ 4. 挂载 req.user │ │
│ └──────┬────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 权限检查 (hasPermission) │ │
│ │ │ │
│ │ User.roles = [Role{code: 'admin'}, Role{code: 'device_manager'}]│ │
│ │ │ │
│ │ hasPermission('admin') → true (拥有admin角色) │ │
│ │ hasPermission('device_mgr') → true (拥有device_manager角色) │ │
│ │ hasPermission('system') → false (无此角色) │ │
│ │ │ │
│ │ // 权限层级 │ │
│ │ admin > device_manager > operator > viewer │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 允许访问 │ │ 拒绝访问 │ │ 404 Not │ │
│ │ 返回数据 │ │ 403 Forbidden│ │ Found │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 11.5 备份恢复流程
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 备份恢复系统架构 │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 备份触发机制 │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ 手动触发 │ │ 定时触发 │ │ 远程同步触发 │ │ │
│ │ │ /backup │ │ node-cron │ │ webhook │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │
│ │ │ │ │ │ │
│ └─────────┼─────────────────┼─────────────────┼────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 备份管理器 (BackupManager) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ 本地备份 │ │ 远程备份 │ │ 备份日志 │ │ │
│ │ │ backup.js │ │ remoteBackup.js│ │ backupLog.js │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐│ │
│ │ │ 支持的远程协议 ││ │
│ │ │ FTP │ SFTP │ WebDAV │ SMB/CIFS │ Amazon S3 │ 阿里云OSS │ ││ │
│ │ └──────────────────────────────────────────────────────────────┘│ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 备份存储层 │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ 备份文件 │ │ │
│ │ │ backup_YYYYMMDD_HHmmss.sql (MySQL) │ │ │
│ │ │ backup_YYYYMMDD_HHmmss.db (SQLite) │ │ │
│ │ │ │ │ │
│ │ │ 存储位置: │ │ │
│ │ │ ├── 本地: ./backups/ │ │ │
│ │ │ ├── FTP: /remote/backups/ │ │ │
│ │ │ ├── SFTP: /var/backups/ │ │ │
│ │ │ └── OSS: oss://idc-backup/ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ 备份元数据 │ │ │
│ │ │ { │ │ │
│ │ │ filename: "backup_20260507_120000.sql", │ │ │
│ │ │ size: 1048576, │ │ │
│ │ │ createdAt: "2026-05-07T12:00:00Z", │ │ │
2026-06-23 10:40:10 +08:00
│ │ │ database: "yunrui_management", │ │ │
│ │ │ tables: ["devices", "racks", "tickets", ...], │ │ │
│ │ │ status: "completed" │ │ │
│ │ │ } │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ 恢复执行流程 │
│ │
│ POST /api/backup/restore │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 1. 验证备份文件 │ │
│ │ GET /api/backup/validate/:filename │ │
│ └──────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 2. 创建临时恢复点 (可选) │ │
│ │ - 备份当前数据库状态 │ │
│ │ - 确保可回滚 │ │
│ └──────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 3. 关闭现有连接 │ │
│ │ - await sequelize.close() │ │
│ └──────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 4. 执行恢复 │ │
│ │ │ │
│ │ MySQL: │ │
│ │ mysql -u user -p dbname < backup_xxx.sql │ │
│ │ │ │
│ │ SQLite: │ │
2026-06-23 10:40:10 +08:00
│ │ cp backup_xxx.db yunrui_management.db │ │
│ │ │ │
│ └──────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 5. 重建连接 │ │
│ │ sequelize = new Sequelize(...) │ │
│ │ await sequelize.authenticate() │ │
│ └──────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 6. 验证数据完整性 │ │
│ │ - 检查关键表是否存在 │ │
│ │ - 验证记录数 │ │
│ └──────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 7. 返回恢复结果 │ │
│ │ { success: true, message: "恢复成功", backupId: "xxx" } │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## 附录
### A. 常用命令
```bash
# 安装依赖
npm run install:all
# 启动开发环境
npm start
# 后端测试
cd backend && npm test
# 前端构建
cd frontend && npm run build
# 代码检查
cd backend && npm run lint
cd frontend && npm run lint
# 数据库迁移
cd backend && node scripts/migrate-all.js
```
### B. 目录约定
| 目录 | 说明 |
|------|------|
| frontend/src/components | React组件 |
| frontend/src/pages | 页面组件 |
| frontend/src/hooks | 自定义Hooks |
| frontend/src/context | Context状态管理 |
| backend/models | Sequelize模型 |
| backend/routes | Express路由 |
| backend/middleware | Express中间件 |
| backend/utils | 工具函数 |
| backend/scripts | 数据库脚本 |
### C. 命名约定
| 类型 | 约定 | 示例 |
|------|------|------|
| 组件文件 | PascalCase.jsx | DeviceManagement.jsx |
| 工具文件 | camelCase.js | secureStorage.js |
| 数据模型 | PascalCase | Device.js |
| 数据库表 | snake_case | device_ports |
| API路径 | camelCase | /api/devices |
| Context | PascalCase | AuthContext.jsx |
| Hooks | use{camelCase}.js | useApi.js |
---
**文档维护说明:**
- 本文档由Code Wiki自动生成工具维护
- 更新日期:2026-05-07
- 版本:2.0.0
- 如有更新建议,请提交Issue