1336 lines
52 KiB
Markdown
1336 lines
52 KiB
Markdown
# Ether 物业管理系统 - 概要设计文档
|
||
|
||
**文档版本**: v1.0
|
||
**编制日期**: 2026-05-18
|
||
**数据来源**: REVERSE-AUTH/MDM/ASSET/OPS/FINANCE 反推设计文档 + BEST-PRACTICES-REPORT + CONSISTENCY-REPORT
|
||
**系统版本**: ether-pms v1.x (Spring Boot 3.x)
|
||
|
||
---
|
||
|
||
## 一、系统架构设计
|
||
|
||
### 1.1 总体架构
|
||
|
||
Ether 物业管理系统采用**单体模块化**架构,后端为 Spring Boot 3.x 单体应用,通过 Maven 多模块组织业务边界;前端为 Vue3 SPA 应用,按角色拆分为三套独立前端。
|
||
|
||
**技术栈总览**:
|
||
|
||
| 层次 | 技术选型 | 说明 |
|
||
|------|---------|------|
|
||
| 后端框架 | Spring Boot 3.x + Spring Security 6 | Java 17+,Servlet 容器 |
|
||
| 持久层 | Spring Data JPA + Hibernate | ORM 框架,自动建表 |
|
||
| 数据库 | PostgreSQL 15+ | JSONB/UUID/ltree 扩展 |
|
||
| 缓存 | Redis | 登录锁定、Session、热数据 |
|
||
| 认证 | JWT (HMAC-SHA256) | 无状态 Token 认证 |
|
||
| 前端框架 | Vue3 + TypeScript + Vite | Composition API + `<script setup>` |
|
||
| UI 组件库 | Ant Design Vue 4.x | 统一视觉风格 |
|
||
| 状态管理 | Pinia | Composition API 风格 |
|
||
| HTTP 客户端 | Axios | 请求/响应拦截器封装 |
|
||
| 构建工具 | Maven (后端) / npm (前端) | 多模块构建 |
|
||
|
||
**架构图**:
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────────────┐
|
||
│ 客户端层 │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ ether-admin │ │ether-app- │ │ether-app- │ │
|
||
│ │ (管理后台) │ │ employee │ │ owner │ │
|
||
│ │ :5175 │ │ (员工H5) │ │ (业主H5) │ │
|
||
│ │ Vue3+AntD │ │ :5174 │ │ :5176 │ │
|
||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||
│ │ │ │ │
|
||
│ └─────────────────┼─────────────────┘ │
|
||
│ │ RESTful API (JSON) │
|
||
│ │ Authorization: Bearer {JWT} │
|
||
│ │ X-Project-ID: {projectId} │
|
||
└───────────────────────────┼──────────────────────────────────────────┘
|
||
│
|
||
┌───────────────────────────┼──────────────────────────────────────────┐
|
||
│ 后端服务层 (ether-pms :8080) │
|
||
│ │ │
|
||
│ ┌────────────────────────┼────────────────────────────────────┐ │
|
||
│ │ Spring Security Filter Chain │ │
|
||
│ │ JwtAuthenticationFilter → LoginAttemptFilter → ... │ │
|
||
│ └────────────────────────┼────────────────────────────────────┘ │
|
||
│ │ │
|
||
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
|
||
│ │module- │ │module- │ │module- │ │module- │ │module- │ │
|
||
│ │common │ │auth │ │mdm │ │asset │ │wo │ │
|
||
│ │ │ │ │ │ │ │ │ │ │ │
|
||
│ │公共工具│ │认证授权│ │主数据 │ │设备资产│ │运维工单│ │
|
||
│ │异常/响应│ │用户/角色│ │项目/空间│ │设备/健康│ │工单/维保│ │
|
||
│ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ │
|
||
│ │ │
|
||
│ ┌────────────────────────┼────────────────────────────────────┐ │
|
||
│ │ JPA / Hibernate / Repository │ │
|
||
│ └────────────────────────┼────────────────────────────────────┘ │
|
||
│ │ │
|
||
└───────────────────────────┼──────────────────────────────────────────┘
|
||
│
|
||
┌───────────────────────────┼──────────────────────────────────────────┐
|
||
│ 数据层 │
|
||
│ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ PostgreSQL │ │ Redis │ │
|
||
│ │ 15+ │ │ │ │
|
||
│ │ JSONB/UUID │ │ 登录锁定 │ │
|
||
│ │ B-tree/GIN │ │ Token缓存 │ │
|
||
│ └──────────────┘ └──────────────┘ │
|
||
└──────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 1.2 模块划分
|
||
|
||
| 模块 | 职责 | 核心实体 | API 前缀 |
|
||
|------|------|---------|---------|
|
||
| **module-common** | 公共工具、异常体系、统一响应、基础配置 | ErrorCode、ApiResponse | — |
|
||
| **module-auth** | 身份认证、用户管理、RBAC 权限、审计日志 | User、Role、Permission、Dept、AuditLog | `/api/auth` |
|
||
| **module-mdm** | 项目管理、空间节点、巡检标准、备件库存、能耗计量 | Project、SpaceNode、InspectionItem、SparePart、EnergyMeter | `/api/mdm`、`/api/ops` |
|
||
| **module-asset** | 设备台账、健康评分、故障历史、归属主体 | Equipment、EquipmentElevator/Hvac/Fire/Energy、EquipmentHealthScore | `/api/asset` |
|
||
| **module-wo** | 工单管理、维保计划、维保任务、巡检模板 | WorkOrder、MaintenancePlan、MaintenanceTask、InspectionTemplate | `/api/wo`、`/api/ops` |
|
||
| **module-finance** | 收费项目、账单管理、支付记录(**待实现**) | FeeItem、FeeBill、FeePayment(未实现) | `/api/finance`(待定) |
|
||
|
||
**模块依赖关系**:
|
||
|
||
```
|
||
module-common ← module-auth ← module-mdm ← module-asset
|
||
↑ ↑ ↑
|
||
└──────────────┴────────────┘
|
||
module-wo
|
||
```
|
||
|
||
- module-common 是所有模块的基础依赖
|
||
- module-auth 提供用户/权限基础数据,被其他业务模块引用
|
||
- module-mdm 提供项目/空间基础数据,被 asset/wo 模块引用
|
||
- module-asset 和 module-wo 存在交叉引用(设备-工单关联)
|
||
|
||
### 1.3 前后端交互架构
|
||
|
||
**认证流程**:
|
||
|
||
```
|
||
1. 客户端 POST /api/auth/login {username, password}
|
||
2. 后端验证 → 生成 JWT Token(含 userId/username/roles)
|
||
3. 客户端存储 Token → 后续请求 Header: Authorization: Bearer {token}
|
||
4. 后端 JwtAuthenticationFilter 拦截 → 验证签名和过期 → 设置 SecurityContext
|
||
```
|
||
|
||
**项目隔离机制**:
|
||
|
||
```
|
||
1. 登录后获取用户项目列表 GET /api/auth/users/{id}/projects
|
||
2. 用户选择项目 → 后续请求携带 X-Project-ID Header
|
||
3. 后端 ProjectContextInterceptor 拦截 → 设置 ThreadLocal
|
||
4. 业务层 DataScopeService 过滤数据 → 按 projectId 限定数据范围
|
||
```
|
||
|
||
**统一响应格式**:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"data": { ... }
|
||
}
|
||
```
|
||
|
||
**错误响应格式**:
|
||
|
||
```json
|
||
{
|
||
"code": 1001,
|
||
"message": "用户名或密码错误",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
**错误码分段规则**:
|
||
|
||
| 段号 | 模块 | 示例 |
|
||
|------|------|------|
|
||
| 1xxx | 认证 | 1001 用户名或密码错误 |
|
||
| 2xxx | 用户 | 2001 用户不存在 |
|
||
| 3xxx | 角色 | 3001 角色代码已存在 |
|
||
| 4xxx | 权限 | 4001 权限代码已存在 |
|
||
| 5xxx | 项目 | 5001 项目编码已存在 |
|
||
| 6xxx | 空间/能耗 | 6101 仪表不存在 |
|
||
| 7xxx | 文件 | 7001 文件格式不支持 |
|
||
|
||
### 1.4 部署架构
|
||
|
||
| 组件 | 端口 | 部署方式 | 说明 |
|
||
|------|------|---------|------|
|
||
| ether-pms | 8080 | JAR 包部署 | 后端单体服务 |
|
||
| ether-admin | 5175 | Nginx 静态托管 | 管理后台前端 |
|
||
| ether-app-employee | 5174 | Nginx 静态托管 | 员工 H5 前端 |
|
||
| ether-app-owner | 5176 | Nginx 静态托管 | 业主 H5 前端 |
|
||
| ether-app-commercial | 5177 | Nginx 静态托管 | 商办 H5 前端 |
|
||
| PostgreSQL | 5432 | 独立部署 | 主数据库 |
|
||
| Redis | 6379 | 独立部署 | 缓存/登录锁定 |
|
||
|
||
**启动脚本**:项目根目录提供 `start-services.sh` / `stop-services.sh` 统一管理服务启停。
|
||
|
||
---
|
||
|
||
## 二、业务域划分与边界
|
||
|
||
### 2.1 六大业务域定义
|
||
|
||
| 域编号 | 域名称 | 核心职责 | 对应模块 | 核心实体数 |
|
||
|--------|--------|---------|---------|-----------|
|
||
| D1 | 身份与权限域 | 用户认证、RBAC 权限、项目隔离、审计日志 | module-auth | 14 |
|
||
| D2 | 空间与项目域 | 项目管理、空间节点树、项目配置、项目统计 | module-mdm | 13 |
|
||
| D3 | 设备与资产域 | 设备台账、专业扩展、健康评分、故障历史、归属管理 | module-asset | 8 |
|
||
| D4 | 运营与服务域 | 工单管理、维保计划/任务、巡检模板、备件库存 | module-wo + module-mdm(部分) | 6 |
|
||
| D5 | 财务与收费域 | 收费项目、账单管理、支付记录、能耗计费 | module-finance(待实现) | 0(待实现) |
|
||
| D6 | 前端交互域 | 管理后台、员工端、业主端、商办端 | ether-admin + ether-app-* | — |
|
||
|
||
### 2.2 域间依赖关系图
|
||
|
||
```mermaid
|
||
graph TD
|
||
D1[D1 身份与权限域<br/>module-auth]
|
||
D2[D2 空间与项目域<br/>module-mdm]
|
||
D3[D3 设备与资产域<br/>module-asset]
|
||
D4[D4 运营与服务域<br/>module-wo]
|
||
D5[D5 财务与收费域<br/>module-finance]
|
||
D6[D6 前端交互域<br/>ether-admin/apps]
|
||
|
||
D1 -->|用户/角色/权限| D2
|
||
D1 -->|用户/角色/权限| D3
|
||
D1 -->|用户/角色/权限| D4
|
||
D1 -->|用户/角色/权限| D5
|
||
|
||
D2 -->|项目/空间节点| D3
|
||
D2 -->|项目/空间节点| D4
|
||
D2 -->|项目/空间节点| D5
|
||
|
||
D3 -->|设备信息| D4
|
||
D3 -->|设备健康数据| D5
|
||
|
||
D4 -->|工单费用| D5
|
||
|
||
D1 -.->|API调用| D6
|
||
D2 -.->|API调用| D6
|
||
D3 -.->|API调用| D6
|
||
D4 -.->|API调用| D6
|
||
D5 -.->|API调用| D6
|
||
```
|
||
|
||
**依赖说明**:
|
||
|
||
| 依赖关系 | 依赖内容 | 实现方式 |
|
||
|---------|---------|---------|
|
||
| D2 → D1 | 项目成员管理需要用户数据 | UserProject 关联 + ProjectStaff 扩展 |
|
||
| D3 → D2 | 设备关联空间节点 | Equipment.spaceNodeId → SpaceNode.id |
|
||
| D4 → D2 | 工单关联项目和空间 | WorkOrder.projectId / spaceId |
|
||
| D4 → D3 | 工单关联设备 | WorkOrder.equipmentId → Equipment.id |
|
||
| D4 → D3 | 维保任务完成后更新设备 | MaintenanceTask → Equipment 联动更新 |
|
||
| D5 → D2 | 账单关联项目和空间 | FeeBill.projectId / spaceNodeId |
|
||
| D5 → D3 | 能耗费用对接设备 | EnergyConsumption → Equipment |
|
||
|
||
### 2.3 域间数据流图
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph D1[身份与权限域]
|
||
User[User]
|
||
Role[Role]
|
||
end
|
||
|
||
subgraph D2[空间与项目域]
|
||
Project[Project]
|
||
SpaceNode[SpaceNode]
|
||
EnergyMeter[EnergyMeter]
|
||
end
|
||
|
||
subgraph D3[设备与资产域]
|
||
Equipment[Equipment]
|
||
HealthScore[HealthScore]
|
||
FailureHistory[FailureHistory]
|
||
end
|
||
|
||
subgraph D4[运营与服务域]
|
||
WorkOrder[WorkOrder]
|
||
MaintenancePlan[MaintenancePlan]
|
||
MaintenanceTask[MaintenanceTask]
|
||
SparePart[SparePart]
|
||
end
|
||
|
||
subgraph D5[财务与收费域]
|
||
FeeItem[FeeItem]
|
||
FeeBill[FeeBill]
|
||
FeePayment[FeePayment]
|
||
end
|
||
|
||
User -->|项目成员| Project
|
||
Project -->|1:N| SpaceNode
|
||
SpaceNode -->|spaceNodeId| Equipment
|
||
Equipment -->|equipmentId| WorkOrder
|
||
Equipment -->|equipmentId| MaintenanceTask
|
||
MaintenancePlan -->|planId| MaintenanceTask
|
||
MaintenanceTask -->|完成后联动| Equipment
|
||
WorkOrder -->|relatedOrderId| SparePart
|
||
EnergyMeter -->|费用计算| FeeBill
|
||
Equipment -->|健康数据| HealthScore
|
||
Equipment -->|故障记录| FailureHistory
|
||
FailureHistory -->|关联工单| WorkOrder
|
||
```
|
||
|
||
### 2.4 域间接口契约
|
||
|
||
| 接口 | 提供方 | 消费方 | 契约内容 |
|
||
|------|--------|--------|---------|
|
||
| 用户项目列表 | D1 (auth) | D2 (mdm) | `GET /api/auth/users/{id}/projects` → `List<UserProject>` |
|
||
| 项目成员管理 | D2 (mdm) | D1 (auth) | `GET/POST/DELETE /api/mdm/projects/{id}/members` |
|
||
| 空间节点查询 | D2 (mdm) | D3 (asset) | Equipment.spaceNodeId → SpaceNode.id,通过 JPA 关联 |
|
||
| 设备查询 | D3 (asset) | D4 (wo) | WorkOrder.equipmentId → Equipment.id,通过 JPA 关联 |
|
||
| 维保完成后设备更新 | D4 (wo) | D3 (asset) | MaintenanceTask 完成时更新 Equipment.maintenanceVendor/nextInspectionDate |
|
||
| 备件出库关联工单 | D4 (wo) | D2 (mdm) | SparePartRecord.relatedOrderId → WorkOrder.id |
|
||
| 能耗数据对接账单 | D2 (mdm) | D5 (finance) | EnergyConsumption.amount → FeeBill 数据来源(待实现) |
|
||
|
||
---
|
||
|
||
## 三、核心技术实现路径
|
||
|
||
### 3.1 多租户数据隔离
|
||
|
||
**隔离策略**:共享数据库 + projectId 字段隔离
|
||
|
||
**实现机制**:
|
||
|
||
| 层次 | 机制 | 实现细节 |
|
||
|------|------|---------|
|
||
| 数据层 | projectId 字段 | 所有业务表均含 project_id 列,索引覆盖 |
|
||
| 请求层 | X-Project-ID Header | 前端每次请求携带当前项目 ID |
|
||
| 拦截层 | ProjectContextInterceptor | 拦截请求,将 projectId 存入 ThreadLocal |
|
||
| 服务层 | DataScopeService | 提供 canAccessAllData()、getPermittedProjectIds() 等判断方法 |
|
||
| 查询层 | Repository 方法 | findByProjectIdAndXxx() 系列方法,强制带项目条件 |
|
||
|
||
**数据范围过滤**:
|
||
|
||
```java
|
||
// 四级数据范围
|
||
ALL → 不做数据过滤,可查看所有项目数据
|
||
PROJECT → 过滤 project_id = 当前项目
|
||
DEPARTMENT → 过滤 project_id + dept_id = 用户部门
|
||
SELF → 过滤 assigned_to / creator = 当前用户
|
||
```
|
||
|
||
**已知不足与改进方向**:
|
||
|
||
- 当前 DataScopeService 仅提供判断方法,未实现 SQL 自动注入,数据过滤依赖业务层手动调用,存在遗漏风险
|
||
- 改进方案:通过 JPA Specification 或 MyBatis Interceptor 自动拼接数据过滤条件
|
||
|
||
### 3.2 RBAC 权限模型
|
||
|
||
**模型结构**:User ←M2M→ Role ←M2M→ Permission
|
||
|
||
**三级角色体系**:
|
||
|
||
| 角色类型 | 枚举值 | 说明 | 数据范围 |
|
||
|---------|--------|------|---------|
|
||
| 系统级 | SYSTEM | 跨项目全局角色 | ALL |
|
||
| 项目级 | PROJECT | 绑定特定项目的角色 | PROJECT |
|
||
| 部门级 | DEPARTMENT | 部门范围角色 | DEPARTMENT |
|
||
|
||
**双层角色分配**:
|
||
|
||
```
|
||
用户所有角色 = 用户直接角色(auth_user_role) ∪ 项目员工角色(ProjectStaffRole)
|
||
```
|
||
|
||
- 用户直接角色:通过 `auth_user_role` 中间表关联,用户创建时或 UserController.assignRoles 分配
|
||
- 项目员工角色:通过 `ProjectStaffRole` 关联,项目成员管理时分配
|
||
|
||
**权限类型**:
|
||
|
||
| 类型 | 说明 | 示例 |
|
||
|------|------|------|
|
||
| MENU | 菜单权限 | 系统管理、设备管理 |
|
||
| BUTTON | 按钮权限 | 新增设备、删除工单 |
|
||
| API | 接口权限 | POST /api/asset/equipment |
|
||
|
||
**权限代码格式**:`module:resource:action`,如 `asset:equipment:create`
|
||
|
||
**四级数据范围**:
|
||
|
||
| 数据范围 | 编码 | 过滤逻辑 |
|
||
|---------|------|---------|
|
||
| 全部数据 | ALL | 不做数据过滤 |
|
||
| 本项目数据 | PROJECT | WHERE project_id = ? |
|
||
| 本部门数据 | DEPARTMENT | WHERE project_id = ? AND dept_id = ? |
|
||
| 仅本人数据 | SELF | WHERE assigned_to = ? OR created_by = ? |
|
||
|
||
### 3.3 树形结构管理
|
||
|
||
**适用实体**:SpaceNode(空间节点)、Dept(部门)、SparePartCategory(备件分类)
|
||
|
||
**实现方案**:treePath 字符串路径 + parentId 自引用
|
||
|
||
**核心字段**:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| parentId | UUID | 父节点 ID(NULL=顶级) |
|
||
| treePath | String(1000) | 物理路径,格式 `id1.id2.id3` |
|
||
| treePathName | String(1000) | 名称路径,格式 `项目/楼栋/单元/房间` |
|
||
| level | Integer | 层级深度,根节点为 0 |
|
||
|
||
**SpaceNode 空间层级**:
|
||
|
||
```
|
||
项目 (Project)
|
||
├── 楼栋 (BUILDING) ← NodeCategory.BUILDING, level=1
|
||
│ ├── 单元 (UNIT) ← NodeCategory.BUILDING, level=2
|
||
│ │ ├── 楼层 (FLOOR) ← NodeCategory.BUILDING, level=3
|
||
│ │ │ └── 房间 (ROOM) ← NodeCategory.BUILDING, level=4
|
||
│ │ └── 房间 (ROOM) ← 可跳过楼层直接挂房间
|
||
│ └── 公共用房 (PUBLIC_ROOM)
|
||
├── 商铺 (SHOP) ← NodeCategory.BUILDING, level=2
|
||
├── 车库 (GARAGE) ← NodeCategory.PARKING, level=1
|
||
│ ├── 停车区域 (PARKING_AREA) ← level=2
|
||
│ │ └── 车位 (PARKING_SPACE) ← level=3
|
||
├── 设备房 (EQUIPMENT_ROOM) ← NodeCategory.FACILITY, level=1
|
||
├── 公共区域 (PUBLIC_AREA) ← NodeCategory.AREA, level=1
|
||
└── 绿化区域 (GREEN_AREA) ← NodeCategory.AREA, level=1
|
||
```
|
||
|
||
**查询模式**:
|
||
|
||
| 查询需求 | 实现方式 | SQL 示例 |
|
||
|---------|---------|---------|
|
||
| 查子节点 | parentId 查询 | `WHERE parent_id = ?` |
|
||
| 查所有子孙 | treePath LIKE | `WHERE tree_path LIKE 'parentPath.%'` |
|
||
| 查祖先链 | treePath 解析 | 应用层拆分 treePath 获取祖先 ID 列表 |
|
||
| 构建完整树 | 扁平列表 + 递归 | Controller 层递归构建树形 VO |
|
||
|
||
**索引设计**:
|
||
|
||
```sql
|
||
idx_space_node_tree_path (tree_path) -- 路径前缀查询
|
||
idx_sn_project_parent (project_id, parent_id) -- 项目+父节点
|
||
idx_sn_project_type (project_id, node_type) -- 项目+类型
|
||
```
|
||
|
||
**已知不足**:treePath LIKE 查询在大数据量下性能不佳,未使用 PostgreSQL ltree 扩展或闭包表方案。后续可评估升级。
|
||
|
||
### 3.4 设备扩展表模式
|
||
|
||
**设计原则**:主表存储公共字段,扩展表存储专业参数,通过 equipmentType 决定扩展表。
|
||
|
||
**模式结构**:
|
||
|
||
```
|
||
Equipment (主表, ~40个公共字段)
|
||
├── EquipmentElevator (电梯扩展) ← equipment_id UNIQUE
|
||
├── EquipmentHvac (暖通扩展) ← equipment_id UNIQUE
|
||
├── EquipmentFire (消防扩展) ← equipment_id UNIQUE
|
||
└── EquipmentEnergy (能源计量扩展) ← equipment_id UNIQUE
|
||
```
|
||
|
||
**设备类型与扩展表映射**:
|
||
|
||
| 设备类型 | 枚举值 | 扩展表 | 无扩展表时 |
|
||
|---------|--------|--------|-----------|
|
||
| 电梯系统 | ELEVATOR | EquipmentElevator | — |
|
||
| 暖通空调 | HVAC | EquipmentHvac | — |
|
||
| 消防系统 | FIRE_PROTECTION | EquipmentFire | — |
|
||
| 能源计量 | ENERGY_METER | EquipmentEnergy | — |
|
||
| 给排水系统 | PLUMBING | — | 使用 attributes JSON |
|
||
| 电气系统 | ELECTRICAL | — | 使用 attributes JSON |
|
||
| 弱电系统 | SECURITY | — | 使用 attributes JSON |
|
||
| 景观绿化 | LANDSCAPE | — | 使用 attributes JSON |
|
||
| 厨余设备 | KITCHEN | — | 使用 attributes JSON |
|
||
| 其他设备 | OTHER | — | 使用 attributes JSON |
|
||
|
||
**扩展表服务接口模式**:
|
||
|
||
```java
|
||
public interface EquipmentElevatorService {
|
||
Optional<EquipmentElevator> getByEquipmentId(UUID equipmentId);
|
||
EquipmentElevator saveOrUpdate(EquipmentElevator elevator);
|
||
}
|
||
```
|
||
|
||
**扩展表 API 路径**:
|
||
|
||
```
|
||
GET /api/asset/equipment/{id}/elevator → 获取电梯扩展
|
||
PUT /api/asset/equipment/{id}/elevator → 更新电梯扩展
|
||
GET /api/asset/equipment/{id}/hvac → 获取暖通扩展
|
||
PUT /api/asset/equipment/{id}/hvac → 更新暖通扩展
|
||
GET /api/asset/equipment/{id}/fire → 获取消防扩展
|
||
PUT /api/asset/equipment/{id}/fire → 更新消防扩展
|
||
GET /api/asset/equipment/{id}/energy → 获取能源扩展
|
||
PUT /api/asset/equipment/{id}/energy → 更新能源扩展
|
||
```
|
||
|
||
**已知问题**:module-mdm 的 SpaceNode 也内嵌了设备扩展字段(isEquipment 标记),与 module-asset 的独立 Equipment 实体形成双体系并存,需统一。
|
||
|
||
### 3.5 工单状态机
|
||
|
||
**WorkOrder 状态机(6 状态)**:
|
||
|
||
```
|
||
┌─────────────┐
|
||
│ PENDING │ ← 创建工单(默认状态)
|
||
│ (待分配) │
|
||
└──────┬──────┘
|
||
│ assign (派单)
|
||
▼
|
||
┌─────────────┐
|
||
│ ASSIGNED │ ← 分配负责人/服务商
|
||
│ (已派单) │
|
||
└──────┬──────┘
|
||
│ start (开始执行)
|
||
▼
|
||
┌─────────────┐
|
||
│ IN_PROGRESS │ ← 记录 actualStart
|
||
│ (执行中) │
|
||
└──────┬──────┘
|
||
│ complete (完成)
|
||
▼
|
||
┌─────────────┐
|
||
│ COMPLETED │ ← 记录故障原因/解决方案/费用
|
||
│ (已完成) │
|
||
└──────┬──────┘
|
||
│ verify (验收)
|
||
▼
|
||
┌─────────────┐
|
||
│ VERIFIED │ ← 记录验收人/评分/备注
|
||
│ (已验收) │
|
||
└─────────────┘
|
||
|
||
特殊: CANCELLED ← PENDING/ASSIGNED/IN_PROGRESS 可取消
|
||
COMPLETED/VERIFIED 不可取消
|
||
```
|
||
|
||
**状态流转约束**:
|
||
|
||
| 当前状态 | 允许操作 | 下一状态 | 自动处理 |
|
||
|---------|---------|---------|---------|
|
||
| PENDING | assign | ASSIGNED | 设置 assignedTo/assignedVendor/assignedDate |
|
||
| ASSIGNED | start | IN_PROGRESS | 设置 actualStart = now() |
|
||
| IN_PROGRESS | complete | COMPLETED | 设置 actualEnd = now(),自动计算 actualHours |
|
||
| COMPLETED | verify | VERIFIED | 设置 verifiedBy/verifiedDate/rating |
|
||
| PENDING/ASSIGNED/IN_PROGRESS | cancel | CANCELLED | — |
|
||
|
||
**MaintenanceTask 状态机(6 状态,与工单类似但有差异)**:
|
||
|
||
| 差异点 | WorkOrder | MaintenanceTask |
|
||
|--------|-----------|-----------------|
|
||
| 完成方式 | 仅一种 complete | 两种:complete(简版)+ complete-details(详版) |
|
||
| 评分方式 | 验收时评分 | 验收时评分 + 独立 rate 接口 |
|
||
| 删除策略 | 物理删除 | 逻辑删除(设为 CANCELLED) |
|
||
| 完成后联动 | 无 | 自动更新设备维保记录 |
|
||
|
||
**工单编号生成规则**:
|
||
|
||
| 类型 | 格式 | 示例 |
|
||
|------|------|------|
|
||
| 工单 | WO-YYYYMMDD-XXXX | WO-20260518-0001 |
|
||
| 维保任务 | EQ-YYYYMMDD-XXXX | EQ-20260518-0001 |
|
||
|
||
**已知风险**:编号生成使用 findMaxWorkNoByPrefix 查询最大编号 +1,并发场景可能生成重复编号,需改用数据库序列或 Redis INCR。
|
||
|
||
### 3.6 健康评分算法 [Beta]
|
||
|
||
**评分公式**:
|
||
|
||
```
|
||
健康度 = 基础分(100) - 故障扣分 - 维保扣分 - 年龄扣分
|
||
最低分 = 0(不会为负数)
|
||
```
|
||
|
||
**三维扣分规则**:
|
||
|
||
| 扣分项 | 计算公式 | 参数 | 上限 |
|
||
|--------|----------|------|------|
|
||
| 故障扣分 | 故障次数(近30天) x 5 | FAILURE_DEDUCTION_PER_COUNT = 5 | 无上限 |
|
||
| 维保扣分 | (1 - 维保完成率) x 20 | MAINTENANCE_DEDUCTION_FACTOR = 20 | 20 分 |
|
||
| 年龄扣分 | 设备年龄(年) x 2 | AGE_DEDUCTION_PER_YEAR = 2 | 10 分 (MAX_AGE_DEDUCTION) |
|
||
|
||
**5 级健康等级**:
|
||
|
||
| 等级 | 枚举值 | 分数范围 | 前端颜色 |
|
||
|------|--------|---------|---------|
|
||
| 优秀 | EXCELLENT | [90, 100) | 绿色 |
|
||
| 良好 | GOOD | [75, 90) | 青色 |
|
||
| 一般 | FAIR | [60, 75) | 橙色 |
|
||
| 较差 | POOR | [40, 60) | 红色 |
|
||
| 危急 | CRITICAL | [0, 40) | 红色 |
|
||
|
||
**MTBF 计算(平均故障间隔时间)**:
|
||
|
||
```
|
||
MTBF = 运行时间(小时) / 故障次数
|
||
```
|
||
|
||
- 计算周期:默认 30 天,可自定义
|
||
- 运行时间:从第一个故障到最后一个故障的时间跨度
|
||
- 仅 1 次故障:使用默认期间天数 x 24 小时
|
||
- 无故障时返回 0
|
||
|
||
**MTTR 计算(平均修复时间)**:
|
||
|
||
```
|
||
MTTR = 总修复时间(小时) / 修复次数
|
||
```
|
||
|
||
- 仅统计已完成修复的故障记录(repairDurationHours 不为空)
|
||
- 无修复记录时返回 0
|
||
|
||
**已知问题**:
|
||
|
||
1. calculateEquipmentAge() 错误使用 SpaceNode.maintenanceContractStart 而非 Equipment.installationDate
|
||
2. projectId 硬编码为全零 UUID `00000000-0000-0000-0000-000000000000`
|
||
3. 维保完成率固定返回 1.0(TODO 标记)
|
||
4. 扣分系数(5、20、2、10)硬编码在代码中,未提取为配置
|
||
|
||
**Beta → 正式演进路径**:
|
||
|
||
| 阶段 | 时间 | 目标 |
|
||
|------|------|------|
|
||
| Beta(当前) | 2026 Q2 | 修复已知 Bug,提取配置项 |
|
||
| V1 | 2026 Q3 | 完善维保完成率计算,积累真实数据校准系数 |
|
||
| V2 | 2026 Q4 | 引入 IoT 数据,增加运行参数扣分维度 |
|
||
| V3 | 2027 Q1 | 机器学习模型,基于历史数据预测故障概率 |
|
||
|
||
### 3.7 审计日志
|
||
|
||
**记录方式**:`@OperationLog` 注解 + AOP 切面
|
||
|
||
```java
|
||
@OperationLog(operation = "创建用户", module = "USER", action = AuditLog.ActionType.CREATE)
|
||
public User createUser(User user) { ... }
|
||
```
|
||
|
||
**异步持久化**:通过 `auditLogExecutor` 线程池异步保存,避免影响业务性能
|
||
|
||
**核心字段**:
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| userId / username | 操作人 |
|
||
| operation | 操作描述 |
|
||
| module | 模块标识(USER/ROLE/PERMISSION/PROJECT/AUTH/DEPT) |
|
||
| action | 操作类型(CREATE/UPDATE/DELETE/QUERY/VIEW/LOGIN/LOGOUT/EXPORT/IMPORT/ASSIGN/REVOKE) |
|
||
| targetType / targetId | 操作目标 |
|
||
| content | 操作内容 |
|
||
| params / result | 请求参数 / 操作结果 |
|
||
| executionTimeMs | 执行耗时 |
|
||
| status | SUCCESS / FAIL |
|
||
|
||
**查询窗口**:强制限制 30 天,超过 30 天的查询自动截断起始时间
|
||
|
||
**归档策略**:90 天以上日志归档(当前实现为直接删除,TODO: 导出至对象存储保留 3-5 年)
|
||
|
||
**索引设计**:
|
||
|
||
```sql
|
||
idx_audit_log_created_at (created_at)
|
||
idx_audit_log_user_id (user_id)
|
||
idx_audit_log_module (module)
|
||
idx_audit_log_action (action)
|
||
idx_al_user_createdat (user_id, created_at DESC)
|
||
```
|
||
|
||
**已知不足**:
|
||
|
||
1. 审计日志可篡改(无防篡改机制),需改为只追加
|
||
2. 90 天归档实为删除,不符合合规要求(物业行业需保留 3-5 年)
|
||
3. 改进方案:数据库触发器禁止 UPDATE/DELETE + 90 天后导出至 OSS/S3
|
||
|
||
### 3.8 编码生成规则
|
||
|
||
| 实体 | 编码格式 | 生成方式 | 唯一性保障 |
|
||
|------|---------|---------|-----------|
|
||
| 项目编码 | 正则 `^[a-zA-Z0-9_-]+$` | 后端 generateCode() | 数据库 UNIQUE |
|
||
| 空间编码 | **未实现** | — | — |
|
||
| 设备编码 | equipmentCode | 创建时指定 | 数据库 UNIQUE |
|
||
| 工单编号 | WO-YYYYMMDD-XXXX | findMaxWorkNoByPrefix + 1 | 应用层(有并发风险) |
|
||
| 维保任务编号 | EQ-YYYYMMDD-XXXX | 同上 | 应用层(有并发风险) |
|
||
| 计划编码 | planCode | 创建时指定 | 数据库 UNIQUE |
|
||
| 备件编码 | sparePartCode | 创建时指定 | 数据库 UNIQUE |
|
||
| 计量点编码 | EM + yyyyMMddHHmmss | generateMeterCode() | 冲突时追加后缀 |
|
||
| 记录编码 | recordCode | 创建时指定 | 数据库 UNIQUE |
|
||
|
||
**改进方向**:
|
||
|
||
1. 空间编码需实现自动生成规则(如 项目编码-楼栋号-单元号-房号)
|
||
2. 工单/任务编号改用数据库序列或 Redis INCR 保障并发安全
|
||
3. 统一编码生成服务,避免各模块各自实现
|
||
|
||
---
|
||
|
||
## 四、难点识别与应对
|
||
|
||
### 4.1 跨域业务流程的一致性保障
|
||
|
||
**难点描述**:维保任务完成后需联动更新设备信息(维保商、下次巡检日期),工单完成后需关联备件出库,能耗数据需对接账单生成。这些跨域操作需要保证数据一致性。
|
||
|
||
**应对方案**:
|
||
|
||
| 方案 | 适用场景 | 实现方式 | 一致性级别 |
|
||
|------|---------|---------|-----------|
|
||
| 同步调用 + 事务 | 强一致性要求 | @Transactional 跨模块 Service 调用 | 强一致 |
|
||
| 事件驱动 + 最终一致性 | 弱一致性可接受 | Spring ApplicationEvent + 异步监听 | 最终一致 |
|
||
| 补偿事务 | 需要回滚能力 | Saga 模式,失败时执行补偿操作 | 最终一致 |
|
||
|
||
**当前实现**:维保任务完成后同步调用 EquipmentService 更新设备,异常容错(更新设备失败不影响工单完成)。
|
||
|
||
**改进计划**:
|
||
|
||
1. 短期(Q2 2026):保持同步调用,增加重试机制(@Retryable)
|
||
2. 中期(Q3 2026):引入 Spring ApplicationEvent,关键操作发布领域事件
|
||
3. 远期(Q4 2026):评估引入消息队列(RabbitMQ/Kafka)解耦跨域通信
|
||
|
||
### 4.2 工单状态机的并发控制
|
||
|
||
**难点描述**:多个用户可能同时对同一工单执行状态变更操作(如同时派单、同时完成),需要保证状态流转的正确性和幂等性。
|
||
|
||
**应对方案**:
|
||
|
||
| 机制 | 实现方式 | 说明 |
|
||
|------|---------|------|
|
||
| 乐观锁 | version 字段 + @Version | JPA 自动版本控制,更新时检查版本号 |
|
||
| 状态校验 | 业务层前置检查 | 状态变更前校验当前状态是否允许该操作 |
|
||
| 幂等性 | 幂等键 + 去重 | 重复请求返回当前状态而非报错 |
|
||
| 分布式锁 | Redis SETNX | 防止同一工单被并发操作 |
|
||
|
||
**当前实现**:仅业务层前置状态校验,无乐观锁和分布式锁。
|
||
|
||
**改进计划**:
|
||
|
||
1. 为 WorkOrder 和 MaintenanceTask 添加 version 字段(@Version)
|
||
2. 状态变更操作使用 Redis 分布式锁(key: `work_order:lock:{id}`)
|
||
3. 状态变更接口设计为幂等:重复操作返回当前状态
|
||
|
||
### 4.3 树形结构的高效查询
|
||
|
||
**难点描述**:SpaceNode 使用 treePath 字符串路径(如 `uuid1.uuid2.uuid3`),查询子孙节点需 LIKE 匹配,大数据量下性能堪忧。
|
||
|
||
**应对方案**:
|
||
|
||
| 方案 | 查询性能 | 维护成本 | 适用场景 |
|
||
|------|---------|---------|---------|
|
||
| treePath LIKE(当前) | O(n) 全表扫描 | 低 | 数据量 < 1万 |
|
||
| PostgreSQL ltree 扩展 | O(log n) 索引扫描 | 中 | 数据量 1-10万 |
|
||
| 闭包表 (Closure Table) | O(1) 直接查询 | 高(需额外表) | 数据量 > 10万 |
|
||
| 递归 CTE | O(depth) | 低 | 深度有限(< 10层) |
|
||
|
||
**当前实现**:treePath LIKE + B-tree 索引
|
||
|
||
**改进计划**:
|
||
|
||
1. 短期(Q2 2026):确保 treePath 索引存在,添加 GIN 索引支持前缀查询
|
||
2. 中期(Q3 2026):评估迁移至 PostgreSQL ltree 扩展,利用专用的 ltree 索引
|
||
3. 远期(Q4 2026):若数据量超过 10 万节点,考虑闭包表方案
|
||
|
||
**递归 CTE 查询示例**(当前可用的优化方案):
|
||
|
||
```sql
|
||
WITH RECURSIVE space_tree AS (
|
||
SELECT * FROM mdm_space_node WHERE id = :rootId
|
||
UNION ALL
|
||
SELECT n.* FROM mdm_space_node n
|
||
JOIN space_tree st ON n.parent_id = st.id
|
||
)
|
||
SELECT * FROM space_tree;
|
||
```
|
||
|
||
### 4.4 健康评分的数据积累与算法迭代
|
||
|
||
**难点描述**:健康评分算法当前为 Beta 版,扣分系数基于经验设定,缺乏真实数据校准。维保完成率固定为 1.0,影响评分准确性。
|
||
|
||
**应对方案**:
|
||
|
||
| 阶段 | 目标 | 具体措施 |
|
||
|------|------|---------|
|
||
| Beta → V1 | 修复 Bug + 提取配置 | 修正设备年龄计算、projectId 硬编码;提取扣分系数为 @ConfigurationProperties |
|
||
| V1 → V2 | 数据校准 + 完善计算 | 实现维保完成率真实计算;积累 3 个月数据后用线性回归校准系数 |
|
||
| V2 → V3 | 引入 IoT + 预测模型 | 增加运行参数扣分维度;基于历史数据训练故障预测模型 |
|
||
|
||
**配置化改进**:
|
||
|
||
```yaml
|
||
# application.yml
|
||
equipment:
|
||
health:
|
||
failure-deduction-per-count: 5
|
||
maintenance-deduction-factor: 20
|
||
age-deduction-per-year: 2
|
||
max-age-deduction: 10
|
||
calculation-period-days: 30
|
||
default-inspection-cycle-days: 30
|
||
```
|
||
|
||
### 4.5 备件库存的并发出入库
|
||
|
||
**难点描述**:多个工单可能同时使用同一备件,导致库存超卖或数据不一致。
|
||
|
||
**应对方案**:
|
||
|
||
| 机制 | 实现方式 | 说明 |
|
||
|------|---------|------|
|
||
| 乐观锁 | currentStock + @Version | 出库时检查版本号,冲突时重试 |
|
||
| 库存校验 | 出库前检查 currentStock >= quantity | 防止库存为负 |
|
||
| 事务隔离 | @Transactional(isolation = SERIALIZABLE) | 最高隔离级别,性能影响大 |
|
||
| Redis 原子操作 | DECRBY 命令 | 原子性扣减,性能最优 |
|
||
|
||
**当前实现**:出库时校验库存不能为负,但无乐观锁。
|
||
|
||
**改进计划**:
|
||
|
||
1. 为 SparePart 添加 version 字段(@Version)
|
||
2. 出库操作使用 Redis DECRBY 原子扣减,成功后异步同步到数据库
|
||
3. 引入库存预占机制:工单创建时预占库存,完成时确认扣减,取消时释放
|
||
|
||
### 4.6 能耗数据的 IoT 接入
|
||
|
||
**难点描述**:当前能耗数据仅支持手动录入(MANUAL),IoT 自动采集(IOT)枚举已定义但未实现对接。
|
||
|
||
**当前状态**:
|
||
|
||
```java
|
||
public enum RecordMethod {
|
||
MANUAL, // 手动录入 — 已实现
|
||
IOT // IoT自动采集 — 枚举已定义,对接未实现
|
||
}
|
||
```
|
||
|
||
**扩展路径**:
|
||
|
||
| 阶段 | 目标 | 技术方案 |
|
||
|------|------|---------|
|
||
| Phase 1(当前) | 手动录入 | 前端表单 + 后端 API |
|
||
| Phase 2 | IoT 数据接入 | MQTT Broker + Spring Integration 消费消息 |
|
||
| Phase 3 | 实时监控 | WebSocket 推送 + 时序数据库(TimescaleDB) |
|
||
| Phase 4 | 智能告警 | 规则引擎 + 异常检测算法 |
|
||
|
||
**Phase 2 架构设计**:
|
||
|
||
```
|
||
IoT 设备 → MQTT Broker → Spring Integration → EnergyConsumptionService.recordConsumption()
|
||
↓
|
||
recordMethod = IOT
|
||
自动获取 previousReading
|
||
自动计算 consumption 和 amount
|
||
```
|
||
|
||
---
|
||
|
||
## 五、跨模块跨功能实现规范
|
||
|
||
### 5.1 API 设计规范
|
||
|
||
**路径前缀**:
|
||
|
||
```
|
||
/api/{module}/{resource}
|
||
```
|
||
|
||
| 模块 | 路径前缀 | 示例 |
|
||
|------|---------|------|
|
||
| auth | /api/auth | /api/auth/users, /api/auth/roles |
|
||
| mdm | /api/mdm | /api/mdm/projects, /api/mdm/space-nodes |
|
||
| asset | /api/asset | /api/asset/equipment |
|
||
| wo | /api/wo | /api/wo/work-orders |
|
||
| ops | /api/ops | /api/ops/maintenance-tasks, /api/ops/energy |
|
||
|
||
**版本管理**(待实现):
|
||
|
||
```
|
||
当前: /api/{module}/{resource}
|
||
目标: /api/v1/{module}/{resource}
|
||
```
|
||
|
||
**分页规范**:
|
||
|
||
```java
|
||
// 请求参数
|
||
GET /api/mdm/projects?page=0&size=20&sortBy=createdAt&sortDirection=DESC
|
||
|
||
// 响应格式
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"data": {
|
||
"content": [...],
|
||
"totalElements": 100,
|
||
"totalPages": 5,
|
||
"number": 0,
|
||
"size": 20
|
||
}
|
||
}
|
||
```
|
||
|
||
**约束**:所有列表接口必须支持分页,禁止全量返回。WorkOrder 当前返回 `List<WorkOrder>` 全量数据需改造。
|
||
|
||
**错误码规范**:
|
||
|
||
| 段号 | 模块 | 范围 |
|
||
|------|------|------|
|
||
| 1xxx | 认证 | 1000-1999 |
|
||
| 2xxx | 用户 | 2000-2999 |
|
||
| 3xxx | 角色 | 3000-3999 |
|
||
| 4xxx | 权限 | 4000-4999 |
|
||
| 5xxx | 项目 | 5000-5999 |
|
||
| 6xxx | 空间/能耗 | 6000-6999 |
|
||
| 7xxx | 文件 | 7000-7999 |
|
||
| 8xxx | 工单 | 8000-8999(待分配) |
|
||
| 9xxx | 财务 | 9000-9999(待分配) |
|
||
|
||
**统一响应封装**:
|
||
|
||
```java
|
||
public class ApiResponse<T> {
|
||
private int code;
|
||
private String message;
|
||
private T data;
|
||
}
|
||
```
|
||
|
||
### 5.2 数据模型规范
|
||
|
||
**主键策略**:
|
||
|
||
```java
|
||
@Id
|
||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||
private UUID id; // 所有实体使用 UUID 主键
|
||
```
|
||
|
||
**软删除**:
|
||
|
||
```java
|
||
private Boolean isDeleted = false; // 核心业务实体必须有此字段
|
||
|
||
// Repository 查询方法
|
||
findByIdAndIsDeletedFalse(UUID id);
|
||
findByProjectIdAndIsDeletedFalse(UUID projectId);
|
||
```
|
||
|
||
**约束**:WorkOrder 当前使用物理删除,需统一改为逻辑删除。
|
||
|
||
**审计字段**:
|
||
|
||
```java
|
||
@CreatedDate
|
||
@Column(nullable = false, updatable = false)
|
||
private LocalDateTime createdAt;
|
||
|
||
@LastModifiedDate
|
||
@Column(nullable = false)
|
||
private LocalDateTime updatedAt;
|
||
|
||
@Column(updatable = false)
|
||
private UUID createdBy;
|
||
|
||
private UUID updatedBy;
|
||
```
|
||
|
||
**约束**:所有实体必须有 createdAt 和 updatedAt 字段,使用 JPA Auditing 自动填充。
|
||
|
||
**JSONB 使用规范**:
|
||
|
||
| 场景 | 字段类型 | 说明 |
|
||
|------|---------|------|
|
||
| 照片列表 | JSONB | `List<String>` 或 `List<EquipmentPhoto>` |
|
||
| 文档列表 | JSONB | `List<EquipmentDocument>` |
|
||
| 扩展属性 | JSONB | `Map<String, Object>` 或 String 存储 JSON |
|
||
| 检查项结果 | JSONB | 结构化 JSON 数组 |
|
||
|
||
**约束**:SpaceNode.attributes 当前为 String(2000),需改为 JSONB 类型以支持 GIN 索引和原生 JSON 查询。
|
||
|
||
**枚举策略**:
|
||
|
||
```java
|
||
// 数据库存储为字符串,JPA 实体使用枚举类型
|
||
@Enumerated(EnumType.STRING)
|
||
private UserStatus status;
|
||
|
||
// 枚举定义
|
||
public enum UserStatus {
|
||
ACTIVE, LOCKED, DISABLED
|
||
}
|
||
```
|
||
|
||
**约束**:禁止使用 EnumType.ORDINAL,统一使用 EnumType.STRING。
|
||
|
||
### 5.3 异常处理规范
|
||
|
||
**三层异常体系**:
|
||
|
||
```
|
||
BusinessException (业务异常)
|
||
├── ErrorCode (错误码枚举)
|
||
└── GlobalExceptionHandler (全局异常处理器)
|
||
```
|
||
|
||
**BusinessException 使用规范**:
|
||
|
||
```java
|
||
// 正确用法
|
||
throw new BusinessException(ErrorCode.USER_NOT_FOUND);
|
||
|
||
// 错误用法(禁止)
|
||
throw new RuntimeException("用户不存在");
|
||
```
|
||
|
||
**约束**:WorkOrderServiceImpl 中存在 `throw new RuntimeException("工单不存在")`,需替换为 BusinessException + ErrorCode。
|
||
|
||
**GlobalExceptionHandler 处理规则**:
|
||
|
||
| 异常类型 | HTTP 状态码 | 处理方式 |
|
||
|---------|-----------|---------|
|
||
| BusinessException | 200 (body 含错误码) | 返回 ApiResponse(code, message, null) |
|
||
| MethodArgumentNotValidException | 200 | 返回参数校验错误信息 |
|
||
| DataAccessException | 200 | 脱敏后返回数据库错误 |
|
||
| Exception | 200 | 通用异常兜底 |
|
||
|
||
**改进方向**:业务错误码 4xx/5xx 应映射为对应 HTTP 状态码,非标准码保持 200 但在 body 中体现。
|
||
|
||
### 5.4 权限控制规范
|
||
|
||
**注解式权限**:
|
||
|
||
```java
|
||
@PreAuthorize("hasAuthority('asset:equipment:create')")
|
||
public Equipment createEquipment(Equipment equipment) { ... }
|
||
```
|
||
|
||
**数据范围过滤**:
|
||
|
||
```java
|
||
// Service 层手动调用
|
||
if (!dataScopeService.canAccessAllData(currentUser)) {
|
||
List<UUID> permittedProjectIds = dataScopeService.getPermittedProjectIds(currentUser);
|
||
// 过滤查询结果
|
||
}
|
||
```
|
||
|
||
**改进方向**:通过 JPA Specification 自动拼接数据过滤条件,避免业务层遗漏。
|
||
|
||
**菜单动态渲染**(待实现):
|
||
|
||
```
|
||
1. 前端登录后请求 GET /api/auth/users/{id}/menus
|
||
2. 后端根据用户角色返回有权限的菜单列表
|
||
3. 前端动态生成路由和菜单
|
||
```
|
||
|
||
### 5.5 前后端协作规范
|
||
|
||
**TypeScript 类型同步**:
|
||
|
||
```typescript
|
||
// 前端类型定义必须与后端枚举一致
|
||
// equipment.ts
|
||
export enum EquipmentType {
|
||
ELEVATOR = 'ELEVATOR',
|
||
HVAC = 'HVAC',
|
||
FIRE_PROTECTION = 'FIRE_PROTECTION',
|
||
// ... 与后端 EquipmentType 枚举完全一致
|
||
}
|
||
```
|
||
|
||
**约束**:当前前端能源类型(ELECTRICITY/WATER/GAS/CENTRAL_HEATING/CENTRAL_COOLING)与后端(LIGHTING/HVAC/POWER/SPECIAL/WATER/GAS)不一致,需统一。
|
||
|
||
**枚举一致性检查清单**:
|
||
|
||
| 枚举 | 前端文件 | 后端位置 | 是否一致 |
|
||
|------|---------|---------|---------|
|
||
| EquipmentType | equipment.ts | Equipment.java | 需校验 |
|
||
| EnergyType | energy.ts | EnergyMeter.java | 不一致(需修复) |
|
||
| WorkOrderStatus | work-order.ts | WorkOrder.java | 需校验 |
|
||
| MaintenanceTaskStatus | maintenance.ts | MaintenanceTask.java | 不一致(需修复) |
|
||
| TriggerType | maintenance.ts | MaintenanceTask.java | 不一致(需修复) |
|
||
|
||
**API 调用封装**:
|
||
|
||
```typescript
|
||
// src/api/xxx.ts
|
||
import request from '@/utils/request'
|
||
|
||
export function getEquipment(id: string) {
|
||
return request.get<ApiResponse<Equipment>>(`/api/asset/equipment/${id}`)
|
||
}
|
||
```
|
||
|
||
**约束**:维保计划存在两套前端 API(maintenance.ts + maintenance-plan.ts),需合并为统一 API 文件。
|
||
|
||
### 5.6 测试规范
|
||
|
||
**TDD 开发流程**:
|
||
|
||
```
|
||
1. 编写失败测试(Red)
|
||
2. 编写最小实现代码(Green)
|
||
3. 重构优化(Refactor)
|
||
```
|
||
|
||
**测试分层**:
|
||
|
||
| 层次 | 框架 | 覆盖范围 | 当前状态 |
|
||
|------|------|---------|---------|
|
||
| 单元测试 | JUnit 5 + Mockito | Service 层业务逻辑 | 仅 EnergyMeterServiceTest |
|
||
| API 测试 | Spring MockMvc | Controller 层接口 | 缺失 |
|
||
| 集成测试 | @SpringBootTest | 跨模块交互 | 缺失 |
|
||
| E2E 测试 | Playwright/Cypress | 前端用户流程 | 缺失 |
|
||
|
||
**优先补充的单元测试**:
|
||
|
||
1. WorkOrderService — 工单状态机流转
|
||
2. LoginService — 登录认证流程
|
||
3. EquipmentHealthService — 健康评分算法
|
||
4. SparePartService — 出入库库存校验
|
||
|
||
---
|
||
|
||
## 六、数据库设计概要
|
||
|
||
### 6.1 ER 图(核心实体关系)
|
||
|
||
```mermaid
|
||
erDiagram
|
||
User ||--o{ Role : "auth_user_role"
|
||
Role ||--o{ Permission : "auth_role_permission"
|
||
User ||--|| EnterpriseUser : "共享主键1:1"
|
||
User ||--|| ProjectStaff : "1:1"
|
||
User ||--|| Resident : "共享主键1:1"
|
||
ProjectStaff ||--o{ ProjectStaffRole : "1:N"
|
||
ProjectStaffRole }o--|| Role : "N:1"
|
||
Resident ||--o{ ResidentSpace : "1:N"
|
||
ResidentSpace }o--|| Space : "N:1"
|
||
User ||--o{ UserProject : "1:N"
|
||
UserProject }o--|| Project : "N:1"
|
||
Dept ||--o{ Dept : "parentId自引用"
|
||
|
||
Project ||--|| ProjectConfig : "1:1"
|
||
Project ||--|| ProjectStatistics : "1:1"
|
||
Project ||--o{ ProjectStatusHistory : "1:N"
|
||
Project ||--o{ SpaceNode : "1:N"
|
||
SpaceNode ||--o{ SpaceNode : "parentId自引用"
|
||
|
||
Equipment ||--o| EquipmentElevator : "1:1"
|
||
Equipment ||--o| EquipmentHvac : "1:1"
|
||
Equipment ||--o| EquipmentFire : "1:1"
|
||
Equipment ||--o| EquipmentEnergy : "1:1"
|
||
Equipment ||--o{ EquipmentHealthScore : "1:N"
|
||
Equipment ||--o{ EquipmentFailureHistory : "1:N"
|
||
Equipment }o--|| OwnershipEntity : "N:1"
|
||
Equipment }o--o| SpaceNode : "spaceNodeId"
|
||
|
||
MaintenancePlan ||--o{ MaintenanceTask : "1:N"
|
||
WorkOrder ||--o{ WorkOrderItem : "1:N"
|
||
WorkOrder }o--o| MaintenancePlan : "planId"
|
||
WorkOrder }o--o| Equipment : "equipmentId"
|
||
MaintenanceTask }o--o| Equipment : "equipmentId"
|
||
|
||
SparePartCategory ||--o{ SparePartCategory : "parentId自引用"
|
||
SparePartCategory ||--o{ SparePart : "1:N"
|
||
SparePart ||--o{ SparePartRecord : "1:N"
|
||
|
||
EnergyMeter ||--o{ EnergyConsumption : "1:N"
|
||
EnergyMeter }o--o| SpaceNode : "spaceNodeId"
|
||
```
|
||
|
||
### 6.2 表命名规范
|
||
|
||
**格式**:`{模块前缀}_{实体名}`
|
||
|
||
| 模块前缀 | 示例 | 说明 |
|
||
|---------|------|------|
|
||
| auth_ | auth_user, auth_role, auth_permission | 认证授权模块 |
|
||
| mdm_ | mdm_project, mdm_space_node | 主数据模块 |
|
||
| asset_ | asset_equipment, asset_equipment_elevator | 设备资产模块 |
|
||
| ops_ | ops_work_order, ops_maintenance_plan | 运维工单模块 |
|
||
| fin_ | fin_fee_item, fin_fee_bill | 财务模块(待实现) |
|
||
| sys_ | sys_audit_log, sys_config | 系统级表 |
|
||
|
||
**列命名规范**:
|
||
|
||
- 使用 snake_case:`project_id`、`created_at`、`is_deleted`
|
||
- 外键列:`{关联实体}_id`:`equipment_id`、`space_node_id`
|
||
- 布尔列:`is_` 前缀:`is_deleted`、`is_equipment`
|
||
- 时间列:`_at` 后缀:`created_at`、`updated_at`
|
||
- 日期列:`_date` 后缀:`installation_date`、`next_inspection_date`
|
||
|
||
### 6.3 索引策略
|
||
|
||
**B-tree 索引**(等值查询、范围查询、排序):
|
||
|
||
| 表 | 索引列 | 用途 |
|
||
|----|--------|------|
|
||
| ops_work_order | (project_id, status) | 按项目+状态查询工单 |
|
||
| ops_work_order | (status, created_at) | 按状态+时间查询 |
|
||
| ops_work_order | (created_at DESC) | 时间倒序 |
|
||
| asset_equipment | (project_id, status) | 按项目+状态查询设备 |
|
||
| asset_equipment | (project_id, equipment_type) | 按项目+类型查询设备 |
|
||
| asset_equipment | (equipment_code) UNIQUE | 编码唯一 |
|
||
| mdm_space_node | (project_id, parent_id) | 项目+父节点 |
|
||
| mdm_space_node | (project_id, node_type) | 项目+类型 |
|
||
| mdm_space_node | (tree_path) | 路径前缀查询 |
|
||
| ops_equipment_failure_history | (equipment_id, failure_time DESC) | 设备故障时间线 |
|
||
| sys_audit_log | (user_id, created_at DESC) | 用户操作时间线 |
|
||
|
||
**GIN 索引**(JSONB 查询,待添加):
|
||
|
||
```sql
|
||
CREATE INDEX idx_equipment_attributes ON asset_equipment USING GIN (attributes);
|
||
CREATE INDEX idx_equipment_photos ON asset_equipment USING GIN (photos);
|
||
CREATE INDEX idx_space_node_attributes ON mdm_space_node USING GIN (attributes);
|
||
```
|
||
|
||
**JSONB 索引使用场景**:
|
||
|
||
| 表 | 字段 | 查询场景 |
|
||
|----|------|---------|
|
||
| asset_equipment | attributes | 按扩展属性筛选设备 |
|
||
| asset_equipment | photos | 按照片类型查询 |
|
||
| ops_maintenance_task | parts_used | 按备件 ID 查询使用该备件的工单 |
|
||
| mdm_inspection_record | items | 按检查项结果查询 |
|
||
|
||
### 6.4 数据迁移策略
|
||
|
||
**工具选型**:Flyway
|
||
|
||
**迁移脚本命名规范**:
|
||
|
||
```
|
||
V{版本号}__{描述}.sql
|
||
示例:
|
||
V1__init_auth_tables.sql
|
||
V2__init_mdm_tables.sql
|
||
V3__init_asset_tables.sql
|
||
V4__init_ops_tables.sql
|
||
V5__add_equipment_health_score.sql
|
||
V6__add_space_node_indexes.sql
|
||
```
|
||
|
||
**当前状态**:未使用 Flyway,依赖 JPA 自动建表(ddl-auto: update)。
|
||
|
||
**改进计划**:
|
||
|
||
1. 短期:在开发环境继续使用 JPA 自动建表
|
||
2. 中期:引入 Flyway,将现有 schema 导出为初始迁移脚本
|
||
3. 长期:生产环境禁止 ddl-auto: update,所有变更通过 Flyway 迁移脚本执行
|
||
|
||
---
|
||
|
||
## 七、技术选型与依赖
|
||
|
||
### 7.1 后端技术栈
|
||
|
||
| 技术 | 版本 | 用途 | 说明 |
|
||
|------|------|------|------|
|
||
| Spring Boot | 3.x | 应用框架 | Java 17+,Servlet 容器 |
|
||
| Spring Security | 6.x | 安全框架 | JWT 认证 + RBAC 授权 |
|
||
| Spring Data JPA | 3.x | ORM 框架 | Hibernate 实现,自动建表 |
|
||
| Hibernate | 6.x | JPA 实现 | 实体映射、懒加载、审计 |
|
||
| MapStruct | — | 对象映射 | Entity ↔ DTO 转换 |
|
||
| Lombok | — | 代码简化 | @Data、@Builder、@RequiredArgsConstructor |
|
||
| BCrypt | — | 密码加密 | Spring Security PasswordEncoder |
|
||
| JJWT | — | JWT 工具 | Token 生成与验证 |
|
||
| Spring AOP | — | 切面编程 | 审计日志 @OperationLog |
|
||
| Spring Validation | — | 参数校验 | @NotNull、@Size、@Pattern |
|
||
| Spring Web | — | REST API | @RestController、@RequestMapping |
|
||
|
||
### 7.2 前端技术栈
|
||
|
||
| 技术 | 版本 | 用途 | 说明 |
|
||
|------|------|------|------|
|
||
| Vue | 3.x | 前端框架 | Composition API + `<script setup>` |
|
||
| TypeScript | 5.x | 类型系统 | 类型安全 |
|
||
| Ant Design Vue | 4.x | UI 组件库 | 统一视觉风格 |
|
||
| Pinia | 2.x | 状态管理 | Composition API 风格,支持持久化 |
|
||
| Vite | 5.x | 构建工具 | 快速 HMR |
|
||
| Axios | 1.x | HTTP 客户端 | 请求/响应拦截器封装 |
|
||
| Vue Router | 4.x | 路由管理 | 路由守卫 + 权限控制 |
|
||
| ECharts | 5.x | 图表库 | 健康评分趋势图 |
|
||
|
||
### 7.3 数据库
|
||
|
||
| 技术 | 版本 | 用途 | 说明 |
|
||
|------|------|------|------|
|
||
| PostgreSQL | 15+ | 主数据库 | JSONB、UUID、ltree 扩展 |
|
||
| JSONB | — | 半结构化数据 | 照片列表、文档列表、扩展属性、检查项结果 |
|
||
| UUID | — | 主键类型 | 所有实体主键使用 UUID |
|
||
| Redis | 7+ | 缓存 | 登录失败锁定、Token 缓存 |
|
||
|
||
**PostgreSQL 特性使用**:
|
||
|
||
| 特性 | 使用场景 | 当前状态 |
|
||
|------|---------|---------|
|
||
| JSONB | Equipment.photos/documents/attributes、InspectionRecord.items/problems | 部分使用(SpaceNode.attributes 降级为 String) |
|
||
| UUID | 所有实体主键 | 已使用 |
|
||
| ltree | 空间树形结构 | 未使用(使用 treePath 字符串替代) |
|
||
| GIN 索引 | JSONB 字段查询 | 未使用(待添加) |
|
||
| PostGIS | 地图空间查询 | 未使用 |
|
||
|
||
### 7.4 开发工具
|
||
|
||
| 工具 | 用途 | 说明 |
|
||
|------|------|------|
|
||
| Maven | 后端构建 | 多模块项目管理 |
|
||
| npm | 前端构建 | 依赖管理 |
|
||
| Git | 版本控制 | 分支管理 |
|
||
| Flyway | 数据库迁移 | 待引入 |
|
||
| SpringDoc/OpenAPI | API 文档 | 待引入 |
|
||
|
||
---
|
||
|
||
## 附录 A:实现状态总览
|
||
|
||
| 功能模块 | 实现状态 | 核心实体 | 说明 |
|
||
|---------|---------|---------|------|
|
||
| 用户管理 | 已实现 | User/EnterpriseUser/ProjectStaff/Resident | 4 种用户类型扩展 |
|
||
| 角色权限 | 已实现 | Role/Permission/ProjectStaffRole | 三级角色 + 四级数据范围 |
|
||
| 部门管理 | 已实现 | Dept | 树形结构,5 种部门类型 |
|
||
| 项目管理 | 已实现 | Project/ProjectConfig/ProjectStatistics | 状态流转 + 配置 + 统计 |
|
||
| 空间节点 | 已实现 | SpaceNode | 统一空间模型 + 设备扩展字段 |
|
||
| 设备台账 | 已实现 | Equipment + 4 张扩展表 | 主表 + 扩展表模式 |
|
||
| 健康评分 | 已实现(Beta) | EquipmentHealthScore | MTBF/MTTR + 三维扣分 |
|
||
| 故障历史 | 已实现 | EquipmentFailureHistory | 4 级故障 + 4 种修复结果 |
|
||
| 归属管理 | 已实现 | OwnershipEntity | 4 种归属 + 3 种主体 |
|
||
| 工单管理 | 已实现 | WorkOrder/WorkOrderItem | 6 状态状态机 |
|
||
| 维保计划 | 已实现 | MaintenancePlan | 周期性维保调度 |
|
||
| 维保任务 | 已实现 | MaintenanceTask | 6 状态 + 自动优先级判定 |
|
||
| 巡检标准 | 已实现 | InspectionItem/InspectionTemplate | 标准项库 + 模板管理 |
|
||
| 巡检记录 | 已实现 | InspectionRecord | 签到 + JSONB 结果 |
|
||
| 备件库存 | 已实现 | SparePart/SparePartCategory/SparePartRecord | 出入库 + 低库存预警 |
|
||
| 能耗计量 | 已实现 | EnergyMeter/EnergyConsumption | 手动录入 + 简单费用计算 |
|
||
| 审计日志 | 已实现 | AuditLog | AOP + 异步 + 30 天窗口 |
|
||
| 系统配置 | 已实现 | SysConfig | 键值对配置 |
|
||
| 消息通知 | **未实现** | — | NotificationChannel/Template/Rule/History |
|
||
| 收费项目 | **未实现** | — | FeeItem |
|
||
| 账单管理 | **未实现** | — | FeeBill |
|
||
| 支付记录 | **未实现** | — | FeePayment |
|
||
| 设备二维码 | **未实现** | — | 原需求有但未实现 |
|
||
| IoT 集成 | **未实现** | — | 枚举已定义,对接未实现 |
|
||
| 地图服务 | **未实现** | — | PostGIS + 高德地图 |
|
||
| SLA 管理 | **未实现** | — | 响应时间/处理时间/超时升级 |
|
||
|
||
## 附录 B:关键风险与优先行动
|
||
|
||
| 优先级 | 风险 | 行动项 | 预估工作量 |
|
||
|--------|------|--------|-----------|
|
||
| P0 | 财务域核心功能完全缺失 | 创建 module-finance,实现 FeeItem/FeeBill/FeePayment | 5 天 |
|
||
| P0 | 消息通知系统完全缺失 | 实现 NotificationChannel/Template/Rule/History | 8 天 |
|
||
| P0 | 工单全量查询 OOM 风险 | 改为分页查询 | 1 天 |
|
||
| P0 | 健康评分算法 Bug | 修正设备年龄计算和 projectId 硬编码 | 1 天 |
|
||
| P1 | 工单状态机缺少挂起/退回 | 增加 SUSPENDED/RETURNED 状态 | 3 天 |
|
||
| P1 | 工单流转记录缺失 | 实现 WorkOrderFlow | 2 天 |
|
||
| P1 | 前后端枚举不一致 | 统一能源类型/维保状态/触发类型 | 1 天 |
|
||
| P1 | 权限相关端点缺失 | 实现权限树/权限校验/用户菜单 | 3 天 |
|
||
| P2 | API 无版本管理 | 引入 /api/v1/ 前缀 | 3 天 |
|
||
| P2 | SpaceNode.attributes 非 JSONB | 改为 JSONB + GIN 索引 | 2 天 |
|
||
| P2 | 审计日志可篡改 | 改为只追加 + 归档而非删除 | 2 天 |
|
||
| P2 | 设备模型双体系并存 | 统一 MDM SpaceNode 内嵌设备与 ASSET Equipment | 3 天 |
|
||
|
||
---
|
||
|
||
> **文档说明**:本概要设计文档基于 REVERSE-AUTH/MDM/ASSET/OPS/FINANCE 五份反推设计文档、BEST-PRACTICES-REPORT 行业最佳实践评估报告、CONSISTENCY-REPORT 需求一致性评估报告综合编制,反映 2026-05-18 代码库状态。财务域(D5)核心功能标注为"待实现",需优先启动开发。
|