空间与项目域 - 详细设计
文档版本: v1.0
生成日期: 2026-05-18
数据来源: module-mdm 实际代码 + REVERSE-MDM.md
对照需求: 02-SPACE_NODE_DESIGN.md
一、功能点清单
| 功能ID |
功能名称 |
优先级 |
实现状态 |
对应需求ID |
| SPACE-001 |
项目CRUD |
P0 |
已实现 |
02-SPACE-001 |
| SPACE-002 |
项目编码自动生成 |
P0 |
已实现 |
02-SPACE-002 |
| SPACE-003 |
项目状态流转 |
P0 |
已实现 |
02-SPACE-003 |
| SPACE-004 |
项目成员管理 |
P0 |
已实现 |
02-SPACE-004 |
| SPACE-005 |
项目统计数据 |
P1 |
已实现 |
02-SPACE-005 |
| SPACE-006 |
项目配置管理 |
P1 |
已实现 |
02-SPACE-006 |
| SPACE-007 |
项目删除前检查 |
P0 |
已实现 |
02-SPACE-007 |
| SPACE-008 |
项目选择器列表 |
P1 |
已实现 |
02-SPACE-008 |
| SPACE-009 |
空间节点CRUD |
P0 |
已实现 |
02-SPACE-010 |
| SPACE-010 |
空间节点树形查询 |
P0 |
已实现 |
02-SPACE-011 |
| SPACE-011 |
空间节点批量创建 |
P1 |
已实现 |
02-SPACE-012 |
| SPACE-012 |
空间节点删除检查 |
P0 |
已实现 |
02-SPACE-013 |
| SPACE-013 |
空间节点级联删除 |
P0 |
已实现 |
02-SPACE-014 |
| SPACE-014 |
设备管理(CRUD) |
P1 |
已实现(@Deprecated) |
02-SPACE-020 |
| SPACE-015 |
设备批量创建 |
P2 |
已实现(@Deprecated) |
02-SPACE-021 |
| SPACE-016 |
设备Excel导入 |
P2 |
已实现(@Deprecated) |
02-SPACE-022 |
| SPACE-017 |
特种设备查询 |
P2 |
已实现(@Deprecated) |
02-SPACE-023 |
| SPACE-018 |
即将年检设备查询 |
P2 |
已实现(@Deprecated) |
02-SPACE-024 |
| SPACE-019 |
楼栋楼层信息 |
P1 |
已实现 |
02-SPACE-030 |
| SPACE-020 |
巡检标准项CRUD |
P0 |
已实现 |
02-SPACE-040 |
| SPACE-021 |
巡检模板管理 |
P1 |
已实现 |
02-SPACE-041 |
| SPACE-022 |
巡检记录CRUD |
P0 |
已实现 |
02-SPACE-042 |
| SPACE-023 |
巡检完成确认 |
P1 |
已实现 |
02-SPACE-043 |
| SPACE-024 |
备件CRUD |
P1 |
已实现 |
02-SPACE-050 |
| SPACE-025 |
备件出入库 |
P0 |
已实现 |
02-SPACE-051 |
| SPACE-026 |
备件低库存预警 |
P1 |
已实现 |
02-SPACE-052 |
| SPACE-027 |
备件分类管理 |
P1 |
已实现 |
02-SPACE-053 |
| SPACE-028 |
计量点CRUD |
P1 |
已实现 |
02-SPACE-060 |
| SPACE-029 |
能耗记录录入 |
P1 |
已实现 |
02-SPACE-061 |
| SPACE-030 |
能耗统计 |
P2 |
已实现 |
02-SPACE-062 |
二、数据结构设计
2.1 实体定义
2.1.1 Project(项目)
表名: mdm_project
| 字段名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK, 自动生成 |
项目唯一标识符 |
| code |
VARCHAR(50) |
NOT NULL, UNIQUE, 正则^[a-zA-Z0-9_-]+$, 2-50位 |
项目编码 |
| name |
VARCHAR(100) |
NOT NULL, 2-100位 |
项目名称 |
| description |
VARCHAR(500) |
|
项目描述 |
| address |
VARCHAR(100) |
|
项目地址 |
| projectType |
VARCHAR(20) |
@Enumerated(STRING), 默认RESIDENTIAL |
项目类型枚举 |
| province |
VARCHAR(50) |
|
省 |
| city |
VARCHAR(50) |
|
市 |
| district |
VARCHAR(50) |
|
区 |
| longitude |
Double |
|
经度 |
| latitude |
Double |
|
纬度 |
| status |
VARCHAR(20) |
NOT NULL, 默认ACTIVE |
项目状态 |
| buildingCount |
Integer |
|
楼栋数 |
| unitCount |
Integer |
|
单元数 |
| roomCount |
Integer |
|
房间数 |
| floorCount |
Integer |
|
楼层数 |
| logo |
VARCHAR(200) |
|
项目Logo |
| contact |
VARCHAR(200) |
|
联系人 |
| contactPhone |
VARCHAR(20) |
正则^1[3-9]\d{9}$ |
联系电话 |
| createdAt |
LocalDateTime |
NOT NULL, @PrePersist自动填充 |
创建时间 |
| updatedAt |
LocalDateTime |
NOT NULL, @PreUpdate自动填充 |
更新时间 |
枚举: ProjectType
| 值 |
描述 |
| RESIDENTIAL |
住宅 |
| OFFICE |
办公 |
| INDUSTRIAL_PARK |
产业园区 |
项目状态值(String类型,非枚举):
| 值 |
描述 |
| ACTIVE |
正常 |
| DISABLED |
禁用 |
| PENDING |
待审核 |
| ARCHIVED |
已归档 |
索引:
mdm_project_code_key → code (UNIQUE)
2.1.2 SpaceNode(空间节点)-- 核心实体
表名: mdm_space_node
基础字段
| 字段名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK, 自动生成 |
节点唯一标识符 |
| projectId |
UUID |
NOT NULL, column: project_code |
项目ID(注意列名为project_code) |
| code |
VARCHAR(50) |
@JsonIgnore |
空间编码(保留字段,暂未使用) |
| name |
VARCHAR(100) |
NOT NULL, 1-100位 |
节点名称 |
| fullName |
VARCHAR(500) |
|
全路径名称 |
| shortName |
VARCHAR(50) |
|
简称 |
| nodeCategory |
VARCHAR(20) |
NOT NULL, @Enumerated(STRING), column: node_category |
节点大类枚举 |
| nodeType |
VARCHAR(30) |
NOT NULL, @Enumerated(STRING), column: node_type |
节点类型枚举 |
| usageType |
VARCHAR(30) |
|
用途类型 |
| parentId |
UUID |
column: parent_id |
父节点ID |
| treePath |
VARCHAR(1000) |
column: tree_path |
物理路径 id.id.id |
| treePathName |
VARCHAR(1000) |
column: tree_path_name |
名称路径 项目/楼栋/单元/房间 |
| level |
Integer |
默认0 |
层级深度 |
| sortOrder |
Integer |
column: sort_order, 默认0 |
排序号 |
| status |
VARCHAR(20) |
默认ACTIVE |
状态 |
| deliveryStatus |
VARCHAR(20) |
column: delivery_status |
交付状态 |
| decorationStatus |
VARCHAR(20) |
column: decoration_status |
装修状态 |
面积信息
| 字段名 |
类型 |
约束 |
说明 |
| buildingArea |
BigDecimal(10,2) |
column: building_area |
建筑面积(㎡) |
| usableArea |
BigDecimal(10,2) |
column: usable_area |
使用面积(㎡) |
| sharedArea |
BigDecimal(10,2) |
column: shared_area |
公摊面积(㎡) |
| landArea |
BigDecimal(10,2) |
column: land_area |
占地面积(㎡) |
地理信息
| 字段名 |
类型 |
约束 |
说明 |
| longitude |
BigDecimal(10) |
|
经度 |
| latitude |
BigDecimal(10) |
|
纬度 |
| altitude |
BigDecimal(8,2) |
|
海拔 |
| floorNumber |
Integer |
column: floor_number |
楼层号(正数地上,负数地下) |
地址信息
| 字段名 |
类型 |
约束 |
说明 |
| province |
VARCHAR(50) |
|
省 |
| city |
VARCHAR(50) |
|
市 |
| district |
VARCHAR(50) |
|
区 |
| street |
VARCHAR(100) |
|
街道 |
| address |
VARCHAR(255) |
|
详细地址 |
扩展属性
| 字段名 |
类型 |
约束 |
说明 |
| attributes |
VARCHAR(2000) |
|
类型特定属性(JSON格式) |
系统字段
| 字段名 |
类型 |
约束 |
说明 |
| createdAt |
LocalDateTime |
column: created_at |
创建时间 |
| updatedAt |
LocalDateTime |
column: updated_at |
更新时间 |
| createdBy |
UUID |
column: created_by |
创建人 |
| updatedBy |
UUID |
column: updated_by |
更新人 |
| isDeleted |
Boolean |
column: is_deleted, 默认false |
软删除标记 |
设备扩展字段(@Deprecated -- 请使用 module-asset 的 Equipment 实体)
| 字段名 |
类型 |
约束 |
说明 |
| isEquipment |
Boolean |
@Deprecated, column: is_equipment, 默认false |
是否为设备节点 |
| designLifeYears |
Integer |
@Deprecated, column: design_life_years |
设计寿命(年) |
| ratedPower |
BigDecimal(10,2) |
@Deprecated, column: rated_power |
额定功率 |
| ratedVoltage |
BigDecimal(10,2) |
@Deprecated, column: rated_voltage |
额定电压 |
| ratedCurrent |
BigDecimal(10,2) |
@Deprecated, column: rated_current |
额定电流 |
| maintenanceVendor |
VARCHAR(100) |
@Deprecated, column: maintenance_vendor |
维保厂商 |
| maintenanceVendorContact |
VARCHAR(50) |
@Deprecated, column: maintenance_vendor_contact |
维保联系人 |
| maintenanceVendorPhone |
VARCHAR(20) |
@Deprecated, column: maintenance_vendor_phone |
维保电话 |
| maintenanceContractNo |
VARCHAR(50) |
@Deprecated, column: maintenance_contract_no |
维保合同号 |
| maintenanceContractStart |
LocalDate |
@Deprecated, column: maintenance_contract_start |
合同开始日期 |
| maintenanceContractEnd |
LocalDate |
@Deprecated, column: maintenance_contract_end |
合同结束日期 |
| specialEquipmentType |
VARCHAR(50) |
@Deprecated, column: special_equipment_type |
特种设备类型 |
| specialEquipmentCert |
VARCHAR(100) |
@Deprecated, column: special_equipment_cert |
特种设备证书 |
| inspectionCycle |
Integer |
@Deprecated, column: inspection_cycle |
巡检周期(天) |
| nextInspectionDate |
LocalDate |
@Deprecated, column: next_inspection_date |
下次年检日期 |
| lastInspectionDate |
LocalDate |
@Deprecated, column: last_inspection_date |
上次年检日期 |
| lastInspectionResult |
VARCHAR(20) |
@Deprecated, column: last_inspection_result |
上次年检结果 |
| commonSpareParts |
VARCHAR(2000) |
@Deprecated, column: common_spare_parts |
常用备件(JSON) |
| energyConsumptionStandard |
BigDecimal(12,2) |
@Deprecated, column: energy_consumption_standard |
能耗标准 |
| installationEnvironment |
VARCHAR(50) |
@Deprecated, column: installation_environment |
安装环境 |
| protectionLevel |
VARCHAR(20) |
@Deprecated, column: protection_level |
防护等级 |
索引:
| 索引名 |
列 |
说明 |
| idx_space_node_project |
project_id |
按项目查询 |
| idx_space_node_parent |
parent_id |
查子节点 |
| idx_space_node_type |
node_type |
按类型查询 |
| idx_space_node_tree_path |
tree_path |
路径查询 |
| idx_sn_project_parent |
project_id, parent_id |
项目+父节点 |
| idx_sn_project_type |
project_id, node_type |
项目+类型 |
| idx_sn_project_isequipment |
project_id, is_equipment |
项目+设备 |
| idx_sn_project_nextinspection |
project_id, next_inspection_date |
年检预警 |
2.1.3 ProjectConfig(项目配置)
表名: mdm_project_config
| 字段名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK, 自动生成 |
主键 |
| projectId |
UUID |
NOT NULL, UNIQUE, column: project_id |
项目ID |
| enableReservation |
Boolean |
column: enable_reservation, 默认false |
预约功能 |
| enableVisitor |
Boolean |
column: enable_visitor, 默认false |
访客功能 |
| enableComplaint |
Boolean |
column: enable_complaint, 默认true |
投诉功能 |
| enablePayment |
Boolean |
column: enable_payment, 默认false |
缴费功能 |
| enableAnnouncement |
Boolean |
column: enable_announcement, 默认true |
公告功能 |
| enableSurvey |
Boolean |
column: enable_survey, 默认false |
问卷功能 |
| enableVote |
Boolean |
column: enable_vote, 默认false |
投票功能 |
| enableMaintenance |
Boolean |
column: enable_maintenance, 默认true |
报修功能 |
| enableAsset |
Boolean |
column: enable_asset, 默认false |
资产功能 |
| customConfig |
VARCHAR(5000) |
column: custom_config |
自定义配置JSON |
| createdAt |
LocalDateTime |
column: created_at |
创建时间 |
| updatedAt |
LocalDateTime |
column: updated_at |
更新时间 |
索引:
mdm_project_config_project_id_key → projectId (UNIQUE)
2.1.4 ProjectStatistics(项目统计)
表名: mdm_project_statistics
| 字段名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK, 自动生成 |
主键 |
| projectId |
UUID |
NOT NULL, UNIQUE, column: project_id |
项目ID |
| memberCount |
Integer |
column: member_count, 默认0 |
成员数 |
| buildingCount |
Integer |
column: building_count, 默认0 |
楼栋数 |
| unitCount |
Integer |
column: unit_count, 默认0 |
单元数 |
| roomCount |
Integer |
column: room_count, 默认0 |
房间数 |
| ownerCount |
Integer |
column: owner_count, 默认0 |
业主数 |
| tenantCount |
Integer |
column: tenant_count, 默认0 |
租户数 |
| lastSyncedAt |
LocalDateTime |
column: last_synced_at |
最后同步时间 |
| createdAt |
LocalDateTime |
column: created_at |
创建时间 |
| updatedAt |
LocalDateTime |
column: updated_at |
更新时间 |
索引:
mdm_project_statistics_project_id_key → projectId (UNIQUE)
2.1.5 ProjectStatusHistory(状态变更历史)
表名: mdm_project_status_history
| 字段名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK, 自动生成 |
主键 |
| projectId |
UUID |
NOT NULL, column: project_id |
项目ID |
| fromStatus |
VARCHAR(20) |
column: from_status |
原状态 |
| toStatus |
VARCHAR(20) |
NOT NULL, column: to_status |
新状态 |
| reason |
VARCHAR(500) |
|
变更原因 |
| operatorId |
UUID |
column: operator_id |
操作人ID |
| operatorName |
VARCHAR(50) |
column: operator_name |
操作人姓名 |
| createdAt |
LocalDateTime |
column: created_at, @PrePersist自动填充 |
创建时间 |
2.1.6 InspectionItem(巡检标准项)
表名: mdm_inspection_item
| 字段名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK, 自动生成 |
主键 |
| equipmentType |
VARCHAR(50) |
column: equipment_type |
设备类型 |
| systemType |
VARCHAR(50) |
column: system_type |
系统类型 |
| itemName |
VARCHAR(200) |
NOT NULL, column: item_name |
检查项名称 |
| checkMethod |
VARCHAR(200) |
column: check_method |
检查方法 |
| standardValue |
VARCHAR(100) |
column: standard_value |
标准值 |
| isRequired |
Boolean |
column: is_required, 默认true |
是否必检 |
| remark |
VARCHAR(500) |
|
备注 |
| sortOrder |
Integer |
column: sort_order |
排序号 |
| status |
VARCHAR(20) |
NOT NULL, @Enumerated(STRING), 默认ACTIVE |
状态枚举 |
| createdAt |
LocalDateTime |
column: created_at, @PrePersist自动填充 |
创建时间 |
| updatedAt |
LocalDateTime |
column: updated_at, @PreUpdate自动填充 |
更新时间 |
枚举: Status
| 值 |
描述 |
| ACTIVE |
启用 |
| INACTIVE |
停用 |
2.1.7 InspectionRecord(巡检记录)
表名: mdm_inspection_record
| 字段名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK, 自动生成 |
主键 |
| planId |
UUID |
column: plan_id |
巡检计划ID |
| equipmentId |
UUID |
NOT NULL, column: equipment_id |
设备ID |
| inspectionDate |
LocalDate |
NOT NULL, column: inspection_date |
巡检日期 |
| inspector |
VARCHAR(200) |
NOT NULL |
巡检人 |
| status |
VARCHAR(20) |
@Enumerated(STRING), 默认NORMAL |
检查状态枚举 |
| checkInTime |
LocalDateTime |
column: check_in_time |
签到时间 |
| checkInLocation |
VARCHAR(100) |
column: check_in_location |
签到位置 |
| checkInPhoto |
VARCHAR(200) |
column: check_in_photo |
签到照片 |
| items |
JSONB |
@JdbcTypeCode(JSON), columnDefinition: jsonb |
检查项结果列表 |
| problems |
JSONB |
@JdbcTypeCode(JSON), columnDefinition: jsonb |
异常问题列表 |
| completed |
Boolean |
默认false |
是否完成 |
| completedTime |
LocalDateTime |
column: completed_time |
完成时间 |
| createdAt |
LocalDateTime |
column: created_at, @PrePersist自动填充 |
创建时间 |
枚举: CheckStatus
| 值 |
描述 |
| NORMAL |
正常 |
| WARNING |
预警 |
| ABNORMAL |
异常 |
items JSONB结构:
[
{"itemId": "uuid", "itemName": "检查项", "value": "实测值", "result": "PASS/FAIL", "remark": "备注"}
]
problems JSONB结构:
[
{"desc": "问题描述", "photo": "照片URL", "severity": "LOW/MEDIUM/HIGH"}
]
索引:
| 索引名 |
列 |
| idx_ir_equipment_date |
equipment_id, inspection_date |
| idx_ir_inspectiondate |
inspection_date |
2.2 枚举定义
NodeCategory(节点大类)
| 值 |
描述 |
| BUILDING |
建筑空间 |
| PARKING |
停车空间 |
| FACILITY |
设施空间 |
| AREA |
区域空间 |
NodeType(节点类型)-- 含分类和层级
| 值 |
描述 |
所属大类 |
层级序号 |
| BUILDING |
楼栋 |
BUILDING |
1 |
| UNIT |
单元 |
BUILDING |
2 |
| FLOOR |
楼层 |
BUILDING |
3 |
| ROOM |
房间 |
BUILDING |
4 |
| SHOP |
商铺 |
BUILDING |
2 |
| GARAGE |
车库 |
PARKING |
1 |
| PARKING_AREA |
停车区域 |
PARKING |
2 |
| PARKING_SPACE |
车位 |
PARKING |
3 |
| EQUIPMENT_ROOM |
设备房 |
FACILITY |
1 |
| PROPERTY_OFFICE |
物业用房 |
FACILITY |
1 |
| SECURITY_ROOM |
门岗 |
FACILITY |
1 |
| PUBLIC_ROOM |
公共用房 |
FACILITY |
1 |
| PUBLIC_AREA |
公共区域 |
AREA |
1 |
| GREEN_AREA |
绿化区域 |
AREA |
1 |
| ROAD |
道路 |
AREA |
1 |
ProjectType(项目类型)
| 值 |
描述 |
| RESIDENTIAL |
住宅 |
| OFFICE |
办公 |
| INDUSTRIAL_PARK |
产业园区 |
InspectionItem.Status(巡检标准项状态)
| 值 |
描述 |
| ACTIVE |
启用 |
| INACTIVE |
停用 |
CheckStatus(巡检检查状态)
| 值 |
描述 |
| NORMAL |
正常 |
| WARNING |
预警 |
| ABNORMAL |
异常 |
2.3 ER关系图(Mermaid)
erDiagram
Project ||--o{ SpaceNode : "1:N projectId"
Project ||--|| ProjectConfig : "1:1 projectId UNIQUE"
Project ||--|| ProjectStatistics : "1:1 projectId UNIQUE"
Project ||--o{ ProjectStatusHistory : "1:N projectId"
SpaceNode ||--o{ SpaceNode : "自引用树形 parentId"
SpaceNode ||--o| SpaceNode : "设备扩展 isEquipment=true"
InspectionItem {
UUID id PK
String equipmentType
String systemType
String itemName
String status
}
InspectionTemplate {
UUID id PK
UUID projectId
String templateCode
String equipmentType
String inspectionItems
}
InspectionRecord {
UUID id PK
UUID planId
UUID equipmentId
LocalDate inspectionDate
String inspector
String status
}
InspectionRecord }o--|| SpaceNode : "equipmentId"
SparePartCategory ||--o{ SparePartCategory : "自引用树形 parentId"
SparePartCategory ||--o{ SparePart : "1:N categoryId"
SparePart ||--o{ SparePartRecord : "1:N sparePartId"
EnergyMeter ||--o{ EnergyConsumption : "1:N meterId"
EnergyMeter }o--o| SpaceNode : "spaceNodeId"
三、API设计
3.1 ProjectController -- /api/mdm/projects
| 编号 |
方法 |
路径 |
说明 |
请求参数 |
响应格式 |
权限要求 |
例外情况 |
| PROJ-API-001 |
GET |
/ |
分页查询项目列表 |
keyword(String, 可选), status(String, 可选), page(int, 默认0), size(int, 默认10), sortBy(String, 可选), sortDirection(String, 可选) |
{code, data: PageResponse<Project>} |
已登录 |
分页参数越界 |
| PROJ-API-002 |
GET |
/selector |
获取项目选择器列表 |
无 |
{code, data: [ProjectSelectorItem]} |
已登录 |
无 |
| PROJ-API-003 |
GET |
/generate-code |
生成项目编码 |
无 |
{code, data: "PRJ-xxx"} |
管理员 |
无 |
| PROJ-API-004 |
GET |
/{id} |
按ID查询项目 |
id(UUID, Path) |
{code, data: Project} |
已登录 |
项目不存在 |
| PROJ-API-005 |
GET |
/code/{code} |
按编码查询项目 |
code(String, Path) |
{code, data: Project} |
已登录 |
编码不存在 |
| PROJ-API-006 |
POST |
/ |
创建项目 |
Body: Project实体 |
{code, data: Project} |
管理员 |
项目编码已存在; 字段校验失败 |
| PROJ-API-007 |
PUT |
/{id} |
更新项目 |
id(UUID, Path), Body: Project实体 |
{code, data: Project} |
管理员 |
项目不存在; 编码冲突 |
| PROJ-API-008 |
DELETE |
/{id} |
删除项目 |
id(UUID, Path) |
{code, data: null} |
管理员 |
项目不存在; 有关联数据 |
| PROJ-API-009 |
GET |
/{id}/members |
获取项目成员列表 |
id(UUID, Path), page(int, 默认0), size(int, 默认20) |
{code, data: PageResponse<ProjectMemberDTO>} |
项目成员 |
项目不存在 |
| PROJ-API-010 |
POST |
/{id}/members |
添加项目成员 |
id(UUID, Path), Body: AddMemberRequest {userIds: [UUID], roleInProject: String} |
{code, data: null} |
项目管理员 |
项目不存在; 用户不存在; 已是成员 |
| PROJ-API-011 |
DELETE |
/{id}/members/{memberId} |
移除项目成员 |
id(UUID, Path), memberId(UUID, Path) |
{code, data: null} |
项目管理员 |
成员不存在 |
| PROJ-API-012 |
GET |
/{id}/statistics |
获取项目统计数据 |
id(UUID, Path) |
{code, data: ProjectStatistics} |
项目成员 |
项目不存在 |
| PROJ-API-013 |
PUT |
/{id}/status |
变更项目状态 |
id(UUID, Path), Body: ChangeStatusRequest {status: String, reason?: String} |
{code, data: null} |
管理员 |
项目不存在; 状态流转非法 |
| PROJ-API-014 |
GET |
/{id}/config |
获取项目配置 |
id(UUID, Path) |
{code, data: ProjectConfigDTO} |
项目成员 |
项目不存在 |
| PROJ-API-015 |
PUT |
/{id}/config |
更新项目配置 |
id(UUID, Path), Body: ProjectConfigDTO |
{code, data: ProjectConfigDTO} |
项目管理员 |
项目不存在 |
| PROJ-API-016 |
GET |
/{projectId}/delete-check |
项目删除前检查 |
projectId(UUID, Path) |
{code, data: ProjectDeleteCheckVO} |
管理员 |
项目不存在 |
关键DTO:
// ProjectQueryRequest
{ keyword?: string; status?: string; page?: number; size?: number; sortBy?: string; sortDirection?: string }
// ChangeStatusRequest
{ status: string; reason?: string }
// AddMemberRequest
{ userIds: string[]; roleInProject: string }
// ProjectDeleteCheckVO
{ canDelete: boolean; reason?: string; statistics: ProjectDeleteStatistics }
// ProjectSelectorItem
{ id: UUID; code: string; name: string; status: string }
3.2 SpaceNodeController -- /api/mdm/space-nodes
| 编号 |
方法 |
路径 |
说明 |
请求参数 |
响应格式 |
权限要求 |
例外情况 |
| SN-API-001 |
GET |
/ |
分页查询空间节点 |
page(int, 默认0), size(int, 默认10) |
{code, data: Page<SpaceNode>} |
已登录 |
分页参数越界 |
| SN-API-002 |
GET |
/{id} |
查询节点详情 |
id(UUID, Path) |
{code, data: SpaceNode} |
已登录 |
节点不存在 |
| SN-API-003 |
GET |
/project/{projectId} |
按项目查询节点列表 |
projectId(UUID, Path) |
{code, data: [SpaceNode]} |
项目成员 |
项目不存在 |
| SN-API-004 |
GET |
/project/{projectId}/tree |
获取项目空间树 |
projectId(UUID, Path) |
{code, data: [SpaceNodeTreeDTO]} |
项目成员 |
项目不存在 |
| SN-API-005 |
GET |
/project/{projectId}/roots |
获取项目根节点 |
projectId(UUID, Path) |
{code, data: [SpaceNode]} |
项目成员 |
项目不存在 |
| SN-API-006 |
GET |
/project/{projectId}/type/{nodeType} |
按项目+类型查询 |
projectId(UUID, Path), nodeType(NodeType, Path) |
{code, data: [SpaceNode]} |
项目成员 |
无效NodeType |
| SN-API-007 |
GET |
/parent/{parentId}/children |
获取子节点列表 |
parentId(UUID, Path) |
{code, data: [SpaceNode]} |
已登录 |
父节点不存在 |
| SN-API-008 |
POST |
/ |
创建空间节点 |
Body: SpaceNodeCreateDTO |
{code, data: SpaceNode} |
项目管理员 |
项目不存在; 父节点不存在; 字段校验失败 |
| SN-API-009 |
POST |
/batch |
批量创建节点 |
Body: [SpaceNodeCreateDTO] |
{code, data: [SpaceNode]} |
项目管理员 |
批量数量超限; 父节点不存在 |
| SN-API-010 |
PUT |
/{id} |
更新节点 |
id(UUID, Path), Body: SpaceNodeUpdateDTO |
{code, data: SpaceNode} |
项目管理员 |
节点不存在 |
| SN-API-011 |
DELETE |
/{id} |
删除节点 |
id(UUID, Path) |
{code, data: null} |
项目管理员 |
节点不存在; 有子节点 |
| SN-API-012 |
GET |
/{id}/delete-check |
删除前检查 |
id(UUID, Path) |
{code, data: SpaceNodeDeleteCheckDTO} |
项目管理员 |
节点不存在 |
| SN-API-013 |
DELETE |
/{id}/cascade |
级联删除(含子节点) |
id(UUID, Path) |
{code, data: null} |
项目管理员 |
节点不存在 |
| SN-API-014 |
GET |
/{id}/equipment |
获取设备详情 |
id(UUID, Path) |
{code, data: SpaceNodeEquipmentDTO} |
项目成员(@Deprecated) |
节点不存在 |
| SN-API-015 |
GET |
/equipment |
获取设备列表 |
projectId(UUID, Param) |
{code, data: [SpaceNodeEquipmentDTO]} |
项目成员(@Deprecated) |
项目不存在 |
| SN-API-016 |
GET |
/special-equipment |
获取特种设备列表 |
projectId(UUID, Param) |
{code, data: [SpaceNodeEquipmentDTO]} |
项目成员(@Deprecated) |
项目不存在 |
| SN-API-017 |
GET |
/expiring-inspection |
获取即将年检设备 |
projectId(UUID, Param), daysAhead(Integer, 默认90) |
{code, data: [SpaceNodeEquipmentDTO]} |
项目成员(@Deprecated) |
项目不存在 |
| SN-API-018 |
POST |
/equipment |
创建设备 |
Body: EquipmentCreateDTO |
{code, data: SpaceNode} |
项目管理员(@Deprecated) |
项目不存在; 字段校验失败 |
| SN-API-019 |
POST |
/equipment/batch |
批量创建设备 |
Body: [EquipmentCreateDTO] |
{code, data: [SpaceNode]} |
项目管理员(@Deprecated) |
批量数量超限 |
| SN-API-020 |
POST |
/equipment/import |
Excel导入设备 |
file(Multipart), projectId(UUID, Param) |
{code, data: Object} |
项目管理员(@Deprecated) |
文件为空; 文件类型不支持; 文件超10MB; 行数超1000 |
| SN-API-021 |
GET |
/{buildingId}/floor-info |
获取楼栋楼层信息 |
buildingId(String, Path) |
{code, data: FloorInfoVO} |
项目成员 |
楼栋不存在 |
| SN-API-022 |
GET |
/debug/floor-numbers |
调试:检查房间楼层号 |
projectId(UUID, Param) |
{code, data: Map} |
管理员 |
项目不存在 |
关键DTO:
// SpaceNodeCreateDTO
{ projectId: UUID; name: String; nodeCategory: NodeCategory; nodeType: NodeType; parentId?: UUID; buildingArea?: BigDecimal; ... }
// SpaceNodeTreeDTO extends SpaceNode
{ ...SpaceNode; children: SpaceNodeTreeDTO[] }
// SpaceNodeDeleteCheckDTO
{ nodeId: UUID; nodeName: String; childCount: Integer; childTypeCount: Map; totalDescendantCount: Integer }
// EquipmentCreateDTO
{ projectId: UUID; name: String; nodeType: NodeType; parentId?: UUID; ...设备扩展字段 }
// FloorInfoVO
{ buildingId: UUID; buildingName: String; totalFloors: Integer; undergroundFloors: Integer; floors: FloorDetailVO[] }
3.3 InspectionItemController -- /api/mdm/inspection-items
| 编号 |
方法 |
路径 |
说明 |
请求参数 |
响应格式 |
权限要求 |
例外情况 |
| II-API-001 |
POST |
/ |
创建巡检标准项 |
Body: InspectionItem |
{code, data: InspectionItem} |
管理员 |
字段校验失败 |
| II-API-002 |
GET |
/ |
查询巡检标准项列表 |
equipmentType?(String), systemType?(String), activeOnly?(Boolean) |
{code, data: [InspectionItem]} |
已登录 |
无 |
| II-API-003 |
GET |
/{id} |
获取标准项详情 |
id(UUID, Path) |
{code, data: InspectionItem} |
已登录 |
标准项不存在 |
| II-API-004 |
PUT |
/{id} |
更新标准项 |
id(UUID, Path), Body: InspectionItem |
{code, data: InspectionItem} |
管理员 |
标准项不存在 |
| II-API-005 |
DELETE |
/{id} |
删除标准项 |
id(UUID, Path) |
{code, data: null} |
管理员 |
标准项不存在; 已被模板引用 |
查询逻辑优先级:
- activeOnly=true → 仅返回ACTIVE状态项
- equipmentType + systemType → 双条件过滤
- equipmentType → 单条件过滤
- systemType → 系统类型过滤
- 无参数 → 返回全部
3.4 InspectionTemplateController -- /api/ops/inspection-templates
| 编号 |
方法 |
路径 |
说明 |
请求参数 |
响应格式 |
权限要求 |
例外情况 |
| IT-API-001 |
GET |
/ |
获取项目模板列表 |
projectId(UUID, Param) |
{code, data: [InspectionTemplate]} |
项目成员 |
项目不存在 |
| IT-API-002 |
POST |
/ |
创建模板 |
Body: InspectionTemplate |
{code, data: InspectionTemplate} |
管理员 |
模板编码已存在 |
| IT-API-003 |
GET |
/{id} |
获取模板详情 |
id(UUID, Path) |
{code, data: InspectionTemplate} |
已登录 |
模板不存在 |
| IT-API-004 |
PUT |
/{id} |
更新模板 |
id(UUID, Path), Body: InspectionTemplate |
{code, data: InspectionTemplate} |
管理员 |
模板不存在 |
| IT-API-005 |
POST |
/{id}/copy |
复制模板 |
id(UUID, Path), newName(String, Param) |
{code, data: InspectionTemplate} |
管理员 |
模板不存在 |
| IT-API-006 |
GET |
/by-type/{equipmentType} |
按设备类型查模板 |
equipmentType(String, Path) |
{code, data: [InspectionTemplate]} |
已登录 |
无 |
3.5 InspectionRecordController -- /api/mdm/inspection-records
| 编号 |
方法 |
路径 |
说明 |
请求参数 |
响应格式 |
权限要求 |
例外情况 |
| IR-API-001 |
POST |
/ |
创建巡检记录 |
Body: InspectionRecord |
{code, data: InspectionRecord} |
项目成员 |
设备不存在; 字段校验失败 |
| IR-API-002 |
GET |
/ |
查询巡检记录列表 |
equipmentId?(UUID), planId?(UUID), inspector?(String), status?(CheckStatus), startDate?(LocalDate), endDate?(LocalDate) |
{code, data: [InspectionRecord]} |
项目成员 |
无 |
| IR-API-003 |
GET |
/{id} |
获取记录详情 |
id(UUID, Path) |
{code, data: InspectionRecord} |
已登录 |
记录不存在 |
| IR-API-004 |
PUT |
/{id} |
更新记录 |
id(UUID, Path), Body: InspectionRecord |
{code, data: InspectionRecord} |
项目成员 |
记录不存在 |
| IR-API-005 |
DELETE |
/{id} |
删除记录 |
id(UUID, Path) |
{code, data: null} |
管理员 |
记录不存在 |
| IR-API-006 |
POST |
/{id}/complete |
完成巡检记录 |
id(UUID, Path) |
{code, data: null} |
项目成员 |
记录不存在; 已完成 |
查询逻辑优先级:
- equipmentId + 日期范围 → 设备+日期
- equipmentId → 设备
- planId → 计划
- inspector → 巡检人
- status → 状态
- 日期范围 → 时间段
- 无参数 → 全部
3.6 SparePartController -- /api/ops/spare-parts
| 编号 |
方法 |
路径 |
说明 |
请求参数 |
响应格式 |
权限要求 |
例外情况 |
| SP-API-001 |
GET |
/categories |
获取分类列表 |
无 |
{code, data: [SparePartCategory]} |
已登录 |
无 |
| SP-API-002 |
POST |
/categories |
创建分类 |
Body: SparePartCategory |
{code, data: SparePartCategory} |
管理员 |
分类编码已存在 |
| SP-API-003 |
GET |
/ |
获取备件列表 |
projectId(UUID, Param), categoryId?(UUID) |
{code, data: [SparePart]} |
项目成员 |
项目不存在 |
| SP-API-004 |
GET |
/{id} |
获取备件详情 |
id(UUID, Path) |
{code, data: SparePart} |
已登录 |
备件不存在 |
| SP-API-005 |
POST |
/ |
创建备件 |
Body: SparePart |
{code, data: SparePart} |
管理员 |
备件编码已存在 |
| SP-API-006 |
PUT |
/{id} |
更新备件 |
id(UUID, Path), Body: SparePart |
{code, data: SparePart} |
管理员 |
备件不存在 |
| SP-API-007 |
DELETE |
/{id} |
删除备件 |
id(UUID, Path) |
{code, data: null} |
管理员 |
备件不存在; 有出入库记录 |
| SP-API-008 |
GET |
/low-stock |
获取低库存备件 |
projectId(UUID, Param) |
{code, data: [SparePart]} |
项目成员 |
项目不存在 |
| SP-API-009 |
POST |
/in-stock |
入库操作 |
Body: StockRequest {sparePartId, quantity, recordedBy?, remarks?} |
{code, data: null} |
项目成员 |
备件不存在; 数量<=0 |
| SP-API-010 |
POST |
/out-stock |
出库操作 |
Body: OutStockRequest {sparePartId, quantity, relatedOrderId?, recordedBy?, remarks?} |
{code, data: null} |
项目成员 |
备件不存在; 库存不足; 数量<=0 |
| SP-API-011 |
GET |
/{id}/records |
获取备件出入库记录 |
id(UUID, Path) |
{code, data: [SparePartRecord]} |
已登录 |
备件不存在 |
3.7 EnergyController -- /api/ops/energy
| 编号 |
方法 |
路径 |
说明 |
请求参数 |
响应格式 |
权限要求 |
例外情况 |
| EN-API-001 |
POST |
/meters |
创建计量点 |
Body: EnergyMeter |
{code, data: EnergyMeter} |
管理员 |
计量点编码已存在 |
| EN-API-002 |
GET |
/meters |
获取计量点列表 |
projectId(UUID, Param), energyType?(String) |
{code, data: [EnergyMeter]} |
项目成员 |
项目不存在 |
| EN-API-003 |
GET |
/meters/{id} |
获取计量点详情 |
id(UUID, Path) |
{code, data: EnergyMeter} |
已登录 |
计量点不存在 |
| EN-API-004 |
PUT |
/meters/{id} |
更新计量点 |
id(UUID, Path), Body: EnergyMeter |
{code, data: EnergyMeter} |
管理员 |
计量点不存在 |
| EN-API-005 |
DELETE |
/meters/{id} |
删除计量点 |
id(UUID, Path) |
{code, data: null} |
管理员 |
计量点不存在; 有能耗记录 |
| EN-API-006 |
POST |
/consumption |
录入能耗记录 |
Body: RecordConsumptionRequest {meterId, currentReading, recordedBy?} |
{code, data: null} |
项目成员 |
计量点不存在; 读数小于上次 |
| EN-API-007 |
GET |
/consumption/{meterId} |
获取能耗记录 |
meterId(UUID, Path), startDate?(LocalDate), endDate?(LocalDate) |
{code, data: [EnergyConsumption]} |
项目成员 |
计量点不存在 |
| EN-API-008 |
GET |
/statistics/by-type |
按类型统计能耗 |
projectId(UUID, Param), month(String, Param) |
{code, data: Map<EnergyType, BigDecimal>} |
项目成员 |
项目不存在 |
| EN-API-009 |
GET |
/statistics/unit-consumption |
单位面积能耗 |
projectId(UUID, Param), month(String, Param) |
{code, data: BigDecimal} |
项目成员 |
项目不存在 |
四、业务规则
4.1 空间节点树形管理(创建时自动生成treePath/删除前检查/级联删除)
4.1.1 树形结构规则
项目 (Project)
├── 楼栋 (BUILDING) ← NodeCategory.BUILDING, order=1
│ ├── 单元 (UNIT) ← NodeCategory.BUILDING, order=2
│ │ ├── 楼层 (FLOOR) ← NodeCategory.BUILDING, order=3
│ │ │ └── 房间 (ROOM) ← NodeCategory.BUILDING, order=4
│ │ └── 房间 (ROOM) ← 可跳过楼层直接挂房间
│ └── 公共用房 (PUBLIC_ROOM)
├── 商铺 (SHOP) ← NodeCategory.BUILDING, order=2(直接挂在项目下)
├── 车库 (GARAGE) ← NodeCategory.PARKING, order=1
│ ├── 停车区域 (PARKING_AREA) ← NodeCategory.PARKING, order=2
│ │ └── 车位 (PARKING_SPACE) ← NodeCategory.PARKING, order=3
├── 设备房 (EQUIPMENT_ROOM) ← NodeCategory.FACILITY, order=1
├── 物业用房 (PROPERTY_OFFICE)
├── 门岗 (SECURITY_ROOM)
├── 公共区域 (PUBLIC_AREA) ← NodeCategory.AREA, order=1
├── 绿化区域 (GREEN_AREA)
└── 道路 (ROAD)
4.1.2 树形路径维护
4.1.3 删除规则
-
删除前检查: 调用 GET /{id}/delete-check 获取子节点信息
- 返回:
{nodeId, nodeName, childCount, childTypeCount, totalDescendantCount}
-
普通删除: DELETE /{id}
- 有子节点时拒绝删除
- 软删除: 设置
isDeleted=true
-
级联删除: DELETE /{id}/cascade
- 删除当前节点及所有子孙节点
- 软删除: 所有节点设置
isDeleted=true
4.1.4 批量操作
- 批量创建:
POST /batch,受 BatchOperationValidator.validateUpdateSize() 限制数量
- Excel导入设备:
POST /equipment/import
- 支持格式: .xlsx, .xls
- 文件大小限制: 10MB
- 行数限制: 1000行
- 文件类型校验: contentType + 扩展名双重校验
4.2 项目状态流转(ACTIVE/DISABLED/PENDING/ARCHIVED的转换规则)
4.2.1 状态值与转换规则
| 当前状态 |
可流转到 |
说明 |
| PENDING |
ACTIVE |
审核通过 |
| PENDING |
DISABLED |
审核拒绝 |
| ACTIVE |
DISABLED |
禁用项目 |
| ACTIVE |
ARCHIVED |
归档项目 |
| DISABLED |
ACTIVE |
重新启用 |
| DISABLED |
ARCHIVED |
归档禁用项目 |
| ARCHIVED |
- |
终态,不可再变更 |
4.2.2 状态变更规则
- 每次状态变更必须记录
ProjectStatusHistory(fromStatus, toStatus, reason, operatorId, operatorName)
- 变更接口:
PUT /{id}/status,请求体: {status, reason}
- 非法状态转换抛出 BusinessException
4.2.3 项目成员角色
| 角色 |
说明 |
| PROJECT_MANAGER |
项目经理 |
| PROJECT_ADMIN |
项目管理员 |
| OPERATION_STAFF |
运营人员 |
| FINANCE_STAFF |
财务人员 |
| VIEWER |
查看者 |
4.3 空间编码自动生成(BLD-{projSuffix}-{seq}格式)
当前状态: SpaceNode实体有 code 字段(VARCHAR(50), @JsonIgnore),但自动编码规则尚未完整实现。
设计规则(参考原需求 02-SPACE_NODE_DESIGN.md):
| NodeType |
编码格式 |
示例 |
| BUILDING |
BLD-{projSuffix}-{seq} |
BLD-XY-001 |
| UNIT |
BLD-{projSuffix}-{bldSeq}-{seq} |
BLD-XY-001-01 |
| FLOOR |
BLD-{projSuffix}-{bldSeq}-F{floorNo} |
BLD-XY-001-F3 |
| ROOM |
BLD-{projSuffix}-{bldSeq}-{unitSeq}-{seq} |
BLD-XY-001-01-0301 |
| GARAGE |
GRG-{projSuffix}-{seq} |
GRG-XY-001 |
| PARKING_SPACE |
GRG-{projSuffix}-{grgSeq}-{seq} |
GRG-XY-001-A001 |
| EQUIPMENT_ROOM |
EQP-{projSuffix}-{seq} |
EQP-XY-001 |
唯一性约束: 同项目同类型下编码唯一(原设计 UNIQUE(project_id, code),当前代码未实现此约束)
4.4 巡检标准项管理(按设备类型/系统类型分类)
4.4.1 标准项库
- 巡检标准项按
equipmentType(设备类型)和 systemType(系统类型)分类
- 每个标准项包含: 检查项名称、检查方法、标准值、是否必检
- 标准项状态: ACTIVE(启用) / INACTIVE(停用)
- 支持按
activeOnly=true 仅查询启用项
4.4.2 巡检模板
- 模板按项目+设备类型组织
- 模板包含检查项列表(JSON格式存储,字段
inspectionItems)
- 支持模板版本管理(
version 字段)
- 支持模板复制(
POST /{id}/copy)
- 模板与标准项通过
equipmentType 逻辑关联
4.4.3 巡检记录
- 记录关联设备(
equipmentId)和计划(planId)
- 检查结果状态: NORMAL(正常) / WARNING(预警) / ABNORMAL(异常)
- 支持签到信息: 时间(
checkInTime)、位置(checkInLocation)、照片(checkInPhoto)
- 检查项结果和异常问题以JSONB存储
- 完成操作: 调用
POST /{id}/complete 标记完成
4.5 巡检记录管理(签到/问题上报/完成确认)
4.5.1 创建巡检记录流程
- 选择设备(
equipmentId)
- 填写巡检日期(
inspectionDate)和巡检人(inspector)
- 逐项填写检查结果(
items JSONB)
- 上报异常问题(
problems JSONB)
- 签到: 记录时间、位置、照片
4.5.2 完成确认
- 调用
POST /{id}/complete
- 设置
completed=true, completedTime=当前时间
- 已完成的记录不可再修改
4.5.3 状态判定
- NORMAL: 所有检查项通过,无异常问题
- WARNING: 存在轻微异常
- ABNORMAL: 存在严重异常
五、执行约束
5.1 项目编码唯一性
- 数据库约束:
mdm_project.code 列 UNIQUE
- 校验规则: 正则
^[a-zA-Z0-9_-]+$,长度2-50位
- 违反后果: 创建/更新项目时抛出 DataIntegrityViolationException
- 自动生成:
GET /generate-code 接口生成唯一编码
5.2 空间节点树完整性(不能形成环/父节点必须存在)
- 约束说明: 空间节点通过 parentId 自引用形成树形结构
- 父节点校验: 创建/更新节点时,parentId 指向的节点必须存在且属于同一项目
- 环路检测: 更新 parentId 时,沿 parentId 链向上遍历,检查是否会回到当前节点
- 违反后果: 抛出 BusinessException,提示"不能将节点设置为其子节点的下级"
- 项目一致性: 子节点的 projectId 必须与父节点一致
5.3 项目删除前检查(有空间节点/设备/工单时不能删除)
- 检查接口:
GET /{projectId}/delete-check
- 返回结果:
ProjectDeleteCheckVO { canDelete, reason, statistics }
- 阻止删除条件:
- 存在空间节点(SpaceNode)
- 存在设备(isEquipment=true的SpaceNode)
- 存在应收未收费用
- 存在关联工单
- 违反后果: canDelete=false,返回具体原因
5.4 空间编码唯一性(同项目同类型下唯一)
- 约束说明: 同一项目下,相同 NodeType 的节点 code 应唯一
- 当前状态: 数据库层面未建立
UNIQUE(project_id, code) 约束
- 业务层校验: 创建/更新节点时检查同项目同类型下编码是否重复
- 违反后果: 抛出 BusinessException
5.5 备件出库库存约束
- 约束说明: 出库数量不能超过当前库存
- 校验方式:
currentStock - quantity >= 0
- 违反后果: 抛出 BusinessException,提示"库存不足"
5.6 能耗读数递增约束
- 约束说明: 本次读数必须大于上次读数
- 校验方式:
currentReading > previousReading
- 违反后果: 抛出 BusinessException,提示"本次读数不能小于上次读数"
六、权限控制
6.1 API端点权限矩阵
| 控制器 |
端点 |
所需角色 |
数据范围过滤 |
| ProjectController |
GET / |
已登录用户 |
ALL/PROJECT(按用户项目过滤) |
| ProjectController |
GET /selector |
已登录用户 |
PROJECT(仅用户参与的项目) |
| ProjectController |
GET /generate-code |
管理员 |
无 |
| ProjectController |
GET /{id} |
项目成员 |
PROJECT |
| ProjectController |
GET /code/{code} |
项目成员 |
PROJECT |
| ProjectController |
POST / |
管理员(SYSTEM级) |
无 |
| ProjectController |
PUT /{id} |
项目管理员 |
PROJECT |
| ProjectController |
DELETE /{id} |
管理员(SYSTEM级) |
无 |
| ProjectController |
GET /{id}/members |
项目成员 |
PROJECT |
| ProjectController |
POST /{id}/members |
项目管理员 |
PROJECT |
| ProjectController |
DELETE /{id}/members/{memberId} |
项目管理员 |
PROJECT |
| ProjectController |
GET /{id}/statistics |
项目成员 |
PROJECT |
| ProjectController |
PUT /{id}/status |
管理员(SYSTEM级) |
无 |
| ProjectController |
GET /{id}/config |
项目成员 |
PROJECT |
| ProjectController |
PUT /{id}/config |
项目管理员 |
PROJECT |
| ProjectController |
GET /{projectId}/delete-check |
管理员(SYSTEM级) |
无 |
| SpaceNodeController |
GET / |
已登录用户 |
ALL/PROJECT |
| SpaceNodeController |
GET /{id} |
已登录用户 |
PROJECT |
| SpaceNodeController |
GET /project/{projectId}/* |
项目成员 |
PROJECT |
| SpaceNodeController |
POST / |
项目管理员 |
PROJECT |
| SpaceNodeController |
POST /batch |
项目管理员 |
PROJECT |
| SpaceNodeController |
PUT /{id} |
项目管理员 |
PROJECT |
| SpaceNodeController |
DELETE /{id} |
项目管理员 |
PROJECT |
| SpaceNodeController |
DELETE /{id}/cascade |
项目管理员 |
PROJECT |
| SpaceNodeController |
POST /equipment* |
项目管理员(@Deprecated) |
PROJECT |
| SpaceNodeController |
GET /{buildingId}/floor-info |
项目成员 |
PROJECT |
| InspectionItemController |
GET / |
已登录用户 |
ALL |
| InspectionItemController |
GET /{id} |
已登录用户 |
ALL |
| InspectionItemController |
POST / |
管理员 |
无 |
| InspectionItemController |
PUT /{id} |
管理员 |
无 |
| InspectionItemController |
DELETE /{id} |
管理员 |
无 |
| InspectionTemplateController |
GET / |
项目成员 |
PROJECT |
| InspectionTemplateController |
POST / |
管理员 |
无 |
| InspectionTemplateController |
GET /{id} |
已登录用户 |
PROJECT |
| InspectionTemplateController |
PUT /{id} |
管理员 |
无 |
| InspectionTemplateController |
POST /{id}/copy |
管理员 |
无 |
| InspectionRecordController |
POST / |
项目成员 |
PROJECT |
| InspectionRecordController |
GET / |
项目成员 |
PROJECT |
| InspectionRecordController |
GET /{id} |
已登录用户 |
PROJECT |
| InspectionRecordController |
PUT /{id} |
项目成员 |
PROJECT |
| InspectionRecordController |
DELETE /{id} |
管理员 |
无 |
| InspectionRecordController |
POST /{id}/complete |
项目成员 |
PROJECT |
| SparePartController |
GET /categories |
已登录用户 |
ALL |
| SparePartController |
POST /categories |
管理员 |
无 |
| SparePartController |
GET / |
项目成员 |
PROJECT |
| SparePartController |
GET /{id} |
已登录用户 |
ALL |
| SparePartController |
POST / |
管理员 |
PROJECT |
| SparePartController |
PUT /{id} |
管理员 |
PROJECT |
| SparePartController |
DELETE /{id} |
管理员 |
PROJECT |
| SparePartController |
GET /low-stock |
项目成员 |
PROJECT |
| SparePartController |
POST /in-stock |
项目成员 |
PROJECT |
| SparePartController |
POST /out-stock |
项目成员 |
PROJECT |
| SparePartController |
GET /{id}/records |
已登录用户 |
ALL |
| EnergyController |
POST /meters |
管理员 |
PROJECT |
| EnergyController |
GET /meters |
项目成员 |
PROJECT |
| EnergyController |
GET /meters/{id} |
已登录用户 |
ALL |
| EnergyController |
PUT /meters/{id} |
管理员 |
PROJECT |
| EnergyController |
DELETE /meters/{id} |
管理员 |
PROJECT |
| EnergyController |
POST /consumption |
项目成员 |
PROJECT |
| EnergyController |
GET /consumption/{meterId} |
项目成员 |
PROJECT |
| EnergyController |
GET /statistics/* |
项目成员 |
PROJECT |
七、例外情况处理
7.1 项目相关例外
| 例外场景 |
错误码 |
错误信息 |
处理方式 |
| 项目不存在 |
404 |
"项目不存在" |
findById返回空时抛出 |
| 项目编码已存在 |
409 |
"项目编码{code}已存在" |
创建/更新前查询 |
| 项目编码格式错误 |
400 |
"项目代码只能包含字母、数字、连字符和下划线" |
正则校验 |
| 项目名称为空 |
400 |
"项目名称不能为空" |
@NotNull校验 |
| 联系电话格式错误 |
400 |
"联系电话格式不正确" |
正则校验 |
| 非法状态转换 |
400 |
"不允许从{from}变更为{to}" |
状态机校验 |
| 删除项目有空间节点 |
400 |
"项目下存在空间节点,无法删除" |
delete-check检查 |
| 删除项目有应收费用 |
400 |
"项目存在应收未收费用,无法删除" |
delete-check检查 |
| 项目已归档不可变更 |
400 |
"已归档项目不可操作" |
ARCHIVED终态检查 |
7.2 空间节点相关例外
| 例外场景 |
错误码 |
错误信息 |
处理方式 |
| 节点不存在 |
404 |
"空间节点不存在" |
findById返回空时抛出 |
| 父节点不存在 |
400 |
"父节点不存在" |
创建/更新时校验parentId |
| 父节点不属于同一项目 |
400 |
"父节点不属于当前项目" |
projectId一致性校验 |
| 有子节点不能普通删除 |
400 |
"该节点存在{count}个子节点,请使用级联删除" |
delete-check检查 |
| 树形结构形成环 |
400 |
"不能将节点设置为其子节点的下级" |
parentId链遍历检查 |
| 节点名称为空 |
400 |
"空间节点名称不能为空" |
@NotNull校验 |
| nodeType与nodeCategory不匹配 |
400 |
"节点类型与节点大类不匹配" |
枚举关联校验 |
| 批量创建数量超限 |
400 |
"批量操作数量超过限制" |
BatchOperationValidator校验 |
7.3 设备相关例外(@Deprecated)
| 例外场景 |
错误码 |
错误信息 |
处理方式 |
| 设备不存在 |
404 |
"设备不存在" |
findById返回空时抛出 |
| Excel文件为空 |
400 |
"文件不能为空" |
file.isEmpty()检查 |
| Excel文件类型不支持 |
400 |
"不支持的文件类型,仅支持 Excel 文件(.xlsx, .xls)" |
contentType+扩展名校验 |
| Excel文件过大 |
400 |
"文件大小不能超过 10MB" |
file.getSize()检查 |
| Excel行数超限 |
400 |
"Excel 行数不能超过 1000 行,当前 {n} 行" |
POI解析行数检查 |
| Excel解析失败 |
400 |
"文件解析失败: {message}" |
IOException捕获 |
7.4 巡检相关例外
| 例外场景 |
错误码 |
错误信息 |
处理方式 |
| 巡检标准项不存在 |
404 |
"巡检标准项不存在" |
findById返回空时抛出 |
| 标准项已被模板引用 |
400 |
"该标准项已被巡检模板引用,无法删除" |
检查模板引用 |
| 巡检记录不存在 |
404 |
"巡检记录不存在" |
findById返回空时抛出 |
| 巡检记录已完成 |
400 |
"巡检记录已完成,不可修改" |
completed=true检查 |
| 设备ID不存在 |
400 |
"设备不存在" |
equipmentId校验 |
| 巡检模板编码已存在 |
409 |
"模板编码{code}已存在" |
创建前查询 |
| 巡检模板不存在 |
404 |
"巡检模板不存在" |
findById返回空时抛出 |
7.5 备件相关例外
| 例外场景 |
错误码 |
错误信息 |
处理方式 |
| 备件不存在 |
404 |
"备件不存在" |
findById返回空时抛出 |
| 备件编码已存在 |
409 |
"备件编码{code}已存在" |
创建前查询 |
| 出库数量超过库存 |
400 |
"库存不足,当前库存{stock},出库数量{qty}" |
currentStock校验 |
| 出入库数量为0或负数 |
400 |
"数量必须大于0" |
quantity > 0校验 |
| 分类编码已存在 |
409 |
"分类编码{code}已存在" |
创建前查询 |
| 备件有出入库记录不能删除 |
400 |
"该备件存在出入库记录,无法删除" |
检查SparePartRecord |
7.6 能耗相关例外
| 例外场景 |
错误码 |
错误信息 |
处理方式 |
| 计量点不存在 |
404 |
"计量点不存在" |
findById返回空时抛出 |
| 计量点编码已存在 |
409 |
"计量点编码{code}已存在" |
创建前查询 |
| 本次读数小于上次读数 |
400 |
"本次读数不能小于上次读数" |
currentReading > previousReading |
| 计量点有能耗记录不能删除 |
400 |
"该计量点存在能耗记录,无法删除" |
检查EnergyConsumption |
| 项目无建筑面积 |
400 |
"项目无建筑面积数据,无法计算单位面积能耗" |
统计时校验 |
7.7 通用例外
| 例外场景 |
错误码 |
错误信息 |
处理方式 |
| 分页参数越界 |
200(自动修正) |
无(使用getSafeSize修正) |
PaginationValidator校验 |
| 项目ID为空 |
400 |
"项目ID不能为空" |
@NotNull校验 |
| 无权限访问项目数据 |
403 |
"无权访问该项目数据" |
DataScopeService校验 |