diff --git a/01-REQUIREMENTS/SYSTEM-REQUIREMENTS.md b/01-REQUIREMENTS/SYSTEM-REQUIREMENTS.md new file mode 100644 index 0000000..f01546d --- /dev/null +++ b/01-REQUIREMENTS/SYSTEM-REQUIREMENTS.md @@ -0,0 +1,857 @@ +# Ether 物业管理系统 - 系统需求规格说明书 + +**文档版本**: v1.0 +**创建日期**: 2026-05-18 +**数据来源**: FEATURE_LIST.md / REVERSE-AUTH.md / REVERSE-MDM.md / REVERSE-ASSET.md / REVERSE-OPS.md / REVERSE-FINANCE.md / 00-项目总览.md +**维护原则**: 每个需求项标注唯一ID与实现状态,变更时同步更新 + +--- + +## 一、系统概述 + +### 1.1 系统定位与目标 + +Ether 物业管理平台是一个面向住宅物业和商业物业的全场景智慧管理解决方案,旨在通过数字化手段实现物业运营全流程闭环管理。 + +**核心目标**: +- 统一物业管理数据基座,消除信息孤岛 +- 实现设备全生命周期管理,从采购安装到报废退役 +- 建立运维工单闭环,覆盖报修、派单、执行、验收全流程 +- 构建预防性维保体系,降低设备故障率 +- 实现财务收费自动化,支持多种计费方式与支付渠道 +- 提供多端协同能力(管理后台、员工APP、业主小程序、商办小程序) + +**系统组成**: + +| 应用 | 目录 | 端口 | 说明 | +|------|------|------|------| +| 后端服务 | ether-pms | 8080 | Spring Boot 单体应用 | +| Web 管理后台 | ether-admin | 5175 | Vue3 + TypeScript + Ant Design Vue | +| 员工 APP | ether-app-employee | 5174 | 移动端应用 | +| 业主小程序 | ether-app-owner | 5176 | 微信/支付宝小程序 | +| 商办小程序 | ether-app-commercial | 5177 | 微信/支付宝小程序 | + +**后端模块划分**: + +| 模块 | 职责 | +|------|------| +| module-auth | 认证授权(用户/角色/权限/部门/审计日志) | +| module-mdm | 主数据管理(项目/空间节点/巡检标准/备件/能耗) | +| module-asset | 资产设备管理(设备台账/扩展表/健康评分/故障历史/归属主体) | +| module-wo | 运维工单(工单/维保计划/维保任务/巡检模板) | +| module-finance | 财务管理(待建设) | +| module-common | 公共组件与工具 | + +### 1.2 用户角色定义 + +| 角色 | 说明 | 用户类型 | 典型操作 | +|------|------|----------|----------| +| 系统管理员 | 平台运维人员 | ENTERPRISE | 用户管理、角色配置、系统设置 | +| 项目经理 | 物业项目负责人 | ENTERPRISE | 项目管理、成员分配、工单审批 | +| 维保人员 | 设备维修保养人员 | PROJECT_STAFF | 接单、执行维保、填写报告 | +| 巡检人员 | 设备巡检执行人员 | PROJECT_STAFF | 执行巡检、签到、异常上报 | +| 业主 | 房屋产权人 | RESIDENT | 报修、缴费、查看公告 | +| 租户 | 房屋承租人 | RESIDENT | 报修、缴费 | + +### 1.3 系统边界与约束 + +**系统边界**: +- 本系统管理物业项目从入驻到退场的全周期运营 +- 涵盖设备资产从采购安装到报废退役的全生命周期 +- 不包含房地产开发阶段的销售管理 +- 不包含物业管理公司内部的人力资源管理(仅管理项目人员配置) + +**技术约束**: +- 后端:Spring Boot 3.x 单体架构,PostgreSQL 数据库 +- 前端:Vue3 + TypeScript + Ant Design Vue +- 认证:JWT Token(HMAC-SHA256),BCrypt 密码加密 +- 数据隔离:基于项目维度的数据隔离(X-Project-ID Header) +- 移动端:uni-app 跨端框架 + +**业务约束**: +- 所有业务数据以项目为基本归属维度 +- 用户可参与多个项目,需切换项目上下文 +- 角色权限支持项目级隔离(系统级/项目级/部门级) + +### 1.4 术语定义 + +| 术语 | 定义 | +|------|------| +| MDM | Master Data Management,主数据管理 | +| RBAC | Role-Based Access Control,基于角色的访问控制 | +| MTBF | Mean Time Between Failures,平均故障间隔时间 | +| MTTR | Mean Time To Repair,平均修复时间 | +| JWT | JSON Web Token,用于身份认证的令牌 | +| SpaceNode | 空间节点,统一空间体系中的树形节点 | +| WorkOrder | 工单,运维服务的业务单据 | +| MaintenanceTask | 维保任务,设备维保的执行单元 | +| OwnershipEntity | 归属主体,设备的所有权/管理权归属方 | +| DataScope | 数据范围,控制用户可访问的数据边界 | + +--- + +## 二、业务域需求 + +### 2.1 身份与权限域 + +#### 2.1.1 用户管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-AUTH-001 | 用户创建 | 支持创建用户,用户名3-50位字母数字下划线(正则`^[a-zA-Z0-9_]{3,50}$`),密码BCrypt加密存储,手机号格式校验(正则`^1[3-9]\d{9}$`),邮箱格式校验,用户类型必填(ENTERPRISE/PROJECT_STAFF/RESIDENT/CUSTOMER),默认状态ACTIVE | 高 | 已实现 | module-auth: UserController | +| REQ-AUTH-002 | 用户查询 | 支持分页查询用户列表,可按用户名/姓名/手机号/状态/用户类型筛选 | 高 | 已实现 | module-auth: UserController | +| REQ-AUTH-003 | 用户更新 | 支持更新用户基本信息(真实姓名/手机号/邮箱/头像/状态/部门),状态枚举:ACTIVE/LOCKED/DISABLED | 高 | 已实现 | module-auth: UserController | +| REQ-AUTH-004 | 用户删除 | 支持删除用户,删除时记录审计日志 | 中 | 已实现 | module-auth: UserController | +| REQ-AUTH-005 | 密码修改 | 需验证原密码,新密码需通过强度校验(8-20位,大小写+数字+特殊字符),弱密码检测(黑名单:password/123456/admin/qwerty/letmein/welcome/monkey/dragon),旧密码兼容检测(非BCrypt格式强制重置) | 高 | 已实现 | module-auth: UserController | +| REQ-AUTH-006 | 多类型用户扩展 | 企业员工(EnterpriseUser)含员工工号/部门/职位/入职日期/员工类别;项目员工(ProjectStaff)含所属项目/员工类型/班次/班组长/在岗状态;住户(Resident)含身份证/住户类型/认证状态 | 高 | 已实现 | module-auth: EnterpriseUser/ProjectStaff/Resident | +| REQ-AUTH-007 | 用户角色分配 | 支持为用户分配多个角色(覆盖模式),通过auth_user_role中间表关联 | 高 | 已实现 | module-auth: UserController.assignRoles | +| REQ-AUTH-008 | 用户项目关联 | 支持查看用户参与的项目列表、添加用户到项目、从项目移除用户,关联信息含项目角色(leader/member/viewer)和加入时间 | 高 | 已实现 | module-auth: UserController | + +#### 2.1.2 角色与权限 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-AUTH-010 | 角色管理 | 支持创建/查询/更新/删除角色,角色代码2-50位字母数字下划线(唯一),角色类型三级:SYSTEM(系统级)/PROJECT(项目级)/DEPARTMENT(部门级),角色状态:ENABLED/DISABLED | 高 | 已实现 | module-auth: RoleController | +| REQ-AUTH-011 | 权限管理 | 支持创建/查询/更新/删除权限,权限代码格式`module:resource:action`(仅字母数字冒号下划线),权限类型:MENU/BUTTON/API,支持资源路径和HTTP方法(API类型),支持父权限代码(parentCode)构建权限树 | 高 | 已实现 | module-auth: PermissionController | +| REQ-AUTH-012 | 角色权限分配 | 支持为角色分配多个权限(覆盖模式),通过auth_role_permission中间表关联,支持查询角色权限列表 | 高 | 已实现 | module-auth: RoleController.assignPermissions | +| REQ-AUTH-013 | 四级数据范围 | 角色数据范围枚举:ALL(全部数据,不过滤)/PROJECT(本项目数据,过滤project_id)/DEPARTMENT(本部门数据,过滤project_id+dept_id)/SELF(仅本人数据,过滤assigned_to/creator) | 高 | 已实现 | module-auth: DataScopeService | +| REQ-AUTH-014 | 双层角色分配 | 用户直接角色(auth_user_role表)+ 项目员工角色(ProjectStaffRole表),登录时收集全部角色 = 用户直接角色 U 项目员工角色 | 高 | 已实现 | module-auth: AuthService | +| REQ-AUTH-015 | 项目级角色查询 | 支持按项目ID查询该项目下的角色列表 | 中 | 已实现 | module-auth: RoleController | +| REQ-AUTH-016 | 角色关联用户查询 | 支持查询拥有指定角色的用户列表 | 中 | 已实现 | module-auth: RoleController | +| REQ-AUTH-017 | 权限树端点 | 提供权限树结构查询接口(GET /permissions/tree) | 中 | 未实现 | - | +| REQ-AUTH-018 | 权限校验端点 | 提供权限校验接口(POST /check),验证用户是否拥有指定权限 | 中 | 未实现 | - | +| REQ-AUTH-019 | 用户菜单端点 | 提供用户菜单查询接口(GET /users/{id}/menus),返回用户可见菜单树 | 中 | 未实现 | - | +| REQ-AUTH-020 | 用户权限查询端点 | 提供用户权限查询接口(GET /users/{id}/permissions),返回用户全部权限列表 | 中 | 未实现 | - | +| REQ-AUTH-021 | 角色业务属性 | 角色扩展字段:businessType(业务类型)/terminalType(终端类型)/isFrontline(是否一线岗位) | 低 | 未实现 | - | +| REQ-AUTH-022 | 权限菜单路由属性 | 权限扩展字段:path(路由路径)/component(组件路径)/icon(图标)/level(层级) | 低 | 未实现 | - | + +#### 2.1.3 组织架构 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-AUTH-030 | 部门管理 | 支持创建/查询/更新/删除部门,部门名称1-100位,支持树形结构(parentId自引用),部门类型5种:ADMIN(行政管理)/ENGINEERING(工程部)/SECURITY(安保部)/CS(客服部)/CLEANING(保洁部),部门状态:ACTIVE/DISABLED | 高 | 已实现 | module-auth: DeptController | +| REQ-AUTH-031 | 部门树查询 | 后端返回扁平列表,Controller层递归构建树形结构(DeptVO含children字段) | 高 | 已实现 | module-auth: DeptController.getDeptTree | +| REQ-AUTH-032 | 部门成员查询 | 支持查询指定部门下的成员列表 | 中 | 已实现 | module-auth: DeptController.getDeptMembers | +| REQ-AUTH-033 | 按类型查询部门 | 支持按部门类型(deptType)筛选部门列表 | 中 | 已实现 | module-auth: DeptController.getDeptsByType | +| REQ-AUTH-034 | 部门删除约束 | 删除部门前需先删除子部门,否则拒绝操作 | 高 | 已实现 | module-auth: DeptService | + +#### 2.1.4 项目成员管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-AUTH-040 | 项目成员列表 | 支持分页查询指定项目的成员列表 | 高 | 已实现 | module-auth: ProjectMemberController | +| REQ-AUTH-041 | 可用成员查询 | 获取可添加到项目的企业员工列表,支持search参数模糊搜索(用户名/姓名) | 高 | 已实现 | module-auth: ProjectMemberController | +| REQ-AUTH-042 | 添加项目成员 | 创建/更新ProjectStaff记录(设定员工类型、分配状态),覆盖模式更新角色(先删后加),员工类型6种:SECURITY/CLEANING/GARDEN/MAINTENANCE/CUSTOMER_SERVICE/GENERAL | 高 | 已实现 | module-auth: ProjectMemberController | +| REQ-AUTH-043 | 移除项目成员 | 删除ProjectStaff记录(级联删除ProjectStaffRole角色关联) | 高 | 已实现 | module-auth: ProjectMemberController | +| REQ-AUTH-044 | 项目员工班次 | 项目员工班次类型3种:DAY(白班)/NIGHT(夜班)/ROTATION(轮班),在岗状态3种:ASSIGNED(在岗)/ON_LEAVE(休假)/TRANSFERRED(已调离) | 中 | 已实现 | module-auth: ProjectStaff | +| REQ-AUTH-045 | 项目员工角色 | 项目员工在项目中的角色分配(ProjectStaffRole),支持一个员工多个项目角色 | 中 | 已实现 | module-auth: ProjectStaffRole | + +#### 2.1.5 住户管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-AUTH-050 | 住户信息管理 | 住户(Resident)通过共享主键一对一关联User,含身份证号(18位,JSON忽略脱敏)、住户类型3种:OWNER(业主)/FAMILY(家属)/TENANT(租户) | 高 | 已实现 | module-auth: Resident | +| REQ-AUTH-051 | 住户认证流程 | 认证状态4种流转:UNVERIFIED(未认证)-> PENDING(待审核)-> VERIFIED(已认证)/REJECTED(已拒绝),记录认证时间和认证人 | 高 | 已实现 | module-auth: Resident | +| REQ-AUTH-052 | 住户房屋绑定 | 住户-房屋关联(ResidentSpace),关系类型3种:OWNER/FAMILY/TENANT,绑定状态4种:PENDING(待确认)/ACTIVE(生效中)/EXPIRED(已过期)/CANCELLED(已取消),支持生效日期和失效日期(NULL=永久) | 高 | 已实现 | module-auth: ResidentSpace | +| REQ-AUTH-053 | 房屋空间管理 | 房屋空间(Space)含楼栋/单元/房号/房屋类型(RESIDENTIAL/COMMERCIAL)/楼层/建筑面积/状态,归属项目 | 中 | 已实现 | module-auth: Space | + +#### 2.1.6 认证与授权 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-AUTH-060 | 用户登录 | POST /api/auth/login,请求体{username, password},流程:检查登录锁定 -> 查询用户(含角色) -> 验证密码(BCrypt) -> 检查用户状态(LOCKED/DISABLED拒绝) -> 收集全部角色 -> 生成JWT Token -> 返回{token, userId, username, realName, roles} | 高 | 已实现 | module-auth: AuthController | +| REQ-AUTH-061 | 用户登出 | POST /api/auth/logout,需携带Authorization Header | 高 | 已实现 | module-auth: AuthController | +| REQ-AUTH-062 | Token刷新 | POST /api/auth/refresh,需携带有效Token,生成新Token返回 | 高 | 已实现 | module-auth: AuthController | +| REQ-AUTH-063 | 获取当前用户 | GET /api/auth/me,需携带Authorization Header,返回当前用户信息 | 高 | 已实现 | module-auth: AuthController | +| REQ-AUTH-064 | JWT Token规范 | 算法HMAC-SHA256,Claims含userId(subject)/username/roles列表,过期时间可配置(默认86400000ms=24小时) | 高 | 已实现 | module-auth: JwtTokenProvider | +| REQ-AUTH-065 | 登录失败锁定 | 基于Redis实现,Key格式`login:attempt:{username}`,5次失败后锁定10分钟,每次失败递增计数并设置TTL,登录成功清除计数,支持手动解锁 | 高 | 已实现 | module-auth: LoginAttemptService | +| REQ-AUTH-066 | 项目上下文传递 | 请求头`X-Project-ID`携带当前项目ID,ProjectContextInterceptor拦截器设置ThreadLocal,业务层通过DataScopeService过滤数据 | 高 | 已实现 | module-auth: ProjectContextInterceptor | + +#### 2.1.7 审计日志 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-AUTH-070 | 操作日志记录 | 通过`@OperationLog`注解+AOP切面自动记录操作日志,记录内容:操作用户/操作描述/模块/操作类型/目标类型/目标ID/操作内容/请求参数/操作结果/IP地址/用户代理/请求URL/请求方法/执行耗时/状态/错误信息 | 高 | 已实现 | module-auth: OperationLogAspect | +| REQ-AUTH-071 | 异步持久化 | 通过`auditLogExecutor`线程池异步保存审计日志,避免影响业务性能 | 高 | 已实现 | module-auth: AuditLogService | +| REQ-AUTH-072 | 审计日志查询 | 支持分页查询,可按module/action/username/日期范围筛选,强制限制查询范围不超过30天(超过自动截断起始时间) | 高 | 已实现 | module-auth: AuditLogController | +| REQ-AUTH-073 | 模块与操作类型 | 记录模块8种:USER/ROLE/PERMISSION/PROJECT/AUTH/DEPT/PROJECT_MEMBER/SYSTEM;操作类型11种:CREATE/UPDATE/DELETE/QUERY/VIEW/LOGIN/LOGOUT/EXPORT/IMPORT/ASSIGN/REVOKE | 中 | 已实现 | module-auth: AuditLog | +| REQ-AUTH-074 | 日志统计 | 支持获取最近30天日志统计数据,提供模块列表和操作类型列表(用于筛选下拉) | 中 | 已实现 | module-auth: AuditLogController | +| REQ-AUTH-075 | 日志归档 | 90天以上日志归档(当前实现为直接删除,TODO: 导出至对象存储) | 低 | 部分实现 | module-auth: AuditLogService | + +#### 2.1.8 系统配置 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-AUTH-080 | 键值配置管理 | 支持创建/查询/更新系统配置项,配置键(configKey)唯一,配置值最大5000字符,支持单个更新和批量更新 | 高 | 已实现 | module-auth: SysConfigController | +| REQ-AUTH-081 | 密码策略配置 | 密码强度规则可配置(前缀`password.*`):最小长度(默认8)/最大长度(默认20)/需大写(默认true)/需小写(默认true)/需数字(默认true)/需特殊字符(默认true) | 高 | 已实现 | module-auth: PasswordStrengthValidator | +| REQ-AUTH-082 | 数据访问授权 | 通用数据访问控制(DataAccess),支持按dataType/dataId/accessType/accessId/accessLevel进行细粒度授权,已存在相同记录则更新accessLevel | 中 | 已实现 | module-auth: DataAccessController | + +--- + +### 2.2 空间与项目域 + +#### 2.2.1 项目管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-MDM-001 | 项目创建 | 支持创建项目,项目编码唯一(正则`^[a-zA-Z0-9_-]+$`,2-50位),项目名称2-100位,项目类型3种:RESIDENTIAL(住宅)/OFFICE(办公)/INDUSTRIAL_PARK(产业园区),支持自动生成项目编码,创建后自动初始化ProjectConfig(默认配置) | 高 | 已实现 | module-mdm: ProjectController | +| REQ-MDM-002 | 项目查询 | 支持分页查询项目列表,可按keyword/status筛选,支持排序;支持按ID/编码查询项目详情;支持项目选择器列表(用于下拉选择) | 高 | 已实现 | module-mdm: ProjectController | +| REQ-MDM-003 | 项目更新与删除 | 支持更新项目基本信息;删除前必须调用delete-check检查关联数据(存在应收未收费用时无法删除) | 高 | 已实现 | module-mdm: ProjectController | +| REQ-MDM-004 | 项目状态流转 | 4种状态:PENDING(待审核)/ACTIVE(正常)/DISABLED(禁用)/ARCHIVED(已归档),流转规则:PENDING->ACTIVE/DISABLED, ACTIVE->DISABLED/ARCHIVED, DISABLED->ACTIVE/ARCHIVED, ARCHIVED为终态不可流转 | 高 | 已实现 | module-mdm: ProjectController.changeStatus | +| REQ-MDM-005 | 状态变更历史 | 每次状态变更记录ProjectStatusHistory(fromStatus/toStatus/reason/operatorId/operatorName) | 中 | 已实现 | module-mdm: ProjectStatusHistory | +| REQ-MDM-006 | 项目统计 | 支持查询项目统计数据(ProjectStatistics快照):成员数/楼栋数/单元数/房间数/业主数/租户数 | 中 | 已实现 | module-mdm: ProjectController.getStatistics | +| REQ-MDM-007 | 项目配置管理 | 项目功能开关10项:预约/访客/投诉(默认开)/缴费/公告(默认开)/问卷/投票/报修(默认开)/资产/自定义配置JSON | 高 | 已实现 | module-mdm: ProjectConfigController | +| REQ-MDM-008 | 项目成员管理(MDM侧) | 支持查看项目成员列表(分页)、添加成员(从企业员工中选择,支持批量)、移除成员;成员角色5种:PROJECT_MANAGER/PROJECT_ADMIN/OPERATION_STAFF/FINANCE_STAFF/VIEWER | 高 | 已实现 | module-mdm: ProjectMemberController | +| REQ-MDM-009 | 项目删除检查 | 删除前检查关联数据,返回{canDelete, reason, statistics},存在应收未收费用时canDelete=false | 高 | 已实现 | module-mdm: ProjectController | + +#### 2.2.2 空间节点管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-MDM-010 | 空间节点创建 | 支持创建空间节点,节点名称1-100位,节点大类4种(NodeCategory):BUILDING(建筑空间)/PARKING(停车空间)/FACILITY(设施空间)/AREA(区域空间),节点类型15种(NodeType,含层级序号),支持批量创建 | 高 | 已实现 | module-mdm: SpaceNodeController | +| REQ-MDM-011 | 15种节点类型 | BUILDING(楼栋,L1)/UNIT(单元,L2)/FLOOR(楼层,L3)/ROOM(房间,L4)/SHOP(商铺,L2)/GARAGE(车库,L1)/PARKING_AREA(停车区域,L2)/PARKING_SPACE(车位,L3)/EQUIPMENT_ROOM(设备房,L1)/PROPERTY_OFFICE(物业用房,L1)/SECURITY_ROOM(门岗,L1)/PUBLIC_ROOM(公共用房,L1)/PUBLIC_AREA(公共区域,L1)/GREEN_AREA(绿化区域,L1)/ROAD(道路,L1) | 高 | 已实现 | module-mdm: SpaceNode | +| REQ-MDM-012 | 树形结构维护 | 支持树形路径维护:treePath(物理路径id.id.id,用于快速查询子孙)、treePathName(名称路径项目/楼栋/单元/房间,用于展示)、level(层级深度,根节点为0),创建/移动节点时自动维护 | 高 | 已实现 | module-mdm: SpaceNodeService | +| REQ-MDM-013 | 空间节点查询 | 支持按项目查询节点列表、获取项目空间树、获取项目根节点、按项目+类型查询、获取子节点列表、按ID查询详情 | 高 | 已实现 | module-mdm: SpaceNodeController | +| REQ-MDM-014 | 空间节点更新与删除 | 支持更新节点信息;删除前必须调用delete-check检查子节点数量;普通删除(有子节点时拒绝);级联删除(DELETE /{id}/cascade,含子节点);软删除(isDeleted标记) | 高 | 已实现 | module-mdm: SpaceNodeController | +| REQ-MDM-015 | 面积信息管理 | 支持记录建筑面积/使用面积/公摊面积/占地面积(BigDecimal(10,2)) | 中 | 已实现 | module-mdm: SpaceNode | +| REQ-MDM-016 | 地理信息管理 | 支持记录经度/纬度/海拔/楼层号(正数地上,负数地下) | 中 | 已实现 | module-mdm: SpaceNode | +| REQ-MDM-017 | 设备扩展模式 | SpaceNode通过isEquipment字段标记设备节点,设备节点额外使用设备扩展字段(维保信息/巡检周期/特种设备证书/常用备件/能耗标准/安装环境/防护等级等20+字段) | 高 | 已实现 | module-mdm: SpaceNode | +| REQ-MDM-018 | 扩展属性JSON | 支持attributes字段(String(2000) JSON格式)存储类型特定属性 | 中 | 已实现 | module-mdm: SpaceNode | +| REQ-MDM-019 | 空间编码自动生成 | 自动生成空间编码规则,唯一约束UNIQUE(project_id, code) | 中 | 未实现 | - | +| REQ-MDM-020 | 节点移动 | 支持移动节点到新的父节点(PUT /{id}/move),自动维护treePath | 中 | 未实现 | - | +| REQ-MDM-021 | 祖先/子孙查询 | 支持查询指定节点的祖先链(GET /{id}/ancestors)和子孙节点(GET /{id}/descendants) | 低 | 未实现 | - | +| REQ-MDM-022 | 空间统计分析 | 支持空间统计接口:按类型统计/按状态统计/空间总览 | 低 | 未实现 | - | +| REQ-MDM-023 | PostGIS空间查询 | 支持PostGIS空间查询(范围搜索/距离计算),GEOMETRY(Point)/GEOMETRY(Polygon)字段,GIST空间索引 | 低 | 未实现 | - | +| REQ-MDM-024 | 地图服务 | 高德地图集成,支持标注/绘制/路径,MapEditor组件,地图标记/边界查询API | 低 | 未实现 | - | +| REQ-MDM-025 | 空间节点导入导出 | 支持空间节点批量导入和导出,导入模板下载 | 中 | 未实现 | - | + +#### 2.2.3 巡检标准项 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-MDM-030 | 巡检标准项CRUD | 支持创建/查询/更新/删除巡检标准项,按设备类型(equipmentType)和系统类型(systemType)分类,每项含检查项名称/检查方法/标准值/是否必检(默认true)/排序号/状态(ACTIVE/INACTIVE) | 高 | 已实现 | module-mdm: InspectionItemController | +| REQ-MDM-031 | 标准项查询筛选 | 支持按activeOnly=true仅查询启用项;equipmentType+systemType双条件过滤;equipmentType单条件过滤;systemType过滤 | 中 | 已实现 | module-mdm: InspectionItemService | +| REQ-MDM-032 | 巡检模板管理 | 支持创建/查询/更新/复制巡检模板,模板按项目+设备类型组织,含检查项列表(JSON格式存储),支持版本管理(version字段),模板与标准项通过equipmentType逻辑关联 | 高 | 已实现 | module-mdm: InspectionTemplateController | +| REQ-MDM-033 | 巡检记录管理 | 支持创建/查询/更新/删除/完成巡检记录,记录关联设备(equipmentId)和计划(planId),检查结果状态3种:NORMAL/WARNING/ABNORMAL,支持签到信息(时间/位置/照片),检查项结果和异常问题以JSONB存储 | 高 | 已实现 | module-mdm: InspectionRecordController | +| REQ-MDM-034 | 巡检记录查询 | 支持按设备/计划/巡检人/状态/日期范围筛选,支持按设备+日期范围组合查询 | 中 | 已实现 | module-mdm: InspectionRecordService | + +--- + +### 2.3 设备与资产域 + +#### 2.3.1 设备台账 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-ASSET-001 | 设备创建 | 支持创建设备,设备编码全局唯一(equipmentCode),设备名称必填,设备类型10种:ELEVATOR/HVAC/FIRE_PROTECTION/PLUMBING/ELECTRICAL/ENERGY_METER/SECURITY/LANDSCAPE/KITCHEN/OTHER,归属类型4种(默认PROJECT):PROJECT(项目自有)/COMPANY(公司统筹)/OWNER(业主自置)/RENTAL(租赁设备),设备状态4种(默认ACTIVE):ACTIVE(在用)/INACTIVE(停用)/MAINTENANCE(维保中)/SCRAPPED(已报废) | 高 | 已实现 | module-asset: EquipmentController | +| REQ-ASSET-002 | 设备查询 | 支持按项目查询/按空间查询/按类型查询/按归属查询设备列表,支持按ID查询设备详情 | 高 | 已实现 | module-asset: EquipmentController | +| REQ-ASSET-003 | 设备更新与删除 | 支持更新设备信息;支持单个删除和批量删除(POST /batch-delete);逻辑删除(isDeleted标记) | 高 | 已实现 | module-asset: EquipmentController | +| REQ-ASSET-004 | 设备与空间关联 | 设备通过spaceNodeId关联空间节点树,支持按空间节点查询设备列表 | 高 | 已实现 | module-asset: Equipment.spaceNodeId | +| REQ-ASSET-005 | 系统类型分类 | 设备系统类型9种(商业地产8大系统+其他):HVAC(暖通空调)/FIRE(消防)/ELEVATOR(电梯)/ELECTRICAL(电气)/PLUMBING(给排水)/BAS(弱电智能化)/KITCHEN(餐饮厨房)/LANDSCAPE(景观)/OTHER(其他) | 高 | 已实现 | module-asset: Equipment.systemType | +| REQ-ASSET-006 | 设备统计 | 支持按类型统计/按归属统计/项目设备总数查询 | 中 | 已实现 | module-asset: EquipmentController | +| REQ-ASSET-007 | 设备导入 | 支持Excel批量导入设备(.xlsx/.xls),文件大小最大10MB,行数最大1000行,文件类型校验,返回{successCount, failCount, failDetails} | 高 | 已实现 | module-asset: EquipmentController.importEquipment | +| REQ-ASSET-008 | 设备导出 | 支持按项目导出设备列表为Excel文件(.xlsx格式) | 中 | 已实现 | module-asset: EquipmentController.exportEquipment | +| REQ-ASSET-009 | 设备照片管理 | 支持设备照片JSONB内嵌存储,照片类型4种:外观/铭牌/安装位置/环境,每张照片含type/url/remark | 中 | 已实现 | module-asset: Equipment.photos | +| REQ-ASSET-010 | 设备文档管理 | 支持电子文档JSONB内嵌存储,文档类型4种:manual/certificate/contract/other,每份文档含name/url/size/type/remark | 中 | 已实现 | module-asset: Equipment.documents | +| REQ-ASSET-011 | 设备二维码 | 支持设备二维码生成、扫码查看设备详情、扫码快速报修 | 中 | 未实现 | - | +| REQ-ASSET-012 | 设备规格字段 | 设备规格型号字段(specifications) | 低 | 未实现 | - | + +#### 2.3.2 设备扩展信息 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-ASSET-020 | 电梯扩展表 | EquipmentElevator(asset_equipment_elevator),通过equipment_id UNIQUE一对一关联主表,含:电梯类型/型号/载重(kg)/速度(m/s)/楼层数/井道尺寸/底坑深度/顶层高度/注册登记号/检验证书/下次检验日期/能耗/维保等级/应急救援预案 | 高 | 已实现 | module-asset: EquipmentElevatorService | +| REQ-ASSET-021 | 暖通扩展表 | EquipmentHvac(asset_equipment_hvac),含:暖通类型/制冷量/制热量/风量/制冷剂类型/充注量/能效比(EER)/性能系数(COP)/安装日期/保修到期/滤网更换周期/上次更换日期/风管类型/风管尺寸 | 高 | 已实现 | module-asset: EquipmentHvacService | +| REQ-ASSET-022 | 消防扩展表 | EquipmentFire(asset_equipment_fire),含:消防设备类型/安装面积/安装高度/探测范围/系统类型/分区编号/回路编号/是否启用联动/联动动作/巡检周期/上次/下次巡检日期/巡检结果/特殊要求 | 高 | 已实现 | module-asset: EquipmentFireService | +| REQ-ASSET-023 | 能源计量扩展表 | EquipmentEnergy(asset_equipment_energy),含:表计类型/能源类型/表计型号/规格/表常数/精度等级/抄表方式/上次读数/当前读数/单价/计费方式/通讯方式/通讯地址/检定周期/下次检定日期/检定证书 | 高 | 已实现 | module-asset: EquipmentEnergyService | +| REQ-ASSET-024 | 扩展表读写模式 | 扩展表通过主设备ID路径读写(GET/PUT /{id}/elevator),采用saveOrUpdate模式:存在则更新,不存在则新建 | 高 | 已实现 | module-asset: Equipment*Service | +| REQ-ASSET-025 | 无扩展表设备类型 | PLUMBING/ELECTRICAL/SECURITY/LANDSCAPE/KITCHEN/OTHER无扩展表,使用主表通用字段+attributes JSON扩展 | 中 | 已实现 | module-asset: Equipment.attributes | + +#### 2.3.3 设备健康评分 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-ASSET-030 | 健康评分算法 | 健康度 = 基础分(100) - 故障扣分 - 维保扣分 - 年龄扣分,最低0分。故障扣分=故障次数(近30天)x5(无上限);维保扣分=(1-维保完成率)x20(上限20分);年龄扣分=设备年龄(年)x2(上限10分) | 高 | 已实现[Beta] | module-asset: EquipmentHealthServiceImpl | +| REQ-ASSET-031 | 5级健康等级 | EXCELLENT(优秀,[90,100))/GOOD(良好,[75,90))/FAIR(一般,[60,75))/POOR(较差,[40,60))/CRITICAL(危急,[0,40)) | 高 | 已实现[Beta] | module-asset: HealthLevel | +| REQ-ASSET-032 | MTBF计算 | 平均故障间隔时间 = 运行时间(小时) / 故障次数,计算周期默认30天可自定义,仅1次故障时使用默认期间天数x24小时,无故障返回0 | 中 | 已实现[Beta] | module-asset: EquipmentHealthServiceImpl | +| REQ-ASSET-033 | MTTR计算 | 平均修复时间 = 总修复时间(小时) / 修复次数,仅统计已完成修复的故障记录,无修复记录返回0 | 中 | 已实现[Beta] | module-asset: EquipmentHealthServiceImpl | +| REQ-ASSET-034 | 健康评分查询 | 支持获取设备最新健康度、健康度历史、手动触发计算 | 中 | 已实现[Beta] | module-asset: EquipmentHealthController | +| REQ-ASSET-035 | 维保完成率计算 | 维保完成率用于健康评分扣分,当前TODO标记暂返回1.0 | 中 | 部分实现 | module-asset: EquipmentHealthServiceImpl | + +#### 2.3.4 故障历史 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-ASSET-040 | 故障记录 | 支持记录设备故障,故障类型4种:SUDDEN(突发)/SCHEDULED(计划停机)/WARNING(预警)/OTHER(其他),故障等级4级:LOW(轻微)/MEDIUM(一般)/HIGH(严重)/CRITICAL(危急),修复结果4种:FIXED(已修复)/PARTIAL_FIXED(部分修复)/REPLACED(更换新设备)/PENDING(待处理) | 高 | 已实现 | module-asset: EquipmentHealthController | +| REQ-ASSET-041 | 故障自动计算 | 记录故障时自动计算:修复时长=(repairEndTime-repairStartTime)/60分钟转小时;停机时长=(repairStartTime-failureTime)/60分钟转小时 | 中 | 已实现 | module-asset: EquipmentFailureHistory | +| REQ-ASSET-042 | 故障历史查询 | 支持按设备ID查询故障历史列表 | 中 | 已实现 | module-asset: EquipmentHealthController | +| REQ-ASSET-043 | 故障自动工单 | 故障记录时自动创建维修工单(事件驱动) | 中 | 未实现 | - | + +#### 2.3.5 归属主体管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-ASSET-050 | 归属主体CRUD | 支持创建/查询/更新/删除归属主体,主体类型3种:COMPANY(公司)/OWNER(业主)/RENTAL_COMPANY(租赁公司),含主体名称/编码/联系人/联系电话/联系地址/营业执照/法人代表/合同编号/合同起止日期/租赁费用 | 高 | 已实现 | module-asset: OwnershipEntityController | +| REQ-ASSET-051 | 归属主体查询 | 支持按类型查询归属主体、获取全部未删除主体 | 中 | 已实现 | module-asset: OwnershipEntityController | +| REQ-ASSET-052 | 归属主体逻辑删除 | 删除操作为逻辑删除(isDeleted标记),查询时过滤已删除记录 | 高 | 已实现 | module-asset: OwnershipEntityController | +| REQ-ASSET-053 | 设备归属冗余 | Equipment表同时存储owningEntityId和owningEntityName,避免频繁关联查询 | 中 | 已实现 | module-asset: Equipment | + +#### 2.3.6 特种设备管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-ASSET-060 | 特种设备标记 | 设备主表含specialEquipmentType(特种设备类型)和specialEquipmentCert(特种设备证书)字段 | 中 | 已实现 | module-asset: Equipment | +| REQ-ASSET-061 | 年检周期管理 | 设备含inspectionCycle(年检周期/月)和nextInspectionDate(下次年检日期)字段 | 中 | 已实现 | module-asset: Equipment | +| REQ-ASSET-062 | 特种设备列表查询 | 支持查询项目下的特种设备列表 | 中 | 已实现 | module-mdm: SpaceNodeController.getSpecialEquipmentList | +| REQ-ASSET-063 | 即将年检预警 | 支持查询即将年检设备(默认90天内),按项目筛选 | 中 | 已实现 | module-mdm: SpaceNodeController.getExpiringInspectionEquipment | +| REQ-ASSET-064 | 维保到期定时提醒 | 后端定时任务检查维保合同到期/年检到期,自动发送提醒通知 | 中 | 未实现 | - | + +--- + +### 2.4 运营与服务域 + +#### 2.4.1 工单管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-OPS-001 | 工单创建 | 支持创建工单,工单编号自动生成(格式WO-YYYYMMDD-XXXX,4位序号按日递增),工单来源6种:OWNER(业主报修)/MAINTENANCE(维保计划)/INSPECTION(巡检触发)/FAULT(故障触发)/REGULATORY(法规巡检)/MANUAL(手动创建),工单类型6种:REPAIR/INSPECTION/SECURITY/CLEANING/PROPERTY/CONSULTATION | 高 | 已实现 | module-wo: WorkOrderController | +| REQ-OPS-002 | 工单查询 | 支持查询工单列表,可按projectId/equipmentId/source/type/status/assignedTo筛选(参数互斥,按优先级依次判断),支持按ID查询工单详情 | 高 | 已实现 | module-wo: WorkOrderController | +| REQ-OPS-003 | 工单状态流转 | 6种状态:PENDING(待分配)->ASSIGNED(已派单)->IN_PROGRESS(执行中)->COMPLETED(已完成)->VERIFIED(已验收),CANCELLED(已取消,PENDING/ASSIGNED/IN_PROGRESS可转入),COMPLETED/VERIFIED不可取消 | 高 | 已实现 | module-wo: WorkOrderService | +| REQ-OPS-004 | 工单派单 | POST /{id}/assign,指定负责人(assignedTo)/服务商(assignedVendor)/派单日期(assignedDate),状态PENDING->ASSIGNED | 高 | 已实现 | module-wo: WorkOrderController | +| REQ-OPS-005 | 工单执行 | POST /{id}/start,记录实际开始时间(actualStart=now()),状态ASSIGNED->IN_PROGRESS | 高 | 已实现 | module-wo: WorkOrderController | +| REQ-OPS-006 | 工单完成 | POST /{id}/complete,记录故障原因/解决方案/处理结果/费用(人工费+备件费=总费用),自动计算实际工时(actualHours=Duration.between(actualStart,actualEnd)),状态IN_PROGRESS->COMPLETED | 高 | 已实现 | module-wo: WorkOrderController | +| REQ-OPS-007 | 工单验收 | POST /{id}/verify,记录验收人(verifiedBy)/评分(rating,1-5星)/备注(remark),状态COMPLETED->VERIFIED | 高 | 已实现 | module-wo: WorkOrderController | +| REQ-OPS-008 | 工单取消 | POST /{id}/cancel,PENDING/ASSIGNED/IN_PROGRESS可取消,COMPLETED/VERIFIED不可取消(抛出异常"无法取消已完成的工单") | 中 | 已实现 | module-wo: WorkOrderController | +| REQ-OPS-009 | 工单优先级 | 4种优先级:LOW/MEDIUM(默认)/HIGH/URGENT | 高 | 已实现 | module-wo: WorkOrder | +| REQ-OPS-010 | 工单明细管理 | 支持查询/批量添加工单明细(WorkOrderItem),明细类型3种:PART(备件)/INSPECTION_ITEM(巡检项)/CHECKPOINT(检查点),含名称/数量/单位/单价/总价/是否正常/观察记录/建议 | 中 | 已实现 | module-wo: WorkOrderController | +| REQ-OPS-011 | 工单统计 | 支持工单多维度统计:总数/待分配/已派单/执行中/已完成/已验收/已取消/今日完成/今日创建/逾期/平均完成工时/平均评分/按来源分布/按类型分布/按优先级分布 | 中 | 已实现 | module-wo: WorkOrderController | +| REQ-OPS-012 | 工单挂起与恢复 | 支持工单挂起(SUSPENDED状态)和恢复操作,任意执行中状态可挂起 | 中 | 未实现 | - | +| REQ-OPS-013 | 工单退回 | 支持已派单工单退回重分配(RETURNED状态),ASSIGNED状态可退回 | 中 | 未实现 | - | +| REQ-OPS-014 | 工单流转记录 | 记录工单每次状态变更的流转历史(WorkOrderFlow实体) | 中 | 未实现 | - | +| REQ-OPS-015 | 工单报修人信息 | 工单含报修人信息字段:reporterId/reporterName/reporterPhone/reporterAddress | 中 | 未实现 | - | +| REQ-OPS-016 | 工单分页查询 | 工单列表接口支持分页返回(当前为全量List返回) | 中 | 未实现 | - | + +#### 2.4.2 维保计划 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-OPS-020 | 维保计划创建 | 支持创建维保计划,计划编码自动生成(唯一),计划名称必填,关联设备(equipmentId)必填,计划类型2种:PREVENTIVE(预防性)/CORRECTIVE(纠正性),周期天数(cycleDays),预估工时,指定服务商 | 高 | 已实现 | module-wo: MaintenancePlan | +| REQ-OPS-021 | 维保计划状态管理 | 计划状态3种:ACTIVE(启用)/INACTIVE(停用)/SUSPENDED(暂停),删除行为为逻辑删除(将状态设为INACTIVE) | 高 | 已实现 | module-wo: MaintenancePlan | +| REQ-OPS-022 | 维保计划周期调度 | 通过cycleDays计算下次执行日期(nextDate),上次执行日期(lastDate) | 中 | 已实现 | module-wo: MaintenancePlan | +| REQ-OPS-023 | 维保计划自动调度 | 定时任务根据维保计划周期自动生成维保任务 | 中 | 未实现 | - | + +#### 2.4.3 维保任务 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-OPS-030 | 维保任务创建 | 支持创建维保任务,任务编号自动生成(格式EQ-YYYYMMDD-XXXX),关联设备(equipmentId)必填,任务类型3种:PREVENTIVE(预防性)/CORRECTIVE(纠正性)/EMERGENCY(紧急维修) | 高 | 已实现 | module-wo: MaintenanceTaskController | +| REQ-OPS-031 | 维保任务状态流转 | 6种状态:PENDING->ASSIGNED->IN_PROGRESS->COMPLETED->VERIFIED,CANCELLED(PENDING/ASSIGNED/IN_PROGRESS可转入),与工单状态机一致 | 高 | 已实现 | module-wo: MaintenanceTaskService | +| REQ-OPS-032 | 4种触发方式 | 触发类型4种:PLAN(计划触发)/INSPECTION(巡检触发)/FAULT(故障触发)/MANUAL(手动创建) | 高 | 已实现 | module-wo: MaintenanceTask | +| REQ-OPS-033 | 自动优先级判定 | 紧急维修类型->URGENT;故障触发+紧急关键词(困人/漏水/停电/火灾/爆炸/漏电/冒烟/故障停机)->URGENT;故障触发无紧急关键词->HIGH;巡检触发->HIGH;计划触发->MEDIUM;其他->MEDIUM | 高 | 已实现 | module-wo: MaintenanceTaskServiceImpl.autoDeterminePriority | +| REQ-OPS-034 | 双完成接口 | 简版完成(POST /{id}/complete):处理结果/实际工时/总费用/完成人;详版完成(POST /{id}/complete-details):含故障原因/解决方案/使用备件(JSONB)/人工费/备件费 | 中 | 已实现 | module-wo: MaintenanceTaskController | +| REQ-OPS-035 | 维保任务验收 | POST /{id}/verify,记录验收人/评分(1-5)/备注;独立评分接口POST /{id}/rate | 中 | 已实现 | module-wo: MaintenanceTaskController | +| REQ-OPS-036 | 完成后设备联动 | 维保任务完成时自动更新设备信息:维保商=任务服务商;预防性维护更新下次巡检日期(now()+inspectionCycle,默认30天);异常容错(更新设备失败不影响工单完成) | 高 | 已实现 | module-wo: MaintenanceTaskServiceImpl | +| REQ-OPS-037 | 维保任务统计 | 支持多维度统计:总数/待分配/已派单/执行中/已完成/已验收/已取消/今日完成/今日创建/逾期/平均完成工时/平均评分/按优先级分布/按触发类型分布/人工费总计/备件费总计/费用总计 | 中 | 已实现 | module-wo: MaintenanceTaskController | +| REQ-OPS-038 | 使用备件记录 | 维保任务完成时可记录使用备件列表(partsUsed JSONB),含备件ID/名称/数量/单价/总价 | 中 | 已实现 | module-wo: MaintenanceTask | + +#### 2.4.4 巡检管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-OPS-040 | 巡检模板管理 | 支持创建/查询/更新/复制巡检模板,模板编码自动生成(唯一),模板状态2种:ACTIVE/INACTIVE,模板含检查项列表(@Transient非持久化,通过templateId关联InspectionItem) | 高 | 已实现 | module-wo: InspectionTemplate | +| REQ-OPS-041 | 巡检项管理 | 检查项通过templateId关联模板,含检查项名称/描述/检查方法/检查标准/是否必检(默认true)/是否需要正常判定(默认true)/排序号 | 高 | 已实现 | module-wo: InspectionItem | +| REQ-OPS-042 | 巡检签到 | 巡检记录支持签到信息:签到时间(checkInTime)/签到位置(checkInLocation)/签到照片(checkInPhoto) | 中 | 已实现 | module-mdm: InspectionRecord | +| REQ-OPS-043 | 巡检异常上报 | 巡检记录支持异常问题列表(problems JSONB),含问题描述/照片/严重程度(LOW/MEDIUM/HIGH) | 中 | 已实现 | module-mdm: InspectionRecord | +| REQ-OPS-044 | 巡检扫码签到 | 支持扫描设备二维码进行巡检签到,记录位置信息 | 中 | 未实现 | - | + +#### 2.4.5 备件管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-OPS-050 | 备件分类管理 | 支持创建/查询备件分类,分类编码唯一,支持树形分类(parentId自关联),含分类名称/描述/排序号 | 高 | 已实现 | module-mdm: SparePartController | +| REQ-OPS-051 | 备件台账CRUD | 支持创建/查询/更新/删除备件,备件编码唯一,含名称/分类/规格型号/计量单位/安全库存(默认0)/当前库存(默认0)/单价/供应商/供应商联系方式/存放位置/状态(ACTIVE/INACTIVE) | 高 | 已实现 | module-mdm: SparePartController | +| REQ-OPS-052 | 入库操作 | POST /in-stock,增加库存,记录备件ID/数量/操作人/备注,更新currentStock,记录SparePartRecord(类型IN,含操作后余额balance) | 高 | 已实现 | module-mdm: SparePartController | +| REQ-OPS-053 | 出库操作 | POST /out-stock,减少库存,记录备件ID/数量/关联工单ID(可选)/操作人/备注,校验库存不能为负,更新currentStock,记录SparePartRecord(类型OUT) | 高 | 已实现 | module-mdm: SparePartController | +| REQ-OPS-054 | 低库存预警 | 当currentStock < safeStock时触发预警,GET /low-stock返回低于安全库存的备件列表 | 中 | 已实现 | module-mdm: SparePartController | +| REQ-OPS-055 | 出入库记录查询 | 支持查询指定备件的所有出入库记录流水 | 中 | 已实现 | module-mdm: SparePartController | +| REQ-OPS-056 | 盘点与调整 | 支持盘点(CHECK)和调整(ADJUST)操作,记录SparePartRecord | 中 | 已实现 | module-mdm: SparePartRecord(RecordType) | + +#### 2.4.6 能耗管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-OPS-060 | 计量点管理 | 支持创建/查询/更新/删除计量点,计量点编码自动生成(EM+yyyyMMddHHmmss,冲突时追加后缀),能源类型6种:LIGHTING(照明插座用电)/HVAC(空调用电)/POWER(动力用电)/SPECIAL(特殊用电)/WATER(给排水)/GAS(燃气),关联空间节点,含额定容量/单价(4位小数精度)/状态(ACTIVE/INACTIVE) | 高 | 已实现 | module-mdm: EnergyController | +| REQ-OPS-061 | 能耗抄表录入 | POST /consumption,录入能耗数据{meterId, currentReading, recordedBy},校验:仪表状态必须ACTIVE(错误码6102)/当前读数不能小于上次读数(错误码6103)/仪表必须存在(错误码6101),自动获取上次读数(首次为0),自动计算消耗量(current-previous),自动计算费用(consumption x unitPrice,单价为空时为0),默认记录方式MANUAL | 高 | 已实现 | module-mdm: EnergyConsumptionService | +| REQ-OPS-062 | 能耗记录查询 | 支持按计量点查询能耗记录,可按日期范围(startDate/endDate)筛选 | 中 | 已实现 | module-mdm: EnergyController | +| REQ-OPS-063 | 按类型统计能耗 | GET /statistics/by-type,按项目+月份统计各能源类型消耗量(注意:当前实现将总消耗全部归入LIGHTING类型,存在已知缺陷) | 中 | 部分实现 | module-mdm: EnergyConsumptionService | +| REQ-OPS-064 | 单位面积能耗 | GET /statistics/unit-consumption,计算总能耗/总建筑面积 | 中 | 已实现 | module-mdm: EnergyConsumptionService | +| REQ-OPS-065 | IoT自动抄表 | 支持对接IoT平台自动采集能耗数据(RecordMethod.IOT) | 低 | 未实现 | - | +| REQ-OPS-066 | 前后端能源类型统一 | 后端LIGHTING/HVAC/POWER/SPECIAL/WATER/GAS与前端ELECTRICITY/WATER/GAS/CENTRAL_HEATING/CENTRAL_COOLING需对齐 | 中 | 未实现 | - | + +--- + +### 2.5 财务与收费域 + +#### 2.5.1 收费项目管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-FIN-001 | 收费项目实体 | FeeItem实体,含code/name/type/billingMethod/unitPrice/unit/billDay/dueDay/overdueDay/enableLateFee/lateFeeRate/maxLateFee,收费类型8种:PROPERTY/PARKING/WATER/ELECTRICITY/GAS/HEATING/REPAIR_FUND/OTHER,计费方式4种:FIXED/AREA/USAGE/CUSTOM | 高 | 未实现 | - | +| REQ-FIN-002 | 收费项目CRUD | 支持创建/查询/更新/禁用收费项目 | 高 | 未实现 | - | + +#### 2.5.2 收费账单管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-FIN-010 | 账单实体 | FeeBill实体,含billNo/feeItemId/spaceNodeId/ownerId/billPeriod/billDate/dueDate/overdueDate/usageAmount/amount/lateFee/discount/payableAmount/paidAmount/status,账单状态5种:UNPAID/PARTIAL_PAID/PAID/OVERDUE/CANCELLED | 高 | 未实现 | - | +| REQ-FIN-011 | 账单自动生成 | 根据收费项目规则,按账期自动生成账单 | 高 | 未实现 | - | +| REQ-FIN-012 | 账单状态流转 | UNPAID->PARTIAL_PAID->PAID / OVERDUE->CANCELLED | 高 | 未实现 | - | +| REQ-FIN-013 | 批量账单生成 | 一次性为项目所有业主生成账单 | 中 | 未实现 | - | +| REQ-FIN-014 | 账单导出 | 导出Excel/PDF格式账单 | 中 | 未实现 | - | + +#### 2.5.3 支付记录管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-FIN-020 | 支付记录实体 | FeePayment实体,含paymentNo/amount/method/thirdPartyNo/status/failReason/paymentTime,支付方式5种:WECHAT/ALIPAY/CASH/BANK_TRANSFER/CARD,支付状态4种:PENDING/SUCCESS/FAILED/REFUNDED | 高 | 未实现 | - | +| REQ-FIN-021 | 线下收款登记 | 支持登记线下收款记录 | 高 | 未实现 | - | +| REQ-FIN-022 | 支付记录查询 | 支持查询支付记录列表 | 中 | 未实现 | - | + +#### 2.5.4 退款管理 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-FIN-030 | 退款实体 | FeeRefund实体,含refundNo/amount/reason/status/approverId/approveTime/thirdPartyRefundNo/refundTime,退款状态4种:PENDING/APPROVED/REJECTED/REFUNDED | 中 | 未实现 | - | +| REQ-FIN-031 | 退款流程 | 退款申请->审批->执行 | 中 | 未实现 | - | + +#### 2.5.5 费用催缴与滞纳金 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-FIN-040 | 费用催缴定时任务 | 到期提醒(3天内)、逾期催缴、周汇总 | 中 | 未实现 | - | +| REQ-FIN-041 | 滞纳金自动计算 | 逾期天数 x 日利率,不超过上限 | 中 | 未实现 | - | + +#### 2.5.6 能耗计费 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-FIN-050 | 能耗数据与账单对接 | 将EnergyConsumption的amount作为按用量计费的账单数据来源,实现能耗抄表->费用计算->账单生成的闭环 | 高 | 未实现 | - | +| REQ-FIN-051 | 按面积计费 | 根据房产面积 x 单价计算物业费 | 中 | 未实现 | - | +| REQ-FIN-052 | 固定金额计费 | 每月固定金额的收费项目(如停车费) | 中 | 未实现 | - | +| REQ-FIN-053 | 支付网关对接 | 微信支付/支付宝SDK集成 | 中 | 未实现 | - | +| REQ-FIN-054 | 财务报表 | 收费统计/欠费分析/收入趋势 | 低 | 未实现 | - | + +--- + +### 2.6 前端交互域 + +#### 2.6.1 登录与权限控制流程 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-UI-001 | 登录页面 | /auth/Login.vue,输入用户名+密码,调用POST /api/auth/login,存储Token到本地,跳转至主页 | 高 | 已实现 | ether-admin: Login.vue | +| REQ-UI-002 | 前端权限指令 | v-permission指令,根据用户角色/权限控制元素显示隐藏 | 高 | 已实现 | ether-admin: v-permission | +| REQ-UI-003 | 路由权限守卫 | 前端路由守卫,根据用户权限动态渲染菜单和路由 | 高 | 已实现 | ether-admin: router | +| REQ-UI-004 | Token管理优化 | 双Token机制(accessToken+refreshToken)、加密存储 | 高 | 未实现 | OPT-SEC-001 | +| REQ-UI-005 | XSS防护增强 | xss库过滤、白名单配置 | 高 | 未实现 | OPT-SEC-002 | +| REQ-UI-006 | CSRF防护 | CSRF Token、SameSite Cookie | 高 | 未实现 | OPT-SEC-003 | + +#### 2.6.2 项目切换与数据隔离 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-UI-010 | 项目选择 | 登录后获取用户项目列表GET /api/auth/users/{id}/projects,用户选择项目,后续请求携带X-Project-ID Header | 高 | 已实现 | ether-admin | +| REQ-UI-011 | 项目切换 | 支持在已参与的项目间切换,切换后刷新当前页面数据 | 高 | 已实现 | ether-admin | +| REQ-UI-012 | 菜单动态渲染 | 根据用户角色和项目配置(ProjectConfig功能开关)动态渲染菜单 | 高 | 已实现 | ether-admin | + +#### 2.6.3 各模块页面交互规范 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | 对应代码模块 | +|--------|----------|----------|--------|----------|-------------| +| REQ-UI-020 | 用户管理页面 | /system/Users.vue + /system/UserDetail.vue,用户列表(分页/搜索/状态筛选)/创建用户/编辑/删除/修改密码/分配角色/查看项目 | 高 | 已实现 | ether-admin | +| REQ-UI-021 | 角色管理页面 | /system/Roles.vue,角色列表(分页/搜索)/创建/编辑/删除/分配权限/查看关联用户 | 高 | 已实现 | ether-admin | +| REQ-UI-022 | 权限管理页面 | /system/Permissions.vue,权限列表(分页/搜索)/创建/编辑/删除/按类型筛选/查看菜单权限 | 高 | 已实现 | ether-admin | +| REQ-UI-023 | 部门管理页面 | /system/Depts.vue,部门树展示/创建/编辑/删除(需先删除子部门)/查看成员/按类型筛选 | 高 | 已实现 | ether-admin | +| REQ-UI-024 | 审计日志页面 | /system/Audit.vue,日志列表(分页/模块/操作类型/用户名/日期范围筛选)/查看统计/模块和操作类型下拉 | 中 | 已实现 | ether-admin | +| REQ-UI-025 | 系统设置页面 | /system/Settings.vue,查看所有配置项(键值对)/单个或批量更新配置值 | 中 | 已实现 | ether-admin | +| REQ-UI-026 | 设备列表页面 | EquipmentList.vue,设备台账主列表含筛选/新增/编辑/删除/导入导出/批量删除 | 高 | 已实现 | ether-admin | +| REQ-UI-027 | 设备详情页面 | EquipmentDetail.vue,设备完整信息展示(基本信息/技术参数/维保/财务/照片/文档) | 高 | 已实现 | ether-admin | +| REQ-UI-028 | 设备健康页面 | EquipmentHealth.vue,健康度评分/趋势图/MTBF/MTTR/故障历史 | 中 | 已实现 | ether-admin | +| REQ-UI-029 | 工单管理页面 | WorkOrder.vue,工单列表/新建/详情/派单/执行/完成/验收/取消 | 高 | 已实现 | ether-admin | +| REQ-UI-030 | 维保计划页面 | PlanList.vue,维保计划列表/新建/编辑/停用 | 高 | 已实现 | ether-admin | +| REQ-UI-031 | 维保任务页面 | TaskList.vue,维保任务列表/接受/开始/完成/取消 | 高 | 已实现 | ether-admin | +| REQ-UI-032 | 能耗管理页面 | MeterList.vue + ConsumptionRecord.vue + EnergyStatistics.vue,计量点管理/能耗录入/能耗统计 | 中 | 已实现 | ether-admin | +| REQ-UI-033 | 列表虚拟滚动 | 后端分页、虚拟表格优化长列表性能 | 中 | 未实现 | OPT-PERF-001 | +| REQ-UI-034 | 请求优化 | 取消重复请求、请求缓存 | 中 | 未实现 | OPT-PERF-002 | +| REQ-UI-035 | WebSocket替代轮询 | 实时通信、断线重连 | 低 | 未实现 | OPT-PERF-003 | +| REQ-UI-036 | 响应式设计 | CSS媒体查询、移动端适配 | 低 | 未实现 | OPT-UX-002 | +| REQ-UI-037 | 国际化支持 | vue-i18n、中英文切换 | 低 | 未实现 | OPT-UX-005 | + +--- + +## 三、跨域业务流程需求 + +### 3.1 业主报修全流程 + +**流程描述**:业主发起报修到工单验收评价的完整闭环。 + +| 步骤 | 操作 | 涉及域 | 涉及功能点 | 实现状态 | +|------|------|--------|-----------|----------| +| 1 | 业主认证身份 | 身份与权限域 | REQ-AUTH-051 住户认证流程 | 已实现 | +| 2 | 业主选择项目 | 前端交互域 | REQ-UI-010 项目选择 | 已实现 | +| 3 | 业主定位设备 | 空间与项目域 | REQ-MDM-013 空间节点查询 | 已实现 | +| 4 | 创建报修工单 | 运营与服务域 | REQ-OPS-001 工单创建(source=OWNER) | 已实现 | +| 5 | 工单派单 | 运营与服务域 | REQ-OPS-004 工单派单 | 已实现 | +| 6 | 维保人员执行 | 运营与服务域 | REQ-OPS-005 工单执行 -> REQ-OPS-006 工单完成 | 已实现 | +| 7 | 工单验收 | 运营与服务域 | REQ-OPS-007 工单验收 | 已实现 | +| 8 | 业主评价 | 运营与服务域 | REQ-OPS-007 工单验收(评分) | 已实现 | +| - | 业主端小程序 | 前端交互域 | 业主端23项特性(APP-O01~O23) | 未实现 | + +### 3.2 设备全生命周期管理 + +**流程描述**:设备从采购安装到报废退役的全生命周期管理。 + +| 步骤 | 操作 | 涉及域 | 涉及功能点 | 实现状态 | +|------|------|--------|-----------|----------| +| 1 | 设备采购登记 | 设备与资产域 | REQ-ASSET-001 设备创建(purchaseDate/purchasePrice) | 已实现 | +| 2 | 设备安装就位 | 设备与资产域 | REQ-ASSET-004 设备与空间关联 + REQ-ASSET-020~023 扩展表录入 | 已实现 | +| 3 | 设备运行监控 | 设备与资产域 | REQ-ASSET-030~034 健康评分/MTBF/MTTR | 已实现[Beta] | +| 4 | 预防性维保 | 运营与服务域 | REQ-OPS-020~022 维保计划 + REQ-OPS-030~036 维保任务 | 已实现 | +| 5 | 定期巡检 | 运营与服务域 | REQ-OPS-040~043 巡检模板/签到/异常上报 | 已实现 | +| 6 | 故障处理 | 设备与资产域 | REQ-ASSET-040~042 故障记录 | 已实现 | +| 7 | 故障触发工单 | 运营与服务域 | REQ-OPS-001 工单创建(source=FAULT) | 已实现 | +| 8 | 设备报废 | 设备与资产域 | REQ-ASSET-001 设备状态变更为SCRAPPED | 已实现 | + +### 3.3 预防性维保调度流程 + +**流程描述**:从维保计划制定到维保执行验收的完整调度闭环。 + +| 步骤 | 操作 | 涉及域 | 涉及功能点 | 实现状态 | +|------|------|--------|-----------|----------| +| 1 | 制定维保计划 | 运营与服务域 | REQ-OPS-020 维保计划创建(type=PREVENTIVE) | 已实现 | +| 2 | 设置周期规则 | 运营与服务域 | REQ-OPS-022 维保计划周期调度(cycleDays/nextDate) | 已实现 | +| 3 | 自动生成维保任务 | 运营与服务域 | REQ-OPS-023 维保计划自动调度 | 未实现 | +| 4 | 手动创建维保任务 | 运营与服务域 | REQ-OPS-030 维保任务创建(triggerType=PLAN) | 已实现 | +| 5 | 分配维保人员 | 运营与服务域 | REQ-OPS-031 维保任务状态流转(ASSIGNED) | 已实现 | +| 6 | 执行维保 | 运营与服务域 | REQ-OPS-034 维保任务双完成接口 | 已实现 | +| 7 | 维保验收 | 运营与服务域 | REQ-OPS-035 维保任务验收 | 已实现 | +| 8 | 更新设备记录 | 设备与资产域 | REQ-OPS-036 完成后设备联动 | 已实现 | + +### 3.4 巡检异常处理流程 + +**流程描述**:巡检发现异常到问题解决的完整处理流程。 + +| 步骤 | 操作 | 涉及域 | 涉及功能点 | 实现状态 | +|------|------|--------|-----------|----------| +| 1 | 配置巡检模板 | 运营与服务域 | REQ-OPS-040 巡检模板管理 | 已实现 | +| 2 | 执行巡检签到 | 运营与服务域 | REQ-OPS-042 巡检签到 | 已实现 | +| 3 | 发现异常上报 | 运营与服务域 | REQ-OPS-043 巡检异常上报 | 已实现 | +| 4 | 创建维修工单 | 运营与服务域 | REQ-OPS-001 工单创建(source=INSPECTION) | 已实现 | +| 5 | 工单处理 | 运营与服务域 | REQ-OPS-004~007 工单派单/执行/完成/验收 | 已实现 | +| 6 | 复检确认 | 运营与服务域 | REQ-MDM-033 巡检记录管理(再次巡检) | 已实现 | + +### 3.5 能耗计费流程 + +**流程描述**:从能耗抄表到费用收取的完整流程。 + +| 步骤 | 操作 | 涉及域 | 涉及功能点 | 实现状态 | +|------|------|--------|-----------|----------| +| 1 | 配置计量点 | 运营与服务域 | REQ-OPS-060 计量点管理(含单价) | 已实现 | +| 2 | 能耗抄表 | 运营与服务域 | REQ-OPS-061 能耗抄表录入 | 已实现 | +| 3 | 费用计算 | 运营与服务域 | REQ-OPS-061 自动计算费用(consumption x unitPrice) | 已实现 | +| 4 | 生成账单 | 财务与收费域 | REQ-FIN-050 能耗数据与账单对接 | 未实现 | +| 5 | 业主缴费 | 财务与收费域 | REQ-FIN-020~022 支付记录管理 | 未实现 | +| 6 | 能耗统计分析 | 运营与服务域 | REQ-OPS-063~064 能耗统计 | 部分实现 | + +--- + +## 四、非功能需求 + +### 4.1 安全需求 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | +|--------|----------|----------|--------|----------| +| REQ-NFR-001 | 密码加密 | 用户密码使用BCrypt算法加密存储,不存储明文 | 高 | 已实现 | +| REQ-NFR-002 | JWT认证 | 使用HMAC-SHA256算法生成JWT Token,Token含userId/username/roles,过期时间可配置(默认24小时) | 高 | 已实现 | +| REQ-NFR-003 | 登录失败锁定 | 5次登录失败后锁定账户10分钟,基于Redis实现 | 高 | 已实现 | +| REQ-NFR-004 | 密码强度校验 | 8-20位,必须包含大小写字母+数字+特殊字符,弱密码黑名单检测 | 高 | 已实现 | +| REQ-NFR-005 | 项目数据隔离 | 基于X-Project-ID Header实现项目级数据隔离,DataScopeService四级数据范围过滤 | 高 | 已实现 | +| REQ-NFR-006 | 操作审计 | AOP切面自动记录操作日志,异步持久化,30天查询窗口 | 高 | 已实现 | +| REQ-NFR-007 | 敏感字段脱敏 | 审计日志中敏感字段自动脱敏(如密码、身份证号) | 中 | 已实现 | +| REQ-NFR-008 | XSS防护 | 前端输入过滤、白名单配置 | 高 | 未实现 | +| REQ-NFR-009 | CSRF防护 | CSRF Token、SameSite Cookie | 高 | 未实现 | +| REQ-NFR-010 | 敏感信息保护 | 移除敏感Header、错误信息脱敏 | 中 | 未实现 | + +### 4.2 性能需求 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | +|--------|----------|----------|--------|----------| +| REQ-NFR-020 | 分页查询 | 所有列表接口支持分页查询,避免全量数据返回 | 高 | 部分实现(工单列表为全量返回) | +| REQ-NFR-021 | 异步日志 | 审计日志通过线程池异步保存,不影响业务接口响应时间 | 高 | 已实现 | +| REQ-NFR-022 | 数据库索引 | 关键查询字段建立索引(审计日志/空间节点/设备/工单/能耗记录等) | 高 | 已实现 | +| REQ-NFR-023 | 列表虚拟滚动 | 前端长列表使用虚拟滚动技术,减少DOM节点 | 中 | 未实现 | +| REQ-NFR-024 | 请求去重与缓存 | 前端取消重复请求、请求结果缓存 | 中 | 未实现 | +| REQ-NFR-025 | WebSocket实时通信 | 替代轮询,实现工单状态变更实时推送 | 低 | 未实现 | + +### 4.3 可用性需求 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | +|--------|----------|----------|--------|----------| +| REQ-NFR-030 | 多项目切换 | 用户可在参与的项目间自由切换,切换后数据自动刷新 | 高 | 已实现 | +| REQ-NFR-031 | 角色权限动态菜单 | 根据用户角色和项目配置动态渲染菜单和路由 | 高 | 已实现 | +| REQ-NFR-032 | 前端权限指令 | v-permission指令控制页面元素显示隐藏 | 高 | 已实现 | +| REQ-NFR-033 | 表单分步优化 | 复杂表单使用Steps组件分步验证 | 中 | 未实现 | +| REQ-NFR-034 | 加载状态优化 | 骨架屏、防抖等加载状态优化 | 中 | 未实现 | +| REQ-NFR-035 | 列表交互增强 | 批量操作、视图切换 | 低 | 未实现 | + +### 4.4 可扩展性需求 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | +|--------|----------|----------|--------|----------| +| REQ-NFR-040 | JSONB扩展 | 设备attributes/巡检items&problems/工单photos&partsUsed等使用JSONB存储,支持灵活扩展 | 高 | 已实现 | +| REQ-NFR-041 | 扩展表模式 | 设备主表+4张扩展表模式,新增设备类型只需添加扩展表 | 高 | 已实现 | +| REQ-NFR-042 | 自定义配置 | 项目配置(ProjectConfig)支持功能开关和customConfig JSON | 高 | 已实现 | +| REQ-NFR-043 | 系统键值配置 | SysConfig支持运行时动态参数调整 | 中 | 已实现 | +| REQ-NFR-044 | 数据访问授权 | DataAccess通用数据访问控制,支持细粒度授权扩展 | 中 | 已实现 | + +### 4.5 兼容性需求 + +| 需求ID | 需求名称 | 需求描述 | 优先级 | 实现状态 | +|--------|----------|----------|--------|----------| +| REQ-NFR-050 | 浏览器兼容 | 管理后台支持Chrome/Firefox/Edge/Safari最新2个主版本 | 高 | 已实现 | +| REQ-NFR-051 | 移动端适配 | 员工APP和业主小程序基于uni-app跨端框架,支持iOS/Android | 高 | 未实现(框架待搭建) | +| REQ-NFR-052 | 响应式设计 | 管理后台支持不同屏幕尺寸的响应式布局 | 中 | 未实现 | + +--- + +## 五、需求追踪矩阵 + +### 5.1 身份与权限域 + +| 需求ID | 名称 | 优先级 | 实现状态 | 设计文档 | 代码模块 | +|--------|------|--------|----------|----------|----------| +| REQ-AUTH-001 | 用户创建 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: UserController | +| REQ-AUTH-002 | 用户查询 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: UserController | +| REQ-AUTH-003 | 用户更新 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: UserController | +| REQ-AUTH-004 | 用户删除 | 中 | 已实现 | REVERSE-AUTH.md | module-auth: UserController | +| REQ-AUTH-005 | 密码修改 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: UserController | +| REQ-AUTH-006 | 多类型用户扩展 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: EnterpriseUser/ProjectStaff/Resident | +| REQ-AUTH-007 | 用户角色分配 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: UserController | +| REQ-AUTH-008 | 用户项目关联 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: UserController | +| REQ-AUTH-010 | 角色管理 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: RoleController | +| REQ-AUTH-011 | 权限管理 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: PermissionController | +| REQ-AUTH-012 | 角色权限分配 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: RoleController | +| REQ-AUTH-013 | 四级数据范围 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: DataScopeService | +| REQ-AUTH-014 | 双层角色分配 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: AuthService | +| REQ-AUTH-015 | 项目级角色查询 | 中 | 已实现 | REVERSE-AUTH.md | module-auth: RoleController | +| REQ-AUTH-016 | 角色关联用户查询 | 中 | 已实现 | REVERSE-AUTH.md | module-auth: RoleController | +| REQ-AUTH-017 | 权限树端点 | 中 | 未实现 | - | - | +| REQ-AUTH-018 | 权限校验端点 | 中 | 未实现 | - | - | +| REQ-AUTH-019 | 用户菜单端点 | 中 | 未实现 | - | - | +| REQ-AUTH-020 | 用户权限查询端点 | 中 | 未实现 | - | - | +| REQ-AUTH-021 | 角色业务属性 | 低 | 未实现 | - | - | +| REQ-AUTH-022 | 权限菜单路由属性 | 低 | 未实现 | - | - | +| REQ-AUTH-030 | 部门管理 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: DeptController | +| REQ-AUTH-031 | 部门树查询 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: DeptController | +| REQ-AUTH-032 | 部门成员查询 | 中 | 已实现 | REVERSE-AUTH.md | module-auth: DeptController | +| REQ-AUTH-033 | 按类型查询部门 | 中 | 已实现 | REVERSE-AUTH.md | module-auth: DeptController | +| REQ-AUTH-034 | 部门删除约束 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: DeptService | +| REQ-AUTH-040 | 项目成员列表 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: ProjectMemberController | +| REQ-AUTH-041 | 可用成员查询 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: ProjectMemberController | +| REQ-AUTH-042 | 添加项目成员 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: ProjectMemberController | +| REQ-AUTH-043 | 移除项目成员 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: ProjectMemberController | +| REQ-AUTH-044 | 项目员工班次 | 中 | 已实现 | REVERSE-AUTH.md | module-auth: ProjectStaff | +| REQ-AUTH-045 | 项目员工角色 | 中 | 已实现 | REVERSE-AUTH.md | module-auth: ProjectStaffRole | +| REQ-AUTH-050 | 住户信息管理 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: Resident | +| REQ-AUTH-051 | 住户认证流程 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: Resident | +| REQ-AUTH-052 | 住户房屋绑定 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: ResidentSpace | +| REQ-AUTH-053 | 房屋空间管理 | 中 | 已实现 | REVERSE-AUTH.md | module-auth: Space | +| REQ-AUTH-060 | 用户登录 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: AuthController | +| REQ-AUTH-061 | 用户登出 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: AuthController | +| REQ-AUTH-062 | Token刷新 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: AuthController | +| REQ-AUTH-063 | 获取当前用户 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: AuthController | +| REQ-AUTH-064 | JWT Token规范 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: JwtTokenProvider | +| REQ-AUTH-065 | 登录失败锁定 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: LoginAttemptService | +| REQ-AUTH-066 | 项目上下文传递 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: ProjectContextInterceptor | +| REQ-AUTH-070 | 操作日志记录 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: OperationLogAspect | +| REQ-AUTH-071 | 异步持久化 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: AuditLogService | +| REQ-AUTH-072 | 审计日志查询 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: AuditLogController | +| REQ-AUTH-073 | 模块与操作类型 | 中 | 已实现 | REVERSE-AUTH.md | module-auth: AuditLog | +| REQ-AUTH-074 | 日志统计 | 中 | 已实现 | REVERSE-AUTH.md | module-auth: AuditLogController | +| REQ-AUTH-075 | 日志归档 | 低 | 部分实现 | REVERSE-AUTH.md | module-auth: AuditLogService | +| REQ-AUTH-080 | 键值配置管理 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: SysConfigController | +| REQ-AUTH-081 | 密码策略配置 | 高 | 已实现 | REVERSE-AUTH.md | module-auth: PasswordStrengthValidator | +| REQ-AUTH-082 | 数据访问授权 | 中 | 已实现 | REVERSE-AUTH.md | module-auth: DataAccessController | + +### 5.2 空间与项目域 + +| 需求ID | 名称 | 优先级 | 实现状态 | 设计文档 | 代码模块 | +|--------|------|--------|----------|----------|----------| +| REQ-MDM-001 | 项目创建 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: ProjectController | +| REQ-MDM-002 | 项目查询 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: ProjectController | +| REQ-MDM-003 | 项目更新与删除 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: ProjectController | +| REQ-MDM-004 | 项目状态流转 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: ProjectController | +| REQ-MDM-005 | 状态变更历史 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: ProjectStatusHistory | +| REQ-MDM-006 | 项目统计 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: ProjectController | +| REQ-MDM-007 | 项目配置管理 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: ProjectConfigController | +| REQ-MDM-008 | 项目成员管理 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: ProjectMemberController | +| REQ-MDM-009 | 项目删除检查 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: ProjectController | +| REQ-MDM-010 | 空间节点创建 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: SpaceNodeController | +| REQ-MDM-011 | 15种节点类型 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: SpaceNode | +| REQ-MDM-012 | 树形结构维护 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: SpaceNodeService | +| REQ-MDM-013 | 空间节点查询 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: SpaceNodeController | +| REQ-MDM-014 | 空间节点更新与删除 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: SpaceNodeController | +| REQ-MDM-015 | 面积信息管理 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: SpaceNode | +| REQ-MDM-016 | 地理信息管理 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: SpaceNode | +| REQ-MDM-017 | 设备扩展模式 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: SpaceNode | +| REQ-MDM-018 | 扩展属性JSON | 中 | 已实现 | REVERSE-MDM.md | module-mdm: SpaceNode | +| REQ-MDM-019 | 空间编码自动生成 | 中 | 未实现 | - | - | +| REQ-MDM-020 | 节点移动 | 中 | 未实现 | - | - | +| REQ-MDM-021 | 祖先/子孙查询 | 低 | 未实现 | - | - | +| REQ-MDM-022 | 空间统计分析 | 低 | 未实现 | - | - | +| REQ-MDM-023 | PostGIS空间查询 | 低 | 未实现 | - | - | +| REQ-MDM-024 | 地图服务 | 低 | 未实现 | - | - | +| REQ-MDM-025 | 空间节点导入导出 | 中 | 未实现 | - | - | +| REQ-MDM-030 | 巡检标准项CRUD | 高 | 已实现 | REVERSE-MDM.md | module-mdm: InspectionItemController | +| REQ-MDM-031 | 标准项查询筛选 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: InspectionItemService | +| REQ-MDM-032 | 巡检模板管理 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: InspectionTemplateController | +| REQ-MDM-033 | 巡检记录管理 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: InspectionRecordController | +| REQ-MDM-034 | 巡检记录查询 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: InspectionRecordService | + +### 5.3 设备与资产域 + +| 需求ID | 名称 | 优先级 | 实现状态 | 设计文档 | 代码模块 | +|--------|------|--------|----------|----------|----------| +| REQ-ASSET-001 | 设备创建 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentController | +| REQ-ASSET-002 | 设备查询 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentController | +| REQ-ASSET-003 | 设备更新与删除 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentController | +| REQ-ASSET-004 | 设备与空间关联 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: Equipment | +| REQ-ASSET-005 | 系统类型分类 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: Equipment | +| REQ-ASSET-006 | 设备统计 | 中 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentController | +| REQ-ASSET-007 | 设备导入 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentController | +| REQ-ASSET-008 | 设备导出 | 中 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentController | +| REQ-ASSET-009 | 设备照片管理 | 中 | 已实现 | REVERSE-ASSET.md | module-asset: Equipment | +| REQ-ASSET-010 | 设备文档管理 | 中 | 已实现 | REVERSE-ASSET.md | module-asset: Equipment | +| REQ-ASSET-011 | 设备二维码 | 中 | 未实现 | - | - | +| REQ-ASSET-012 | 设备规格字段 | 低 | 未实现 | - | - | +| REQ-ASSET-020 | 电梯扩展表 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentElevatorService | +| REQ-ASSET-021 | 暖通扩展表 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentHvacService | +| REQ-ASSET-022 | 消防扩展表 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentFireService | +| REQ-ASSET-023 | 能源计量扩展表 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentEnergyService | +| REQ-ASSET-024 | 扩展表读写模式 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: Equipment*Service | +| REQ-ASSET-025 | 无扩展表设备类型 | 中 | 已实现 | REVERSE-ASSET.md | module-asset: Equipment | +| REQ-ASSET-030 | 健康评分算法 | 高 | 已实现[Beta] | REVERSE-ASSET.md | module-asset: EquipmentHealthServiceImpl | +| REQ-ASSET-031 | 5级健康等级 | 高 | 已实现[Beta] | REVERSE-ASSET.md | module-asset: HealthLevel | +| REQ-ASSET-032 | MTBF计算 | 中 | 已实现[Beta] | REVERSE-ASSET.md | module-asset: EquipmentHealthServiceImpl | +| REQ-ASSET-033 | MTTR计算 | 中 | 已实现[Beta] | REVERSE-ASSET.md | module-asset: EquipmentHealthServiceImpl | +| REQ-ASSET-034 | 健康评分查询 | 中 | 已实现[Beta] | REVERSE-ASSET.md | module-asset: EquipmentHealthController | +| REQ-ASSET-035 | 维保完成率计算 | 中 | 部分实现 | REVERSE-ASSET.md | module-asset: EquipmentHealthServiceImpl | +| REQ-ASSET-040 | 故障记录 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentHealthController | +| REQ-ASSET-041 | 故障自动计算 | 中 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentFailureHistory | +| REQ-ASSET-042 | 故障历史查询 | 中 | 已实现 | REVERSE-ASSET.md | module-asset: EquipmentHealthController | +| REQ-ASSET-043 | 故障自动工单 | 中 | 未实现 | - | - | +| REQ-ASSET-050 | 归属主体CRUD | 高 | 已实现 | REVERSE-ASSET.md | module-asset: OwnershipEntityController | +| REQ-ASSET-051 | 归属主体查询 | 中 | 已实现 | REVERSE-ASSET.md | module-asset: OwnershipEntityController | +| REQ-ASSET-052 | 归属主体逻辑删除 | 高 | 已实现 | REVERSE-ASSET.md | module-asset: OwnershipEntityController | +| REQ-ASSET-053 | 设备归属冗余 | 中 | 已实现 | REVERSE-ASSET.md | module-asset: Equipment | +| REQ-ASSET-060 | 特种设备标记 | 中 | 已实现 | REVERSE-ASSET.md | module-asset: Equipment | +| REQ-ASSET-061 | 年检周期管理 | 中 | 已实现 | REVERSE-ASSET.md | module-asset: Equipment | +| REQ-ASSET-062 | 特种设备列表查询 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: SpaceNodeController | +| REQ-ASSET-063 | 即将年检预警 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: SpaceNodeController | +| REQ-ASSET-064 | 维保到期定时提醒 | 中 | 未实现 | - | - | + +### 5.4 运营与服务域 + +| 需求ID | 名称 | 优先级 | 实现状态 | 设计文档 | 代码模块 | +|--------|------|--------|----------|----------|----------| +| REQ-OPS-001 | 工单创建 | 高 | 已实现 | REVERSE-OPS.md | module-wo: WorkOrderController | +| REQ-OPS-002 | 工单查询 | 高 | 已实现 | REVERSE-OPS.md | module-wo: WorkOrderController | +| REQ-OPS-003 | 工单状态流转 | 高 | 已实现 | REVERSE-OPS.md | module-wo: WorkOrderService | +| REQ-OPS-004 | 工单派单 | 高 | 已实现 | REVERSE-OPS.md | module-wo: WorkOrderController | +| REQ-OPS-005 | 工单执行 | 高 | 已实现 | REVERSE-OPS.md | module-wo: WorkOrderController | +| REQ-OPS-006 | 工单完成 | 高 | 已实现 | REVERSE-OPS.md | module-wo: WorkOrderController | +| REQ-OPS-007 | 工单验收 | 高 | 已实现 | REVERSE-OPS.md | module-wo: WorkOrderController | +| REQ-OPS-008 | 工单取消 | 中 | 已实现 | REVERSE-OPS.md | module-wo: WorkOrderController | +| REQ-OPS-009 | 工单优先级 | 高 | 已实现 | REVERSE-OPS.md | module-wo: WorkOrder | +| REQ-OPS-010 | 工单明细管理 | 中 | 已实现 | REVERSE-OPS.md | module-wo: WorkOrderController | +| REQ-OPS-011 | 工单统计 | 中 | 已实现 | REVERSE-OPS.md | module-wo: WorkOrderController | +| REQ-OPS-012 | 工单挂起与恢复 | 中 | 未实现 | - | - | +| REQ-OPS-013 | 工单退回 | 中 | 未实现 | - | - | +| REQ-OPS-014 | 工单流转记录 | 中 | 未实现 | - | - | +| REQ-OPS-015 | 工单报修人信息 | 中 | 未实现 | - | - | +| REQ-OPS-016 | 工单分页查询 | 中 | 未实现 | - | - | +| REQ-OPS-020 | 维保计划创建 | 高 | 已实现 | REVERSE-OPS.md | module-wo: MaintenancePlan | +| REQ-OPS-021 | 维保计划状态管理 | 高 | 已实现 | REVERSE-OPS.md | module-wo: MaintenancePlan | +| REQ-OPS-022 | 维保计划周期调度 | 中 | 已实现 | REVERSE-OPS.md | module-wo: MaintenancePlan | +| REQ-OPS-023 | 维保计划自动调度 | 中 | 未实现 | - | - | +| REQ-OPS-030 | 维保任务创建 | 高 | 已实现 | REVERSE-OPS.md | module-wo: MaintenanceTaskController | +| REQ-OPS-031 | 维保任务状态流转 | 高 | 已实现 | REVERSE-OPS.md | module-wo: MaintenanceTaskService | +| REQ-OPS-032 | 4种触发方式 | 高 | 已实现 | REVERSE-OPS.md | module-wo: MaintenanceTask | +| REQ-OPS-033 | 自动优先级判定 | 高 | 已实现 | REVERSE-OPS.md | module-wo: MaintenanceTaskServiceImpl | +| REQ-OPS-034 | 双完成接口 | 中 | 已实现 | REVERSE-OPS.md | module-wo: MaintenanceTaskController | +| REQ-OPS-035 | 维保任务验收 | 中 | 已实现 | REVERSE-OPS.md | module-wo: MaintenanceTaskController | +| REQ-OPS-036 | 完成后设备联动 | 高 | 已实现 | REVERSE-OPS.md | module-wo: MaintenanceTaskServiceImpl | +| REQ-OPS-037 | 维保任务统计 | 中 | 已实现 | REVERSE-OPS.md | module-wo: MaintenanceTaskController | +| REQ-OPS-038 | 使用备件记录 | 中 | 已实现 | REVERSE-OPS.md | module-wo: MaintenanceTask | +| REQ-OPS-040 | 巡检模板管理 | 高 | 已实现 | REVERSE-OPS.md | module-wo: InspectionTemplate | +| REQ-OPS-041 | 巡检项管理 | 高 | 已实现 | REVERSE-OPS.md | module-wo: InspectionItem | +| REQ-OPS-042 | 巡检签到 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: InspectionRecord | +| REQ-OPS-043 | 巡检异常上报 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: InspectionRecord | +| REQ-OPS-044 | 巡检扫码签到 | 中 | 未实现 | - | - | +| REQ-OPS-050 | 备件分类管理 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: SparePartController | +| REQ-OPS-051 | 备件台账CRUD | 高 | 已实现 | REVERSE-MDM.md | module-mdm: SparePartController | +| REQ-OPS-052 | 入库操作 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: SparePartController | +| REQ-OPS-053 | 出库操作 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: SparePartController | +| REQ-OPS-054 | 低库存预警 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: SparePartController | +| REQ-OPS-055 | 出入库记录查询 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: SparePartController | +| REQ-OPS-056 | 盘点与调整 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: SparePartRecord | +| REQ-OPS-060 | 计量点管理 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: EnergyController | +| REQ-OPS-061 | 能耗抄表录入 | 高 | 已实现 | REVERSE-MDM.md | module-mdm: EnergyConsumptionService | +| REQ-OPS-062 | 能耗记录查询 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: EnergyController | +| REQ-OPS-063 | 按类型统计能耗 | 中 | 部分实现 | REVERSE-MDM.md | module-mdm: EnergyConsumptionService | +| REQ-OPS-064 | 单位面积能耗 | 中 | 已实现 | REVERSE-MDM.md | module-mdm: EnergyConsumptionService | +| REQ-OPS-065 | IoT自动抄表 | 低 | 未实现 | - | - | +| REQ-OPS-066 | 前后端能源类型统一 | 中 | 未实现 | - | - | + +### 5.5 财务与收费域 + +| 需求ID | 名称 | 优先级 | 实现状态 | 设计文档 | 代码模块 | +|--------|------|--------|----------|----------|----------| +| REQ-FIN-001 | 收费项目实体 | 高 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-002 | 收费项目CRUD | 高 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-010 | 账单实体 | 高 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-011 | 账单自动生成 | 高 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-012 | 账单状态流转 | 高 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-013 | 批量账单生成 | 中 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-014 | 账单导出 | 中 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-020 | 支付记录实体 | 高 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-021 | 线下收款登记 | 高 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-022 | 支付记录查询 | 中 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-030 | 退款实体 | 中 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-031 | 退款流程 | 中 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-040 | 费用催缴定时任务 | 中 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-041 | 滞纳金自动计算 | 中 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-050 | 能耗数据与账单对接 | 高 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-051 | 按面积计费 | 中 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-052 | 固定金额计费 | 中 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-053 | 支付网关对接 | 中 | 未实现 | REVERSE-FINANCE.md | - | +| REQ-FIN-054 | 财务报表 | 低 | 未实现 | REVERSE-FINANCE.md | - | + +### 5.6 需求统计汇总 + +| 业务域 | 已实现 | 部分实现 | 未实现 | 合计 | +|--------|--------|----------|--------|------| +| 身份与权限域 | 37 | 1 | 6 | 44 | +| 空间与项目域 | 23 | 0 | 7 | 30 | +| 设备与资产域 | 28 | 1 | 4 | 33 | +| 运营与服务域 | 36 | 1 | 9 | 46 | +| 财务与收费域 | 0 | 0 | 19 | 19 | +| 前端交互域 | 14 | 0 | 7 | 21 | +| 非功能需求 | 12 | 1 | 8 | 21 | +| **合计** | **150** | **4** | **60** | **214** | + +**实现率**:已实现 70.1% | 部分实现 1.9% | 未实现 28.0% + +--- + +> **文档说明**: 本文档基于7份源文档整合生成,所有需求项均标注唯一ID和实现状态。已实现功能以反推文档(REVERSE-*.md)中的实际代码为准,未实现功能从原需求文档和特性清单中提取。需求描述力求具体到可验证程度。 diff --git a/02-DESIGN/detail/DETAIL-ASSET.md b/02-DESIGN/detail/DETAIL-ASSET.md new file mode 100644 index 0000000..2d8631f --- /dev/null +++ b/02-DESIGN/detail/DETAIL-ASSET.md @@ -0,0 +1,1036 @@ +# 设备与资产域 - 详细设计 + +**文档编号**: DETAIL-ASSET +**版本**: v1.0 +**模块**: module-asset +**生成日期**: 2026-05-18 +**数据来源**: ether-pms/module-asset 实际代码 + REVERSE-ASSET.md 反推文档 + +--- + +## 一、功能点清单 + +| 功能ID | 功能名称 | 优先级 | 实现状态 | 对应需求ID | +|--------|----------|--------|----------|------------| +| ASSET-001 | 设备台账CRUD | P0 | 已完成 | 03-ASSET-001 | +| ASSET-002 | 设备批量删除 | P1 | 已完成 | 03-ASSET-001 | +| ASSET-003 | 设备按项目查询 | P0 | 已完成 | 03-ASSET-002 | +| ASSET-004 | 设备按空间查询 | P0 | 已完成 | 03-ASSET-002 | +| ASSET-005 | 设备按类型查询 | P1 | 已完成 | 03-ASSET-002 | +| ASSET-006 | 设备按归属查询 | P1 | 已完成 | 03-ASSET-002 | +| ASSET-007 | 设备按类型统计 | P1 | 已完成 | 03-ASSET-003 | +| ASSET-008 | 设备按归属统计 | P1 | 已完成 | 03-ASSET-003 | +| ASSET-009 | 项目设备总数 | P1 | 已完成 | 03-ASSET-003 | +| ASSET-010 | 电梯扩展表管理 | P0 | 已完成 | 03-ASSET-004 | +| ASSET-011 | 暖通扩展表管理 | P0 | 已完成 | 03-ASSET-004 | +| ASSET-012 | 消防扩展表管理 | P0 | 已完成 | 03-ASSET-004 | +| ASSET-013 | 能源计量扩展表管理 | P0 | 已完成 | 03-ASSET-004 | +| ASSET-014 | 设备Excel导入 | P1 | 已完成 | 03-ASSET-005 | +| ASSET-015 | 设备Excel导出 | P1 | 已完成 | 03-ASSET-005 | +| ASSET-016 | 设备健康评分计算 | P1 | Beta | 03-ASSET-006 | +| ASSET-017 | 设备健康历史查询 | P1 | Beta | 03-ASSET-006 | +| ASSET-018 | MTBF计算 | P1 | Beta | 03-ASSET-006 | +| ASSET-019 | MTTR计算 | P1 | Beta | 03-ASSET-006 | +| ASSET-020 | 故障历史记录 | P0 | 已完成 | 03-ASSET-007 | +| ASSET-021 | 故障历史查询 | P0 | 已完成 | 03-ASSET-007 | +| ASSET-022 | 归属主体CRUD | P1 | 已完成 | 03-ASSET-008 | +| ASSET-023 | 归属主体按类型查询 | P1 | 已完成 | 03-ASSET-008 | +| ASSET-024 | 设备照片管理 | P2 | 已完成 | 03-ASSET-009 | +| ASSET-025 | 设备文档管理 | P2 | 已完成 | 03-ASSET-009 | +| ASSET-026 | 设备二维码 | P2 | 未实现 | 03-ASSET-010 | +| ASSET-027 | IoT集成 | P3 | 未实现 | 03-ASSET-011 | +| ASSET-028 | 故障自动工单(事件驱动) | P2 | 未实现 | 03-ASSET-012 | +| ASSET-029 | 维保到期定时提醒 | P2 | 未实现 | 03-ASSET-013 | + +--- + +## 二、数据结构设计 + +### 2.1 实体定义 + +#### 2.1.1 Equipment(设备主表) + +**表名**: `asset_equipment` +**实体类**: `com.ether.pms.asset.entity.Equipment` + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| projectId | UUID | 是 | - | 所属项目ID | +| spaceNodeId | UUID | 否 | - | 关联空间节点ID | +| equipmentCode | VARCHAR(50) | 是 | - | 设备编码,全局唯一 | +| equipmentName | VARCHAR(100) | 是 | - | 设备名称 | +| equipmentType | VARCHAR(30) | 是 | - | 设备类型枚举(10种) | +| equipmentCategory | VARCHAR(50) | 否 | - | 设备分类 | +| systemType | VARCHAR(50) | 否 | - | 系统类型枚举(9种) | +| ownershipType | VARCHAR(20) | 是 | PROJECT | 归属类型枚举(4种) | +| owningEntityId | UUID | 否 | - | 归属主体ID | +| owningEntityName | VARCHAR(100) | 否 | - | 归属主体名称(冗余) | +| assetCode | VARCHAR(50) | 否 | - | 资产编号 | +| serialNumber | VARCHAR(100) | 否 | - | 出厂编号 | +| model | VARCHAR(100) | 否 | - | 型号 | +| manufacturer | VARCHAR(100) | 否 | - | 厂商 | +| supplier | VARCHAR(100) | 否 | - | 供应商 | +| status | VARCHAR(20) | 是 | ACTIVE | 设备状态枚举(4种) | +| operationStatus | VARCHAR(20) | 否 | - | 运行状态 | +| installationLocation | VARCHAR(200) | 否 | - | 安装位置 | +| installationDate | DATE | 否 | - | 安装日期 | +| designLifeYears | INTEGER | 否 | - | 设计寿命(年) | +| ratedPower | DECIMAL(10,2) | 否 | - | 额定功率(kW) | +| ratedVoltage | VARCHAR(20) | 否 | - | 额定电压(V) | +| ratedCurrent | DECIMAL(10,2) | 否 | - | 额定电流(A) | +| maintenanceVendor | VARCHAR(100) | 否 | - | 维保商 | +| maintenanceVendorContact | VARCHAR(50) | 否 | - | 维保联系人 | +| maintenanceVendorPhone | VARCHAR(20) | 否 | - | 维保电话 | +| maintenanceContractNo | VARCHAR(50) | 否 | - | 维保合同编号 | +| maintenanceContractStart | DATE | 否 | - | 维保合同开始日期 | +| maintenanceContractEnd | DATE | 否 | - | 维保合同结束日期 | +| purchaseDate | DATE | 否 | - | 购置日期 | +| purchasePrice | DECIMAL(12,2) | 否 | - | 购置价格(元) | +| warrantyExpireDate | DATE | 否 | - | 保修到期日期 | +| energyConsumptionStandard | DECIMAL(12,2) | 否 | - | 能耗标准(kW*h/年) | +| inspectionCycle | INTEGER | 否 | - | 年检周期(月) | +| nextInspectionDate | DATE | 否 | - | 下次年检日期 | +| lastInspectionDate | DATE | 否 | - | 上次年检日期 | +| lastInspectionResult | VARCHAR(20) | 否 | - | 上次年检结果 | +| specialEquipmentType | VARCHAR(50) | 否 | - | 特种设备类型 | +| specialEquipmentCert | VARCHAR(100) | 否 | - | 特种设备证书 | +| attributes | JSON/TEXT | 否 | - | 扩展属性(JSON) | +| photos | JSONB | 否 | - | 设备照片列表(List\) | +| documents | JSONB | 否 | - | 电子文档列表(List\) | +| manualUrl | VARCHAR(500) | 否 | - | 手册URL | +| remarks | VARCHAR(1000) | 否 | - | 备注 | +| isDeleted | BOOLEAN | 否 | false | 逻辑删除标记 | +| createdAt | DATETIME | 是 | now() | 创建时间 | +| updatedAt | DATETIME | 是 | now() | 更新时间 | +| createdBy | UUID | 否 | - | 创建人 | +| updatedBy | UUID | 否 | - | 更新人 | + +**索引**: + +| 索引名 | 字段 | 类型 | 用途 | +|--------|------|------|------| +| idx_equipment_project | project_id | 普通索引 | 按项目查询 | +| idx_equipment_space | space_node_id | 普通索引 | 按空间查询 | +| idx_equipment_type | equipment_type | 普通索引 | 按类型查询 | +| idx_equipment_ownership | ownership_type | 普通索引 | 按归属查询 | +| idx_equipment_code | equipment_code | 唯一索引 | 编码唯一性 | +| idx_equipment_status | status | 普通索引 | 按状态查询 | +| idx_eq_project_status | project_id, status | 联合索引 | 项目+状态组合查询 | +| idx_eq_project_type | project_id, equipment_type | 联合索引 | 项目+类型组合查询 | +| idx_eq_project_deleted | project_id, is_deleted | 联合索引 | 项目+未删除组合查询 | + +**内嵌类型**: + +EquipmentPhoto(设备照片): +| 字段 | 类型 | 说明 | +|------|------|------| +| type | String | 照片类型:外观/铭牌/安装位置/环境 | +| url | String | 照片URL | +| remark | String | 备注 | + +EquipmentDocument(电子文档): +| 字段 | 类型 | 说明 | +|------|------|------| +| name | String | 文档名称 | +| url | String | 文档URL | +| size | Long | 文件大小 | +| type | String | 文档类型:manual/certificate/contract/other | +| remark | String | 备注 | + +#### 2.1.2 EquipmentElevator(电梯扩展表) + +**表名**: `asset_equipment_elevator` +**实体类**: `com.ether.pms.asset.entity.EquipmentElevator` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| id | UUID | 是 | 主键 | +| equipmentId | UUID | 是 | 关联设备ID(UNIQUE外键) | +| elevatorType | VARCHAR(30) | 否 | 电梯类型 | +| elevatorModel | VARCHAR(50) | 否 | 电梯型号 | +| loadCapacity | INTEGER | 否 | 载重(kg) | +| speed | DECIMAL(10,2) | 否 | 速度(m/s) | +| floorCount | INTEGER | 否 | 楼层数 | +| shaftDimensions | VARCHAR(50) | 否 | 井道尺寸 | +| pitDepth | DECIMAL(10,2) | 否 | 底坑深度 | +| overheadHeight | DECIMAL(10,2) | 否 | 顶层高度 | +| registrationNo | VARCHAR(50) | 否 | 注册登记号 | +| inspectionCertificate | VARCHAR(100) | 否 | 检验证书 | +| nextInspectionDate | DATE | 否 | 下次检验日期 | +| energyConsumption | DECIMAL(12,2) | 否 | 能耗 | +| maintenanceLevel | VARCHAR(20) | 否 | 维保等级 | +| rescuePlan | VARCHAR(5000) | 否 | 应急救援预案 | +| createdAt | DATETIME | 否 | 创建时间 | +| updatedAt | DATETIME | 否 | 更新时间 | + +#### 2.1.3 EquipmentHvac(暖通空调扩展表) + +**表名**: `asset_equipment_hvac` +**实体类**: `com.ether.pms.asset.entity.EquipmentHvac` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| id | UUID | 是 | 主键 | +| equipmentId | UUID | 是 | 关联设备ID(UNIQUE外键) | +| hvacType | VARCHAR(30) | 否 | 暖通类型 | +| coolingCapacity | DECIMAL(12,2) | 否 | 制冷量 | +| heatingCapacity | DECIMAL(12,2) | 否 | 制热量 | +| airFlow | DECIMAL(12,2) | 否 | 风量 | +| refrigerantType | VARCHAR(30) | 否 | 制冷剂类型 | +| refrigerantCharge | DECIMAL(10,2) | 否 | 制冷剂充注量 | +| energyEfficiencyRatio | DECIMAL(10,2) | 否 | 能效比(EER) | +| coefficientOfPerformance | DECIMAL(10,2) | 否 | 性能系数(COP) | +| installationDate | DATE | 否 | 安装日期 | +| warrantyExpireDate | DATE | 否 | 保修到期日期 | +| filterReplacementCycle | INTEGER | 否 | 滤网更换周期 | +| lastFilterReplacement | DATE | 否 | 上次滤网更换日期 | +| ductType | VARCHAR(30) | 否 | 风管类型 | +| ductDimensions | VARCHAR(50) | 否 | 风管尺寸 | +| createdAt | DATETIME | 否 | 创建时间 | +| updatedAt | DATETIME | 否 | 更新时间 | + +#### 2.1.4 EquipmentFire(消防扩展表) + +**表名**: `asset_equipment_fire` +**实体类**: `com.ether.pms.asset.entity.EquipmentFire` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| id | UUID | 是 | 主键 | +| equipmentId | UUID | 是 | 关联设备ID(UNIQUE外键) | +| fireEquipmentType | VARCHAR(30) | 否 | 消防设备类型 | +| installationArea | DECIMAL(10,2) | 否 | 安装面积 | +| installationHeight | DECIMAL(10,2) | 否 | 安装高度 | +| detectionRange | DECIMAL(10,2) | 否 | 探测范围 | +| systemType | VARCHAR(30) | 否 | 系统类型 | +| zoneNumber | VARCHAR(20) | 否 | 分区编号 | +| loopNumber | VARCHAR(20) | 否 | 回路编号 | +| linkageEnabled | BOOLEAN | 否 | 是否启用联动 | +| linkageAction | VARCHAR(100) | 否 | 联动动作 | +| inspectionCycle | INTEGER | 否 | 巡检周期 | +| lastInspectionDate | DATE | 否 | 上次巡检日期 | +| nextInspectionDate | DATE | 否 | 下次巡检日期 | +| inspectionResult | VARCHAR(20) | 否 | 巡检结果 | +| specialRequirement | VARCHAR(2000) | 否 | 特殊要求 | +| createdAt | DATETIME | 否 | 创建时间 | +| updatedAt | DATETIME | 否 | 更新时间 | + +#### 2.1.5 EquipmentEnergy(能源计量扩展表) + +**表名**: `asset_equipment_energy` +**实体类**: `com.ether.pms.asset.entity.EquipmentEnergy` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| id | UUID | 是 | 主键 | +| equipmentId | UUID | 是 | 关联设备ID(UNIQUE外键) | +| meterType | VARCHAR(30) | 否 | 表计类型 | +| energyType | VARCHAR(30) | 否 | 能源类型 | +| meterModel | VARCHAR(50) | 否 | 表计型号 | +| meterSpecification | VARCHAR(50) | 否 | 表计规格 | +| meterConstant | DECIMAL(10,4) | 否 | 表常数 | +| accuracyClass | VARCHAR(10) | 否 | 精度等级 | +| readingType | VARCHAR(20) | 否 | 抄表方式 | +| lastReadingDate | DATE | 否 | 上次抄表日期 | +| lastReadingValue | DECIMAL(12,2) | 否 | 上次读数 | +| currentReadingValue | DECIMAL(12,2) | 否 | 当前读数 | +| unitPrice | DECIMAL(10,4) | 否 | 单价 | +| billingType | VARCHAR(20) | 否 | 计费方式 | +| communicationType | VARCHAR(30) | 否 | 通讯方式 | +| communicationAddress | VARCHAR(50) | 否 | 通讯地址 | +| verificationCycle | INTEGER | 否 | 检定周期 | +| nextVerificationDate | DATE | 否 | 下次检定日期 | +| verificationCertificate | VARCHAR(100) | 否 | 检定证书 | +| createdAt | DATETIME | 否 | 创建时间 | +| updatedAt | DATETIME | 否 | 更新时间 | + +#### 2.1.6 EquipmentHealthScore(健康评分表) + +**表名**: `ops_equipment_health_score` +**实体类**: `com.ether.pms.asset.entity.EquipmentHealthScore` +**状态**: Beta -- 数据准确性待验证 + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| id | UUID | 是 | 主键 | +| projectId | UUID | 是 | 项目ID | +| equipmentId | UUID | 是 | 设备ID | +| equipmentCode | VARCHAR(50) | 否 | 设备编码(冗余) | +| equipmentName | VARCHAR(100) | 否 | 设备名称(冗余) | +| healthScore | DECIMAL(5,2) | 是 | 健康度评分(0-100) | +| failureDeduction | DECIMAL(5,2) | 否 | 故障扣分 | +| maintenanceDeduction | DECIMAL(5,2) | 否 | 维保扣分 | +| ageDeduction | DECIMAL(5,2) | 否 | 年龄扣分 | +| failureCount30d | INTEGER | 否 | 近30天故障次数 | +| maintenanceCompletionRate | DECIMAL(5,4) | 否 | 维保完成率 | +| equipmentAgeYears | DECIMAL(10,2) | 否 | 设备年龄(年) | +| operationHours30d | DECIMAL(12,2) | 否 | 近30天运行时长 | +| mtbfHours | DECIMAL(12,2) | 否 | MTBF(小时) | +| mttrHours | DECIMAL(12,2) | 否 | MTTR(小时) | +| healthLevel | VARCHAR(20) | 否 | 健康等级枚举(5级) | +| calculatedAt | DATETIME | 是 | 计算时间 | +| calculationPeriodDays | INTEGER | 否 | 计算周期天数 | +| remarks | VARCHAR(1000) | 否 | 备注 | +| createdAt | DATETIME | 否 | 创建时间 | +| updatedAt | DATETIME | 否 | 更新时间 | + +**索引**: + +| 索引名 | 字段 | 用途 | +|--------|------|------| +| idx_health_equipment | equipment_id | 按设备查询 | +| idx_health_calc_time | calculated_at | 按计算时间查询 | +| idx_health_project | project_id | 按项目查询 | + +#### 2.1.7 EquipmentFailureHistory(故障历史表) + +**表名**: `ops_equipment_failure_history` +**实体类**: `com.ether.pms.asset.entity.EquipmentFailureHistory` + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| projectId | UUID | 是 | - | 项目ID | +| equipmentId | UUID | 是 | - | 设备ID | +| equipmentCode | VARCHAR(50) | 否 | - | 设备编码(冗余) | +| equipmentName | VARCHAR(100) | 否 | - | 设备名称(冗余) | +| failureTime | DATETIME | 是 | - | 故障时间 | +| failureType | VARCHAR(50) | 否 | - | 故障类型枚举(4种) | +| failureLevel | VARCHAR(20) | 否 | - | 故障等级枚举(4级) | +| failureReason | VARCHAR(2000) | 否 | - | 故障原因 | +| failureDescription | VARCHAR(2000) | 否 | - | 故障描述 | +| repairStartTime | DATETIME | 否 | - | 修复开始时间 | +| repairEndTime | DATETIME | 否 | - | 修复结束时间 | +| repairDurationHours | DOUBLE | 否 | 自动计算 | 修复时长(小时) | +| repairPerson | VARCHAR(100) | 否 | - | 修复人 | +| repairResult | VARCHAR(20) | 否 | - | 修复结果枚举(4种) | +| downtimeHours | DOUBLE | 否 | 自动计算 | 停机时长(小时) | +| maintenanceCost | DECIMAL(12,2) | 否 | - | 维修费用 | +| isScheduled | BOOLEAN | 是 | false | 是否计划停机 | +| relatedTaskId | UUID | 否 | - | 关联工单ID | +| remarks | VARCHAR(1000) | 否 | - | 备注 | +| createdAt | DATETIME | 否 | - | 创建时间 | +| updatedAt | DATETIME | 否 | - | 更新时间 | +| createdBy | UUID | 否 | - | 创建人 | + +**索引**: + +| 索引名 | 字段 | 用途 | +|--------|------|------| +| idx_failure_equipment | equipment_id | 按设备查询 | +| idx_failure_time | failure_time | 按时间查询 | +| idx_failure_project | project_id | 按项目查询 | +| idx_efh_project_time | project_id, failure_time | 项目+时间组合查询 | +| idx_efh_equipment_time | equipment_id, failure_time DESC | 设备+时间倒序查询 | + +#### 2.1.8 OwnershipEntity(归属主体表) + +**表名**: `asset_ownership_entity` +**实体类**: `com.ether.pms.asset.entity.OwnershipEntity` + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| entityType | VARCHAR(20) | 是 | - | 主体类型枚举(3种) | +| entityName | VARCHAR(100) | 是 | - | 主体名称 | +| entityCode | VARCHAR(50) | 否 | - | 主体编码 | +| contactPerson | VARCHAR(50) | 否 | - | 联系人 | +| contactPhone | VARCHAR(20) | 否 | - | 联系电话 | +| contactAddress | VARCHAR(255) | 否 | - | 联系地址 | +| businessLicense | VARCHAR(50) | 否 | - | 营业执照号 | +| legalRepresentative | VARCHAR(50) | 否 | - | 法人代表 | +| contractNo | VARCHAR(50) | 否 | - | 合同编号 | +| contractStartDate | DATE | 否 | - | 合同开始日期 | +| contractEndDate | DATE | 否 | - | 合同结束日期 | +| rentalFee | DECIMAL(12,2) | 否 | - | 租赁费用 | +| status | VARCHAR(20) | 否 | ACTIVE | 状态 | +| isDeleted | BOOLEAN | 否 | false | 逻辑删除 | +| createdAt | DATETIME | 否 | - | 创建时间 | +| updatedAt | DATETIME | 否 | - | 更新时间 | + +**索引**: + +| 索引名 | 字段 | 用途 | +|--------|------|------| +| idx_ownership_entity_type | entity_type | 按类型查询 | +| idx_ownership_entity_code | entity_code | 按编码查询 | + +### 2.2 枚举定义 + +#### EquipmentType(设备类型 -- 10种) + +| 枚举值 | 中文 | 对应扩展表 | 说明 | +|--------|------|------------|------| +| ELEVATOR | 电梯系统 | EquipmentElevator | 含载重/速度/注册号/检验证书/应急救援预案 | +| HVAC | 暖通空调 | EquipmentHvac | 含制冷/制热量/制冷剂/能效比(EER)/性能系数(COP) | +| FIRE_PROTECTION | 消防系统 | EquipmentFire | 含探测范围/分区/回路编号/联动控制/巡检周期 | +| ENERGY_METER | 能源计量 | EquipmentEnergy | 含表常数/精度等级/通讯方式/检定周期 | +| PLUMBING | 给排水系统 | -- | 使用主表通用字段 + attributes JSON | +| ELECTRICAL | 电气系统 | -- | 使用主表通用字段 + attributes JSON | +| SECURITY | 弱电系统 | -- | 使用主表通用字段 + attributes JSON | +| LANDSCAPE | 景观绿化 | -- | 使用主表通用字段 + attributes JSON | +| KITCHEN | 厨余设备 | -- | 使用主表通用字段 + attributes JSON | +| OTHER | 其他设备 | -- | 使用主表通用字段 + attributes JSON | + +#### SystemType(系统类型 -- 9种,商业地产8大系统+其他) + +| 枚举值 | 中文 | 说明 | +|--------|------|------| +| HVAC | 暖通空调 | 对应设备类型 HVAC | +| FIRE | 消防系统 | 对应设备类型 FIRE_PROTECTION | +| ELEVATOR | 电梯系统 | 对应设备类型 ELEVATOR | +| ELECTRICAL | 电气系统 | 对应设备类型 ELECTRICAL | +| PLUMBING | 给排水 | 对应设备类型 PLUMBING | +| BAS | 弱电智能化 | 对应设备类型 SECURITY | +| KITCHEN | 餐饮厨房 | 对应设备类型 KITCHEN | +| LANDSCAPE | 景观 | 对应设备类型 LANDSCAPE | +| OTHER | 其他 | 对应设备类型 OTHER | + +#### EquipmentStatus(设备状态 -- 4种) + +| 枚举值 | 中文 | 说明 | +|--------|------|------| +| ACTIVE | 在用 | 设备正常运行 | +| INACTIVE | 停用 | 设备暂停使用 | +| MAINTENANCE | 维保中 | 设备正在维保 | +| SCRAPPED | 已报废 | 设备已报废处理 | + +#### OwnershipType(归属类型 -- 4种) + +| 枚举值 | 中文 | 关联主体类型 | 说明 | +|--------|------|-------------|------| +| PROJECT | 项目自有 | 无需关联 | 项目自有的设备 | +| COMPANY | 公司统筹 | EntityType=COMPANY | 公司统一调配的设备 | +| OWNER | 业主自置 | EntityType=OWNER | 业主自行购置的设备 | +| RENTAL | 租赁设备 | EntityType=RENTAL_COMPANY | 租赁使用的设备 | + +#### FailureType(故障类型 -- 4种) + +| 枚举值 | 中文 | 说明 | +|--------|------|------| +| SUDDEN | 突发故障 | 非预期的设备故障 | +| SCHEDULED | 计划停机 | 计划内的设备停机 | +| WARNING | 预警 | 设备异常预警 | +| OTHER | 其他 | 其他类型故障 | + +#### FailureLevel(故障等级 -- 4级) + +| 枚举值 | 中文 | 典型场景 | 响应时间要求 | +|--------|------|----------|-------------| +| LOW | 轻微 | 指示灯故障、外观损伤 | 48小时内 | +| MEDIUM | 一般 | 单个传感器故障、局部渗漏 | 24小时内 | +| HIGH | 严重 | 主控制器故障、系统部分失效 | 4小时内 | +| CRITICAL | 危急 | 电梯困人、消防系统瘫痪 | 立即响应(30分钟内) | + +#### RepairResult(修复结果 -- 4种) + +| 枚举值 | 中文 | 说明 | +|--------|------|------| +| FIXED | 已修复 | 故障已完全修复 | +| PARTIAL_FIXED | 部分修复 | 故障部分修复,需后续跟进 | +| REPLACED | 更换新设备 | 设备整体更换 | +| PENDING | 待处理 | 故障尚未处理 | + +#### HealthLevel(健康等级 -- 5级) + +| 枚举值 | 中文 | 分数范围 | 前端颜色 | +|--------|------|----------|----------| +| EXCELLENT | 优秀 | [90, 100) | 绿色 | +| GOOD | 良好 | [75, 90) | 青色 | +| FAIR | 一般 | [60, 75) | 橙色 | +| POOR | 较差 | [40, 60) | 红色 | +| CRITICAL | 危急 | [0, 40) | 红色 | + +#### EntityType(归属主体类型 -- 3种) + +| 枚举值 | 中文 | 对应归属类型 | +|--------|------|-------------| +| COMPANY | 公司 | OwnershipType=COMPANY | +| OWNER | 业主 | OwnershipType=OWNER | +| RENTAL_COMPANY | 租赁公司 | OwnershipType=RENTAL | + +### 2.3 ER关系图 + +```mermaid +erDiagram + Equipment ||--o| EquipmentElevator : "1:1 equipmentId" + Equipment ||--o| EquipmentHvac : "1:1 equipmentId" + Equipment ||--o| EquipmentFire : "1:1 equipmentId" + Equipment ||--o| EquipmentEnergy : "1:1 equipmentId" + Equipment ||--o{ EquipmentHealthScore : "1:N equipmentId" + Equipment ||--o{ EquipmentFailureHistory : "1:N equipmentId" + Equipment }o--o| OwnershipEntity : "owningEntityId" + Equipment }o--o| SpaceNode : "spaceNodeId (module-mdm)" + + Equipment { + UUID id PK + UUID projectId FK + UUID spaceNodeId FK + String equipmentCode UK + String equipmentName + EquipmentType equipmentType + SystemType systemType + OwnershipType ownershipType + UUID owningEntityId FK + String owningEntityName + EquipmentStatus status + } + + EquipmentElevator { + UUID id PK + UUID equipmentId UK_FK + String elevatorType + Integer loadCapacity + BigDecimal speed + String registrationNo + String rescuePlan + } + + EquipmentHvac { + UUID id PK + UUID equipmentId UK_FK + String hvacType + BigDecimal coolingCapacity + BigDecimal heatingCapacity + BigDecimal energyEfficiencyRatio + } + + EquipmentFire { + UUID id PK + UUID equipmentId UK_FK + String fireEquipmentType + BigDecimal detectionRange + Boolean linkageEnabled + String zoneNumber + } + + EquipmentEnergy { + UUID id PK + UUID equipmentId UK_FK + String meterType + String energyType + BigDecimal meterConstant + String accuracyClass + } + + EquipmentHealthScore { + UUID id PK + UUID equipmentId FK + BigDecimal healthScore + BigDecimal failureDeduction + BigDecimal maintenanceDeduction + BigDecimal ageDeduction + HealthLevel healthLevel + } + + EquipmentFailureHistory { + UUID id PK + UUID equipmentId FK + LocalDateTime failureTime + FailureType failureType + FailureLevel failureLevel + RepairResult repairResult + } + + OwnershipEntity { + UUID id PK + EntityType entityType + String entityName + String entityCode + String contactPerson + } +``` + +--- + +## 三、API设计 + +### 3.1 EquipmentController + +**基础路径**: `/api/asset/equipment` +**控制器类**: `com.ether.pms.asset.controller.EquipmentController` + +#### 设备主表 CRUD + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| POST | `/` | 创建设备 | Equipment | ApiResponse\ | ASSET-001 | +| GET | `/{id}` | 获取设备详情 | - | ApiResponse\ | ASSET-001 | +| PUT | `/{id}` | 更新设备 | Equipment | ApiResponse\ | ASSET-001 | +| DELETE | `/{id}` | 删除设备(逻辑删除) | - | ApiResponse\ | ASSET-001 | +| POST | `/batch-delete` | 批量删除 | List\ | ApiResponse\ | ASSET-002 | + +#### 设备导入导出 + +| 方法 | 路径 | 说明 | 请求 | 响应 | 功能ID | +|------|------|------|------|------|--------| +| POST | `/import` | Excel导入 | MultipartFile + projectId | ApiResponse\\> | ASSET-014 | +| GET | `/export` | Excel导出 | projectId | ResponseEntity\ | ASSET-015 | + +**导入返回结构**: +```json +{ + "successCount": 95, + "failCount": 5, + "failDetails": [ + {"row": 3, "reason": "设备编码已存在"}, + {"row": 7, "reason": "设备类型无效"} + ] +} +``` + +#### 设备查询 + +| 方法 | 路径 | 说明 | 参数 | 响应 | 功能ID | +|------|------|------|------|------|--------| +| GET | `/by-project/{projectId}` | 按项目查询 | - | ApiResponse\\> | ASSET-003 | +| GET | `/by-space/{spaceNodeId}` | 按空间查询 | - | ApiResponse\\> | ASSET-004 | +| GET | `/by-type` | 按类型查询 | type: EquipmentType | ApiResponse\\> | ASSET-005 | +| GET | `/by-ownership` | 按归属查询 | ownership: OwnershipType | ApiResponse\\> | ASSET-006 | + +#### 设备统计 + +| 方法 | 路径 | 说明 | 参数 | 响应 | 功能ID | +|------|------|------|------|------|--------| +| GET | `/stats/by-type/{projectId}` | 按类型统计 | - | ApiResponse\\> | ASSET-007 | +| GET | `/stats/by-ownership/{projectId}` | 按归属统计 | - | ApiResponse\\> | ASSET-008 | +| GET | `/stats/count/{projectId}` | 项目设备总数 | - | ApiResponse\ | ASSET-009 | + +#### 电梯扩展 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| GET | `/{id}/elevator` | 获取电梯扩展 | - | ApiResponse\ | ASSET-010 | +| PUT | `/{id}/elevator` | 更新电梯扩展 | EquipmentElevator | ApiResponse\ | ASSET-010 | + +#### 暖通扩展 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| GET | `/{id}/hvac` | 获取暖通扩展 | - | ApiResponse\ | ASSET-011 | +| PUT | `/{id}/hvac` | 更新暖通扩展 | EquipmentHvac | ApiResponse\ | ASSET-011 | + +#### 能源计量扩展 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| GET | `/{id}/energy` | 获取能源扩展 | - | ApiResponse\ | ASSET-013 | +| PUT | `/{id}/energy` | 更新能源扩展 | EquipmentEnergy | ApiResponse\ | ASSET-013 | + +#### 消防扩展 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| GET | `/{id}/fire` | 获取消防扩展 | - | ApiResponse\ | ASSET-012 | +| PUT | `/{id}/fire` | 更新消防扩展 | EquipmentFire | ApiResponse\ | ASSET-012 | + +### 3.2 EquipmentHealthController + +**基础路径**: `/api/asset/equipment-health` +**控制器类**: `com.ether.pms.asset.controller.EquipmentHealthController` + +| 方法 | 路径 | 说明 | 请求体/参数 | 响应 | 功能ID | +|------|------|------|-------------|------|--------| +| GET | `/{equipmentId}` | 获取最新健康度 | - | ApiResponse\ | ASSET-016 | +| GET | `/{equipmentId}/history` | 健康度历史 | - | ApiResponse\\> | ASSET-017 | +| POST | `/calculate` | 计算健康度 | { equipmentId: UUID } | ApiResponse\ | ASSET-016 | +| POST | `/failure-history` | 记录故障 | EquipmentFailureHistory | ApiResponse\ | ASSET-020 | +| GET | `/failure-history/{equipmentId}` | 获取故障历史 | - | ApiResponse\\> | ASSET-021 | +| GET | `/mtbf/{equipmentId}` | 计算MTBF | days: Integer (默认30) | ApiResponse\ | ASSET-018 | +| GET | `/mttr/{equipmentId}` | 计算MTTR | days: Integer (默认30) | ApiResponse\ | ASSET-019 | + +**请求体定义**: + +```java +// 计算健康度请求 +CalculateHealthRequest { + @NotNull UUID equipmentId; +} + +// MTBF响应 +MTBFResponse { + UUID equipmentId; + Integer days; + BigDecimal mtbfHours; +} + +// MTTR响应 +MTTRResponse { + UUID equipmentId; + Integer days; + BigDecimal mttrHours; +} +``` + +> 注意: 健康评分相关接口返回时附带 "[Beta] 健康评分数据准确性待验证" 提示信息。 + +### 3.3 OwnershipEntityController + +**基础路径**: `/api/asset/ownership-entity` +**控制器类**: `com.ether.pms.asset.controller.OwnershipEntityController` + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| POST | `/` | 创建归属主体 | OwnershipEntity | ApiResponse\ | ASSET-022 | +| GET | `/{id}` | 获取归属主体 | - | ApiResponse\ | ASSET-022 | +| PUT | `/{id}` | 更新归属主体 | OwnershipEntity | ApiResponse\ | ASSET-022 | +| DELETE | `/{id}` | 删除归属主体(逻辑删除) | - | ApiResponse\ | ASSET-022 | +| GET | `/by-type` | 按类型查询 | type: EntityType | ApiResponse\\> | ASSET-023 | +| GET | `/` | 获取全部(未删除) | - | ApiResponse\\> | ASSET-022 | + +> 注意: OwnershipEntityController 直接注入 Repository,未经过 Service 层,属于简化实现,后续应重构为标准三层架构。 + +--- + +## 四、业务规则 + +### 4.1 设备分类体系(EquipmentType x SystemType矩阵) + +设备分类采用双维度体系:设备类型(EquipmentType)描述设备专业属性,系统类型(SystemType)描述设备所属的建筑机电系统。 + +| EquipmentType \ SystemType | HVAC | FIRE | ELEVATOR | ELECTRICAL | PLUMBING | BAS | KITCHEN | LANDSCAPE | OTHER | +|---------------------------|------|------|----------|------------|----------|-----|---------|-----------|-------| +| ELEVATOR | | | V | | | | | | | +| HVAC | V | | | | | | | | | +| FIRE_PROTECTION | | V | | | | | | | | +| ENERGY_METER | | | | V | | | | | | +| PLUMBING | | | | | V | | | | | +| ELECTRICAL | | | | V | | | | | | +| SECURITY | | | | | | V | | | | +| LANDSCAPE | | | | | | | | V | | +| KITCHEN | | | | | | | V | | | +| OTHER | | | | | | | | | V | + +**扩展策略**: +- 4种有扩展表的设备类型(ELEVATOR/HVAC/FIRE_PROTECTION/ENERGY_METER)通过扩展表存储专业参数 +- 6种无扩展表的设备类型通过主表的 `attributes` JSON 字段存储额外属性 +- 设备类型与系统类型存在对应关系,但非严格1:1映射 + +### 4.2 主表+扩展表模式 + +**设计原则**: +- 主表(Equipment)存储所有设备类型的公共字段(约40个) +- 扩展表通过 `equipment_id`(UNIQUE约束)一对一关联主表 +- 扩展表仅存储专业特有字段 +- 扩展表的读写通过主设备ID路径:`GET /{id}/elevator`、`PUT /{id}/elevator` + +**创建设备时扩展表处理规则**: + +| 操作 | 规则 | +|------|------| +| 创建设备 | 按equipmentType自动创建对应扩展表记录(仅4种有扩展表的类型) | +| 更新设备类型 | 不允许修改equipmentType(避免扩展表数据不一致) | +| 删除设备 | 级联删除对应扩展表记录 | +| 读取扩展表 | 通过 `GET /{id}/{type}` 获取,不存在返回null | +| 更新扩展表 | 采用saveOrUpdate模式:存在则更新,不存在则新建 | + +**扩展表服务接口模式**(以电梯为例): +```java +public interface EquipmentElevatorService { + Optional getByEquipmentId(UUID equipmentId); + EquipmentElevator saveOrUpdate(EquipmentElevator elevator); +} +``` + +### 4.3 归属管理 + +**4种归属类型**: + +| 归属类型 | 说明 | 关联主体 | 维保责任方 | +|----------|------|----------|-----------| +| PROJECT | 项目自有 | 无需关联外部主体 | 项目物业团队 | +| COMPANY | 公司统筹 | 可关联 EntityType=COMPANY 的主体 | 公司指定维保商 | +| OWNER | 业主自置 | 可关联 EntityType=OWNER 的主体 | 业主自行负责或委托 | +| RENTAL | 租赁设备 | 可关联 EntityType=RENTAL_COMPANY 的主体 | 租赁合同约定 | + +**归属主体与设备的关联**: +- Equipment 表同时存储 `owningEntityId` 和 `owningEntityName`,避免频繁关联查询 +- 归属类型与主体类型存在对应关系:PROJECT无需关联主体,COMPANY对应公司主体,OWNER对应业主主体,RENTAL对应租赁公司主体 +- 归属主体变更时需同步更新 `owningEntityName` 冗余字段 + +**维保责任划分**: +- PROJECT/COMPANY 归属的设备:由物业方统一安排维保 +- OWNER 归属的设备:业主自行负责维保,物业方监督 +- RENTAL 归属的设备:按租赁合同约定维保责任方 + +### 4.4 健康评分算法[Beta] + +#### 评分公式 + +``` +健康度 = 基础分(100) - 故障扣分 - 维保扣分 - 年龄扣分 +``` + +即: + +``` +H = 100 - D_failure - D_maintenance - D_age +``` + +其中: +- `H` 为健康度评分,范围 [0, 100] +- `D_failure` 为故障扣分 +- `D_maintenance` 为维保扣分 +- `D_age` 为年龄扣分 + +#### 三维扣分算法 + +**1. 故障扣分(D_failure)** + +``` +D_failure = N_30d x P_failure +``` + +| 参数 | 值 | 说明 | +|------|-----|------| +| N_30d | 近30天故障次数 | 从 EquipmentFailureHistory 统计 | +| P_failure | 5 | 每次故障扣5分(FAILURE_DEDUCTION_PER_COUNT) | +| 上限 | 无 | 故障次数无上限,扣分可超过100 | + +**2. 维保扣分(D_maintenance)** + +``` +D_maintenance = (1 - R_completion) x F_maintenance +``` + +| 参数 | 值 | 说明 | +|------|-----|------| +| R_completion | 维保完成率 | 范围 [0, 1],当前固定为1.0(TODO) | +| F_maintenance | 20 | 维保扣分系数(MAINTENANCE_DEDUCTION_FACTOR) | +| 上限 | 20分 | 最大维保扣分 | + +> 当前实现中维保完成率固定为1.0,维保扣分始终为0,标记为TODO待实现。 + +**3. 年龄扣分(D_age)** + +``` +D_age = min(Y_equipment x P_age, M_age) +``` + +| 参数 | 值 | 说明 | +|------|-----|------| +| Y_equipment | 设备年龄(年) | 计算方式见下方 | +| P_age | 2 | 每年扣2分(AGE_DEDUCTION_PER_YEAR) | +| M_age | 10 | 最大年龄扣分(MAX_AGE_DEDUCTION) | + +**设备年龄计算**: + +``` +Y_equipment = years + months / 12 +``` + +- 优先使用 `installationDate`(安装日期) +- 若无安装日期,使用 `createdAt` 的日期部分 +- 均无则返回 0 + +**最终评分**: + +``` +H = max(0, 100 - D_failure - D_maintenance - D_age) +``` + +评分精度:保留2位小数,四舍五入。 + +#### MTBF计算(平均故障间隔时间) + +``` +MTBF = T_operating / N_failure +``` + +| 参数 | 说明 | +|------|------| +| T_operating | 运行时间(小时)= 最后故障时间 - 首次故障时间 | +| N_failure | 故障次数 | + +**计算规则**: +- 计算周期:默认30天,可通过 `days` 参数自定义 +- 若仅1次故障:T_operating 使用 `days x 24` 小时 +- 若 T_operating <= 0:使用 `days x 24` 小时 +- 无故障时返回 0 +- 精度:保留2位小数 + +#### MTTR计算(平均修复时间) + +``` +MTTR = T_repair_total / N_repair +``` + +| 参数 | 说明 | +|------|------| +| T_repair_total | 总修复时间(小时)= SUM(repairDurationHours) | +| N_repair | 修复次数 | + +**计算规则**: +- 仅统计已完成修复的故障记录(repairDurationHours 不为空) +- 无修复记录时返回 0 +- 精度:保留2位小数 + +#### 5级等级划分 + +```java +public static HealthLevel fromScore(BigDecimal score) { + if (score == null) return CRITICAL; + double s = score.doubleValue(); + if (s >= 90 && s < 100) return EXCELLENT; // 优秀 + if (s >= 75 && s < 90) return GOOD; // 良好 + if (s >= 60 && s < 75) return FAIR; // 一般 + if (s >= 40 && s < 60) return POOR; // 较差 + return CRITICAL; // 危急 [0, 40) +} +``` + +> 注意:score=100 时返回 CRITICAL,因为所有区间都是左闭右开 [min, max),100 不在任何区间内。这是当前实现的已知问题。 + +### 4.5 故障等级体系 + +#### 4级故障等级 + +| 等级 | 中文 | 典型场景 | 建议响应时间 | 升级规则 | +|------|------|----------|-------------|---------| +| LOW | 轻微 | 指示灯故障、外观损伤 | 48小时内 | 超过48小时未处理升级为MEDIUM | +| MEDIUM | 一般 | 单个传感器故障、局部渗漏 | 24小时内 | 超过24小时未处理升级为HIGH | +| HIGH | 严重 | 主控制器故障、系统部分失效 | 4小时内 | 超过4小时未处理升级为CRITICAL | +| CRITICAL | 危急 | 电梯困人、消防系统瘫痪 | 30分钟内 | 立即通知项目经理 | + +#### 故障记录自动计算 + +记录故障时自动计算以下字段: + +``` +repairDurationHours = (repairEndTime - repairStartTime) / 60 (分钟转小时) +downtimeHours = (repairStartTime - failureTime) / 60 (故障到开始修复的时间) +``` + +**计算条件**: +- repairDurationHours: 仅当 repairStartTime 和 repairEndTime 均不为空时计算 +- downtimeHours: 仅当 repairStartTime > failureTime 时计算 + +### 4.6 特种设备管理 + +**特种设备识别规则**: +- Equipment 表中 `specialEquipmentType` 不为空时,该设备为特种设备 +- 主要涉及:电梯(ELEVATOR)、消防(FIRE_PROTECTION)、压力容器等 + +**年检提醒机制**: +- `inspectionCycle`:年检周期(月),如12表示每年年检一次 +- `nextInspectionDate`:下次年检日期 +- `lastInspectionDate`:上次年检日期 +- `lastInspectionResult`:上次年检结果 + +**证书管理**: +- `specialEquipmentCert`:特种设备证书编号 +- 电梯扩展表:`inspectionCertificate`(检验证书)、`registrationNo`(注册登记号) +- 能源计量扩展表:`verificationCertificate`(检定证书)、`nextVerificationDate`(下次检定日期) + +**法规合规**: +- 电梯:需按《特种设备安全法》定期检验,注册登记号唯一 +- 消防:需按《消防法》定期巡检,联动系统需定期测试 +- 能源计量:需按《计量法》定期检定,精度等级需符合标准 + +> 当前实现中无后端定时提醒功能,仅前端展示下次年检日期。 + +### 4.7 设备导入导出 + +**Excel导入校验规则**: + +| 校验项 | 规则 | 错误码 | +|--------|------|--------| +| 文件为空 | file.isEmpty() = true | FILE_001 | +| 文件类型 | 仅支持 .xlsx / .xls | FILE_002 | +| Content-Type | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 或 application/vnd.ms-excel | FILE_002 | +| 文件扩展名 | .xlsx 或 .xls | FILE_002 | +| 文件大小 | 最大 10MB | FILE_003 | +| 行数限制 | 最大 1000 行 | FILE_004 | +| 文件解析 | Workbook可正常打开 | FILE_001 | + +**Excel导出规则**: +- 按项目导出所有未删除设备 +- 输出文件名:`设备列表.xlsx` +- Content-Type:`application/octet-stream` + +--- + +## 五、执行约束 + +### 5.1 设备编码唯一性 + +- `equipmentCode` 字段在数据库层设置 UNIQUE 约束 +- 索引 `idx_equipment_code` 确保唯一性 +- 创建设备时若编码已存在,数据库将抛出唯一约束异常 +- 导入时编码重复将记入 failDetails + +### 5.2 扩展表与主表1:1约束 + +- 扩展表的 `equipment_id` 字段设置 UNIQUE 约束 +- 每个设备最多只能有一条对应类型的扩展表记录 +- 更新扩展表采用 saveOrUpdate 模式:先查询是否存在,存在则更新,不存在则新建 +- 扩展表类型必须与主表 equipmentType 匹配(如不能给电梯设备创建暖通扩展表) + +### 5.3 健康评分范围约束 + +- healthScore 范围为 [0, 100] +- 计算结果低于0时强制设为0 +- 精度:DECIMAL(5,2),保留2位小数 +- HealthLevel.fromScore(null) 返回 CRITICAL + +### 5.4 故障时间约束 + +- `repairStartTime <= repairEndTime`:修复开始时间不能晚于修复结束时间 +- `failureTime <= repairStartTime`:故障时间不能晚于修复开始时间 +- 当时间约束不满足时,对应自动计算字段(repairDurationHours/downtimeHours)不计算 + +--- + +## 六、权限控制 + +| API路径 | 操作 | 所需权限 | 说明 | +|---------|------|----------|------| +| POST /api/asset/equipment | 创建设备 | asset:equipment:create | 需项目级权限 | +| GET /api/asset/equipment/{id} | 查看设备 | asset:equipment:read | 需项目级权限 | +| PUT /api/asset/equipment/{id} | 更新设备 | asset:equipment:update | 需项目级权限 | +| DELETE /api/asset/equipment/{id} | 删除设备 | asset:equipment:delete | 需项目级权限 | +| POST /api/asset/equipment/batch-delete | 批量删除 | asset:equipment:delete | 需项目级权限 | +| POST /api/asset/equipment/import | 导入设备 | asset:equipment:import | 需项目级权限 | +| GET /api/asset/equipment/export | 导出设备 | asset:equipment:export | 需项目级权限 | +| PUT /api/asset/equipment/{id}/elevator | 更新电梯扩展 | asset:equipment:update | 同设备更新权限 | +| PUT /api/asset/equipment/{id}/hvac | 更新暖通扩展 | asset:equipment:update | 同设备更新权限 | +| PUT /api/asset/equipment/{id}/fire | 更新消防扩展 | asset:equipment:update | 同设备更新权限 | +| PUT /api/asset/equipment/{id}/energy | 更新能源扩展 | asset:equipment:update | 同设备更新权限 | +| POST /api/asset/equipment-health/calculate | 计算健康度 | asset:health:calculate | 需项目级权限 | +| POST /api/asset/equipment-health/failure-history | 记录故障 | asset:failure:create | 需项目级权限 | +| POST /api/asset/ownership-entity | 创建归属主体 | asset:ownership:create | 需系统级权限 | +| PUT /api/asset/ownership-entity/{id} | 更新归属主体 | asset:ownership:update | 需系统级权限 | +| DELETE /api/asset/ownership-entity/{id} | 删除归属主体 | asset:ownership:delete | 需系统级权限 | + +> 当前实现中权限控制尚未集成Spring Security,以上为设计预期权限矩阵。 + +--- + +## 七、例外情况处理 + +| 例外场景 | 触发条件 | 处理方式 | 错误码 | +|----------|----------|----------|--------| +| 设备编码已存在 | 创建/导入设备时equipmentCode重复 | 抛出数据库唯一约束异常,导入时记入failDetails | DB约束 | +| 扩展表类型不匹配 | 给非电梯设备创建电梯扩展表 | 允许创建(当前无校验),建议增加类型匹配校验 | - | +| 健康评分计算数据不足 | 设备无故障历史记录 | MTBF/MTTR返回0,维保完成率默认1.0 | - | +| 故障记录关联设备不存在 | equipmentId指向不存在的设备 | 抛出BusinessException("设备不存在") | 6001 | +| 无健康度记录 | 查询设备从未计算过健康度 | 抛出BusinessException("没有健康度记录") | 6003 | +| 导入文件格式错误 | 文件非xlsx/xls格式 | 抛出BusinessException("不支持的文件类型") | FILE_002 | +| 导入文件过大 | 文件超过10MB | 抛出BusinessException("文件大小不能超过10MB") | FILE_003 | +| 导入行数超限 | Excel行数超过1000 | 抛出BusinessException("Excel行数不能超过1000行") | FILE_004 | +| 归属主体不存在 | 更新/删除时ID不存在 | 抛出RuntimeException("归属主体不存在") | - | +| 设备不存在 | 操作的设备ID不存在或已删除 | 抛出BusinessException("设备不存在") | 6001 | +| 导入文件解析失败 | Excel文件损坏 | 抛出BusinessException("文件解析失败") | FILE_001 | + +--- + +**附录:已知代码问题** + +| 问题 | 位置 | 影响 | 建议 | +|------|------|------|------| +| HealthLevel.fromScore(100) 返回 CRITICAL | EquipmentHealthScore.HealthLevel | 评分100分的设备被判定为危急 | 修改区间为 [90, 100] 或添加 score==100 特判 | +| 维保完成率固定为1.0 | EquipmentHealthServiceImpl | 维保扣分始终为0 | 对接ops模块查询工单数据计算完成率 | +| OwnershipEntityController绕过Service层 | OwnershipEntityController | 不符合分层架构,缺少业务校验 | 重构为标准三层架构 | +| 前端SystemType与后端不一致 | equipment.ts vs SystemType枚举 | 前端FIRE_PROTECTION vs 后端FIRE | 统一前后端枚举值 | +| 查询接口无分页 | by-project/by-space等 | 数据量大时可能OOM | 改为分页查询 | diff --git a/02-DESIGN/detail/DETAIL-AUTH.md b/02-DESIGN/detail/DETAIL-AUTH.md new file mode 100644 index 0000000..73d9dcb --- /dev/null +++ b/02-DESIGN/detail/DETAIL-AUTH.md @@ -0,0 +1,1058 @@ +# 身份与权限域 - 详细设计 + +**文档版本**: v1.0 +**生成日期**: 2026-05-18 +**数据来源**: module-auth 实际代码 + REVERSE-AUTH.md +**对照需求**: 05-AUTH.md + +--- + +## 一、功能点清单 + +| 功能ID | 功能名称 | 优先级 | 实现状态 | 对应需求ID | +|--------|----------|--------|----------|------------| +| AUTH-001 | 用户登录 | P0 | 已实现 | 05-AUTH-001 | +| AUTH-002 | 用户登出 | P0 | 已实现 | 05-AUTH-002 | +| AUTH-003 | 获取当前用户 | P0 | 已实现 | 05-AUTH-003 | +| AUTH-004 | Token刷新 | P0 | 已实现 | 05-AUTH-004 | +| AUTH-005 | 用户CRUD | P0 | 已实现 | 05-AUTH-010 | +| AUTH-006 | 修改密码 | P0 | 已实现 | 05-AUTH-011 | +| AUTH-007 | 分配用户角色 | P0 | 已实现 | 05-AUTH-020 | +| AUTH-008 | 用户项目关联 | P1 | 已实现 | 05-AUTH-030 | +| AUTH-009 | 企业员工管理 | P1 | 已实现 | 05-AUTH-012 | +| AUTH-010 | 角色CRUD | P0 | 已实现 | 05-AUTH-020 | +| AUTH-011 | 角色权限分配 | P0 | 已实现 | 05-AUTH-021 | +| AUTH-012 | 按项目查询角色 | P1 | 已实现 | 05-AUTH-022 | +| AUTH-013 | 权限CRUD | P0 | 已实现 | 05-AUTH-025 | +| AUTH-014 | 按类型查询权限 | P1 | 已实现 | 05-AUTH-026 | +| AUTH-015 | 菜单权限查询 | P1 | 已实现 | 05-AUTH-027 | +| AUTH-016 | 部门树管理 | P1 | 已实现 | 05-AUTH-040 | +| AUTH-017 | 部门成员查询 | P1 | 已实现 | 05-AUTH-041 | +| AUTH-018 | 项目成员管理 | P0 | 已实现 | 05-AUTH-030 | +| AUTH-019 | 登录失败锁定 | P0 | 已实现 | 05-AUTH-005 | +| AUTH-020 | 密码强度校验 | P0 | 已实现 | 05-AUTH-006 | +| AUTH-021 | 弱密码检测 | P1 | 已实现 | 05-AUTH-007 | +| AUTH-022 | 审计日志查询 | P1 | 已实现 | 05-AUTH-050 | +| AUTH-023 | 审计日志统计 | P2 | 已实现 | 05-AUTH-051 | +| AUTH-024 | 系统配置管理 | P1 | 已实现 | 05-AUTH-060 | +| AUTH-025 | 数据访问授权 | P2 | 已实现(@Deprecated) | 05-AUTH-035 | +| AUTH-026 | 住户认证流程 | P1 | 已实现 | 05-AUTH-015 | +| AUTH-027 | 住户房屋绑定 | P1 | 已实现 | 05-AUTH-016 | +| AUTH-028 | 项目员工角色分配 | P1 | 已实现 | 05-AUTH-031 | + +--- + +## 二、数据结构设计 + +### 2.1 实体定义 + +#### 2.1.1 User(用户) + +**表名**: `auth_user` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| id | UUID | PK, 自动生成 | 用户唯一标识符 | +| username | VARCHAR(50) | NOT NULL, UNIQUE, 正则`^[a-zA-Z0-9_]+$`, 3-50位 | 登录用户名 | +| password | VARCHAR(255) | NOT NULL, @JsonIgnore | BCrypt加密密码 | +| salt | VARCHAR | @JsonIgnore | 密码盐值(BCrypt模式下冗余) | +| realName | VARCHAR(50) | | 真实姓名 | +| phone | VARCHAR(20) | 正则`^1[3-9]\d{9}$` | 手机号码 | +| email | VARCHAR(100) | @Email | 电子邮箱 | +| avatar | VARCHAR | | 头像URL | +| status | VARCHAR(20) | NOT NULL, 默认ACTIVE, @Enumerated(STRING) | 用户状态枚举 | +| userType | VARCHAR(20) | NOT NULL | 用户类型:ENTERPRISE/PROJECT_STAFF/RESIDENT/CUSTOMER | +| deptId | UUID | | 所属部门ID | +| lastLoginTime | LocalDateTime | | 最后登录时间 | +| lastLoginIp | VARCHAR | | 最后登录IP | +| createdAt | LocalDateTime | NOT NULL, @PrePersist自动填充 | 创建时间 | +| updatedAt | LocalDateTime | NOT NULL, @PreUpdate自动填充 | 更新时间 | +| createdBy | UUID | | 创建人ID | + +**索引**: +- `auth_user_username_key` → username (UNIQUE) + +**关系**: +- `roles` → M2M → Role(通过 `auth_user_role` 中间表,LAZY加载) + +**中间表: auth_user_role** + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| user_id | UUID | FK → auth_user, fk_auth_user_role_user | 用户ID | +| role_id | UUID | FK → auth_role, fk_auth_user_role_role | 角色ID | + +--- + +#### 2.1.2 Role(角色) + +**表名**: `auth_role` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| id | UUID | PK, 自动生成 | 角色唯一标识符 | +| code | VARCHAR(50) | NOT NULL, UNIQUE, 正则`^[a-zA-Z0-9_]+$`, 2-50位 | 角色代码 | +| name | VARCHAR(50) | NOT NULL, 2-50位 | 角色名称 | +| description | VARCHAR(200) | | 角色描述 | +| type | VARCHAR(20) | NOT NULL, @Enumerated(STRING) | 角色类型枚举 | +| dataScope | VARCHAR(20) | 默认SELF, @Enumerated(STRING) | 数据范围枚举 | +| projectId | UUID | columnDefinition=uuid | 所属项目ID(NULL=系统级) | +| status | VARCHAR(20) | NOT NULL, 默认ENABLED, @Enumerated(STRING) | 角色状态枚举 | +| createdAt | LocalDateTime | NOT NULL, @PrePersist自动填充 | 创建时间 | +| updatedAt | LocalDateTime | NOT NULL, @PreUpdate自动填充 | 更新时间 | + +**索引**: +- `auth_role_code_key` → code (UNIQUE) + +**关系**: +- `permissions` → M2M → Permission(通过 `auth_role_permission` 中间表,LAZY加载) + +**中间表: auth_role_permission** + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| role_id | UUID | FK → auth_role, fk_auth_role_permission_role | 角色ID | +| permission_id | UUID | FK → auth_permission, fk_auth_role_permission_permission | 权限ID | + +--- + +#### 2.1.3 Permission(权限) + +**表名**: `auth_permission` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| id | UUID | PK, 自动生成 | 权限唯一标识符 | +| code | VARCHAR(100) | NOT NULL, UNIQUE, 正则`^[a-zA-Z0-9_:]+$`, 2-100位 | 权限代码,格式`module:resource:action` | +| name | VARCHAR(100) | NOT NULL, 2-100位 | 权限名称 | +| type | VARCHAR(20) | | 权限类型:MENU/BUTTON/API | +| resource | VARCHAR(50) | | 资源路径(API类型) | +| method | VARCHAR(50) | | HTTP方法(API类型) | +| description | VARCHAR(200) | | 权限描述 | +| parentCode | VARCHAR(50) | | 父权限代码(树形结构) | +| path | VARCHAR(200) | | 路由路径(菜单类型) | +| component | VARCHAR(200) | | 组件路径(菜单类型) | +| icon | VARCHAR(100) | | 图标名称(菜单类型) | +| sortOrder | Integer | | 排序序号 | +| visible | Boolean | NOT NULL, 默认true | 是否可见 | +| createdAt | LocalDateTime | @PrePersist自动填充 | 创建时间 | +| updatedAt | LocalDateTime | @PreUpdate自动填充 | 更新时间 | + +**索引**: +- `auth_permission_code_key` → code (UNIQUE) + +**关系**: +- `roles` → M2M → Role(通过 `auth_role_permission` 中间表,LAZY加载,@JsonIgnoreProperties避免循环序列化) + +--- + +#### 2.1.4 Dept(部门) + +**表名**: `dept` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| id | UUID | PK, 自动生成 | 部门唯一标识符 | +| parentId | UUID | | 上级部门ID(NULL=顶级) | +| deptName | VARCHAR(100) | NOT NULL | 部门名称 | +| deptType | VARCHAR(20) | 默认"ADMIN" | 部门类型 | +| leaderId | UUID | | 部门负责人ID | +| sortOrder | Integer | | 排序序号 | +| status | VARCHAR(20) | 默认"ACTIVE" | 状态:ACTIVE/DISABLED | +| createdAt | LocalDateTime | @PrePersist自动填充 | 创建时间 | +| updatedAt | LocalDateTime | @PreUpdate自动填充 | 更新时间 | +| createdBy | UUID | column: created_by | 创建人ID | +| updatedBy | UUID | column: updated_by | 更新人ID | + +**关系**: +- 自引用树形结构:parentId → Dept.id + +--- + +#### 2.1.5 AuditLog(审计日志) + +**表名**: `sys_audit_log` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| id | UUID | PK, 自动生成 | 日志ID | +| userId | UUID | column: user_id | 操作用户ID | +| username | VARCHAR(64) | NOT NULL | 操作用户名 | +| operation | VARCHAR(128) | NOT NULL | 操作描述 | +| module | VARCHAR(64) | NOT NULL | 模块标识 | +| action | VARCHAR(64) | NOT NULL, @Enumerated(STRING) | 操作类型枚举 | +| targetType | VARCHAR(64) | | 目标类型 | +| targetId | VARCHAR(64) | | 目标ID | +| content | VARCHAR(2000) | | 操作内容 | +| params | LONGVARCHAR(5000) | | 请求参数 | +| result | LONGVARCHAR(5000) | | 操作结果 | +| ipAddress | VARCHAR(64) | column: ip_address | IP地址 | +| userAgent | VARCHAR(512) | | 用户代理 | +| requestUrl | VARCHAR(512) | column: request_url | 请求URL | +| requestMethod | VARCHAR(16) | column: request_method | 请求方法 | +| executionTimeMs | Integer | column: execution_time_ms | 执行耗时(ms) | +| status | VARCHAR(16) | 默认SUCCESS, @Enumerated(STRING) | 状态枚举 | +| errorMsg | VARCHAR(2000) | column: error_msg | 错误信息 | +| createdAt | LocalDateTime | NOT NULL, @CreationTimestamp, 不可更新 | 创建时间 | +| tenantId | UUID | column: tenant_id | 租户ID | + +**索引**: +- `idx_audit_log_created_at` → createdAt +- `idx_audit_log_user_id` → userId +- `idx_audit_log_module` → module +- `idx_audit_log_action` → action +- `idx_al_user_createdat` → userId, createdAt DESC + +--- + +#### 2.1.6 EnterpriseUser(企业员工) + +**表名**: `enterprise_user` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| userId | UUID | PK, FK → auth_user | 用户ID(共享主键) | +| employeeNo | VARCHAR(50) | | 员工工号 | +| deptId | UUID | | 部门ID | +| position | VARCHAR(50) | | 职位 | +| entryDate | LocalDate | | 入职日期 | +| userCategory | VARCHAR(50) | | 员工类别:ENTERPRISE/MANAGEMENT | + +**关系**: +- `user` → @OneToOne → User(@MapsId共享主键) + +--- + +#### 2.1.7 ProjectStaff(项目员工) + +**表名**: `project_staff` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| id | UUID | PK, 自动生成 | 主键ID | +| userId | UUID | FK → auth_user, insertable=false, updatable=false | 用户ID | +| projectId | UUID | column: project_id | 所属项目ID | +| deptId | UUID | column: dept_id | 所属部门ID | +| staffType | VARCHAR(50) | | 员工类型:SECURITY/CLEANING/GARDEN/MAINTENANCE/CUSTOMER_SERVICE/GENERAL | +| shiftType | VARCHAR(20) | | 班次类型:DAY/NIGHT/ROTATION | +| leaderId | UUID | | 班组长ID | +| assignmentStatus | VARCHAR(20) | | 在岗状态:ASSIGNED/ON_LEAVE/TRANSFERRED | +| createdAt | LocalDateTime | column: created_at, 不可更新 | 创建时间 | +| updatedAt | LocalDateTime | column: updated_at | 更新时间 | + +**关系**: +- `user` → @OneToOne → User(FK: fk_project_staff_user) +- `staffRoles` → @OneToMany → ProjectStaffRole(mappedBy="staff", CASCADE=ALL, orphanRemoval=true) + +--- + +#### 2.1.8 ProjectStaffRole(项目员工角色) + +**表名**: `project_staff_role` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| id | UUID | PK, 自动生成 | 主键ID | +| staff_id | UUID | FK → project_staff, NOT NULL | 关联项目员工 | +| role_id | UUID | FK → auth_role, NOT NULL | 关联角色 | +| createdAt | LocalDateTime | column: created_at, @PrePersist自动填充 | 创建时间 | + +**关系**: +- `staff` → @ManyToOne(LAZY) → ProjectStaff +- `role` → @ManyToOne(EAGER) → Role + +--- + +#### 2.1.9 Resident(住户) + +**表名**: `resident` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| userId | UUID | PK, FK → auth_user | 用户ID(共享主键) | +| idCard | VARCHAR(18) | @JsonIgnore | 身份证号码 | +| residentType | VARCHAR(20) | | 住户类型:OWNER/FAMILY/TENANT | +| verificationStatus | VARCHAR(20) | | 认证状态:UNVERIFIED/PENDING/VERIFIED/REJECTED | +| verifiedAt | LocalDateTime | | 认证时间 | +| verifiedBy | UUID | | 认证人ID | +| createdAt | LocalDateTime | NOT NULL, @PrePersist自动填充 | 创建时间 | +| updatedAt | LocalDateTime | NOT NULL, @PreUpdate自动填充 | 更新时间 | +| createdBy | UUID | column: created_by | 创建人ID | +| updatedBy | UUID | column: updated_by | 更新人ID | + +**关系**: +- `user` → @OneToOne → User(@MapsId共享主键) + +--- + +#### 2.1.10 Space(房屋空间) + +**表名**: `space` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| id | UUID | PK, 自动生成 | 空间ID | +| projectId | UUID | column: project_id | 所属项目ID | +| building | VARCHAR(50) | | 楼栋 | +| unit | VARCHAR(50) | | 单元 | +| roomNo | VARCHAR(50) | | 房号 | +| spaceType | VARCHAR(20) | | 房屋类型:RESIDENTIAL/COMMERCIAL | +| floor | Integer | | 楼层 | +| unitArea | BigDecimal | | 建筑面积(㎡) | +| status | VARCHAR(20) | | 状态 | + +--- + +#### 2.1.11 ResidentSpace(住户-房屋关联) + +**表名**: `resident_space` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| id | UUID | PK, 自动生成 | 关联记录ID | +| userId | UUID | NOT NULL, column: user_id | 用户ID | +| spaceId | UUID | NOT NULL, column: space_id | 房屋ID | +| relationType | VARCHAR(20) | | 关系类型:OWNER/FAMILY/TENANT | +| bindingStatus | VARCHAR(20) | | 绑定状态:PENDING/ACTIVE/EXPIRED/CANCELLED | +| startDate | LocalDate | | 生效日期 | +| endDate | LocalDate | | 失效日期(NULL=永久) | + +--- + +#### 2.1.12 UserProject(用户-项目关联) + +**表名**: `user_project` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| id | UUID | PK, 自动生成 | 关联记录ID | +| userId | UUID | NOT NULL, column: user_id | 用户ID | +| projectId | UUID | NOT NULL, column: project_id | 项目ID | +| roleInProject | VARCHAR | NOT NULL, 默认"member", column: role_in_project | 项目中角色:leader/member/viewer | +| joinedAt | LocalDateTime | NOT NULL, 默认当前时间, column: joined_at | 加入时间 | + +--- + +#### 2.1.13 SysConfig(系统配置) + +**表名**: `sys_config` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| id | UUID | PK, 自动生成 | 配置项ID | +| configKey | VARCHAR(128) | NOT NULL, UNIQUE, column: config_key | 配置键 | +| configValue | VARCHAR(5000) | column: config_value | 配置值 | +| description | VARCHAR(256) | | 配置描述 | +| createdAt | LocalDateTime | NOT NULL, @CreationTimestamp, 不可更新 | 创建时间 | +| updatedAt | LocalDateTime | NOT NULL, @UpdateTimestamp | 更新时间 | + +--- + +#### 2.1.14 DataAccess(数据访问授权)@Deprecated + +**表名**: `biz_data_access` + +| 字段名 | 类型 | 约束 | 说明 | +|--------|------|------|------| +| id | UUID | PK, 自动生成 | 授权记录ID | +| dataType | VARCHAR | NOT NULL, column: data_type | 数据类型 | +| dataId | UUID | NOT NULL, column: data_id | 数据ID | +| accessType | VARCHAR | NOT NULL, column: access_type | 访问者类型 | +| accessId | UUID | NOT NULL, column: access_id | 访问者ID | +| accessLevel | VARCHAR | NOT NULL, 默认"read", column: access_level | 访问级别 | +| grantedBy | UUID | column: granted_by | 授权人ID | +| grantedAt | LocalDateTime | NOT NULL, 默认当前时间, column: granted_at | 授权时间 | + +> **注意**: DataAccess 实体及对应控制器已标记 @Deprecated,后续版本将移除。 + +--- + +### 2.2 枚举定义 + +#### UserStatus + +| 值 | 描述 | +|----|------| +| ACTIVE | 正常 | +| LOCKED | 锁定 | +| DISABLED | 禁用 | + +#### RoleType + +| 值 | 描述 | +|----|------| +| SYSTEM | 系统级,可访问所有项目数据 | +| PROJECT | 项目级,仅可访问指定项目数据 | +| DEPARTMENT | 部门级,仅可访问本部门数据 | + +#### DataScope + +| 值 | 描述 | +|----|------| +| ALL | 全部数据 | +| PROJECT | 本项目数据 | +| DEPARTMENT | 本部门数据 | +| SELF | 仅本人数据 | + +#### RoleStatus + +| 值 | 描述 | +|----|------| +| ENABLED | 启用 | +| DISABLED | 禁用 | + +#### DeptType + +| 值 | 描述 | +|----|------| +| ADMIN | 行政管理 | +| ENGINEERING | 工程部 | +| SECURITY | 安保部 | +| CS | 客服部 | +| CLEANING | 保洁部 | + +#### ActionType + +| 值 | 描述 | +|----|------| +| CREATE | 创建 | +| UPDATE | 修改 | +| DELETE | 删除 | +| QUERY | 查询 | +| VIEW | 查看 | +| LOGIN | 登录 | +| LOGOUT | 登出 | +| EXPORT | 导出 | +| IMPORT | 导入 | +| ASSIGN | 分配 | +| REVOKE | 撤销 | + +--- + +### 2.3 ER关系图(Mermaid) + +```mermaid +erDiagram + User ||--o{ Role : "auth_user_role M2M" + Role ||--o{ Permission : "auth_role_permission M2M" + User ||--|| EnterpriseUser : "共享主键 1:1" + User ||--|| ProjectStaff : "FK 1:1" + User ||--|| Resident : "共享主键 1:1" + ProjectStaff ||--o{ ProjectStaffRole : "1:N 级联删除" + ProjectStaffRole }o--|| Role : "N:1 EAGER" + Resident ||--o{ ResidentSpace : "1:N" + ResidentSpace }o--|| Space : "N:1" + User ||--o{ UserProject : "1:N" + UserProject }o--|| Project : "N:1" + + Dept ||--o{ Dept : "自引用树形 parentId" + + AuditLog { + UUID id PK + UUID userId + String username + String operation + String module + String action + String targetType + String targetId + String status + } + + SysConfig { + UUID id PK + String configKey UK + String configValue + } + + DataAccess { + UUID id PK + String dataType + UUID dataId + String accessType + UUID accessId + String accessLevel + } +``` + +--- + +## 三、API设计 + +### 3.1 AuthController -- 认证管理 + +**基础路径**: `/api/auth` + +| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 | +|------|------|------|------|----------|----------|----------|----------| +| AUTH-API-001 | POST | `/login` | 用户登录 | Body: `{username: String(3-50), password: String(8-128)}` | `{code, data: {token, userId, username, realName, roles}}` | 无(公开) | 用户名不存在; 密码错误; 账户锁定; 账户禁用; 登录失败超5次锁定10分钟 | +| AUTH-API-002 | POST | `/logout` | 用户登出 | Header: Authorization: Bearer {token} | `{code, data: null}` | 已登录 | Token无效 | +| AUTH-API-003 | GET | `/me` | 获取当前用户 | Header: Authorization: Bearer {token} | `{code, data: {username}}` | 已登录 | Token缺失; Token无效 | +| AUTH-API-004 | POST | `/refresh` | 刷新Token | Header: Authorization: Bearer {token} | `{code, data: {token}}` | 已登录 | Token缺失; Token无效; Token已过期 | + +**登录响应详细格式**: +```json +{ + "code": 200, + "data": { + "token": "eyJhbGciOiJIUzI1NiJ9...", + "userId": "uuid", + "username": "admin", + "realName": "管理员", + "roles": ["SYSTEM_ADMIN", "PROJECT_MANAGER"] + } +} +``` + +--- + +### 3.2 UserController -- 用户管理 + +**基础路径**: `/api/auth/users` + +| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 | +|------|------|------|------|----------|----------|----------|----------| +| USER-API-001 | GET | `/` | 分页查询用户列表 | page(int, 默认0), size(int, 默认10) | `{code, data: {content: [User], totalElements, totalPages, ...}}` | 已登录 | 分页参数越界 | +| USER-API-002 | GET | `/{id}` | 根据ID获取用户 | id(UUID, Path) | `{code, data: User}` | 已登录 | 用户不存在 | +| USER-API-003 | POST | `/` | 创建用户 | Body: User实体 | `{code, data: User}` | 管理员 | 用户名已存在; 字段校验失败 | +| USER-API-004 | PUT | `/{id}` | 更新用户 | id(UUID, Path), Body: User实体 | `{code, data: User}` | 管理员 | 用户不存在; 用户名冲突 | +| USER-API-005 | DELETE | `/{id}` | 删除用户 | id(UUID, Path) | `{code, data: null}` | 管理员 | 用户不存在; 用户有关联数据 | +| USER-API-006 | PUT | `/{id}/password` | 修改密码 | id(UUID, Path), Body: `{oldPassword: String(8-128), newPassword: String(8-128)}` | `{code, data: null}` | 本人或管理员 | 原密码错误; 新密码强度不足; 新密码为弱密码 | +| USER-API-007 | POST | `/{id}/roles` | 分配角色 | id(UUID, Path), Body: `[roleId1, roleId2, ...]` | `{code, data: null}` | 管理员 | 用户不存在; 角色ID不存在; 分配数量超限 | +| USER-API-008 | GET | `/{id}/projects` | 获取用户项目列表 | id(UUID, Path) | `{code, data: [UserProject]}` | 已登录 | 用户不存在 | +| USER-API-009 | POST | `/{id}/projects` | 添加用户到项目 | id(UUID, Path), Body: `{projectId: UUID, roleInProject: String}` | `{code, data: null}` | 管理员 | 用户不存在; 项目不存在; 已在该项目中 | +| USER-API-010 | DELETE | `/{id}/projects/{projectId}` | 从项目移除用户 | id(UUID, Path), projectId(UUID, Path) | `{code, data: null}` | 管理员 | 关联不存在 | +| USER-API-011 | GET | `/enterprise` | 获取企业员工列表 | 无 | `{code, data: [UserVO]}` | 已登录 | 无 | + +**UserVO格式**: +```json +{ + "id": "uuid", + "username": "zhangsan", + "realName": "张三", + "phone": "13800138000", + "email": "zhangsan@example.com", + "avatar": "url", + "status": "ACTIVE", + "userType": "ENTERPRISE", + "deptId": "uuid" +} +``` + +--- + +### 3.3 RoleController -- 角色管理 + +**基础路径**: `/api/auth/roles` + +| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 | +|------|------|------|------|----------|----------|----------|----------| +| ROLE-API-001 | GET | `/` | 分页查询角色列表 | page(int, 默认0), size(int, 默认10) | `{code, data: Page}` | 已登录 | 分页参数越界 | +| ROLE-API-002 | GET | `/{id}` | 根据ID获取角色 | id(UUID, Path) | `{code, data: Role}` | 已登录 | 角色不存在 | +| ROLE-API-003 | GET | `/project/{projectId}` | 根据项目ID查询角色 | projectId(UUID, Path) | `{code, data: [Role]}` | 已登录 | 项目不存在 | +| ROLE-API-004 | POST | `/` | 创建角色 | Body: Role实体 | `{code, data: Role}` | 管理员 | 角色代码已存在; 字段校验失败 | +| ROLE-API-005 | PUT | `/{id}` | 更新角色 | id(UUID, Path), Body: Role实体 | `{code, data: Role}` | 管理员 | 角色不存在; 角色代码冲突 | +| ROLE-API-006 | DELETE | `/{id}` | 删除角色 | id(UUID, Path) | `{code, data: null}` | 管理员 | 角色不存在; 角色已分配给用户 | +| ROLE-API-007 | POST | `/{id}/permissions` | 为角色分配权限 | id(UUID, Path), Body: `[permissionId1, permissionId2, ...]` | `{code, data: null}` | 管理员 | 角色不存在; 权限ID不存在 | +| ROLE-API-008 | GET | `/{id}/permissions` | 获取角色权限列表 | id(UUID, Path) | `{code, data: [Permission]}` | 已登录 | 角色不存在 | +| ROLE-API-009 | GET | `/{id}/users` | 获取拥有某角色的用户 | id(UUID, Path) | `{code, data: [User]}` | 已登录 | 角色不存在 | + +--- + +### 3.4 PermissionController -- 权限管理 + +**基础路径**: `/api/auth/permissions` + +| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 | +|------|------|------|------|----------|----------|----------|----------| +| PERM-API-001 | GET | `/` | 分页查询权限列表 | page(int, 默认0), size(int, 默认10) | `{code, data: Page}` | 已登录 | 分页参数越界 | +| PERM-API-002 | GET | `/{id}` | 根据ID获取权限 | id(UUID, Path) | `{code, data: Permission}` | 已登录 | 权限不存在 | +| PERM-API-003 | GET | `/type/{type}` | 根据类型查询权限 | type(String, Path): MENU/BUTTON/API | `{code, data: [Permission]}` | 已登录 | 无效类型 | +| PERM-API-004 | GET | `/menus` | 查询所有菜单权限 | 无 | `{code, data: [Permission]}` | 已登录 | 无 | +| PERM-API-005 | POST | `/` | 创建权限 | Body: Permission实体 | `{code, data: Permission}` | 管理员 | 权限代码已存在; 字段校验失败 | +| PERM-API-006 | PUT | `/{id}` | 更新权限 | id(UUID, Path), Body: Permission实体 | `{code, data: Permission}` | 管理员 | 权限不存在; 权限代码冲突 | +| PERM-API-007 | DELETE | `/{id}` | 删除权限 | id(UUID, Path) | `{code, data: null}` | 管理员 | 权限不存在; 权限已分配给角色 | + +--- + +### 3.5 DeptController -- 部门管理 + +**基础路径**: `/api/auth/depts` + +| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 | +|------|------|------|------|----------|----------|----------|----------| +| DEPT-API-001 | GET | `/tree` | 获取部门树 | 无 | `{code, data: [DeptVO]}` | 已登录 | 无 | +| DEPT-API-002 | GET | `/` | 获取所有启用部门 | 无 | `{code, data: [Dept]}` | 已登录 | 无 | +| DEPT-API-003 | GET | `/{id}` | 根据ID获取部门 | id(UUID, Path) | `{code, data: Dept}` | 已登录 | 部门不存在 | +| DEPT-API-004 | POST | `/` | 创建部门 | Body: DeptDTO `{deptName, parentId, deptType, leaderId, sortOrder}` | `{code, data: Dept}` | 管理员 | 父部门不存在; 字段校验失败 | +| DEPT-API-005 | PUT | `/{id}` | 更新部门 | id(UUID, Path), Body: DeptDTO | `{code, data: Dept}` | 管理员 | 部门不存在; 形成环 | +| DEPT-API-006 | DELETE | `/{id}` | 删除部门 | id(UUID, Path) | `{code, data: null}` | 管理员 | 部门不存在; 有子部门; 部门下有用户 | +| DEPT-API-007 | GET | `/{deptId}/members` | 获取部门成员 | deptId(UUID, Path) | `{code, data: [UserVO]}` | 已登录 | 部门不存在 | +| DEPT-API-008 | GET | `/by-type/{deptType}` | 根据类型查询部门 | deptType(String, Path): ADMIN/ENGINEERING/SECURITY/CS/CLEANING | `{code, data: [Dept]}` | 已登录 | 无效类型 | + +**DeptVO格式**: +```json +{ + "id": "uuid", + "parentId": "uuid", + "deptName": "工程部", + "deptType": "ENGINEERING", + "leaderId": "uuid", + "sortOrder": 1, + "status": "ACTIVE", + "children": [DeptVO, DeptVO, ...] +} +``` + +--- + +### 3.6 ProjectMemberController -- 项目成员管理 + +**基础路径**: `/api/auth/projects` + +| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 | +|------|------|------|------|----------|----------|----------|----------| +| PMM-API-001 | GET | `/{projectId}/members` | 查询项目成员列表 | projectId(UUID, Path), page(int, 默认0), size(int, 默认20) | `{code, data: PageResponse}` | 已登录 | 项目不存在 | +| PMM-API-002 | GET | `/{projectId}/available-members` | 获取可添加成员 | projectId(UUID, Path), search(String, 可选) | `{code, data: [UserVO]}` | 已登录 | 项目不存在 | +| PMM-API-003 | POST | `/{projectId}/members` | 添加项目成员 | projectId(UUID, Path), Body: AddProjectMemberDTO `{userId, staffType, roleIds}` | `{code, data: null}` | 项目管理员 | 用户不存在; 项目不存在; 用户已是成员 | +| PMM-API-004 | DELETE | `/{projectId}/members/{userId}` | 移除项目成员 | projectId(UUID, Path), userId(UUID, Path) | `{code, data: null}` | 项目管理员 | 成员不存在 | + +**ProjectMemberVO格式**: +```json +{ + "id": "uuid", + "userId": "uuid", + "username": "zhangsan", + "realName": "张三", + "staffType": "SECURITY", + "shiftType": "DAY", + "assignmentStatus": "ASSIGNED", + "roles": [ + {"id": "uuid", "code": "SECURITY_GUARD", "name": "保安"} + ] +} +``` + +--- + +### 3.7 DataAccessController -- 数据访问授权 @Deprecated + +**基础路径**: `/api/data-access` + +| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 | +|------|------|------|------|----------|----------|----------|----------| +| DA-API-001 | POST | `/` | 授予数据访问权限 | Body: DataAccessRequest `{dataType, dataId, accessType, accessId, accessLevel}` | `{code, data: null}` | 已登录 | 未登录(401); 字段校验失败 | +| DA-API-002 | DELETE | `/{id}` | 撤销数据访问权限 | id(UUID, Path) | `{code, data: null}` | 已登录 | 记录不存在 | +| DA-API-003 | GET | `/` | 查询数据访问记录 | dataType(String), dataId(UUID) | `{code, data: [DataAccess]}` | 已登录 | 参数缺失 | + +> **注意**: 此控制器所有端点已标记 @Deprecated,后续版本将移除。 + +--- + +### 3.8 AuditLogController -- 审计日志 + +**基础路径**: `/api/audit-logs` + +| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 | +|------|------|------|------|----------|----------|----------|----------| +| AUDIT-API-001 | GET | `/` | 分页查询审计日志 | page(int, 默认0), size(int, 默认10), module(String, 可选), action(String, 可选), username(String, 可选), startDate(DateTime, 可选), endDate(DateTime, 可选) | `{code, data: Page}` | 管理员 | 查询范围超30天自动截断 | +| AUDIT-API-002 | GET | `/modules` | 获取模块列表 | 无 | `{code, data: [{value, label}]}` | 管理员 | 无 | +| AUDIT-API-003 | GET | `/actions` | 获取操作类型列表 | 无 | `{code, data: [{value, label}]}` | 管理员 | 无 | +| AUDIT-API-004 | GET | `/stats` | 获取最近30天日志统计 | 无 | `{code, data: {total, retentionDays, description}}` | 管理员 | 无 | + +**模块列表固定值**: USER(用户管理), ROLE(角色管理), PERMISSION(权限管理), PROJECT(项目管理), AUTH(登录认证) + +--- + +### 3.9 SysConfigController -- 系统配置 + +**基础路径**: `/api/config` + +| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 | +|------|------|------|------|----------|----------|----------|----------| +| CFG-API-001 | GET | `/` | 获取所有配置项 | 无 | `{code, data: {key1: value1, key2: value2}}` | 管理员 | 无 | +| CFG-API-002 | GET | `/{configKey}` | 根据键获取配置 | configKey(String, Path) | `{code, data: SysConfig}` | 管理员 | 配置键不存在 | +| CFG-API-003 | PUT | `/{configKey}` | 更新单个配置 | configKey(String, Path), Body: `{configValue: String(NotBlank)}` | `{code, data: SysConfig}` | 管理员 | 配置键不存在; 配置值为空 | +| CFG-API-004 | PUT | `/` | 批量更新配置 | Body: `{key1: value1, key2: value2}` | `{code, data: {key1: value1, key2: value2}}` | 管理员 | 配置键不存在 | + +--- + +## 四、业务规则 + +### 4.1 RBAC权限模型(角色-权限-用户关联规则) + +**模型结构**: User <--M2M--> Role <--M2M--> Permission + +**双层角色分配**: + +1. **用户直接角色**: 通过 `auth_user_role` 中间表关联 + - 用户创建时分配 + - 通过 `UserController.assignRoles` 接口分配 + - 覆盖模式:新分配的角色列表完全替换旧列表 + +2. **项目员工角色**: 通过 `ProjectStaffRole` 关联 + - 项目成员管理时分配 + - 覆盖模式:先删除旧角色,再添加新角色 + - 级联删除:删除 ProjectStaff 时自动删除其所有 ProjectStaffRole + +**登录时角色收集规则**: +``` +用户所有角色 = 用户直接角色(auth_user_role) ∪ 项目员工角色(ProjectStaffRole → Role) +``` + +**角色类型与数据范围对应关系**: + +| 角色类型 | 说明 | 默认数据范围 | projectId | +|---------|------|-------------|-----------| +| SYSTEM | 系统级角色,跨项目 | ALL | NULL | +| PROJECT | 项目级角色,绑定特定项目 | PROJECT | 非NULL | +| DEPARTMENT | 部门级角色 | DEPARTMENT | NULL | + +**角色分配约束**: +- 角色分配数量受 `BatchOperationValidator.validateAssignmentSize()` 限制 +- 分配不存在的角色ID将抛出异常 +- 角色状态为 DISABLED 时不应分配给用户(需业务层校验) + +--- + +### 4.2 数据范围控制(ALL/PROJECT/DEPARTMENT/SELF的SQL过滤规则) + +**四级数据范围**: + +| 数据范围 | 编码 | 说明 | SQL过滤规则 | +|---------|------|------|------------| +| 全部 | ALL | 可查看所有数据 | 不做数据过滤 | +| 项目 | PROJECT | 仅可查看所属项目数据 | `WHERE project_id IN (用户可访问的项目ID列表)` | +| 部门 | DEPARTMENT | 仅可查看本部门数据 | `WHERE project_id IN (...) AND dept_id = 用户部门ID` | +| 本人 | SELF | 仅可查看本人数据 | `WHERE assigned_to = 当前用户ID OR created_by = 当前用户ID` | + +**DataScopeService 核心方法**: + +| 方法 | 说明 | 返回值 | +|------|------|--------| +| `canAccessAllData()` | 检查角色集中是否有ALL范围 | Boolean | +| `isSelfScopeOnly()` | 检查是否所有角色都是SELF范围 | Boolean | +| `getPermittedProjectIds()` | 获取用户可访问的项目ID列表 | List\ | + +**项目上下文传递机制**: +1. 前端请求携带 `X-Project-ID` Header +2. `ProjectContextInterceptor` 拦截器设置 ThreadLocal +3. 业务层通过 DataScopeService 过滤数据 + +--- + +### 4.3 JWT认证流程(登录→Token生成→验证→刷新→登出) + +**Token生成**: +- 算法: HMAC-SHA256 +- Claims: userId(subject), username, roles列表 +- 过期时间: 可配置(默认86400000ms = 24小时) +- 密钥: 配置项 `jwt.secret` + +**完整认证流程**: + +``` +1. 登录 POST /api/auth/login + ├── 检查登录锁定(Redis: login:attempt:{username}) + ├── 查询用户(含角色, LAZY加载) + ├── 验证密码(BCrypt.matches) + ├── 检查用户状态(LOCKED/DISABLED → 拒绝) + ├── 收集角色(直接角色 + 项目员工角色) + ├── 生成JWT Token + ├── 清除登录失败计数 + ├── 更新lastLoginTime/lastLoginIp + └── 返回 {token, userId, username, realName, roles} + +2. 请求认证 + ├── Header: Authorization: Bearer {token} + ├── JwtTokenProvider.validateToken() 验证签名和过期 + └── 解析 userId / username / roles + +3. Token刷新 POST /api/auth/refresh + ├── 验证当前Token有效 + ├── 检查Token未过期 + ├── 生成新Token + └── 返回 {token} + +4. 登出 POST /api/auth/logout + └── 客户端清除Token(服务端无状态) +``` + +--- + +### 4.4 密码策略(BCrypt/强度校验/弱密码检测/登录锁定) + +**加密算法**: BCrypt(Spring Security PasswordEncoder) + +**密码强度校验规则**(可配置,前缀 `password.*`): + +| 规则 | 配置键 | 默认值 | 说明 | +|------|--------|--------|------| +| 最小长度 | password.minLength | 8 | 最小长度 | +| 最大长度 | password.maxLength | 20 | 最大长度 | +| 需要大写字母 | password.requireUppercase | true | 至少1个大写字母 | +| 需要小写字母 | password.requireLowercase | true | 至少1个小写字母 | +| 需要数字 | password.requireDigit | true | 至少1个数字 | +| 需要特殊字符 | password.requireSpecial | true | 至少1个特殊字符 | + +**弱密码黑名单**: password, 123456, admin, qwerty, letmein, welcome, monkey, dragon + +**旧密码兼容策略**: +- 检测非BCrypt格式密码(MD5/SHA-1等) +- 非BCrypt密码强制返回 `matches=false` +- 用户需通过修改密码流程重置为BCrypt格式 + +**登录失败锁定机制**(基于Redis): + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| maxAttempts | 5 | 最大失败次数 | +| lockoutDurationMinutes | 10 | 锁定时长(分钟) | +| Key格式 | `login:attempt:{username}` | Redis Key | + +**锁定流程**: +1. 每次失败: increment计数,首次设置TTL +2. 达到上限(5次): isLockedOut返回true,拒绝登录 +3. 登录成功: 清除计数 +4. 手动解锁: unlock方法清除Key + +--- + +### 4.5 项目成员管理(添加/移除/角色分配/在岗状态变更) + +**添加项目成员流程**: +1. 接收 AddProjectMemberDTO: `{userId, staffType, roleIds}` +2. 创建/更新 ProjectStaff 记录(设定员工类型、分配状态=ASSIGNED) +3. 覆盖模式更新角色: 先删除旧 ProjectStaffRole,再添加新 ProjectStaffRole +4. 创建 UserProject 关联记录 + +**移除项目成员流程**: +1. 删除 ProjectStaff 记录(级联删除 ProjectStaffRole) +2. 删除 UserProject 关联记录 + +**在岗状态变更**: +- ASSIGNED: 已分配(正常在岗) +- ON_LEAVE: 请假(临时离岗) +- TRANSFERRED: 已调离(永久离岗) + +**员工类型**: +- SECURITY(保安), CLEANING(保洁), GARDEN(绿化), MAINTENANCE(维修), CUSTOMER_SERVICE(客服), GENERAL(普通员工) + +**班次类型**: +- DAY(白班), NIGHT(夜班), ROTATION(轮班) + +--- + +### 4.6 住户认证流程(申请→审核→通过/拒绝→房屋绑定) + +**认证状态流转**: + +``` +UNVERIFIED(未认证) → PENDING(待审核) → VERIFIED(已认证) + → REJECTED(已拒绝) → PENDING(重新申请) +``` + +**认证流程**: +1. 住户注册账号,verificationStatus = UNVERIFIED +2. 住户提交认证申请(身份证、住户类型),verificationStatus = PENDING +3. 管理员审核: + - 通过: verificationStatus = VERIFIED, 记录 verifiedAt/verifiedBy + - 拒绝: verificationStatus = REJECTED + +**房屋绑定流程**: +1. 住户认证通过后,创建 ResidentSpace 关联 +2. 关联字段: userId, spaceId, relationType(OWNER/FAMILY/TENANT) +3. 绑定状态: PENDING(待生效) → ACTIVE(生效中) → EXPIRED(已过期) / CANCELLED(已取消) +4. 支持生效/失效日期: startDate / endDate(NULL=永久) + +--- + +## 五、执行约束 + +### 5.1 用户名唯一性约束 + +- **数据库约束**: `auth_user.username` 列 UNIQUE +- **校验规则**: 正则 `^[a-zA-Z0-9_]+$`,长度3-50位 +- **违反后果**: 创建/更新用户时抛出 DataIntegrityViolationException +- **处理方式**: 业务层先查询是否存在相同用户名,存在则抛出 BusinessException + +### 5.2 角色代码唯一性约束 + +- **数据库约束**: `auth_role.code` 列 UNIQUE +- **校验规则**: 正则 `^[a-zA-Z0-9_]+$`,长度2-50位 +- **违反后果**: 创建/更新角色时抛出 DataIntegrityViolationException +- **处理方式**: 业务层先查询是否存在相同角色代码,存在则抛出 BusinessException + +### 5.3 权限代码唯一性约束 + +- **数据库约束**: `auth_permission.code` 列 UNIQUE +- **校验规则**: 正则 `^[a-zA-Z0-9_:]+$`,长度2-100位,格式 `module:resource:action` +- **违反后果**: 创建/更新权限时抛出 DataIntegrityViolationException +- **处理方式**: 业务层先查询是否存在相同权限代码,存在则抛出 BusinessException + +### 5.4 部门树完整性约束(不能形成环) + +- **约束说明**: 部门通过 parentId 自引用形成树形结构,更新 parentId 时不能形成环路 +- **校验方式**: 业务层在更新 parentId 时,沿 parentId 链向上遍历,检查是否会回到当前节点 +- **违反后果**: 抛出 BusinessException,提示"不能将部门设置为其子部门的下级" +- **附加约束**: 删除部门时,必须先删除或移动其所有子部门 + +### 5.5 密码强度约束 + +- **最小长度**: 8位 +- **最大长度**: 20位(业务层校验)/ 128位(API层校验) +- **必须包含**: 大写字母、小写字母、数字、特殊字符各至少1个 +- **弱密码检测**: 不允许使用黑名单中的密码 +- **违反后果**: 修改密码时抛出 BusinessException,提示具体不满足的规则 + +--- + +## 六、权限控制 + +### 6.1 API端点权限矩阵 + +| 控制器 | 端点 | 所需角色 | 数据范围过滤 | +|--------|------|----------|-------------| +| AuthController | POST /login | 公开(无需认证) | 无 | +| AuthController | POST /logout | 已登录用户 | 无 | +| AuthController | GET /me | 已登录用户 | SELF | +| AuthController | POST /refresh | 已登录用户 | 无 | +| UserController | GET / | 管理员 | ALL/PROJECT/DEPARTMENT/SELF | +| UserController | GET /{id} | 管理员 | 同上 | +| UserController | POST / | 管理员(SYSTEM级) | 无 | +| UserController | PUT /{id} | 管理员 | 同上 | +| UserController | DELETE /{id} | 管理员(SYSTEM级) | 无 | +| UserController | PUT /{id}/password | 本人或管理员 | SELF | +| UserController | POST /{id}/roles | 管理员(SYSTEM级) | 无 | +| UserController | GET /{id}/projects | 已登录用户 | SELF | +| UserController | POST /{id}/projects | 管理员(PROJECT级) | 无 | +| UserController | DELETE /{id}/projects/{projectId} | 管理员(PROJECT级) | 无 | +| UserController | GET /enterprise | 已登录用户 | ALL/PROJECT | +| RoleController | GET / | 已登录用户 | ALL/PROJECT | +| RoleController | GET /{id} | 已登录用户 | 同上 | +| RoleController | GET /project/{projectId} | 已登录用户 | PROJECT | +| RoleController | POST / | 管理员(SYSTEM级) | 无 | +| RoleController | PUT /{id} | 管理员(SYSTEM级) | 无 | +| RoleController | DELETE /{id} | 管理员(SYSTEM级) | 无 | +| RoleController | POST /{id}/permissions | 管理员(SYSTEM级) | 无 | +| RoleController | GET /{id}/permissions | 已登录用户 | 同上 | +| RoleController | GET /{id}/users | 已登录用户 | 同上 | +| PermissionController | GET / | 已登录用户 | ALL | +| PermissionController | GET /{id} | 已登录用户 | ALL | +| PermissionController | GET /type/{type} | 已登录用户 | ALL | +| PermissionController | GET /menus | 已登录用户 | SELF(按角色过滤) | +| PermissionController | POST / | 管理员(SYSTEM级) | 无 | +| PermissionController | PUT /{id} | 管理员(SYSTEM级) | 无 | +| PermissionController | DELETE /{id} | 管理员(SYSTEM级) | 无 | +| DeptController | GET /tree | 已登录用户 | ALL | +| DeptController | GET / | 已登录用户 | ALL | +| DeptController | GET /{id} | 已登录用户 | ALL | +| DeptController | POST / | 管理员(SYSTEM级) | 无 | +| DeptController | PUT /{id} | 管理员(SYSTEM级) | 无 | +| DeptController | DELETE /{id} | 管理员(SYSTEM级) | 无 | +| DeptController | GET /{deptId}/members | 已登录用户 | DEPARTMENT | +| DeptController | GET /by-type/{deptType} | 已登录用户 | ALL | +| ProjectMemberController | GET /{projectId}/members | 项目成员 | PROJECT | +| ProjectMemberController | GET /{projectId}/available-members | 项目管理员 | PROJECT | +| ProjectMemberController | POST /{projectId}/members | 项目管理员 | PROJECT | +| ProjectMemberController | DELETE /{projectId}/members/{userId} | 项目管理员 | PROJECT | +| DataAccessController | POST / | 已登录用户(@Deprecated) | 无 | +| DataAccessController | DELETE /{id} | 已登录用户(@Deprecated) | 无 | +| DataAccessController | GET / | 已登录用户(@Deprecated) | 无 | +| AuditLogController | GET / | 管理员(SYSTEM级) | ALL | +| AuditLogController | GET /modules | 管理员(SYSTEM级) | 无 | +| AuditLogController | GET /actions | 管理员(SYSTEM级) | 无 | +| AuditLogController | GET /stats | 管理员(SYSTEM级) | ALL | +| SysConfigController | GET / | 管理员(SYSTEM级) | ALL | +| SysConfigController | GET /{configKey} | 管理员(SYSTEM级) | ALL | +| SysConfigController | PUT /{configKey} | 管理员(SYSTEM级) | 无 | +| SysConfigController | PUT / | 管理员(SYSTEM级) | 无 | + +### 6.2 数据范围过滤规则 + +| 操作类型 | ALL范围 | PROJECT范围 | DEPARTMENT范围 | SELF范围 | +|---------|---------|------------|---------------|---------| +| 查询用户列表 | 全部用户 | 本项目成员 | 本部门用户 | 仅本人 | +| 查询角色列表 | 全部角色 | 本项目角色 | - | - | +| 查询审计日志 | 全部日志 | 本项目日志 | 本部门日志 | 本人操作日志 | +| 查询部门成员 | 全部 | 本项目部门成员 | 本部门成员 | - | + +--- + +## 七、例外情况处理 + +### 7.1 用户相关例外 + +| 例外场景 | 错误码 | 错误信息 | 处理方式 | +|---------|--------|---------|---------| +| 用户名已存在 | 409 | "用户名{username}已存在" | 创建/更新前查询,存在则拒绝 | +| 用户不存在 | 404 | "用户不存在" | findById返回空时抛出 | +| 密码错误 | 401 | "用户名或密码错误" | BCrypt.matches返回false | +| 账户锁定 | 423 | "账户已锁定,请{minutes}分钟后重试" | 检查Redis锁定状态 | +| 账户禁用 | 403 | "账户已被禁用" | 检查UserStatus.DISABLED | +| 原密码错误 | 400 | "原密码不正确" | 修改密码时验证 | +| 新密码强度不足 | 400 | "密码必须包含大写字母、小写字母、数字和特殊字符,长度8-20位" | 密码策略校验 | +| 新密码为弱密码 | 400 | "该密码过于简单,请使用更复杂的密码" | 弱密码黑名单检测 | +| 手机号格式错误 | 400 | "手机号格式不正确" | 正则校验 | +| 邮箱格式错误 | 400 | "邮箱格式不正确" | @Email校验 | +| 删除有关联数据的用户 | 400 | "该用户存在关联数据,无法删除" | 检查UserProject/ProjectStaff等关联 | + +### 7.2 角色相关例外 + +| 例外场景 | 错误码 | 错误信息 | 处理方式 | +|---------|--------|---------|---------| +| 角色代码已存在 | 409 | "角色代码{code}已存在" | 创建/更新前查询 | +| 角色不存在 | 404 | "角色不存在" | findById返回空时抛出 | +| 删除已分配的角色 | 400 | "该角色已分配给用户,无法删除" | 检查auth_user_role关联 | +| 角色代码格式错误 | 400 | "角色代码只能包含字母、数字和下划线" | 正则校验 | + +### 7.3 权限相关例外 + +| 例外场景 | 错误码 | 错误信息 | 处理方式 | +|---------|--------|---------|---------| +| 权限代码已存在 | 409 | "权限代码{code}已存在" | 创建/更新前查询 | +| 权限不存在 | 404 | "权限不存在" | findById返回空时抛出 | +| 删除已分配的权限 | 400 | "该权限已分配给角色,无法删除" | 检查auth_role_permission关联 | +| 权限代码格式错误 | 400 | "权限代码只能包含字母、数字、冒号和下划线" | 正则校验 | + +### 7.4 部门相关例外 + +| 例外场景 | 错误码 | 错误信息 | 处理方式 | +|---------|--------|---------|---------| +| 部门不存在 | 404 | "部门不存在" | getById返回空时返回error | +| 有子部门不能删除 | 400 | "该部门存在子部门,无法删除" | 检查子部门数量 | +| 部门下有用户不能删除 | 400 | "该部门下存在用户,无法删除" | 检查部门成员 | +| 部门树形成环 | 400 | "不能将部门设置为其子部门的下级" | 遍历parentId链检查 | +| 父部门不存在 | 400 | "上级部门不存在" | 创建/更新时校验parentId | + +### 7.5 认证相关例外 + +| 例外场景 | 错误码 | 错误信息 | 处理方式 | +|---------|--------|---------|---------| +| Token缺失 | 401 | "未授权" | Authorization Header为空 | +| Token无效 | 401 | "Token无效" | JWT签名验证失败 | +| Token已过期 | 401 | "Token已过期" | JWT过期时间检查 | +| 登录失败超限 | 423 | "登录失败次数过多,账户已锁定{minutes}分钟" | Redis计数检查 | +| 用户名不存在 | 401 | "用户名或密码错误" | 不暴露具体原因 | + +### 7.6 项目成员相关例外 + +| 例外场景 | 错误码 | 错误信息 | 处理方式 | +|---------|--------|---------|---------| +| 用户已是项目成员 | 409 | "该用户已是项目成员" | 检查ProjectStaff存在性 | +| 项目成员不存在 | 404 | "项目成员不存在" | 删除时校验 | +| 角色ID不存在 | 400 | "角色ID{id}不存在" | 分配角色时校验 | + +### 7.7 系统配置相关例外 + +| 例外场景 | 错误码 | 错误信息 | 处理方式 | +|---------|--------|---------|---------| +| 配置键不存在 | 404 | "配置项{key}不存在" | 查询时校验 | +| 配置值为空 | 400 | "配置值不能为空" | @NotBlank校验 | + +### 7.8 审计日志相关例外 + +| 例外场景 | 错误码 | 错误信息 | 处理方式 | +|---------|--------|---------|---------| +| 查询范围超30天 | 200(自动截断) | 无(自动截断起始时间) | 强制限制查询窗口 | +| 分页参数过大 | 200(自动修正) | 无(使用getSafeSize修正) | PaginationValidator校验 | diff --git a/02-DESIGN/detail/DETAIL-CROSS-DOMAIN.md b/02-DESIGN/detail/DETAIL-CROSS-DOMAIN.md new file mode 100644 index 0000000..1ff3ff5 --- /dev/null +++ b/02-DESIGN/detail/DETAIL-CROSS-DOMAIN.md @@ -0,0 +1,578 @@ +# 跨域业务流程 - 详细设计 + +**文档类型**: 详细设计文档 +**生成日期**: 2026-05-18 +**数据来源**: REVERSE-FINANCE.md + REVERSE-AUTH.md + 路由/API层源码 + 后端实体分析 + +--- + +## 一、业主报修全流程 + +### 1.1 流程概述 + +业主报修是Ether系统中最核心的跨域业务流程,涉及从业主发起报修到最终评价的完整生命周期: + +**业主提交报修 -> 系统创建工单 -> 调度派单 -> 维保人员执行 -> 业主验收 -> 评价反馈** + +### 1.2 涉及域和功能点 + +| 步骤 | 涉及域 | 功能点 | API端点 | +|------|--------|--------|---------| +| 1. 业主身份识别 | 认证域 | 业主登录、Token验证 | `POST /api/auth/login` | +| 2. 项目/空间定位 | 空间域 | 获取业主关联房产 | `GET /api/mdm/space-nodes/project/{projectId}/tree` | +| 3. 设备关联(可选) | 设备域 | 按空间查询设备 | `GET /api/asset/equipment/by-space/{spaceNodeId}` | +| 4. 创建工单 | 运营域 | 创建报修工单 | `POST /api/wo/work-orders` | +| 5. 工单派发 | 运营域 | 指派维保人员 | `POST /api/wo/work-orders/{id}/assign` | +| 6. 开始执行 | 运营域 | 维保人员接单 | `POST /api/wo/work-orders/{id}/start` | +| 7. 完成工单 | 运营域 | 填写处理结果 | `POST /api/wo/work-orders/{id}/complete` | +| 8. 业主验收 | 运营域 | 确认维修结果 | `POST /api/wo/work-orders/{id}/verify` | +| 9. 评价反馈 | 运营域 | 评分评价 | `POST /api/wo/work-orders/{id}/verify` (含rating) | +| 10. 备件消耗 | 备件域 | 出库记录 | `POST /api/ops/spare-parts/out-stock` | +| 11. 审计记录 | 认证域 | 操作日志 | 自动记录(AOP) | + +### 1.3 数据流图 + +```mermaid +flowchart TD + A[业主登录] -->|POST /api/auth/login| B[获取Token+角色] + B --> C[选择项目/房产] + C -->|GET /api/mdm/space-nodes/project/id/tree| D[加载空间树] + D --> E{是否关联设备?} + E -->|是| F[选择设备] + F -->|GET /api/asset/equipment/by-space/spaceNodeId| G[设备列表] + E -->|否| H[直接描述问题] + G --> I[填写报修信息] + H --> I + I -->|POST /api/wo/work-orders| J[工单创建 PENDING] + J -->|POST /api/wo/work-orders/id/assign| K[派发 ASSIGNED] + K -->|POST /api/wo/work-orders/id/start| L[执行中 IN_PROGRESS] + L --> M{是否需要备件?} + M -->|是| N[备件出库] + N -->|POST /api/ops/spare-parts/out-stock| O[更新库存] + M -->|否| P[填写处理结果] + O --> P + P -->|POST /api/wo/work-orders/id/complete| Q[已完成 COMPLETED] + Q -->|POST /api/wo/work-orders/id/verify| R[已验收 VERIFIED] + R --> S[评价评分] + + style A fill:#e1f5fe + style J fill:#fff3e0 + style K fill:#e3f2fd + style L fill:#fff8e1 + style Q fill:#e8f5e9 + style R fill:#e0f7fa +``` + +### 1.4 状态流转图 + +```mermaid +stateDiagram-v2 + [*] --> PENDING: 业主提交报修 + PENDING --> ASSIGNED: 管理员派单 + PENDING --> CANCELLED: 业主取消 + ASSIGNED --> IN_PROGRESS: 维保人员开始执行 + ASSIGNED --> PENDING: 退回重派 + ASSIGNED --> CANCELLED: 管理员取消 + IN_PROGRESS --> SUSPENDED: 挂起(缺件/等审批) + IN_PROGRESS --> RETURNED: 退回(无法处理) + IN_PROGRESS --> COMPLETED: 填写处理结果 + SUSPENDED --> IN_PROGRESS: 恢复执行 + RETURNED --> ASSIGNED: 重新派单 + COMPLETED --> VERIFIED: 业主验收通过 + COMPLETED --> IN_PROGRESS: 验收不通过,返工 + VERIFIED --> [*]: 流程结束 + CANCELLED --> [*]: 流程结束 +``` + +### 1.5 异常场景处理 + +| 异常场景 | 触发条件 | 处理策略 | 涉及API | +|---------|---------|---------|---------| +| 设备不存在 | 业主选择的设备ID在数据库中不存在 | 返回404错误,提示设备不存在,允许不关联设备提交 | `GET /api/asset/equipment/{id}` | +| 派单失败 | 指定的维保人员不存在或不可用 | 返回错误提示,管理员重新选择人员 | `POST /api/wo/work-orders/{id}/assign` | +| 执行超时 | 工单超过SLA时间未完成 | 系统自动标记超时,通知项目经理 | 定时任务检查 | +| 验收不通过 | 业主对维修结果不满意 | 工单回退到IN_PROGRESS,维保人员返工 | `POST /api/wo/work-orders/{id}/complete` -> 重新执行 | +| 备件缺货 | 维修所需备件库存不足 | 工单挂起SUSPENDED,等待备件入库 | `GET /api/ops/spare-parts/low-stock` | +| 业主取消 | 业主在派单前取消 | 工单状态变为CANCELLED | `POST /api/wo/work-orders/{id}/cancel` | +| 重复提交 | 业主短时间内重复提交相同报修 | 前端防抖 + 后端幂等校验 | `POST /api/wo/work-orders` | +| 维保人员退回 | 维保人员无法处理该工单 | 工单退回RETURNED,管理员重新派单 | `POST /api/wo/work-orders/{id}/return` | +| 工单挂起 | 执行中遇到阻塞(等审批/等配件) | 工单挂起SUSPENDED,解除后恢复 | `POST /api/wo/work-orders/{id}/suspend` | +| 业主未验收 | 完成后业主长时间未验收 | 超过7天自动验收通过 | 定时任务检查 | + +### 1.6 权限控制 + +| 角色 | 可执行操作 | 数据范围 | +|------|-----------|---------| +| 业主(OWNER) | 创建工单、查看自己的工单、验收、评价 | 仅自己创建的工单(SELF) | +| 项目管理员(PROJECT_ADMIN) | 查看项目所有工单、派单、取消 | 本项目工单(PROJECT) | +| 维保人员(MAINTENANCE) | 查看分配给自己的工单、开始、完成、退回 | 分配给自己的工单(SELF) | +| 系统管理员(SYS_ADMIN) | 所有操作 | 全部数据(ALL) | + +**权限校验点**: + +``` +创建工单: 业主只能为关联房产创建工单 +派单: 仅项目管理员/系统管理员可派单 +执行: 仅被指派的维保人员可开始/完成 +验收: 仅工单创建者(业主)可验收 +评价: 仅工单创建者(业主)可评价 +``` + +--- + +## 二、设备全生命周期管理 + +### 2.1 流程概述 + +设备全生命周期管理覆盖设备从采购入库到最终报废的完整过程: + +**采购登记 -> 安装调试 -> 正常运行 -> 定期维保 -> 巡检监控 -> 故障维修 -> 报废处置** + +### 2.2 涉及域和功能点 + +| 步骤 | 涉及域 | 功能点 | API端点 | +|------|--------|--------|---------| +| 1. 设备采购登记 | 设备域 | 创建设备记录 | `POST /api/asset/equipment` | +| 2. 安装位置关联 | 空间域 | 关联空间节点 | `PUT /api/asset/equipment/{id}` (spaceNodeId) | +| 3. 扩展信息录入 | 设备域 | 电梯/暖通/消防等扩展 | `PUT /api/asset/equipment/{id}/elevator` 等 | +| 4. 照片/文档上传 | 设备域 | 照片和文档管理 | PhotoManager / DocumentManager | +| 5. 归属主体关联 | 设备域 | 设置归属类型和主体 | `GET /api/asset/ownership-entity` | +| 6. 正常运行监控 | 设备域 | 健康度计算 | `POST /api/asset/equipment-health/calculate` | +| 7. 定期维保 | 维保域 | 创建维保计划 | `POST /api/mdm/maintenance-plans` | +| 8. 维保执行 | 维保域 | 维保工单流转 | `POST /api/ops/maintenance-tasks/{id}/start` 等 | +| 9. 巡检监控 | 巡检域 | 巡检记录 | `POST /api/mdm/inspection-records` | +| 10. 故障维修 | 运营域 | 创建维修工单 | `POST /api/wo/work-orders` | +| 11. 健康度评估 | 设备域 | MTBF/MTTR分析 | `GET /api/asset/equipment-health/mtbf/{id}` | +| 12. 设备报废 | 设备域 | 更新设备状态 | `PUT /api/asset/equipment/{id}` (status=SCRAPPED) | + +### 2.3 数据流图 + +```mermaid +flowchart TD + A[设备采购] -->|POST /api/asset/equipment| B[设备登记] + B --> C[安装调试] + C -->|PUT /api/asset/equipment/id + spaceNodeId| D[关联空间位置] + D -->|PUT /api/asset/equipment/id/elevator| E[录入扩展信息] + E --> F[正常运行] + F -->|POST /api/asset/equipment-health/calculate| G[健康度评估] + G --> H{设备状态判断} + H -->|健康| I[定期维保] + H -->|预警| J[加强巡检] + H -->|异常| K[故障维修] + + I -->|POST /api/mdm/maintenance-plans| L[创建维保计划] + L -->|定时任务| M[生成维保工单] + M -->|POST /api/ops/maintenance-tasks/id/start| N[执行维保] + N -->|POST /api/ops/maintenance-tasks/id/complete| O[维保完成] + O --> F + + J -->|POST /api/mdm/inspection-records| P[巡检记录] + P --> Q{发现异常?} + Q -->|是| K + Q -->|否| F + + K -->|POST /api/wo/work-orders| R[创建维修工单] + R -->|POST /api/wo/work-orders/id/complete| S[维修完成] + S -->|POST /api/asset/equipment-health/calculate| T[重新评估健康度] + T --> H + + F --> U{达到报废条件?} + U -->|是| V[设备报废] + U -->|否| I + V -->|PUT /api/asset/equipment/id status=SCRAPPED| W[报废处置] + + style F fill:#e8f5e9 + style K fill:#ffebee + style V fill:#f3e5f5 +``` + +### 2.4 状态流转图 + +```mermaid +stateDiagram-v2 + [*] --> PURCHASED: 采购登记 + PURCHASED --> INSTALLING: 安装调试 + INSTALLING --> COMMISSIONING: 调试验收 + COMMISSIONING --> RUNNING: 验收通过 + COMMISSIONING --> INSTALLING: 验收不通过 + RUNNING --> MAINTENANCE: 定期维保 + RUNNING --> INSPECTION: 巡检监控 + RUNNING --> FAULT: 故障发生 + MAINTENANCE --> RUNNING: 维保完成 + INSPECTION --> RUNNING: 巡检正常 + INSPECTION --> FAULT: 巡检发现异常 + FAULT --> REPAIRING: 维修中 + REPAIRING --> RUNNING: 维修完成 + REPAIRING --> SCRAPPED: 无法修复 + RUNNING --> SCRAPPED: 达到使用年限 + RUNNING --> SCRAPPED: 经济性报废 + SCRAPPED --> [*] +``` + +### 2.5 异常场景处理 + +| 异常场景 | 触发条件 | 处理策略 | +|---------|---------|---------| +| 设备重复登记 | 相同序列号/资产编码已存在 | 后端校验唯一性,返回409冲突 | +| 安装位置冲突 | 同一空间节点已有同类型设备 | 允许同空间多设备,但给出提示 | +| 维保计划冲突 | 同一设备存在重叠的维保计划 | 校验计划周期不重叠 | +| 健康度计算失败 | 设备缺少历史数据 | 返回默认健康度,标记为"数据不足" | +| 维修无法修复 | 多次维修后仍故障 | 建议报废,升级审批流程 | +| 备件不兼容 | 维修使用的备件与设备不匹配 | 备件选择时按设备类型筛选 | +| 巡检数据异常 | 巡检结果与设备状态不符 | 标记为"需复核",触发二次巡检 | +| 报废审批 | 高价值设备报废 | 需项目经理审批确认 | + +### 2.6 权限控制 + +| 角色 | 可执行操作 | 数据范围 | +|------|-----------|---------| +| 系统管理员(SYS_ADMIN) | 设备全生命周期操作 | ALL | +| 项目管理员(PROJECT_ADMIN) | 本项目设备管理 | PROJECT | +| 工程人员(ENGINEERING) | 设备登记、维保、巡检 | PROJECT + DEPARTMENT | +| 维保人员(MAINTENANCE) | 执行维保工单 | SELF(分配给自己的) | +| 业主(OWNER) | 查看关联设备信息 | SELF(关联房产的设备) | + +--- + +## 三、预防性维保调度流程 + +### 3.1 流程概述 + +预防性维保调度是基于维保计划自动生成维保工单的定时调度流程: + +**创建维保计划 -> 定时调度检查 -> 自动生成维保工单 -> 派单执行 -> 验收 -> 更新设备记录** + +### 3.2 涉及域和功能点 + +| 步骤 | 涉及域 | 功能点 | API端点 | +|------|--------|--------|---------| +| 1. 创建维保计划 | 维保域 | 设置设备、周期、维保内容 | `POST /api/mdm/maintenance-plans` | +| 2. 定时调度检查 | 维保域 | MaintenanceScheduler检查到期计划 | 定时任务(Cron) | +| 3. 自动生成工单 | 运营域 | 根据计划创建维保工单 | `POST /api/ops/maintenance-tasks` | +| 4. 自动派单 | 运营域 | 按计划指定维保人员/供应商 | `POST /api/ops/maintenance-tasks/{id}/assign` | +| 5. 执行维保 | 运营域 | 维保人员执行 | `POST /api/ops/maintenance-tasks/{id}/start` | +| 6. 记录备件消耗 | 备件域 | 出库记录 | `POST /api/ops/spare-parts/out-stock` | +| 7. 完成维保 | 运营域 | 填写维保结果 | `POST /api/ops/maintenance-tasks/{id}/complete-details` | +| 8. 验收 | 运营域 | 验收确认 | `POST /api/ops/maintenance-tasks/{id}/verify` | +| 9. 更新计划 | 维保域 | 更新lastDate和nextDate | `PUT /api/mdm/maintenance-plans/{id}` | +| 10. 更新设备 | 设备域 | 更新设备维保信息 | `PUT /api/asset/equipment/{id}` | + +### 3.3 数据流图 + +```mermaid +flowchart TD + A[管理员创建维保计划] -->|POST /api/mdm/maintenance-plans| B[计划存储 ACTIVE] + B --> C{MaintenanceScheduler} + C -->|每天凌晨检查| D{nextDate <= 今天?} + D -->|是| E{计划状态=ACTIVE?} + D -->|否| C + E -->|是| F[自动生成维保工单] + E -->|否| C + F -->|POST /api/ops/maintenance-tasks| G[工单 PENDING] + G -->|POST /api/ops/maintenance-tasks/id/assign| H[派发 ASSIGNED] + H -->|POST /api/ops/maintenance-tasks/id/start| I[执行中 IN_PROGRESS] + I --> J{需要备件?} + J -->|是| K[备件出库] + K -->|POST /api/ops/spare-parts/out-stock| L[更新库存] + J -->|否| M[填写维保结果] + L --> M + M -->|POST /api/ops/maintenance-tasks/id/complete-details| N[已完成 COMPLETED] + N -->|POST /api/ops/maintenance-tasks/id/verify| O[已验收 VERIFIED] + O -->|PUT /api/mdm/maintenance-plans/id| P[更新计划lastDate/nextDate] + P -->|PUT /api/asset/equipment/id| Q[更新设备维保信息] + Q --> C + + style C fill:#fff3e0 + style F fill:#e3f2fd + style O fill:#e0f7fa +``` + +### 3.4 定时调度设计(MaintenanceScheduler) + +**调度器配置**: + +| 配置项 | 值 | 说明 | +|--------|-----|------| +| Cron表达式 | `0 0 6 * * ?` | 每天凌晨6点执行 | +| 检查范围 | 所有ACTIVE状态的维保计划 | nextDate <= 当天 | +| 生成策略 | 每个到期计划生成一个维保工单 | triggerType = PLAN | +| 防重复 | 检查同一计划+同一天是否已生成工单 | 避免重复生成 | +| 通知 | 生成工单后通知项目管理员 | 待实现 | + +**调度逻辑伪代码**: + +``` +MaintenanceScheduler.execute(): + 1. 查询所有 ACTIVE 状态的维保计划 + 2. 过滤 nextDate <= 今天 的计划 + 3. 对每个到期计划: + a. 检查是否已生成工单(planId + 当天日期) + b. 若未生成,创建维保工单: + - taskType = PREVENTIVE + - triggerType = PLAN + - equipmentId = plan.equipmentId + - title = "预防性维护: " + plan.planName + - assignedVendor = plan.assignedVendor + c. 更新计划 lastDate = 今天, nextDate = 今天 + plan.cycleDays + 4. 发送通知给相关项目管理员 +``` + +**nextDate计算规则**: + +``` +nextDate = lastDate + cycleDays +首次: nextDate = 创建日期 + cycleDays +完成后: nextDate = 实际完成日期 + cycleDays +``` + +### 3.5 异常场景处理 + +| 异常场景 | 触发条件 | 处理策略 | +|---------|---------|---------| +| 计划已暂停 | 计划状态为SUSPENDED | 跳过该计划,不生成工单 | +| 计划已停用 | 计划状态为INACTIVE | 跳过该计划,不生成工单 | +| 设备已报废 | 关联设备状态为SCRAPPED | 自动暂停计划,通知管理员 | +| 重复生成 | 同一计划同一天已生成工单 | 跳过,幂等处理 | +| 供应商不可用 | assignedVendor对应的供应商已失效 | 生成工单但不自动派单,通知管理员手动派单 | +| 备件不足 | 维保所需备件库存不足 | 工单挂起SUSPENDED,等待备件入库 | +| 维保超时 | 工单超过预估时间未完成 | 通知项目经理,标记超时 | +| 调度服务宕机 | 定时任务未执行 | 启动时补偿执行,检查遗漏的计划 | + +### 3.6 权限控制 + +| 角色 | 可执行操作 | 数据范围 | +|------|-----------|---------| +| 系统管理员(SYS_ADMIN) | 创建/修改/删除维保计划 | ALL | +| 项目管理员(PROJECT_ADMIN) | 创建/修改本项目维保计划 | PROJECT | +| 工程人员(ENGINEERING) | 查看维保计划 | PROJECT | +| 维保人员(MAINTENANCE) | 执行分配的维保工单 | SELF | +| 外部供应商 | 执行指派的维保工单 | SELF(通过assignedVendor) | + +--- + +## 四、巡检异常处理流程 + +### 4.1 流程概述 + +巡检异常处理流程描述巡检过程中发现异常后的上报和处理机制: + +**巡检执行 -> 发现异常 -> 上报异常 -> 创建工单 -> 处理异常 -> 复检确认** + +### 4.2 涉及域和功能点 + +| 步骤 | 涉及域 | 功能点 | API端点 | +|------|--------|--------|---------| +| 1. 加载巡检模板 | 巡检域 | 获取点检模板 | `GET /api/ops/inspection-templates?projectId=` | +| 2. 执行巡检 | 巡检域 | 创建巡检记录 | `POST /api/mdm/inspection-records` | +| 3. 记录巡检项 | 巡检域 | 逐项检查并记录 | InspectionRecordItem[] | +| 4. 发现异常 | 巡检域 | 标记异常项 + 问题描述 | problems[] + status=ABNORMAL | +| 5. 完成巡检 | 巡检域 | 提交巡检结果 | `POST /api/mdm/inspection-records/{id}/complete` | +| 6. 自动创建工单 | 运营域 | 根据异常自动创建工单 | `POST /api/wo/work-orders` | +| 7. 派单处理 | 运营域 | 指派维修人员 | `POST /api/wo/work-orders/{id}/assign` | +| 8. 维修执行 | 运营域 | 执行维修 | `POST /api/wo/work-orders/{id}/start` -> `complete` | +| 9. 复检 | 巡检域 | 对异常项进行复检 | `POST /api/mdm/inspection-records` (复检记录) | +| 10. 更新设备状态 | 设备域 | 更新设备运行状态 | `PUT /api/asset/equipment/{id}` | + +### 4.3 数据流图 + +```mermaid +flowchart TD + A[巡检人员选择设备] -->|GET /api/ops/inspection-templates| B[加载点检模板] + B --> C[逐项检查] + C --> D{检查结果} + D -->|全部正常| E[标记NORMAL] + D -->|部分预警| F[标记WARNING] + D -->|发现异常| G[标记ABNORMAL] + + E -->|POST /api/mdm/inspection-records/id/complete| H[巡检完成] + F --> H + G --> I[记录异常详情] + I -->|problems数组| J[提交巡检记录] + J -->|POST /api/mdm/inspection-records/id/complete| K[巡检完成 ABNORMAL] + + K --> L{异常严重度} + L -->|HIGH 严重| M[自动创建紧急工单] + L -->|MEDIUM 中等| N[自动创建普通工单] + L -->|LOW 轻微| O[记录待处理] + + M -->|POST /api/wo/work-orders priority=URGENT| P[工单 PENDING] + N -->|POST /api/wo/work-orders priority=MEDIUM| P + O --> Q[定期汇总处理] + + P -->|POST /api/wo/work-orders/id/assign| R[派发 ASSIGNED] + R -->|POST /api/wo/work-orders/id/start| S[执行中 IN_PROGRESS] + S -->|POST /api/wo/work-orders/id/complete| T[已完成 COMPLETED] + T -->|POST /api/wo/work-orders/id/verify| U[已验收 VERIFIED] + + U --> V[复检] + V -->|POST /api/mdm/inspection-records 复检记录| W{复检结果} + W -->|通过| X[更新设备状态正常] + W -->|不通过| Y[重新创建工单] + X -->|PUT /api/asset/equipment/id| Z[流程结束] + Y --> P + + style G fill:#ffebee + style K fill:#fff3e0 + style M fill:#ffcdd2 + style X fill:#e8f5e9 +``` + +### 4.4 异常场景处理 + +| 异常场景 | 触发条件 | 处理策略 | +|---------|---------|---------| +| 巡检模板缺失 | 设备类型无对应模板 | 允许自由录入巡检项,提示管理员创建模板 | +| 巡检中断 | 巡检过程中网络断开 | 本地暂存巡检数据,恢复后提交 | +| 异常误报 | 巡检人员误判为异常 | 复检时纠正,工单可取消 | +| 工单创建失败 | 自动创建工单时后端异常 | 巡检记录仍保存,手动创建工单 | +| 维修后复检不通过 | 异常未完全修复 | 重新创建工单,升级优先级 | +| 多次复检不通过 | 同一异常反复出现 | 标记为"顽固故障",建议设备报废评估 | +| 巡检人员不足 | 无可用巡检人员 | 延迟巡检,通知项目经理 | +| 设备无法巡检 | 设备位置不可达或停机 | 记录原因,标记为"待巡检" | + +### 4.5 权限控制 + +| 角色 | 可执行操作 | 数据范围 | +|------|-----------|---------| +| 巡检人员(INSPECTOR) | 执行巡检、记录异常 | SELF(分配给自己的巡检任务) | +| 项目管理员(PROJECT_ADMIN) | 查看巡检结果、派单处理 | PROJECT | +| 维保人员(MAINTENANCE) | 处理异常工单 | SELF(分配给自己的工单) | +| 系统管理员(SYS_ADMIN) | 管理巡检模板 | ALL | + +--- + +## 五、能耗计费流程 + +### 5.1 流程概述 + +能耗计费流程覆盖从抄表到最终支付的完整计费链路,部分环节已实现: + +**抄表录入 -> 消耗量计算 -> 费用计算 -> 账单生成 -> 催缴提醒 -> 支付 -> 滞纳金计算 [部分实现]** + +### 5.2 涉及域和功能点 + +| 步骤 | 涉及域 | 功能点 | 实现状态 | API端点 | +|------|--------|--------|---------|---------| +| 1. 计量点管理 | MDM域 | 创建/管理计量点 | 已实现 | `POST/GET/PUT/DELETE /api/ops/energy/meters` | +| 2. 抄表录入 | MDM域 | 录入能耗读数 | 已实现 | `POST /api/ops/energy/consumption` | +| 3. 消耗量计算 | MDM域 | currentReading - previousReading | 已实现 | 自动计算 | +| 4. 费用计算 | MDM域 | consumption * unitPrice | 已实现 | 自动计算 | +| 5. 按类型统计 | MDM域 | 按能源类型汇总 | 已实现(有缺陷) | `GET /api/ops/energy/statistics/by-type` | +| 6. 单方能耗 | MDM域 | 项目总消耗/总面积 | 已实现 | `GET /api/ops/energy/statistics/unit-consumption` | +| 7. 收费项目配置 | 财务域 | 创建按用量计费的收费项目 | 未实现 | `POST /api/finance/fee-items` | +| 8. 账单生成 | 财务域 | 根据能耗数据生成账单 | 未实现 | `POST /api/finance/bills/generate` | +| 9. 催缴提醒 | 财务域 | 到期/逾期提醒 | 未实现 | 定时任务 | +| 10. 滞纳金计算 | 财务域 | 逾期天数 * 日利率 | 未实现 | 定时任务 | +| 11. 线下收款 | 财务域 | 登记支付 | 未实现 | `POST /api/finance/payments` | +| 12. 线上支付 | 财务域 | 微信/支付宝 | 未实现 | `POST /api/finance/payments/online/prepare` | +| 13. 退款 | 财务域 | 退款申请/审批/执行 | 未实现 | `POST /api/finance/refunds` | + +### 5.3 数据流图 + +```mermaid +flowchart TD + subgraph 已实现 + A[创建计量点] -->|POST /api/ops/energy/meters| B[EnergyMeter] + B --> C[抄表录入] + C -->|POST /api/ops/energy/consumption| D[EnergyConsumption] + D -->|自动计算| E[consumption = current - previous] + E -->|自动计算| F[amount = consumption * unitPrice] + F --> G[能耗统计] + G -->|GET /api/ops/energy/statistics/by-type| H[按类型汇总] + G -->|GET /api/ops/energy/statistics/unit-consumption| I[单方能耗] + end + + subgraph 未实现 + F --> J[收费项目配置] + J -->|POST /api/finance/fee-items type=ELECTRICITY/WATER/GAS billingMethod=USAGE| K[FeeItem] + K --> L[账单自动生成] + L -->|POST /api/finance/bills/generate sourceType=AUTO sourceId=EnergyConsumption.id| M[FeeBill UNPAID] + M --> N{到期提醒} + N -->|到期前3天| O[发送提醒] + N -->|逾期| P[计算滞纳金] + P -->|lateFee = 逾期天数 * payableAmount * lateFeeRate| Q[更新滞纳金] + M --> R[支付] + R -->|线下| S[登记收款 POST /api/finance/payments] + R -->|线上| T[发起支付 POST /api/finance/payments/online/prepare] + S --> U[FeePayment SUCCESS] + T --> V[等待回调] + V --> U + U --> W[更新账单状态 PAID] + U --> X{需要退款?} + X -->|是| Y[申请退款 POST /api/finance/refunds] + Y --> Z[审批] + Z --> AA[执行退款] + X -->|否| AB[流程结束] + end + + style A fill:#e8f5e9 + style B fill:#e8f5e9 + style C fill:#e8f5e9 + style D fill:#e8f5e9 + style E fill:#e8f5e9 + style F fill:#e8f5e9 + style G fill:#e8f5e9 + style H fill:#e8f5e9 + style I fill:#e8f5e9 + style J fill:#fff3e0 + style K fill:#fff3e0 + style L fill:#fff3e0 + style M fill:#fff3e0 + style N fill:#fff3e0 + style O fill:#fff3e0 + style P fill:#fff3e0 + style Q fill:#fff3e0 + style R fill:#fff3e0 + style S fill:#fff3e0 + style T fill:#fff3e0 + style U fill:#fff3e0 + style V fill:#fff3e0 + style W fill:#fff3e0 + style X fill:#fff3e0 + style Y fill:#fff3e0 + style Z fill:#fff3e0 + style AA fill:#fff3e0 + style AB fill:#fff3e0 +``` + +### 5.4 异常场景处理 + +| 异常场景 | 触发条件 | 处理策略 | 实现状态 | +|---------|---------|---------|---------| +| 读数递减 | 当前读数小于上次读数 | 拒绝录入,返回错误码6103 | 已实现 | +| 仪表不存在 | meterId在数据库中不存在 | 拒绝录入,返回错误码6101 | 已实现 | +| 仪表已停用 | 仪表状态为INACTIVE | 拒绝录入,返回错误码6102 | 已实现 | +| 单价未设置 | meter.unitPrice为null | amount设为0 | 已实现 | +| 按类型统计错误 | 全部归入LIGHTING | 修复为按meter.energyType分项汇总 | 待修复 | +| 前后端枚举不一致 | 后端6种 vs 前端5种 | 统一枚举定义 | 待修复 | +| 能耗数据缺失 | 账期内无抄表记录 | 生成0元账单,标记"无抄表数据" | 未实现 | +| 业主无关联房产 | 生成账单时业主无房产 | 跳过该业主,记录日志 | 未实现 | +| 重复生成账单 | 同一收费项目+业主+账期 | 幂等校验,跳过已存在的账单 | 未实现 | +| 支付超时 | 线上支付未收到回调 | 对账任务检查,人工介入 | 未实现 | +| 滞纳金溢出 | 计算结果超过maxLateFee | 截断为maxLateFee上限 | 未实现 | +| 部分支付后退款 | 业主部分支付后申请退款 | 按支付记录逐笔退款 | 未实现 | + +### 5.5 权限控制 + +| 角色 | 可执行操作 | 数据范围 | +|------|-----------|---------| +| 系统管理员(SYS_ADMIN) | 管理计量点、收费项目、账单 | ALL | +| 项目管理员(PROJECT_ADMIN) | 查看本项目能耗和账单 | PROJECT | +| 财务人员(FINANCE_STAFF) | 收费登记、账单管理 | PROJECT | +| 财务主管(FINANCE_SUPERVISOR) | 退款审批、财务报表 | PROJECT | +| 业主(OWNER) | 查看自己的账单、在线缴费 | SELF | +| 巡检/维保人员 | 抄表录入 | SELF(分配给自己的) | + +**已实现的权限控制**: + +- 计量点管理:无特定角色限制(仅要求已登录) +- 能耗录入:无特定角色限制(仅要求已登录) +- 能耗统计:无特定角色限制(仅要求已登录) + +**待实现的权限控制**: + +- 收费项目管理:finance:fee-item:* 权限 +- 账单管理:finance:bill:* 权限 +- 支付管理:finance:payment:* 权限 +- 退款管理:finance:refund:* 权限 +- 业主端:仅查看自己的账单和支付记录 diff --git a/02-DESIGN/detail/DETAIL-FINANCE.md b/02-DESIGN/detail/DETAIL-FINANCE.md new file mode 100644 index 0000000..d9475fd --- /dev/null +++ b/02-DESIGN/detail/DETAIL-FINANCE.md @@ -0,0 +1,810 @@ +# 财务与收费域 - 详细设计 + +**文档类型**: 详细设计文档 +**生成日期**: 2026-05-18 +**数据来源**: REVERSE-FINANCE.md(反推文档)+ 04-FINANCE.md(原设计文档)+ 实际代码分析 + +--- + +## 一、功能点清单 + +| 编号 | 功能模块 | 功能点 | 实现状态 | 说明 | +|------|---------|--------|---------|------| +| FIN-001 | 能耗计量管理 | 计量点CRUD | 已实现 | module-mdm 中 EnergyMeter | +| FIN-002 | 能耗计量管理 | 计量点编码自动生成 | 已实现 | EM + yyyyMMddHHmmss | +| FIN-003 | 能耗计量管理 | 计量点软删除 | 已实现 | 状态设为 INACTIVE | +| FIN-004 | 能耗录入 | 能耗数据录入 | 已实现 | 手动录入 + IoT预留 | +| FIN-005 | 能耗录入 | 读数递增校验 | 已实现 | 当前读数 >= 上次读数 | +| FIN-006 | 能耗录入 | 自动计算消耗量与费用 | 已实现 | consumption = current - previous; amount = consumption * unitPrice | +| FIN-007 | 能耗统计 | 按类型统计消耗 | 已实现(有缺陷) | 当前全部归入LIGHTING,需修复 | +| FIN-008 | 能耗统计 | 单方能耗 | 已实现 | 遍历项目ACTIVE计量点累加 | +| FIN-009 | 收费项目管理 | 收费项目CRUD | 未实现 | FeeItem 实体不存在 | +| FIN-010 | 收费项目管理 | 周期性收费配置 | 未实现 | 按月/季/年收费 | +| FIN-011 | 收费项目管理 | 一次性收费配置 | 未实现 | 临时收费项目 | +| FIN-012 | 收费项目管理 | 按面积计费配置 | 未实现 | 面积 * 单价 | +| FIN-013 | 收费项目管理 | 按用量计费配置 | 未实现 | 用量 * 单价(对接能耗) | +| FIN-014 | 收费项目管理 | 固定金额计费配置 | 未实现 | 每月固定金额 | +| FIN-015 | 账单管理 | 账单自动生成 | 未实现 | 根据收费项目规则按账期生成 | +| FIN-016 | 账单管理 | 账单手动生成 | 未实现 | 人工创建单笔账单 | +| FIN-017 | 账单管理 | 批量账单生成 | 未实现 | 一次性为项目所有业主生成 | +| FIN-018 | 账单管理 | 账单状态流转 | 未实现 | UNPAID/PARTIAL_PAID/PAID/OVERDUE/CANCELLED | +| FIN-019 | 账单管理 | 催缴提醒 | 未实现 | 到期前3天提醒 + 逾期催缴 | +| FIN-020 | 账单管理 | 账单导出 | 未实现 | Excel/PDF 格式 | +| FIN-021 | 支付管理 | 线下收款登记 | 未实现 | 现金/银行转账/刷卡 | +| FIN-022 | 支付管理 | 线上支付对接 | 未实现 | 微信/支付宝 | +| FIN-023 | 支付管理 | 支付确认 | 未实现 | 支付状态确认 | +| FIN-024 | 支付管理 | 支付记录查询 | 未实现 | 按账单/业主/时间查询 | +| FIN-025 | 退款管理 | 退款申请 | 未实现 | 业主发起退款 | +| FIN-026 | 退款管理 | 退款审核 | 未实现 | 审批流程 | +| FIN-027 | 退款管理 | 退款执行 | 未实现 | 原路退回/线下退款 | +| FIN-028 | 滞纳金 | 滞纳金自动计算 | 未实现 | 逾期天数 * 日利率 | +| FIN-029 | 滞纳金 | 滞纳金上限控制 | 未实现 | 不超过 maxLateFee | +| FIN-030 | 能耗对接 | 能耗数据与账单对接 | 未实现 | EnergyConsumption -> FeeBill | +| FIN-031 | 能耗修复 | 修复按类型统计 | 未实现 | 按 meter.energyType 真正分项汇总 | +| FIN-032 | 能耗修复 | 统一前后端能源类型枚举 | 未实现 | 后端6种 vs 前端5种 | +| FIN-033 | 财务报表 | 收费统计 | 未实现 | 按项目/类型/时间维度 | +| FIN-034 | 财务报表 | 欠费分析 | 未实现 | 逾期/欠费金额统计 | +| FIN-035 | 财务报表 | 收入趋势 | 未实现 | 月度/季度趋势图 | + +--- + +## 二、数据结构设计 + +### 2.1 已实现实体 + +#### 2.1.1 EnergyMeter(能源计量点) + +**所在模块**: module-mdm +**数据库表**: `ops_energy_meter` +**源码**: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/EnergyMeter.java` + +| 字段名 | 类型 | 数据库列名 | 约束 | 说明 | +|--------|------|-----------|------|------| +| id | UUID | id | PK | 主键 | +| projectId | UUID | project_id | NOT NULL | 所属项目 | +| meterCode | String | meter_code | NOT NULL, UNIQUE | 计量点编码(EM + yyyyMMddHHmmss) | +| meterName | String | meter_name | NOT NULL | 计量点名称 | +| energyType | EnergyType | energy_type | NOT NULL | 能源类型枚举 | +| spaceNodeId | UUID | space_node_id | -- | 关联空间节点 | +| installationLocation | String | installation_location | -- | 安装位置 | +| ratedCapacity | BigDecimal(10,2) | rated_capacity | -- | 额定容量 | +| unitPrice | BigDecimal(10,4) | unit_price | -- | 单价(4位小数精度) | +| status | Status | status | NOT NULL, 默认ACTIVE | 状态枚举 | +| createdAt | LocalDateTime | created_at | -- | 创建时间 | +| updatedAt | LocalDateTime | updated_at | -- | 更新时间 | + +**枚举定义**: + +```java +public enum EnergyType { + LIGHTING("照明插座用电"), + HVAC("空调用电"), + POWER("动力用电"), + SPECIAL("特殊用电"), + WATER("给排水"), + GAS("燃气"); +} + +public enum Status { + ACTIVE, INACTIVE +} +``` + +#### 2.1.2 EnergyConsumption(能耗记录) + +**所在模块**: module-mdm +**数据库表**: `ops_energy_consumption` +**源码**: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/EnergyConsumption.java` + +| 字段名 | 类型 | 数据库列名 | 约束 | 说明 | +|--------|------|-----------|------|------| +| id | UUID | id | PK | 主键 | +| projectId | UUID | project_id | NOT NULL | 所属项目 | +| meterId | UUID | meter_id | NOT NULL | 关联计量点 | +| consumptionDate | LocalDate | consumption_date | NOT NULL | 记录日期 | +| previousReading | BigDecimal(12,2) | previous_reading | -- | 上次读数 | +| currentReading | BigDecimal(12,2) | current_reading | -- | 当前读数 | +| consumption | BigDecimal(12,2) | consumption | NOT NULL | 消耗量 = current - previous | +| amount | BigDecimal(10,2) | amount | -- | 费用 = consumption * unitPrice | +| recordedBy | UUID | recorded_by | -- | 记录人 | +| recordMethod | RecordMethod | record_method | -- | 记录方式,默认MANUAL | +| remarks | String(1000) | remarks | -- | 备注 | +| createdAt | LocalDateTime | created_at | -- | 创建时间 | + +**数据库索引**: + +```sql +idx_ec_meter_date -- (meter_id, consumption_date) +idx_ec_project_date -- (project_id, consumption_date) +``` + +**枚举定义**: + +```java +public enum RecordMethod { + MANUAL, // 手动录入 + IOT // IoT自动采集 +} +``` + +--- + +### 2.2 待实现实体 + +#### 2.2.1 FeeItem(收费项目) + +**目标模块**: module-finance(待创建) +**目标表**: `fin_fee_item` + +| 字段名 | 类型 | 数据库列名 | 约束 | 说明 | +|--------|------|-----------|------|------| +| id | UUID | id | PK | 主键 | +| projectId | UUID | project_id | NOT NULL | 所属项目 | +| code | VARCHAR(50) | code | NOT NULL, UNIQUE | 收费项目编码(FI + yyyyMMddHHmmss) | +| name | VARCHAR(100) | name | NOT NULL | 收费项目名称 | +| type | VARCHAR(20) | type | NOT NULL | 收费类型枚举 | +| billingMethod | VARCHAR(20) | billing_method | NOT NULL | 计费方式枚举 | +| unitPrice | DECIMAL(12,4) | unit_price | -- | 单价(按用量/面积计费时使用) | +| unit | VARCHAR(20) | unit | -- | 计量单位(kWh/吨/平方米/月/次) | +| fixedAmount | DECIMAL(12,2) | fixed_amount | -- | 固定金额(FIXED计费方式) | +| billDay | INT | bill_day | -- | 出账日(每月几号出账,1-28) | +| dueDay | INT | due_day | -- | 到期日(出账后第N天到期) | +| overdueDay | INT | overdue_day | -- | 逾期日(到期后第N天开始计滞纳金) | +| enableLateFee | BOOLEAN | enable_late_fee | NOT NULL, 默认false | 是否启用滞纳金 | +| lateFeeRate | DECIMAL(8,6) | late_fee_rate | -- | 日滞纳金利率(如0.0005 = 万分之五/天) | +| maxLateFee | DECIMAL(12,2) | max_late_fee | -- | 滞纳金上限金额 | +| description | VARCHAR(500) | description | -- | 收费项目说明 | +| status | VARCHAR(20) | status | NOT NULL, 默认ENABLED | 状态:ENABLED/DISABLED | +| createdAt | TIMESTAMP | created_at | NOT NULL | 创建时间 | +| updatedAt | TIMESTAMP | updated_at | NOT NULL | 更新时间 | +| createdBy | UUID | created_by | -- | 创建人 | + +**枚举定义**: + +```java +public enum FeeType { + PROPERTY("物业费"), + PARKING("停车费"), + WATER("水费"), + ELECTRICITY("电费"), + GAS("燃气费"), + HEATING("供暖费"), + REPAIR_FUND("维修基金"), + OTHER("其他"); +} + +public enum BillingMethod { + FIXED("固定金额"), // 每月固定金额 + AREA("按面积计费"), // 面积 * 单价 + USAGE("按用量计费"), // 用量 * 单价(对接能耗) + CUSTOM("自定义"); // 自定义计费规则 +} +``` + +**数据库索引**: + +```sql +idx_fi_project_status -- (project_id, status) +idx_fi_code -- (code) -- UNIQUE约束自带索引 +idx_fi_type -- (type, status) +``` + +**外键关系**: + +```sql +fk_fi_project -- project_id REFERENCES mdm_project(id) +``` + +--- + +#### 2.2.2 FeeBill(收费账单) + +**目标模块**: module-finance(待创建) +**目标表**: `fin_fee_bill` + +| 字段名 | 类型 | 数据库列名 | 约束 | 说明 | +|--------|------|-----------|------|------| +| id | UUID | id | PK | 主键 | +| billNo | VARCHAR(30) | bill_no | NOT NULL, UNIQUE | 账单编号(BL + yyyyMMdd + 4位序号) | +| feeItemId | UUID | fee_item_id | NOT NULL | 关联收费项目 | +| projectId | UUID | project_id | NOT NULL | 所属项目 | +| spaceNodeId | UUID | space_node_id | -- | 关联空间节点(房产) | +| ownerId | UUID | owner_id | -- | 关联业主ID | +| billPeriod | VARCHAR(20) | bill_period | NOT NULL | 账期(如2026-05) | +| billDate | DATE | bill_date | NOT NULL | 出账日期 | +| dueDate | DATE | due_date | NOT NULL | 到期日期 | +| overdueDate | DATE | overdue_date | -- | 逾期日期 | +| usageAmount | DECIMAL(12,2) | usage_amount | -- | 用量(按用量计费时) | +| usageUnit | VARCHAR(20) | usage_unit | -- | 用量单位 | +| area | DECIMAL(12,2) | area | -- | 面积(按面积计费时) | +| amount | DECIMAL(12,2) | amount | NOT NULL | 应收金额 | +| lateFee | DECIMAL(12,2) | late_fee | 默认0 | 滞纳金 | +| discount | DECIMAL(12,2) | discount | 默认0 | 优惠金额 | +| payableAmount | DECIMAL(12,2) | payable_amount | NOT NULL | 应付金额 = amount + lateFee - discount | +| paidAmount | DECIMAL(12,2) | paid_amount | 默认0 | 已付金额 | +| status | VARCHAR(20) | status | NOT NULL, 默认UNPAID | 账单状态枚举 | +| sourceType | VARCHAR(20) | source_type | -- | 来源类型:AUTO/MANUAL/IMPORT | +| sourceId | UUID | source_id | -- | 来源ID(如能耗记录ID) | +| remark | VARCHAR(500) | remark | -- | 备注 | +| createdAt | TIMESTAMP | created_at | NOT NULL | 创建时间 | +| updatedAt | TIMESTAMP | updated_at | NOT NULL | 更新时间 | +| createdBy | UUID | created_by | -- | 创建人 | + +**枚举定义**: + +```java +public enum BillStatus { + UNPAID("未支付"), + PARTIAL_PAID("部分支付"), + PAID("已支付"), + OVERDUE("已逾期"), + CANCELLED("已取消"); +} + +public enum BillSourceType { + AUTO("自动生成"), + MANUAL("手动创建"), + IMPORT("导入"); +} +``` + +**数据库索引**: + +```sql +idx_fb_bill_no -- (bill_no) -- UNIQUE约束自带索引 +idx_fb_project_period -- (project_id, bill_period) +idx_fb_owner_status -- (owner_id, status) +idx_fb_fee_item -- (fee_item_id, bill_period) +idx_fb_space_node -- (space_node_id, bill_period) +idx_fb_due_date -- (due_date, status) -- 用于催缴查询 +idx_fb_overdue -- (status, overdue_date) -- 用于滞纳金计算 +``` + +**外键关系**: + +```sql +fk_fb_fee_item -- fee_item_id REFERENCES fin_fee_item(id) +fk_fb_project -- project_id REFERENCES mdm_project(id) +fk_fb_space_node -- space_node_id REFERENCES mdm_space_node(id) +``` + +--- + +#### 2.2.3 FeePayment(支付记录) + +**目标模块**: module-finance(待创建) +**目标表**: `fin_fee_payment` + +| 字段名 | 类型 | 数据库列名 | 约束 | 说明 | +|--------|------|-----------|------|------| +| id | UUID | id | PK | 主键 | +| paymentNo | VARCHAR(30) | payment_no | NOT NULL, UNIQUE | 支付编号(PY + yyyyMMddHHmmss) | +| billId | UUID | bill_id | NOT NULL | 关联账单 | +| amount | DECIMAL(12,2) | amount | NOT NULL | 支付金额 | +| method | VARCHAR(20) | method | NOT NULL | 支付方式枚举 | +| thirdPartyNo | VARCHAR(100) | third_party_no | -- | 第三方交易号(微信/支付宝) | +| status | VARCHAR(20) | status | NOT NULL, 默认PENDING | 支付状态枚举 | +| failReason | VARCHAR(500) | fail_reason | -- | 失败原因 | +| paymentTime | TIMESTAMP | payment_time | -- | 实际支付时间 | +| operatorId | UUID | operator_id | -- | 操作人(线下收款时为收费员) | +| receiptNo | VARCHAR(30) | receipt_no | -- | 收据编号 | +| remark | VARCHAR(500) | remark | -- | 备注 | +| createdAt | TIMESTAMP | created_at | NOT NULL | 创建时间 | +| updatedAt | TIMESTAMP | updated_at | NOT NULL | 更新时间 | + +**枚举定义**: + +```java +public enum PaymentMethod { + WECHAT("微信支付"), + ALIPAY("支付宝"), + CASH("现金"), + BANK_TRANSFER("银行转账"), + CARD("刷卡"); +} + +public enum PaymentStatus { + PENDING("待支付"), + SUCCESS("支付成功"), + FAILED("支付失败"), + REFUNDED("已退款"); +} +``` + +**数据库索引**: + +```sql +idx_fp_payment_no -- (payment_no) -- UNIQUE约束自带索引 +idx_fp_bill_id -- (bill_id) +idx_fp_status -- (status, payment_time) +idx_fp_method -- (method, payment_time) +idx_fp_operator -- (operator_id, payment_time) +``` + +**外键关系**: + +```sql +fk_fp_bill -- bill_id REFERENCES fin_fee_bill(id) +``` + +--- + +#### 2.2.4 FeeRefund(退款记录) + +**目标模块**: module-finance(待创建) +**目标表**: `fin_fee_refund` + +| 字段名 | 类型 | 数据库列名 | 约束 | 说明 | +|--------|------|-----------|------|------| +| id | UUID | id | PK | 主键 | +| refundNo | VARCHAR(30) | refund_no | NOT NULL, UNIQUE | 退款编号(RF + yyyyMMddHHmmss) | +| paymentId | UUID | payment_id | NOT NULL | 关联支付记录 | +| billId | UUID | bill_id | NOT NULL | 关联账单 | +| amount | DECIMAL(12,2) | amount | NOT NULL | 退款金额 | +| reason | VARCHAR(500) | reason | NOT NULL | 退款原因 | +| status | VARCHAR(20) | status | NOT NULL, 默认PENDING | 退款状态枚举 | +| applicantId | UUID | applicant_id | NOT NULL | 申请人ID | +| approverId | UUID | approver_id | -- | 审批人ID | +| approveTime | TIMESTAMP | approve_time | -- | 审批时间 | +| approveRemark | VARCHAR(500) | approve_remark | -- | 审批备注 | +| thirdPartyRefundNo | VARCHAR(100) | third_party_refund_no | -- | 第三方退款单号 | +| refundTime | TIMESTAMP | refund_time | -- | 实际退款时间 | +| refundMethod | VARCHAR(20) | refund_method | -- | 退款方式:ORIGINAL/OFFLINE | +| remark | VARCHAR(500) | remark | -- | 备注 | +| createdAt | TIMESTAMP | created_at | NOT NULL | 创建时间 | +| updatedAt | TIMESTAMP | updated_at | NOT NULL | 更新时间 | + +**枚举定义**: + +```java +public enum RefundStatus { + PENDING("待审批"), + APPROVED("已审批"), + REJECTED("已拒绝"), + REFUNDED("已退款"), + FAILED("退款失败"); +} + +public enum RefundMethod { + ORIGINAL("原路退回"), + OFFLINE("线下退款"); +} +``` + +**数据库索引**: + +```sql +idx_fr_refund_no -- (refund_no) -- UNIQUE约束自带索引 +idx_fr_payment -- (payment_id) +idx_fr_bill -- (bill_id) +idx_fr_status -- (status) +idx_fr_applicant -- (applicant_id, status) +``` + +**外键关系**: + +```sql +fk_fr_payment -- payment_id REFERENCES fin_fee_payment(id) +fk_fr_bill -- bill_id REFERENCES fin_fee_bill(id) +``` + +--- + +### 2.3 实体关系图 + +``` +EnergyMeter (module-mdm) FeeItem (module-finance) + | | + v v +EnergyConsumption ---费用计算---> FeeBill (自动/手动生成) + (抄表数据) | | + | +---> FeePayment (支付记录) + | | + | v + | FeeRefund (退款记录) + | + +-- sourceType=AUTO, sourceId=EnergyConsumption.id +``` + +**跨模块引用关系**: + +- FeeBill.sourceId 可指向 EnergyConsumption.id(按用量计费时) +- FeeBill.spaceNodeId 引用 mdm_space_node.id +- FeeBill.ownerId 引用 auth_user.id(业主) +- FeePayment.operatorId 引用 auth_user.id(收费员) +- FeeRefund.applicantId / approverId 引用 auth_user.id + +--- + +## 三、API设计 + +### 3.1 已实现API:能耗管理相关端点 + +**基础路径**: `/api/ops/energy` +**控制器**: `EnergyController` + +#### 3.1.1 计量点管理 + +| 方法 | 路径 | 说明 | 请求参数 | 响应类型 | +|------|------|------|---------|---------| +| POST | `/meters` | 创建计量点 | EnergyMeter JSON | `ApiResponse` | +| GET | `/meters` | 查询计量点列表 | projectId(必填), energyType(可选) | `ApiResponse>` | +| GET | `/meters/{id}` | 获取计量点详情 | path: id | `ApiResponse` | +| PUT | `/meters/{id}` | 更新计量点 | path: id, EnergyMeter JSON | `ApiResponse` | +| DELETE | `/meters/{id}` | 删除计量点(软删除) | path: id | `ApiResponse` | + +#### 3.1.2 能耗记录 + +| 方法 | 路径 | 说明 | 请求参数 | 响应类型 | +|------|------|------|---------|---------| +| POST | `/consumption` | 录入能耗数据 | `{meterId, currentReading, recordedBy}` | `ApiResponse` | +| GET | `/consumption/{meterId}` | 查询能耗记录 | path: meterId, startDate(可选), endDate(可选) | `ApiResponse>` | + +#### 3.1.3 能耗统计 + +| 方法 | 路径 | 说明 | 请求参数 | 响应类型 | +|------|------|------|---------|---------| +| GET | `/statistics/by-type` | 按类型统计消耗 | projectId, month(yyyy-MM-dd) | `ApiResponse>` | +| GET | `/statistics/unit-consumption` | 单方能耗 | projectId, month(yyyy-MM-dd) | `ApiResponse` | + +--- + +### 3.2 待实现API:收费项目/账单/支付/退款相关端点 + +#### 3.2.1 FeeItemController -- 收费项目管理 + +**基础路径**: `/api/finance/fee-items` + +| 方法 | 路径 | 说明 | 请求体/参数 | 响应类型 | +|------|------|------|------------|---------| +| POST | `/` | 创建收费项目 | FeeItemForm JSON | `ApiResponse` | +| GET | `/` | 查询收费项目列表 | projectId(必填), type(可选), status(可选), page, size | `ApiResponse>` | +| GET | `/{id}` | 获取收费项目详情 | path: id | `ApiResponse` | +| PUT | `/{id}` | 更新收费项目 | path: id, FeeItemForm JSON | `ApiResponse` | +| PUT | `/{id}/status` | 启用/禁用收费项目 | `{status: "ENABLED"/"DISABLED"}` | `ApiResponse` | +| DELETE | `/{id}` | 删除收费项目 | path: id | `ApiResponse` | + +**FeeItemForm**: + +```json +{ + "projectId": "UUID", + "name": "物业费", + "type": "PROPERTY", + "billingMethod": "AREA", + "unitPrice": 3.50, + "unit": "平方米/月", + "fixedAmount": null, + "billDay": 1, + "dueDay": 15, + "overdueDay": 5, + "enableLateFee": true, + "lateFeeRate": 0.0005, + "maxLateFee": 500.00, + "description": "住宅物业费" +} +``` + +#### 3.2.2 FeeBillController -- 账单管理 + +**基础路径**: `/api/finance/bills` + +| 方法 | 路径 | 说明 | 请求体/参数 | 响应类型 | +|------|------|------|------------|---------| +| POST | `/` | 手动创建账单 | FeeBillForm JSON | `ApiResponse` | +| POST | `/generate` | 按收费项目自动生成账单 | `{projectId, feeItemId, billPeriod}` | `ApiResponse>` | +| POST | `/generate-batch` | 批量生成账单 | `{projectId, billPeriod, feeItemIds[]}` | `ApiResponse` | +| GET | `/` | 查询账单列表 | projectId, ownerId, status, billPeriod, page, size | `ApiResponse>` | +| GET | `/{id}` | 获取账单详情 | path: id | `ApiResponse` | +| PUT | `/{id}` | 更新账单 | path: id, FeeBillForm JSON | `ApiResponse` | +| POST | `/{id}/cancel` | 取消账单 | `{reason}` | `ApiResponse` | +| GET | `/overdue` | 查询逾期账单 | projectId, page, size | `ApiResponse>` | +| GET | `/statistics` | 账单统计 | projectId, billPeriod | `ApiResponse` | +| GET | `/export` | 导出账单 | projectId, billPeriod, format | `Blob` | + +**FeeBillForm**: + +```json +{ + "feeItemId": "UUID", + "projectId": "UUID", + "spaceNodeId": "UUID", + "ownerId": "UUID", + "billPeriod": "2026-05", + "usageAmount": 150.50, + "usageUnit": "kWh", + "area": 120.00, + "amount": 525.00, + "remark": "" +} +``` + +**BillStatistics**: + +```json +{ + "totalAmount": 150000.00, + "paidAmount": 120000.00, + "unpaidAmount": 30000.00, + "overdueAmount": 15000.00, + "lateFeeAmount": 500.00, + "totalCount": 200, + "paidCount": 160, + "unpaidCount": 30, + "overdueCount": 10 +} +``` + +#### 3.2.3 FeePaymentController -- 支付管理 + +**基础路径**: `/api/finance/payments` + +| 方法 | 路径 | 说明 | 请求体/参数 | 响应类型 | +|------|------|------|------------|---------| +| POST | `/` | 创建支付记录(线下收款) | PaymentForm JSON | `ApiResponse` | +| GET | `/` | 查询支付记录 | billId, ownerId, method, status, startDate, endDate, page, size | `ApiResponse>` | +| GET | `/{id}` | 获取支付详情 | path: id | `ApiResponse` | +| POST | `/{id}/confirm` | 确认支付 | `{status: "SUCCESS"/"FAILED", failReason}` | `ApiResponse` | +| POST | `/online/prepare` | 发起线上支付 | `{billId, method}` | `ApiResponse` | +| POST | `/online/callback` | 支付回调 | 第三方回调数据 | `ApiResponse` | + +**PaymentForm**: + +```json +{ + "billId": "UUID", + "amount": 525.00, + "method": "CASH", + "receiptNo": "RCP20260518001", + "remark": "" +} +``` + +#### 3.2.4 FeeRefundController -- 退款管理 + +**基础路径**: `/api/finance/refunds` + +| 方法 | 路径 | 说明 | 请求体/参数 | 响应类型 | +|------|------|------|------------|---------| +| POST | `/` | 申请退款 | RefundForm JSON | `ApiResponse` | +| GET | `/` | 查询退款记录 | billId, status, startDate, endDate, page, size | `ApiResponse>` | +| GET | `/{id}` | 获取退款详情 | path: id | `ApiResponse` | +| POST | `/{id}/approve` | 审批通过 | `{approveRemark}` | `ApiResponse` | +| POST | `/{id}/reject` | 审批拒绝 | `{approveRemark}` | `ApiResponse` | +| POST | `/{id}/execute` | 执行退款 | `{thirdPartyRefundNo, refundMethod}` | `ApiResponse` | + +**RefundForm**: + +```json +{ + "paymentId": "UUID", + "billId": "UUID", + "amount": 525.00, + "reason": "重复缴费", + "refundMethod": "ORIGINAL" +} +``` + +#### 3.2.5 FeeReminderJob -- 催缴定时任务 + +| 任务 | Cron表达式 | 说明 | +|------|-----------|------| +| 到期提醒 | `0 0 9 * * ?` | 每天上午9点检查3天内到期账单,发送提醒 | +| 逾期催缴 | `0 0 10 * * ?` | 每天上午10点检查逾期账单,发送催缴通知 | +| 滞纳金计算 | `0 0 2 * * ?` | 每天凌晨2点计算逾期账单滞纳金 | +| 周汇总 | `0 0 18 ? * MON` | 每周一18点汇总逾期账单,通知项目经理 | + +--- + +## 四、业务规则 + +### 4.1 能耗计费规则(已实现 + 待完善) + +#### 4.1.1 已实现规则 + +| 规则编号 | 规则名称 | 规则描述 | 实现位置 | +|---------|---------|---------|---------| +| E-001 | 仪表状态校验 | 只能对ACTIVE状态的仪表进行抄表 | EnergyConsumptionServiceImpl | +| E-002 | 读数递增校验 | 当前读数不能小于上次读数 | EnergyConsumptionServiceImpl | +| E-003 | 自动获取上次读数 | 从最近一条记录获取previousReading,首次为0 | EnergyConsumptionServiceImpl | +| E-004 | 自动计算消耗量 | consumption = currentReading - previousReading | EnergyConsumptionServiceImpl | +| E-005 | 自动计算费用 | amount = consumption * meter.unitPrice(单价为空时为0) | EnergyConsumptionServiceImpl | +| E-006 | 按月统计 | 根据month参数计算月份起止日期 | EnergyConsumptionServiceImpl | +| E-007 | 单方能耗 | 遍历项目所有ACTIVE计量点,累加月度消耗量 | EnergyConsumptionServiceImpl | + +#### 4.1.2 待修复缺陷 + +| 缺陷编号 | 描述 | 严重度 | 修复方案 | +|---------|------|--------|---------| +| E-BUG-001 | getConsumptionByType()将总消耗全部归入LIGHTING | 高 | 按 meter.energyType 真正分项汇总 | +| E-BUG-002 | 前后端能源类型枚举不一致 | 中 | 统一为后端6种枚举,前端对齐 | + +--- + +### 4.2 收费项目规则(待设计) + +| 规则编号 | 规则名称 | 规则描述 | 适用计费方式 | +|---------|---------|---------|------------| +| FI-001 | 周期性收费 | 按月/季/年定期出账,billDay指定出账日 | FIXED / AREA / USAGE | +| FI-002 | 一次性收费 | 临时创建,不参与自动出账 | CUSTOM | +| FI-003 | 临时收费 | 针对特定业主的临时性收费 | CUSTOM | +| FI-004 | 固定金额计费 | 每期收取固定金额(如停车费300元/月) | FIXED | +| FI-005 | 按面积计费 | 面积 * 单价(如物业费3.5元/平方米/月) | AREA | +| FI-006 | 按用量计费 | 用量 * 单价(如电费0.85元/kWh),对接能耗数据 | USAGE | +| FI-007 | 编码自动生成 | 格式:FI + yyyyMMddHHmmss,冲突时追加后缀 | 全部 | +| FI-008 | 禁用不删除 | 收费项目禁用后不再参与自动出账,已有账单不受影响 | 全部 | +| FI-009 | 项目隔离 | 收费项目属于特定项目,跨项目不可见 | 全部 | + +--- + +### 4.3 账单生成规则(待设计) + +| 规则编号 | 规则名称 | 规则描述 | +|---------|---------|---------| +| BL-001 | 自动生成 | 根据收费项目的billDay,定时任务自动为项目下所有业主生成账单 | +| BL-002 | 手动生成 | 管理员手动为指定业主创建账单 | +| BL-003 | 批量生成 | 一次性为项目所有业主生成某类收费项目的账单 | +| BL-004 | 账单编号生成 | 格式:BL + yyyyMMdd + 4位序号,序号按天递增 | +| BL-005 | 到期日计算 | dueDate = billDate + feeItem.dueDay天 | +| BL-006 | 逾期日计算 | overdueDate = dueDate + feeItem.overdueDay天 | +| BL-007 | 按用量计费账单 | 从EnergyConsumption获取当期用量,计算金额 | +| BL-008 | 按面积计费账单 | 从SpaceNode获取面积,计算金额 = 面积 * 单价 | +| BL-009 | 固定金额账单 | 金额 = feeItem.fixedAmount | +| BL-010 | 防重复生成 | 同一收费项目 + 同一业主 + 同一账期不可重复生成 | +| BL-011 | 催缴提醒 | 到期前3天发送提醒,逾期后发送催缴通知 | +| BL-012 | 账单取消 | 已支付/部分支付的账单不可取消,需先退款 | + +--- + +### 4.4 支付流程(待设计) + +| 规则编号 | 规则名称 | 规则描述 | +|---------|---------|---------| +| PY-001 | 线下收款 | 管理员登记现金/转账/刷卡收款,直接确认成功 | +| PY-002 | 线上支付 | 调用微信/支付宝SDK生成预支付单,等待回调确认 | +| PY-003 | 支付确认 | 线上支付由回调确认,线下支付由操作员确认 | +| PY-004 | 部分支付 | 支持部分支付,账单状态变为PARTIAL_PAID | +| PY-005 | 超额支付 | 支付金额不可超过应付金额(payableAmount - paidAmount) | +| PY-006 | 支付编号生成 | 格式:PY + yyyyMMddHHmmss | +| PY-007 | 账单状态联动 | 支付成功后更新账单paidAmount和status | +| PY-008 | 收据编号 | 线下收款时生成收据编号 | + +**支付状态流转**: + +``` +PENDING --(支付成功)--> SUCCESS +PENDING --(支付失败)--> FAILED +SUCCESS --(发起退款)--> REFUNDED +``` + +**账单状态联动**: + +``` +UNPAID --(部分支付)--> PARTIAL_PAID +UNPAID/PARTIAL_PAID --(全额支付)--> PAID +UNPAID --(超过逾期日)--> OVERDUE +任意状态 --(取消)--> CANCELLED(需先退款) +``` + +--- + +### 4.5 退款流程(待设计) + +| 规则编号 | 规则名称 | 规则描述 | +|---------|---------|---------| +| RF-001 | 退款申请 | 业主或管理员发起退款申请,需指定退款原因 | +| RF-002 | 退款金额限制 | 退款金额不可超过原支付金额 | +| RF-003 | 退款审批 | 退款金额 > 1000元需审批,否则自动通过 | +| RF-004 | 审批通过 | 审批人确认后,退款状态变为APPROVED | +| RF-005 | 审批拒绝 | 审批人可拒绝退款,需填写拒绝原因 | +| RF-006 | 原路退回 | 线上支付的退款原路退回至支付账户 | +| RF-007 | 线下退款 | 现金/转账支付的退款通过线下处理 | +| RF-008 | 退款执行 | 记录第三方退款单号和实际退款时间 | +| RF-009 | 账单金额回退 | 退款成功后,账单paidAmount减少,状态可能回退 | +| RF-010 | 退款编号生成 | 格式:RF + yyyyMMddHHmmss | + +**退款状态流转**: + +``` +PENDING --(审批通过)--> APPROVED +PENDING --(审批拒绝)--> REJECTED +APPROVED --(退款成功)--> REFUNDED +APPROVED --(退款失败)--> FAILED +``` + +--- + +### 4.6 滞纳金计算(待设计) + +| 规则编号 | 规则名称 | 规则描述 | +|---------|---------|---------| +| LF-001 | 计算触发 | 每天凌晨2点定时任务检查逾期账单 | +| LF-002 | 计算公式 | lateFee = 逾期天数 * payableAmount * lateFeeRate | +| LF-003 | 逾期天数 | 从overdueDate开始计算,到当前日期 | +| LF-004 | 上限控制 | lateFee不超过feeItem.maxLateFee | +| LF-005 | 累加计算 | 每天累加,不覆盖之前的滞纳金 | +| LF-006 | 前提条件 | 仅当feeItem.enableLateFee = true时计算 | +| LF-007 | 已支付账单 | 已全额支付的账单不再计算滞纳金 | +| LF-008 | 已取消账单 | 已取消的账单不再计算滞纳金 | +| LF-009 | 部分支付 | 滞纳金基于剩余应付金额计算 | + +**滞纳金计算示例**: + +``` +假设: + payableAmount = 1000元 + lateFeeRate = 0.0005(万分之五/天) + maxLateFee = 500元 + overdueDate = 2026-05-10 + 当前日期 = 2026-05-18 + +计算: + 逾期天数 = 8天 + 每日滞纳金 = 1000 * 0.0005 = 0.5元 + 累计滞纳金 = 8 * 0.5 = 4元(未超过上限500元) +``` + +--- + +## 五、执行约束 + +| 约束编号 | 约束名称 | 约束描述 | +|---------|---------|---------| +| CON-001 | 金额精度 | 所有金额字段使用DECIMAL(12,2),单价使用DECIMAL(12,4),利率使用DECIMAL(8,6) | +| CON-002 | 事务一致性 | 账单生成、支付确认、退款执行必须在同一事务中完成账单状态更新 | +| CON-003 | 并发控制 | 支付操作使用乐观锁(version字段或状态校验),防止重复支付 | +| CON-004 | 审计日志 | 所有收费/支付/退款操作必须记录审计日志 | +| CON-005 | 项目隔离 | 所有查询必须按projectId过滤,确保项目间数据隔离 | +| CON-006 | 软删除 | 收费项目使用启用/禁用替代物理删除,账单/支付/退款不允许删除 | +| CON-007 | 编码唯一性 | billNo/paymentNo/refundNo全局唯一,生成时需处理冲突 | +| CON-008 | 定时任务幂等 | 催缴/滞纳金计算任务必须幂等,重复执行不产生副作用 | + +--- + +## 六、权限控制 + +| 权限编码 | 权限名称 | 适用角色 | 说明 | +|---------|---------|---------|------| +| finance:fee-item:list | 查看收费项目 | 项目管理员/财务人员 | 查看项目下收费项目列表 | +| finance:fee-item:create | 创建收费项目 | 系统管理员 | 创建新的收费项目 | +| finance:fee-item:update | 修改收费项目 | 系统管理员 | 修改收费项目配置 | +| finance:fee-item:delete | 删除收费项目 | 系统管理员 | 删除/禁用收费项目 | +| finance:bill:list | 查看账单 | 项目管理员/财务人员 | 查看项目下账单列表 | +| finance:bill:create | 创建账单 | 财务人员 | 手动创建账单 | +| finance:bill:generate | 生成账单 | 财务人员 | 自动/批量生成账单 | +| finance:bill:cancel | 取消账单 | 财务主管 | 取消账单(需审批) | +| finance:bill:export | 导出账单 | 财务人员 | 导出账单Excel/PDF | +| finance:payment:create | 登记收款 | 财务人员 | 线下收款登记 | +| finance:payment:confirm | 确认支付 | 财务人员 | 确认支付状态 | +| finance:payment:list | 查看支付记录 | 财务人员 | 查看支付记录列表 | +| finance:refund:apply | 申请退款 | 财务人员/业主 | 发起退款申请 | +| finance:refund:approve | 审批退款 | 财务主管 | 审批退款申请 | +| finance:refund:execute | 执行退款 | 财务人员 | 执行退款操作 | +| finance:refund:list | 查看退款记录 | 财务人员 | 查看退款记录列表 | +| finance:statistics:view | 查看财务统计 | 项目管理员/财务主管 | 查看财务报表 | +| finance:late-fee:config | 配置滞纳金 | 系统管理员 | 配置滞纳金规则 | + +**数据级权限**: + +- 财务人员(PROJECT数据范围):只能查看/操作所属项目的财务数据 +- 财务主管(ALL数据范围):可查看所有项目的财务数据 +- 业主(SELF数据范围):只能查看自己的账单和支付记录 + +--- + +## 七、例外情况处理 + +| 例外编号 | 例外场景 | 处理策略 | 错误码 | +|---------|---------|---------|--------| +| EX-001 | 重复生成账单 | 检查feeItemId + ownerId + billPeriod唯一性,已存在则跳过 | 7001 | +| EX-002 | 支付金额超过应付金额 | 校验支付金额 <= payableAmount - paidAmount | 7002 | +| EX-003 | 已支付账单取消 | 拒绝取消,提示需先退款 | 7003 | +| EX-004 | 退款金额超过支付金额 | 校验退款金额 <= 原支付金额 | 7004 | +| EX-005 | 重复支付 | 状态校验,已支付/已退款的支付记录不可再次确认 | 7005 | +| EX-006 | 能耗数据缺失 | 按用量计费时无对应能耗记录,生成0元账单并标记异常 | 7006 | +| EX-007 | 业主无关联房产 | 生成账单时业主无房产关联,跳过并记录日志 | 7007 | +| EX-008 | 线上支付超时 | 支付状态保持PENDING,由对账任务处理 | 7008 | +| EX-009 | 线上支付回调异常 | 记录原始回调数据,人工介入处理 | 7009 | +| EX-010 | 滞纳金计算溢出 | lateFee不超过maxLateFee上限 | 7010 | +| EX-011 | 收费项目被禁用后自动出账 | 跳过已禁用的收费项目 | 7011 | +| EX-012 | 批量生成部分失败 | 记录成功/失败数量和失败原因,返回BatchResult | 7012 | diff --git a/02-DESIGN/detail/DETAIL-FRONTEND.md b/02-DESIGN/detail/DETAIL-FRONTEND.md new file mode 100644 index 0000000..3d3f8d4 --- /dev/null +++ b/02-DESIGN/detail/DETAIL-FRONTEND.md @@ -0,0 +1,1433 @@ +# 前端交互域 - 详细设计 + +**文档类型**: 详细设计文档 +**生成日期**: 2026-05-18 +**数据来源**: ether-admin 代码库分析 + REVERSE-AUTH.md + 路由/状态管理/API层源码 + +--- + +## 一、功能点清单 + +| 编号 | 模块 | 功能点 | 页面文件 | 实现状态 | +|------|------|--------|---------|---------| +| FE-001 | 认证 | 用户登录 | auth/Login.vue | 已实现 | +| FE-002 | 布局 | 主框架布局 | Layout.vue | 已实现 | +| FE-003 | 仪表盘 | 数据概览 | Dashboard.vue | 已实现 | +| FE-004 | 系统管理 | 用户管理 | system/Users.vue | 已实现 | +| FE-005 | 系统管理 | 用户详情 | system/UserDetail.vue | 已实现 | +| FE-006 | 系统管理 | 角色管理 | system/Roles.vue | 已实现 | +| FE-007 | 系统管理 | 权限管理 | system/Permissions.vue | 已实现 | +| FE-008 | 系统管理 | 组织架构 | system/Depts.vue | 已实现 | +| FE-009 | 系统管理 | 审计日志 | system/Audit.vue | 已实现 | +| FE-010 | 系统管理 | 系统设置 | system/Settings.vue | 已实现 | +| FE-011 | 项目管理 | 项目列表 | project/List.vue | 已实现 | +| FE-012 | 项目管理 | 项目选择器 | project/components/ProjectSelector.vue | 已实现 | +| FE-013 | 设备管理 | 设备列表 | equipment/EquipmentList.vue | 已实现 | +| FE-014 | 设备管理 | 设备详情 | equipment/EquipmentDetail.vue | 已实现 | +| FE-015 | 设备管理 | 设备健康预测 | equipment/EquipmentHealth.vue | 已实现 | +| FE-016 | 设备管理 | 维保计划 | equipment/MaintenancePlan.vue | 已实现 | +| FE-017 | 设备管理 | 维保工单 | equipment/MaintenanceTask.vue | 已实现 | +| FE-018 | 设备管理 | 巡检管理 | equipment/Inspection.vue | 已实现 | +| FE-019 | 点检管理 | 点检模板 | inspection/TemplateList.vue | 已实现 | +| FE-020 | 维保管理 | 维保计划 | maintenance/PlanList.vue | 已实现 | +| FE-021 | 维保管理 | 维保任务 | maintenance/TaskList.vue | 已实现 | +| FE-022 | 能耗管理 | 计量点管理 | energy/MeterList.vue | 已实现 | +| FE-023 | 能耗管理 | 能耗录入 | energy/ConsumptionRecord.vue | 已实现 | +| FE-024 | 能耗管理 | 能耗统计 | energy/EnergyStatistics.vue | 已实现 | +| FE-025 | 备件管理 | 备件列表 | sparepart/SparePartList.vue | 已实现 | +| FE-026 | 备件管理 | 备件详情 | sparepart/SparePartDetail.vue | 已实现 | +| FE-027 | 备件管理 | 入库/出库 | sparepart/StockOperation.vue | 已实现 | +| FE-028 | 工单管理 | 工单列表 | ops/WorkOrder.vue | 已实现 | +| FE-029 | 空间管理 | 空间抽屉 | space/SpaceDrawer.vue | 已实现 | +| FE-030 | 设备组件 | 照片管理 | equipment/components/PhotoManager.vue | 已实现 | +| FE-031 | 设备组件 | 文档管理 | equipment/components/DocumentManager.vue | 已实现 | + +--- + +## 二、技术架构 + +### 2.1 技术栈 + +| 技术 | 版本 | 用途 | +|------|------|------| +| Vue | 3.x | 前端框架(Composition API) | +| TypeScript | 5.x | 类型安全 | +| Ant Design Vue | 4.x | UI组件库 | +| Pinia | 2.x | 状态管理 | +| Vue Router | 4.x | 路由管理 | +| Vite | 5.x | 构建工具 | +| Axios | 1.x | HTTP客户端 | +| ECharts | 5.x | 图表可视化 | + +### 2.2 目录结构 + +``` +ether-admin/src/ +├── api/ # API调用层 +│ ├── auth.ts # 认证API +│ ├── user.ts # 用户管理API +│ ├── role.ts # 角色管理API +│ ├── permission.ts # 权限管理API +│ ├── dept.ts # 部门管理API +│ ├── audit.ts # 审计日志API +│ ├── system.ts # 系统配置API +│ ├── project.ts # 项目管理API +│ ├── space.ts # 空间管理API +│ ├── equipment.ts # 设备管理API +│ ├── equipment-health.ts # 设备健康API +│ ├── maintenance-plan.ts # 维保计划API +│ ├── maintenance-task.ts # 维保工单API +│ ├── inspection-template.ts # 点检模板API +│ ├── inspection-record.ts # 巡检记录API +│ ├── inspection-item.ts # 巡检项API +│ ├── energy.ts # 能耗管理API +│ ├── sparepart.ts # 备件管理API +│ ├── work-order.ts # 工单管理API +│ ├── maintenance.ts # 维保API(旧) +│ └── userManagement.ts # 用户管理API(旧) +├── stores/ # Pinia状态管理 +│ └── user.ts # 用户状态Store +├── types/ # TypeScript类型定义 +│ ├── index.ts # 全局类型 +│ ├── project.ts # 项目相关类型 +│ ├── space.ts # 空间相关类型 +│ └── projectMember.ts # 项目成员类型 +├── utils/ # 工具函数 +│ └── request.ts # Axios封装 +├── router/ # 路由配置 +│ └── index.ts # 路由表+守卫 +└── views/ # 页面组件 + ├── auth/ # 认证页面 + ├── system/ # 系统管理页面 + ├── project/ # 项目管理页面 + ├── equipment/ # 设备管理页面 + ├── inspection/ # 点检管理页面 + ├── maintenance/ # 维保管理页面 + ├── energy/ # 能耗管理页面 + ├── sparepart/ # 备件管理页面 + ├── ops/ # 运营管理页面 + └── space/ # 空间管理页面 +``` + +### 2.3 路由设计 + +#### 2.3.1 路由表 + +| 路径 | 路由名 | 组件 | 标题 | 权限要求 | +|------|--------|------|------|---------| +| `/login` | Login | auth/Login.vue | 登录 | 无 | +| `/dashboard` | Dashboard | Dashboard.vue | 仪表盘 | 已登录 | +| `/system/users` | Users | system/Users.vue | 用户管理 | SYS_ADMIN | +| `/system/roles` | Roles | system/Roles.vue | 角色管理 | SYS_ADMIN | +| `/system/permissions` | Permissions | system/Permissions.vue | 权限管理 | SYS_ADMIN | +| `/system/depts` | Depts | system/Depts.vue | 组织架构 | SYS_ADMIN | +| `/system/audit` | Audit | system/Audit.vue | 审计日志 | SYS_ADMIN | +| `/system/settings` | Settings | system/Settings.vue | 系统设置 | SYS_ADMIN | +| `/project/list` | ProjectList | project/List.vue | 项目管理 | 已登录 | +| `/equipment/list` | EquipmentList | equipment/EquipmentList.vue | 设备管理 | 已登录 | +| `/equipment/detail/:id` | EquipmentDetail | equipment/EquipmentDetail.vue | 设备详情 | 已登录 | +| `/equipment/health` | EquipmentHealth | equipment/EquipmentHealth.vue | 设备健康预测 | 已登录 | +| `/equipment/maintenance-plan` | MaintenancePlan | equipment/MaintenancePlan.vue | 维保计划 | 已登录 | +| `/equipment/maintenance-task` | MaintenanceTask | equipment/MaintenanceTask.vue | 维保工单 | 已登录 | +| `/equipment/inspection` | Inspection | equipment/Inspection.vue | 巡检管理 | 已登录 | +| `/inspection/templates` | InspectionTemplates | inspection/TemplateList.vue | 点检模板 | 已登录 | +| `/maintenance/plans` | MaintenancePlans | maintenance/PlanList.vue | 维保计划 | 已登录 | +| `/maintenance/tasks` | MaintenanceTasks | maintenance/TaskList.vue | 维保任务 | 已登录 | +| `/energy/meters` | EnergyMeters | energy/MeterList.vue | 计量点管理 | 已登录 | +| `/energy/consumption` | EnergyConsumption | energy/ConsumptionRecord.vue | 能耗录入 | 已登录 | +| `/energy/statistics` | EnergyStatistics | energy/EnergyStatistics.vue | 能耗统计 | 已登录 | +| `/sparepart/list` | SparePartList | sparepart/SparePartList.vue | 备件管理 | 已登录 | +| `/sparepart/detail/:id` | SparePartDetail | sparepart/SparePartDetail.vue | 备件详情 | 已登录 | +| `/sparepart/stock/in` | SparePartInStock | sparepart/StockOperation.vue | 备件入库 | 已登录 | +| `/sparepart/stock/out` | SparePartOutStock | sparepart/StockOperation.vue | 备件出库 | 已登录 | + +#### 2.3.2 路由守卫 + +```typescript +router.beforeEach((to, _from, next) => { + const userStore = useUserStore() + + // 1. 白名单路由直接放行(/login) + if (to.path === '/login') return next() + + // 2. 未登录跳转登录页 + if (!userStore.isLoggedIn()) return next('/login') + + // 3. 检查角色权限(meta.requiredRoles) + const requiredRoles = to.meta?.requiredRoles as string[] | undefined + if (requiredRoles?.length && !userStore.hasAnyRole(requiredRoles)) { + return next({ path: '/', query: { from: to.fullPath } }) + } + + next() +}) +``` + +#### 2.3.3 懒加载策略 + +所有路由组件均使用动态导入(`() => import()`),Vite自动代码分割: + +```typescript +component: () => import('@/views/system/Users.vue') +``` + +#### 2.3.4 权限控制 + +- **路由级权限**: 通过 `meta.requiredRoles` 声明所需角色,守卫中校验 +- **系统管理模块**: 统一要求 `SYS_ADMIN` 角色 +- **业务模块**: 仅要求已登录(无特定角色限制) +- **无权限处理**: 重定向到首页,通过 query.from 记录来源路径 + +--- + +### 2.4 状态管理 + +#### 2.4.1 Pinia Store设计 + +**useUserStore**(当前唯一Store) + +| 状态 | 类型 | 说明 | 持久化 | +|------|------|------|--------| +| token | `ref` | JWT Token | localStorage | +| userInfo | `ref<{username, realName} \| null>` | 用户基本信息 | localStorage | +| roles | `ref` | 角色列表 | localStorage | + +| 计算属性 | 类型 | 说明 | +|---------|------|------| +| isAdmin | `computed` | 是否SYS_ADMIN角色 | + +| 方法 | 参数 | 返回值 | 说明 | +|------|------|--------|------| +| hasRole | role: string | boolean | 判断是否拥有指定角色 | +| hasAnyRole | checkRoles: string[] | boolean | 判断是否拥有任一角色 | +| login | data: LoginRequest | Promise | 登录并存储Token/角色/用户信息 | +| logout | -- | Promise | 登出并清除所有状态 | +| isLoggedIn | -- | boolean | 检查登录状态(含Token过期校验) | +| initFromStorage | -- | void | 从localStorage恢复状态 | + +#### 2.4.2 持久化策略 + +| 数据 | 存储位置 | Key | 格式 | +|------|---------|-----|------| +| Token | localStorage | `token` | 字符串 | +| 用户信息 | localStorage | `userInfo` | JSON字符串 | +| 角色列表 | localStorage | `roles` | JSON数组字符串 | + +**Token过期校验**: + +```typescript +isLoggedIn() { + // 1. 检查Token存在性 + // 2. 检查JWT三段式格式 + // 3. 解析payload.exp检查过期 + // 4. 过期则自动logout +} +``` + +--- + +### 2.5 API层设计 + +#### 2.5.1 请求封装(request.ts) + +**Axios实例配置**: + +| 配置项 | 值 | 说明 | +|--------|-----|------| +| baseURL | `VITE_API_BASE_URL` | 环境变量配置 | +| timeout | `VITE_REQUEST_TIMEOUT \|\| 10000` | 请求超时 | + +**请求拦截器**: + +1. 从localStorage获取Token +2. 注入 `Authorization: Bearer {token}` Header +3. 初始化重试计数器 `__retryCount` + +**响应拦截器**: + +1. **业务错误处理**: 检查 `ApiResponse.code`,非200/0/00000视为业务错误 +2. **HTTP错误分类处理**: + +| 状态码 | 处理方式 | +|--------|---------| +| 400 | message.error("请求参数错误") | +| 401 | 清除Token,跳转/login | +| 403 | message.error("没有权限执行此操作") | +| 404 | message.error("请求的资源不存在") | +| 422 | message.error("数据验证失败") | +| 429 | message.error("操作太频繁") | +| 500 | message.error("服务器内部错误") | +| 502/503/504 | message.error("服务暂时不可用") | + +3. **网络错误处理**: ERR_NETWORK / ECONNABORTED / ERR_CANCELED +4. **自动重试**: GET请求在5xx/网络错误/超时时自动重试,最多2次,间隔1秒 + +#### 2.5.2 错误处理策略 + +| 错误类型 | 处理方式 | 用户感知 | +|---------|---------|---------| +| 业务错误(ApiResponse.code != 0) | message.error(res.message) | 顶部错误提示 | +| 401未授权 | 清除Token + 跳转登录 | 自动跳转登录页 | +| 403无权限 | message.error | 顶部错误提示 | +| 网络错误 | message.error | 顶部错误提示 | +| 请求超时 | 自动重试1次 + message.error | 顶部错误提示 | +| 请求取消 | 静默处理 | 无感知 | + +#### 2.5.3 类型安全 + +**统一响应类型**: + +```typescript +interface ApiResponse { + code: number + message: string + data: T +} +``` + +**API函数类型约束**: 所有API函数显式指定 `ApiResponse` 泛型参数 + +```typescript +export const getUsers = (params?) => { + return request.get>('/api/auth/users', { params }) +} +``` + +#### 2.5.4 分页封装 + +**分页响应类型**(多版本共存): + +```typescript +// 通用版本(project.ts定义) +interface PageResponse { + content: T[] + totalElements: number + totalPages: number + size: number + number: number + first: boolean + last: boolean + empty: boolean +} + +// 工单版本(work-order.ts定义) +interface PageResponse { + content: T[] + page: number + size: number + totalElements: number + totalPages: number + first: boolean + last: boolean +} +``` + +**分页请求参数**: + +```typescript +params: { page?: number; size?: number; keyword?: string; status?: string } +``` + +--- + +## 三、页面设计 + +### 3.1 登录页 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/login` | +| 路由名 | Login | +| 组件 | `views/auth/Login.vue` | +| 权限要求 | 无 | +| 布局 | 独立页面(不使用Layout) | + +**功能描述**: 用户名+密码登录,调用 `POST /api/auth/login` + +**核心组件**: Ant Design Vue Form + Input + Button + +**数据流**: +1. 用户输入用户名和密码 +2. 调用 `userStore.login({username, password})` +3. login内部调用 `auth.ts -> loginApi()` +4. 成功后存储Token/角色/用户信息到localStorage +5. 路由跳转到 `/dashboard` + +--- + +### 3.2 仪表盘 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/dashboard` | +| 路由名 | Dashboard | +| 组件 | `views/Dashboard.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 系统数据概览,展示关键指标 + +**核心组件**: Ant Design Vue Card + Statistic + ECharts图表 + +**数据流**: 汇总各模块统计数据 + +--- + +### 3.3 用户管理 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/system/users` | +| 路由名 | Users | +| 组件 | `views/system/Users.vue` | +| 权限要求 | SYS_ADMIN | + +**功能描述**: 用户列表CRUD、角色分配、项目关联 + +**核心组件**: Table + Modal + Form + Select + +**数据流**: +- 列表: `GET /api/auth/users` -> Table渲染 +- 创建: Form提交 -> `POST /api/auth/users` +- 编辑: Form提交 -> `PUT /api/auth/users/{id}` +- 删除: 确认弹窗 -> `DELETE /api/auth/users/{id}` +- 角色分配: 多选Modal -> `POST /api/auth/users/{id}/roles` +- 项目查看: `GET /api/auth/users/{id}/projects` + +--- + +### 3.4 用户详情 + +| 属性 | 值 | +|------|-----| +| 页面路径 | 从用户管理跳转 | +| 路由名 | UserDetail | +| 组件 | `views/system/UserDetail.vue` | +| 权限要求 | SYS_ADMIN | + +**功能描述**: 用户详细信息查看、密码修改 + +**核心组件**: Descriptions + Form + Modal + +**数据流**: +- 详情: `GET /api/auth/users/{id}` +- 修改密码: `PUT /api/auth/users/{id}/password` + +--- + +### 3.5 角色管理 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/system/roles` | +| 路由名 | Roles | +| 组件 | `views/system/Roles.vue` | +| 权限要求 | SYS_ADMIN | + +**功能描述**: 角色CRUD、权限分配 + +**核心组件**: Table + Modal + Form + Tree(权限树) + +**数据流**: +- 列表: `GET /api/auth/roles` +- 创建: `POST /api/auth/roles` +- 权限分配: `POST /api/auth/roles/{id}/permissions` +- 角色用户: `GET /api/auth/roles/{id}/users` + +--- + +### 3.6 权限管理 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/system/permissions` | +| 路由名 | Permissions | +| 组件 | `views/system/Permissions.vue` | +| 权限要求 | SYS_ADMIN | + +**功能描述**: 权限CRUD、按类型筛选 + +**核心组件**: Table + Modal + Form + Select(类型筛选) + +**数据流**: +- 列表: `GET /api/auth/permissions` +- 创建: `POST /api/auth/permissions` +- 按模块查询: `GET /api/auth/permissions/module/{module}` + +--- + +### 3.7 组织架构 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/system/depts` | +| 路由名 | Depts | +| 组件 | `views/system/Depts.vue` | +| 权限要求 | SYS_ADMIN | + +**功能描述**: 部门树展示、CRUD、成员查看 + +**核心组件**: Tree + Modal + Form + Table + +**数据流**: +- 部门树: `GET /api/auth/depts/tree` +- 创建: `POST /api/auth/depts` +- 成员: `GET /api/auth/depts/{deptId}/members` +- 按类型: `GET /api/auth/depts/by-type/{deptType}` + +--- + +### 3.8 审计日志 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/system/audit` | +| 路由名 | Audit | +| 组件 | `views/system/Audit.vue` | +| 权限要求 | SYS_ADMIN | + +**功能描述**: 操作日志查询、统计 + +**核心组件**: Table + DatePicker + Select(模块/操作类型) + +**数据流**: +- 日志列表: `GET /api/audit-logs` (分页+筛选) +- 模块列表: `GET /api/audit-logs/modules` +- 操作类型: `GET /api/audit-logs/actions` +- 统计: `GET /api/audit-logs/stats` + +--- + +### 3.9 系统设置 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/system/settings` | +| 路由名 | Settings | +| 组件 | `views/system/Settings.vue` | +| 权限要求 | SYS_ADMIN | + +**功能描述**: 系统配置键值对管理 + +**核心组件**: Form + Input + Button + +**数据流**: +- 获取配置: `GET /api/config` +- 更新配置: `PUT /api/config` + +--- + +### 3.10 项目管理 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/project/list` | +| 路由名 | ProjectList | +| 组件 | `views/project/List.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 项目CRUD、状态管理、成员管理、配置管理 + +**核心组件**: Table + Modal + Form + Tabs + +**数据流**: +- 列表: `GET /api/mdm/projects` (分页) +- 创建: `POST /api/mdm/projects` +- 状态变更: `PUT /api/mdm/projects/{id}/status` +- 成员管理: `GET/POST/DELETE /api/auth/projects/{projectId}/members` +- 编码生成: `GET /api/mdm/projects/generate-code` +- 删除检查: `GET /api/mdm/projects/{id}/delete-check` +- 配置: `GET/PUT /api/mdm/projects/{id}/config` +- 选择器: `GET /api/mdm/projects/selector` + +--- + +### 3.11 设备列表 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/equipment/list` | +| 路由名 | EquipmentList | +| 组件 | `views/equipment/EquipmentList.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 设备CRUD、按项目/类型/归属筛选、导入导出 + +**核心组件**: Table + Modal + Form + Upload + Select + +**数据流**: +- 列表: `GET /api/asset/equipment/by-project/{projectId}` +- 创建: `POST /api/asset/equipment` +- 按类型: `GET /api/asset/equipment/by-type` +- 按归属: `GET /api/asset/equipment/by-ownership` +- 导入: `POST /api/asset/equipment/import` +- 导出: `GET /api/asset/equipment/export` +- 统计: `GET /api/asset/equipment/stats/by-type/{projectId}` + +--- + +### 3.12 设备详情 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/equipment/detail/:id` | +| 路由名 | EquipmentDetail | +| 组件 | `views/equipment/EquipmentDetail.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 设备完整信息展示、扩展信息编辑、照片/文档管理 + +**核心组件**: Tabs + Descriptions + Form + PhotoManager + DocumentManager + +**数据流**: +- 详情: `GET /api/asset/equipment/{id}` +- 电梯扩展: `GET/PUT /api/asset/equipment/{id}/elevator` +- 暖通扩展: `GET/PUT /api/asset/equipment/{id}/hvac` +- 能源扩展: `GET/PUT /api/asset/equipment/{id}/energy` +- 消防扩展: `GET/PUT /api/asset/equipment/{id}/fire` + +--- + +### 3.13 设备健康预测 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/equipment/health` | +| 路由名 | EquipmentHealth | +| 组件 | `views/equipment/EquipmentHealth.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 设备健康度评分、故障历史、MTBF/MTTR分析 + +**核心组件**: Card + ECharts + Table + +**数据流**: +- 健康度: `GET /api/asset/equipment-health/{equipmentId}` +- 历史趋势: `GET /api/asset/equipment-health/{id}/history` +- 计算: `POST /api/asset/equipment-health/calculate` +- 故障历史: `GET /api/asset/equipment-health/failure-history/{id}` +- MTBF: `GET /api/asset/equipment-health/mtbf/{id}` +- MTTR: `GET /api/asset/equipment-health/mttr/{id}` + +--- + +### 3.14 维保计划(设备模块) + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/equipment/maintenance-plan` | +| 路由名 | MaintenancePlan | +| 组件 | `views/equipment/MaintenancePlan.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 维保计划CRUD + +**核心组件**: Table + Modal + Form + +**数据流**: +- 列表: `GET /api/mdm/maintenance-plans?projectId=` +- 创建: `POST /api/mdm/maintenance-plans` +- 更新: `PUT /api/mdm/maintenance-plans/{id}` +- 删除: `DELETE /api/mdm/maintenance-plans/{id}` + +--- + +### 3.15 维保工单(设备模块) + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/equipment/maintenance-task` | +| 路由名 | MaintenanceTask | +| 组件 | `views/equipment/MaintenanceTask.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 维保工单管理、状态流转 + +**核心组件**: Table + Modal + Form + Steps + +**数据流**: +- 列表: `GET /api/ops/maintenance-tasks?projectId=` +- 派单: `POST /api/ops/maintenance-tasks/{id}/assign` +- 开始: `POST /api/ops/maintenance-tasks/{id}/start` +- 完成: `POST /api/ops/maintenance-tasks/{id}/complete-details` +- 验收: `POST /api/ops/maintenance-tasks/{id}/verify` +- 取消: `POST /api/ops/maintenance-tasks/{id}/cancel` +- 统计: `GET /api/ops/maintenance-tasks/stats` + +--- + +### 3.16 巡检管理(设备模块) + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/equipment/inspection` | +| 路由名 | Inspection | +| 组件 | `views/equipment/Inspection.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 巡检记录管理 + +**核心组件**: Table + Modal + Form + +**数据流**: +- 记录列表: `GET /api/mdm/inspection-records?projectId=` +- 创建: `POST /api/mdm/inspection-records` +- 完成: `POST /api/mdm/inspection-records/{id}/complete` + +--- + +### 3.17 点检模板 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/inspection/templates` | +| 路由名 | InspectionTemplates | +| 组件 | `views/inspection/TemplateList.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 点检模板CRUD、检查项管理 + +**核心组件**: Table + Modal + Form + DynamicFormItems + +**数据流**: +- 列表: `GET /api/ops/inspection-templates?projectId=` +- 创建: `POST /api/ops/inspection-templates` +- 更新: `PUT /api/ops/inspection-templates/{id}` +- 删除: `DELETE /api/ops/inspection-templates/{id}` + +--- + +### 3.18 维保计划(维保模块) + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/maintenance/plans` | +| 路由名 | MaintenancePlans | +| 组件 | `views/maintenance/PlanList.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 维保计划列表(与设备模块维保计划共享API) + +--- + +### 3.19 维保任务(维保模块) + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/maintenance/tasks` | +| 路由名 | MaintenanceTasks | +| 组件 | `views/maintenance/TaskList.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 维保任务列表(与设备模块维保工单共享API) + +--- + +### 3.20 计量点管理 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/energy/meters` | +| 路由名 | EnergyMeters | +| 组件 | `views/energy/MeterList.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 能源计量点CRUD + +**核心组件**: Table + Modal + Form + Select + +**数据流**: +- 列表: `GET /api/ops/energy/meters?projectId=` +- 创建: `POST /api/ops/energy/meters` +- 更新: `PUT /api/ops/energy/meters/{id}` +- 删除: `DELETE /api/ops/energy/meters/{id}` + +--- + +### 3.21 能耗录入 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/energy/consumption` | +| 路由名 | EnergyConsumption | +| 组件 | `views/energy/ConsumptionRecord.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 能耗数据录入与查看 + +**核心组件**: Table + Modal + Form + InputNumber + +**数据流**: +- 录入: `POST /api/ops/energy/consumption` +- 记录: `GET /api/ops/energy/consumption/{meterId}` + +--- + +### 3.22 能耗统计 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/energy/statistics` | +| 路由名 | EnergyStatistics | +| 组件 | `views/energy/EnergyStatistics.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 按类型统计、单方能耗 + +**核心组件**: Card + ECharts + DatePicker + +**数据流**: +- 按类型统计: `GET /api/ops/energy/statistics/by-type` +- 单方能耗: `GET /api/ops/energy/statistics/unit-consumption` + +--- + +### 3.23 备件列表 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/sparepart/list` | +| 路由名 | SparePartList | +| 组件 | `views/sparepart/SparePartList.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 备件CRUD、低库存预警 + +**核心组件**: Table + Modal + Form + Badge + +**数据流**: +- 列表: `GET /api/ops/spare-parts?projectId=` +- 创建: `POST /api/ops/spare-parts` +- 低库存: `GET /api/ops/spare-parts/low-stock` + +--- + +### 3.24 备件详情 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/sparepart/detail/:id` | +| 路由名 | SparePartDetail | +| 组件 | `views/sparepart/SparePartDetail.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 备件详情、库存记录 + +**核心组件**: Descriptions + Table + +**数据流**: +- 详情: `GET /api/ops/spare-parts/{id}` +- 记录: `GET /api/ops/spare-parts/{id}/records` + +--- + +### 3.25 入库/出库 + +| 属性 | 值 | +|------|-----| +| 页面路径 | `/sparepart/stock/in` 或 `/sparepart/stock/out` | +| 路由名 | SparePartInStock / SparePartOutStock | +| 组件 | `views/sparepart/StockOperation.vue`(共用) | +| 权限要求 | 已登录 | + +**功能描述**: 备件入库/出库操作 + +**核心组件**: Form + InputNumber + Select + +**数据流**: +- 入库: `POST /api/ops/spare-parts/in-stock` +- 出库: `POST /api/ops/spare-parts/out-stock` + +--- + +### 3.26 工单管理 + +| 属性 | 值 | +|------|-----| +| 页面路径 | 通过路由未直接注册,从其他入口跳转 | +| 路由名 | WorkOrder | +| 组件 | `views/ops/WorkOrder.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 工单全生命周期管理 + +**核心组件**: Table + Modal + Form + Steps + Rate + +**数据流**: +- 列表: `GET /api/wo/work-orders` (分页+多条件筛选) +- 创建: `POST /api/wo/work-orders` +- 派单: `POST /api/wo/work-orders/{id}/assign` +- 开始: `POST /api/wo/work-orders/{id}/start` +- 完成: `POST /api/wo/work-orders/{id}/complete` +- 验收: `POST /api/wo/work-orders/{id}/verify` +- 挂起: `POST /api/wo/work-orders/{id}/suspend` +- 恢复: `POST /api/wo/work-orders/{id}/resume` +- 退回: `POST /api/wo/work-orders/{id}/return` +- 取消: `POST /api/wo/work-orders/{id}/cancel` +- 统计: `GET /api/wo/work-orders/stats` +- 工单项: `GET/POST /api/wo/work-orders/{id}/items` + +--- + +### 3.27 空间抽屉 + +| 属性 | 值 | +|------|-----| +| 页面路径 | 作为组件被其他页面引用 | +| 组件 | `views/space/SpaceDrawer.vue` | +| 权限要求 | 已登录 | + +**功能描述**: 空间树选择抽屉 + +**核心组件**: Drawer + Tree + +**数据流**: +- 空间树: `GET /api/mdm/space-nodes/project/{projectId}/tree` + +--- + +## 四、组件设计 + +### 4.1 公共组件 + +#### 4.1.1 Layout(主框架布局) + +**文件**: `views/Layout.vue` + +**功能**: 系统主布局框架,包含侧边栏菜单、顶部导航、内容区域 + +**核心结构**: +- 左侧:侧边栏菜单(Ant Design Vue Layout.Sider + Menu) +- 顶部:Header(项目选择器、用户信息、登出按钮) +- 中间:Content(router-view渲染区域) + +**菜单分组**: + +| 菜单组 | 子菜单 | +|--------|--------| +| 仪表盘 | Dashboard | +| 系统管理 | 用户管理、角色管理、权限管理、组织架构、审计日志、系统设置 | +| 项目管理 | 项目列表 | +| 设备管理 | 设备列表、设备健康预测、维保计划、维保工单、巡检管理 | +| 点检管理 | 点检模板 | +| 维保管理 | 维保计划、维保任务 | +| 能耗管理 | 计量点管理、能耗录入、能耗统计 | +| 备件管理 | 备件列表、备件入库、备件出库 | + +#### 4.1.2 ProjectSelector(项目选择器) + +**文件**: `views/project/components/ProjectSelector.vue` + +**功能**: 全局项目切换组件,切换后所有API请求自动携带新项目ID + +**核心交互**: +1. 下拉选择项目 +2. 切换后设置 `X-Project-ID` Header +3. 刷新当前页面数据 + +**数据流**: +- 项目列表: `GET /api/mdm/projects/selector` + +#### 4.1.3 SpaceDrawer(空间选择抽屉) + +**文件**: `views/space/spaceDrawer.vue` + +**功能**: 树形空间选择器,用于设备关联空间、账单关联房产等场景 + +**核心交互**: +1. 点击触发打开Drawer +2. 加载空间树 +3. 点击节点选中 +4. 确认后返回选中的SpaceNode + +**数据流**: +- 空间树: `GET /api/mdm/space-nodes/project/{projectId}/tree` + +--- + +### 4.2 业务组件 + +#### 4.2.1 PhotoManager(照片管理) + +**文件**: `views/equipment/components/PhotoManager.vue` + +**功能**: 设备照片上传、预览、删除 + +**核心交互**: +1. 按类型分类(外观、铭牌、安装位置、环境) +2. 上传照片(调用uploadFile模拟) +3. 预览大图 +4. 删除照片 + +**数据结构**: + +```typescript +interface EquipmentPhoto { + type: string // 照片类型 + url: string // 照片URL + remark?: string // 备注 +} +``` + +#### 4.2.2 DocumentManager(文档管理) + +**文件**: `views/equipment/components/DocumentManager.vue` + +**功能**: 设备电子文档上传、下载、删除 + +**核心交互**: +1. 按类型分类(手册manual、证书certificate、合同contract、其他other) +2. 上传文档 +3. 下载文档 +4. 删除文档 + +**数据结构**: + +```typescript +interface EquipmentDocument { + name: string + url: string + size?: number + type: 'manual' | 'certificate' | 'contract' | 'other' + remark?: string +} +``` + +--- + +## 五、类型定义 + +### 5.1 全局类型(types/index.ts) + +```typescript +// API统一响应 +interface ApiResponse { + code: number + message: string + data: T +} + +// 分页信息 +interface PaginationInfo { + current: number + pageSize: number + total?: number +} + +// 登录请求 +interface LoginRequest { + username: string + password: string +} + +// 登录响应 +interface LoginResponse { + token: string + userId: string + username: string + realName: string + roles: string[] +} + +// 用户 +interface User { + id: string + username: string + realName?: string + phone?: string + email?: string + avatar?: string + status: 'ACTIVE' | 'LOCKED' | 'DISABLED' + lastLoginTime?: string + lastLoginIp?: string + roles?: Role[] + employeeNo?: string + position?: string + staffType?: StaffType + projectId?: string +} + +// 角色 +interface Role { + id: string + code: string + name: string + description?: string + type?: string + status: 'ACTIVE' | 'DISABLED' + dataScope?: string + permissions?: Permission[] +} + +// 权限 +interface Permission { + id: string + code: string + name: string + type: string + path?: string + component?: string + icon?: string + resource?: string + method?: string + description?: string + sortOrder?: number + visible?: boolean +} + +// 项目 +interface Project { + id: string + code: string + name: string + description?: string + address?: string + projectType?: 'RESIDENTIAL' | 'OFFICE' | 'INDUSTRIAL_PARK' + province?: string + city?: string + district?: string + status: 'ACTIVE' | 'DISABLED' | 'PENDING' | 'ARCHIVED' + createdAt?: string + updatedAt?: string +} +``` + +### 5.2 各模块类型 + +#### 5.2.1 用户相关类型 + +```typescript +type UserType = 'ENTERPRISE' | 'PROJECT_STAFF' | 'RESIDENT' | 'CUSTOMER' +type EmployeeCategory = 'ENTERPRISE' | 'MANAGEMENT' +type StaffType = 'SECURITY' | 'CLEANING' | 'GARDEN' | 'MAINTENANCE' | 'CUSTOMER_SERVICE' | 'GENERAL' +type ResidentType = 'OWNER' | 'FAMILY' | 'TENANT' +``` + +#### 5.2.2 项目相关类型(types/project.ts) + +```typescript +type ProjectStatus = 'ACTIVE' | 'DISABLED' | 'PENDING' | 'ARCHIVED' +type ProjectType = 'RESIDENTIAL' | 'OFFICE' | 'INDUSTRIAL_PARK' + +interface ProjectQuery { + keyword?: string + status?: ProjectStatus + page?: number + size?: number + sort?: string +} + +interface PageResponse { + content: T[] + totalElements: number + totalPages: number + size: number + number: number + first: boolean + last: boolean + empty: boolean +} + +interface ProjectStatistics { + memberCount: number + buildingCount: number + roomCount: number + ownerCount: number + tenantCount: number + activeTaskCount: number + completedTaskCount: number +} + +interface ProjectMember { + id: string + projectId: string + userId: string + userName: string + realName?: string + phone?: string + roleInProject: string + joinedAt: string + status: 'ACTIVE' | 'INACTIVE' +} + +interface ProjectConfig { + id: string + projectId: string + enableReservation: boolean + enableVisitor: boolean + enableComplaint: boolean + enablePayment: boolean + enableAnnouncement: boolean + enableSurvey: boolean + enableVote: boolean + enableMaintenance: boolean + enableAsset: boolean + customConfig?: string + updatedAt: string +} + +interface ProjectDeleteCheckVO { + canDelete: boolean + reason?: string + statistics: ProjectDeleteStatistics +} +``` + +#### 5.2.3 空间相关类型(types/space.ts) + +```typescript +type SpaceNodeCategory = 'BUILDING' | 'PARKING' | 'FACILITY' | 'AREA' +type SpaceNodeType = 'BUILDING' | 'UNIT' | 'FLOOR' | 'ROOM' | 'SHOP' | 'GARAGE' | + 'PARKING_AREA' | 'PARKING_SPACE' | 'EQUIPMENT_ROOM' | 'PROPERTY_OFFICE' | + 'SECURITY_ROOM' | 'PUBLIC_AREA' | 'GREEN_AREA' | 'ROAD' + +interface SpaceNode { + id: string + projectId: string + code: string + name: string + fullName?: string + nodeCategory: SpaceNodeCategory + nodeType: SpaceNodeType + parentId?: string + treePath?: string + level?: number + buildingArea?: number + usableArea?: number + status?: string + // ...更多字段 +} + +interface SpaceNodeTree extends SpaceNode { + children: SpaceNodeTree[] +} +``` + +#### 5.2.4 设备相关类型(api/equipment.ts内联) + +```typescript +type SystemType = 'ELEVATOR' | 'HVAC' | 'FIRE_PROTECTION' | 'PLUMBING' | + 'ELECTRICAL' | 'SECURITY' | 'LANDSCAPE' | 'ENERGY_METER' +type EquipmentType = 'ELEVATOR' | 'HVAC' | 'FIRE_PROTECTION' | 'PLUMBING' | + 'ELECTRICAL' | 'ENERGY_METER' | 'SECURITY' | 'LANDSCAPE' | 'KITCHEN' | 'OTHER' +type OwnershipType = 'PROJECT' | 'COMPANY' | 'OWNER' | 'RENTAL' + +interface Equipment { + id: string + equipmentCode: string + equipmentName: string + projectId: string + equipmentType?: EquipmentType + systemType?: SystemType + ownershipType?: OwnershipType + status?: string + operationStatus?: string + // ...更多字段 + photos?: EquipmentPhoto[] + documents?: EquipmentDocument[] +} +``` + +#### 5.2.5 工单相关类型(api/work-order.ts内联) + +```typescript +type WorkOrderSource = 'OWNER' | 'MAINTENANCE' | 'INSPECTION' | 'FAULT' | 'REGULATORY' | 'MANUAL' +type WorkOrderType = 'REPAIR' | 'INSPECTION' | 'SECURITY' | 'CLEANING' | 'PROPERTY' | 'CONSULTATION' +type WorkOrderPriority = 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT' +type WorkOrderStatus = 'PENDING' | 'ASSIGNED' | 'IN_PROGRESS' | 'SUSPENDED' | + 'RETURNED' | 'COMPLETED' | 'VERIFIED' | 'CANCELLED' + +interface WorkOrder { + id?: string + workNo?: string + source: WorkOrderSource + type: WorkOrderType + title: string + priority: WorkOrderPriority + status: WorkOrderStatus + projectId?: string + equipmentId?: string + assignedTo?: string + // ...更多字段 +} +``` + +#### 5.2.6 维保相关类型(api/maintenance-*.ts内联) + +```typescript +type PlanType = 'PREVENTIVE' | 'CORRECTIVE' +type PlanStatus = 'ACTIVE' | 'INACTIVE' | 'SUSPENDED' +type TaskType = 'PREVENTIVE' | 'CORRECTIVE' | 'EMERGENCY' +type TaskPriority = 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT' +type TaskStatus = 'PENDING' | 'ASSIGNED' | 'IN_PROGRESS' | 'COMPLETED' | 'VERIFIED' | 'CANCELLED' +type TriggerType = 'PLAN' | 'INSPECTION' | 'FAULT' | 'MANUAL' +``` + +#### 5.2.7 能耗相关类型(api/energy.ts内联) + +```typescript +interface EnergyMeter { + id?: string + meterCode: string + meterName: string + energyType: string + installationLocation?: string + ratedCapacity?: number + unitPrice?: number + status?: string +} + +interface EnergyConsumption { + id?: string + meterId: string + consumptionDate: string + previousReading: number + currentReading: number + consumption: number + amount?: number + recordMethod?: string +} +``` + +#### 5.2.8 备件相关类型(api/sparepart.ts内联) + +```typescript +interface SparePart { + id: string + name: string + code: string + categoryId?: string + projectId: string + unit?: string + currentStock?: number + safeStock?: number + lowStockWarning?: boolean +} + +interface StockRecord { + id: string + sparePartId: string + operationType: 'IN' | 'OUT' + quantity: number + beforeStock?: number + afterStock?: number + relatedOrderId?: string +} +``` + +--- + +## 六、交互规范 + +### 6.1 表单验证规范 + +| 场景 | 验证规则 | 组件 | +|------|---------|------| +| 必填字段 | `required: true, message: '请输入xxx'` | a-form-item + rules | +| 字符串长度 | `min: 2, max: 50, message: '长度2-50位'` | a-input | +| 手机号 | `pattern: /^1[3-9]\d{9}$/, message: '请输入正确手机号'` | a-input | +| 邮箱 | `type: 'email', message: '请输入正确邮箱'` | a-input | +| 数字范围 | `type: 'number', min: 0, max: 99999` | a-input-number | +| 唯一性 | 异步验证,调用后端接口 | 自定义validator | +| 提交前验证 | `form.validate()` 全量校验 | a-form | + +### 6.2 列表查询规范 + +| 规范项 | 实现方式 | +|--------|---------| +| 分页 | 后端分页,参数 `page`(从0开始)+ `size` | +| 搜索 | 关键词搜索参数 `keyword`,模糊匹配用户名/姓名/编码 | +| 筛选 | 下拉选择参数(status/type等),多条件组合 | +| 排序 | 后端默认排序(通常按createdAt DESC) | +| 刷新 | 切换页码/筛选条件后自动重新查询 | +| 空状态 | Ant Design Vue Table `locale.emptyText` | + +### 6.3 状态展示规范 + +**颜色映射表**: + +| 状态类型 | 映射规则 | Tag颜色 | +|---------|---------|---------| +| 用户状态 | ACTIVE=green, LOCKED=red, DISABLED=gray | a-tag color | +| 项目状态 | ACTIVE=success, DISABLED=error, PENDING=warning, ARCHIVED=default | a-tag color | +| 工单状态 | PENDING=default, ASSIGNED=blue, IN_PROGRESS=orange, COMPLETED=green, VERIFIED=cyan, CANCELLED=red | a-tag color | +| 工单优先级 | LOW=green, MEDIUM=blue, HIGH=orange, URGENT=red | a-tag color | +| 工单来源 | OWNER=purple, MAINTENANCE=blue, INSPECTION=cyan, FAULT=red | a-tag color | +| 维保任务状态 | PENDING=default, ASSIGNED=blue, IN_PROGRESS=orange, COMPLETED=green, VERIFIED=cyan, CANCELLED=red | a-tag color | +| 巡检状态 | NORMAL=green, WARNING=orange, ABNORMAL=red | a-tag color | +| 设备状态 | NORMAL=green, WARNING=orange, ABNORMAL=red | a-tag color | + +### 6.4 操作确认规范 + +| 操作类型 | 确认方式 | 说明 | +|---------|---------|------| +| 删除 | `Modal.confirm` + 红色警告 | "确定要删除xxx吗?此操作不可恢复" | +| 状态变更 | `Modal.confirm` | "确定要将状态从xxx变更为yyy吗?" | +| 批量操作 | `Modal.confirm` + 数量提示 | "确定要删除选中的N条记录吗?" | +| 取消工单 | `Modal.confirm` + 原因输入 | 需填写取消原因 | +| 表单提交 | 无确认 | 直接提交 | +| 登出 | 无确认 | 直接登出 | + +--- + +## 七、权限控制 + +### 7.1 路由级权限(meta.requiredRoles) + +**实现方式**: Vue Router `beforeEach` 守卫 + +```typescript +// 路由定义 +{ + path: 'system/users', + meta: { title: '用户管理', requiredRoles: ['SYS_ADMIN'] } +} + +// 守卫校验 +const requiredRoles = to.meta?.requiredRoles as string[] | undefined +if (requiredRoles?.length && !userStore.hasAnyRole(requiredRoles)) { + return next({ path: '/', query: { from: to.fullPath } }) +} +``` + +**当前权限配置**: + +| 路由 | requiredRoles | +|------|--------------| +| /system/* (6个页面) | `['SYS_ADMIN']` | +| 其他业务页面 | 无(仅要求已登录) | + +### 7.2 按钮级权限(v-if + hasRole) + +**实现方式**: Pinia Store `hasRole` / `hasAnyRole` 方法 + +```html + + 删除 + +``` + +**当前使用场景**: 有限,大部分页面未做按钮级权限控制 + +**建议增强**: + +| 操作 | 建议权限检查 | +|------|------------| +| 创建/编辑/删除 | `userStore.hasAnyRole(['SYS_ADMIN', 'PROJECT_ADMIN'])` | +| 审批操作 | `userStore.hasRole('FINANCE_STAFF')` | +| 系统配置 | `userStore.hasRole('SYS_ADMIN')` | + +### 7.3 数据级权限(后端控制) + +**实现方式**: 后端 DataScopeService + X-Project-ID Header + +| 数据范围 | 编码 | 后端过滤逻辑 | +|---------|------|------------| +| 全部数据 | ALL | 不做数据过滤 | +| 项目数据 | PROJECT | 过滤 project_id = X-Project-ID | +| 部门数据 | DEPARTMENT | 过滤 project_id + dept_id | +| 本人数据 | SELF | 过滤 assigned_to / created_by = 当前用户ID | + +**前端配合**: +1. 请求拦截器自动注入 `X-Project-ID` Header(从ProjectSelector获取) +2. 前端不做数据过滤,完全依赖后端返回 +3. 列表页面根据返回数据渲染,不额外过滤 diff --git a/02-DESIGN/detail/DETAIL-OPS.md b/02-DESIGN/detail/DETAIL-OPS.md new file mode 100644 index 0000000..bdfabef --- /dev/null +++ b/02-DESIGN/detail/DETAIL-OPS.md @@ -0,0 +1,1362 @@ +# 运营与服务域 - 详细设计 + +**文档编号**: DETAIL-OPS +**版本**: v1.0 +**模块**: module-wo + module-mdm(备件/能耗部分) +**生成日期**: 2026-05-18 +**数据来源**: ether-pms/module-wo + module-mdm 实际代码 + REVERSE-OPS.md 反推文档 + +--- + +## 一、功能点清单 + +| 功能ID | 功能名称 | 优先级 | 实现状态 | 对应需求ID | +|--------|----------|--------|----------|------------| +| OPS-001 | 工单创建 | P0 | 已完成 | 02-OPERATIONS-001 | +| OPS-002 | 工单查询(分页) | P0 | 已完成 | 02-OPERATIONS-001 | +| OPS-003 | 工单详情 | P0 | 已完成 | 02-OPERATIONS-001 | +| OPS-004 | 工单更新 | P1 | 已完成 | 02-OPERATIONS-001 | +| OPS-005 | 工单删除(逻辑删除) | P1 | 已完成 | 02-OPERATIONS-001 | +| OPS-006 | 工单派单 | P0 | 已完成 | 02-OPERATIONS-002 | +| OPS-007 | 工单开始执行 | P0 | 已完成 | 02-OPERATIONS-002 | +| OPS-008 | 工单完成 | P0 | 已完成 | 02-OPERATIONS-002 | +| OPS-009 | 工单验收 | P0 | 已完成 | 02-OPERATIONS-002 | +| OPS-010 | 工单取消 | P1 | 已完成 | 02-OPERATIONS-002 | +| OPS-011 | 工单挂起 | P1 | 已完成 | 02-OPERATIONS-002 | +| OPS-012 | 工单恢复 | P1 | 已完成 | 02-OPERATIONS-002 | +| OPS-013 | 工单退回 | P1 | 已完成 | 02-OPERATIONS-002 | +| OPS-014 | 工单统计 | P1 | 已完成 | 02-OPERATIONS-003 | +| OPS-015 | 工单明细查询 | P1 | 已完成 | 02-OPERATIONS-004 | +| OPS-016 | 工单明细批量添加 | P1 | 已完成 | 02-OPERATIONS-004 | +| OPS-017 | 维保任务创建 | P0 | 已完成 | 02-OPERATIONS-005 | +| OPS-018 | 维保任务查询 | P0 | 已完成 | 02-OPERATIONS-005 | +| OPS-019 | 维保任务分配 | P0 | 已完成 | 02-OPERATIONS-005 | +| OPS-020 | 维保任务开始执行 | P0 | 已完成 | 02-OPERATIONS-005 | +| OPS-021 | 维保任务完成(简版) | P0 | 已完成 | 02-OPERATIONS-005 | +| OPS-022 | 维保任务完成(详版) | P0 | 已完成 | 02-OPERATIONS-005 | +| OPS-023 | 维保任务验收 | P0 | 已完成 | 02-OPERATIONS-005 | +| OPS-024 | 维保任务取消 | P1 | 已完成 | 02-OPERATIONS-005 | +| OPS-025 | 维保任务评分 | P2 | 已完成 | 02-OPERATIONS-005 | +| OPS-026 | 维保任务统计 | P1 | 已完成 | 02-OPERATIONS-006 | +| OPS-027 | 维保计划管理 | P1 | 已完成 | 02-OPERATIONS-007 | +| OPS-028 | 巡检模板管理 | P1 | 已完成 | 02-OPERATIONS-008 | +| OPS-029 | 巡检模板复制 | P2 | 已完成 | 02-OPERATIONS-008 | +| OPS-030 | 备件CRUD | P0 | 已完成 | 02-OPERATIONS-009 | +| OPS-031 | 备件分类管理 | P1 | 已完成 | 02-OPERATIONS-009 | +| OPS-032 | 备件入库 | P0 | 已完成 | 02-OPERATIONS-010 | +| OPS-033 | 备件出库 | P0 | 已完成 | 02-OPERATIONS-010 | +| OPS-034 | 备件低库存预警 | P1 | 已完成 | 02-OPERATIONS-010 | +| OPS-035 | 备件出入库记录 | P1 | 已完成 | 02-OPERATIONS-010 | +| OPS-036 | 能源计量点管理 | P1 | 已完成 | 02-OPERATIONS-011 | +| OPS-037 | 能耗抄表记录 | P0 | 已完成 | 02-OPERATIONS-012 | +| OPS-038 | 能耗按类型统计 | P1 | 已完成 | 02-OPERATIONS-013 | +| OPS-039 | 单位面积能耗 | P2 | 已完成 | 02-OPERATIONS-013 | +| OPS-040 | 消息通知系统 | P1 | 未实现 | 02-OPERATIONS-014 | +| OPS-041 | 工单流转记录 | P2 | 未实现 | 02-OPERATIONS-015 | +| OPS-042 | SLA监控 | P2 | 未实现 | 02-OPERATIONS-016 | +| OPS-043 | 智能派单 | P3 | 未实现 | 02-OPERATIONS-017 | + +--- + +## 二、数据结构设计 + +### 2.1 WorkOrder(工单) + +**表名**: `ops_work_order` +**实体类**: `com.ether.pms.ops.entity.WorkOrder` + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| workNo | VARCHAR(50) | 是 | 自动生成 | 工单编号,格式: WO-YYYYMMDD-XXXX,UNIQUE | +| source | ENUM | 是 | - | 工单来源(6种) | +| type | ENUM | 是 | - | 工单类型(6种) | +| priority | ENUM | 否 | MEDIUM | 优先级(4级) | +| status | ENUM | 否 | PENDING | 工单状态(8种) | +| previousStatus | ENUM | 否 | - | 前一状态(挂起恢复用) | +| title | VARCHAR(200) | 是 | - | 工单标题 | +| description | VARCHAR(2000) | 否 | - | 工单描述 | +| projectId | UUID | 是 | - | 所属项目 | +| equipmentId | UUID | 否 | - | 关联设备 | +| spaceId | UUID | 否 | - | 关联空间 | +| planId | UUID | 否 | - | 关联维保计划 | +| triggerType | ENUM | 否 | - | 触发类型(4种) | +| assignedTo | VARCHAR(200) | 否 | - | 负责人 | +| assignedVendor | VARCHAR(200) | 否 | - | 服务商 | +| assignedDate | DATE | 否 | - | 派单日期 | +| actualStart | TIMESTAMP | 否 | - | 实际开始时间 | +| actualEnd | TIMESTAMP | 否 | - | 实际结束时间 | +| actualHours | DECIMAL(6,2) | 否 | - | 实际工时(小时) | +| faultCause | VARCHAR(2000) | 否 | - | 故障原因 | +| solution | VARCHAR(5000) | 否 | - | 解决方案 | +| result | VARCHAR(2000) | 否 | - | 处理结果 | +| laborCost | DECIMAL(12,2) | 否 | - | 人工费 | +| partsCost | DECIMAL(12,2) | 否 | - | 备件费 | +| totalCost | DECIMAL(12,2) | 否 | - | 总费用 | +| completedBy | VARCHAR(200) | 否 | - | 完成人 | +| completedDate | DATE | 否 | - | 完成日期 | +| verifiedBy | VARCHAR(200) | 否 | - | 验收人 | +| verifiedDate | DATE | 否 | - | 验收日期 | +| rating | INTEGER | 否 | - | 评分(1-5星) | +| remark | VARCHAR(1000) | 否 | - | 备注 | +| photos | JSONB | 否 | - | 照片URL列表(List\) | +| signature | VARCHAR(2000) | 否 | - | 签名 | +| isDeleted | BOOLEAN | 否 | false | 逻辑删除标记 | +| createdAt | TIMESTAMP | 是 | now() | 创建时间 | +| updatedAt | TIMESTAMP | 是 | now() | 更新时间 | +| createdBy | VARCHAR(200) | 否 | - | 创建人 | + +**枚举定义**: + +| 枚举 | 值 | 中文 | +|------|-----|------| +| Source | OWNER | 业主报修 | +| | MAINTENANCE | 维保计划 | +| | INSPECTION | 巡检触发 | +| | FAULT | 故障触发 | +| | REGULATORY | 法规巡检 | +| | MANUAL | 手动创建 | +| Type | REPAIR | 维修 | +| | INSPECTION | 巡检 | +| | SECURITY | 安保 | +| | CLEANING | 保洁 | +| | PROPERTY | 物业 | +| | CONSULTATION | 咨询 | +| Priority | LOW / MEDIUM / HIGH / URGENT | 低/中/高/紧急 | +| Status | PENDING / ASSIGNED / IN_PROGRESS / SUSPENDED / RETURNED / COMPLETED / VERIFIED / CANCELLED | 见状态机 | +| TriggerType | PLAN / INSPECTION / FAULT / MANUAL | 计划/巡检/故障/手动 | + +**索引**: + +| 索引名 | 字段 | 用途 | +|--------|------|------| +| idx_wo_project_status | project_id, status | 项目+状态查询 | +| idx_wo_priority_status | priority, status | 优先级+状态查询 | +| idx_wo_plan_createdat | plan_id, created_at | 计划关联查询 | +| idx_wo_status_createdat | status, created_at | 状态+时间查询 | +| idx_wo_createdat_desc | created_at DESC | 时间倒序 | + +### 2.2 WorkOrderItem(工单明细) + +**表名**: `ops_work_order_item` +**实体类**: `com.ether.pms.ops.entity.WorkOrderItem` + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| workOrderId | UUID | 是 | - | 所属工单ID | +| itemType | ENUM | 是 | - | 明细类型(3种) | +| itemName | VARCHAR(200) | 是 | - | 明细名称 | +| quantity | DECIMAL(10,2) | 否 | 1 | 数量 | +| unit | VARCHAR(50) | 否 | - | 单位 | +| unitPrice | DECIMAL(12,2) | 否 | - | 单价 | +| totalPrice | DECIMAL(12,2) | 否 | - | 总价 | +| isNormal | BOOLEAN | 否 | - | 是否正常 | +| observation | VARCHAR(2000) | 否 | - | 观察记录 | +| suggestion | VARCHAR(2000) | 否 | - | 建议 | +| sortOrder | INTEGER | 否 | 0 | 排序序号 | +| createdAt | TIMESTAMP | 否 | now() | 创建时间 | + +**枚举定义**: + +| 枚举 | 值 | 中文 | +|------|-----|------| +| ItemType | PART | 备件 | +| | INSPECTION_ITEM | 巡检项 | +| | CHECKPOINT | 检查点 | + +### 2.3 MaintenancePlan(维保计划) + +**表名**: `ops_maintenance_plan` +**实体类**: `com.ether.pms.ops.entity.MaintenancePlan` + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| planCode | VARCHAR(50) | 是 | 自动生成 | 计划编码(UNIQUE) | +| planName | VARCHAR(200) | 是 | - | 计划名称 | +| planContent | VARCHAR(5000) | 否 | - | 计划内容 | +| projectId | UUID | 否 | - | 所属项目 | +| equipmentId | UUID | 是 | - | 关联设备 | +| planType | ENUM | 是 | - | 计划类型(2种) | +| cycleDays | INTEGER | 否 | - | 周期天数 | +| estimatedHours | DECIMAL(6,2) | 否 | - | 预估工时 | +| assignedVendor | VARCHAR(200) | 否 | - | 指定服务商 | +| status | ENUM | 否 | ACTIVE | 计划状态(3种) | +| lastDate | DATE | 否 | - | 上次执行日期 | +| nextDate | DATE | 否 | - | 下次执行日期 | +| createdAt | TIMESTAMP | 否 | now() | 创建时间 | +| updatedAt | TIMESTAMP | 否 | now() | 更新时间 | +| createdBy | VARCHAR(200) | 否 | - | 创建人 | + +**枚举定义**: + +| 枚举 | 值 | 中文 | +|------|-----|------| +| PlanType | PREVENTIVE | 预防性维护 | +| | CORRECTIVE | 纠正性维护 | +| PlanStatus | ACTIVE | 启用 | +| | INACTIVE | 停用 | +| | SUSPENDED | 暂停 | + +### 2.4 MaintenanceTask(维保任务) + +**表名**: `ops_maintenance_task` +**实体类**: `com.ether.pms.ops.entity.MaintenanceTask` + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| taskNo | VARCHAR(50) | 否 | 自动生成 | 任务编号,格式: EQ-YYYYMMDD-XXXX,UNIQUE | +| planId | UUID | 否 | - | 关联维保计划ID | +| equipmentId | UUID | 是 | - | 关联设备ID | +| projectId | UUID | 否 | - | 所属项目 | +| taskType | ENUM | 否 | - | 任务类型(3种) | +| triggerType | ENUM | 否 | - | 触发类型(4种) | +| priority | ENUM | 否 | 自动判定 | 优先级(4级) | +| status | ENUM | 否 | PENDING | 任务状态(6种) | +| title | VARCHAR(200) | 否 | - | 任务标题 | +| description | VARCHAR(2000) | 否 | - | 任务描述 | +| assignedTo | VARCHAR(200) | 否 | - | 负责人 | +| assignedVendor | VARCHAR(200) | 否 | - | 服务商 | +| assignedDate | DATE | 否 | - | 派单日期 | +| actualStart | TIMESTAMP | 否 | - | 实际开始时间 | +| actualEnd | TIMESTAMP | 否 | - | 实际结束时间 | +| actualHours | DECIMAL(6,2) | 否 | - | 实际工时 | +| faultCause | VARCHAR(2000) | 否 | - | 故障原因 | +| solution | VARCHAR(5000) | 否 | - | 解决方案 | +| result | VARCHAR(2000) | 否 | - | 处理结果 | +| partsUsed | JSONB | 否 | - | 使用备件列表(List\) | +| laborCost | DECIMAL(12,2) | 否 | - | 人工费 | +| partsCost | DECIMAL(12,2) | 否 | - | 备件费 | +| totalCost | DECIMAL(12,2) | 否 | - | 总费用 | +| completedBy | VARCHAR(200) | 否 | - | 完成人 | +| completedDate | DATE | 否 | - | 完成日期 | +| verifiedBy | VARCHAR(200) | 否 | - | 验收人 | +| verifiedDate | DATE | 否 | - | 验收日期 | +| rating | INTEGER | 否 | - | 评分(1-5) | +| remark | VARCHAR(1000) | 否 | - | 备注 | +| photos | JSONB | 否 | - | 照片URL列表 | +| signature | VARCHAR(2000) | 否 | - | 签名 | +| createdAt | TIMESTAMP | 否 | now() | 创建时间 | +| updatedAt | TIMESTAMP | 否 | now() | 更新时间 | +| createdBy | VARCHAR(200) | 否 | - | 创建人 | + +**枚举定义**: + +| 枚举 | 值 | 中文 | +|------|-----|------| +| TaskType | PREVENTIVE | 预防性维护 | +| | CORRECTIVE | 纠正性维护 | +| | EMERGENCY | 紧急维修 | +| TriggerType | PLAN | 计划触发 | +| | INSPECTION | 巡检触发 | +| | FAULT | 故障触发 | +| | MANUAL | 手动创建 | +| Priority | LOW / MEDIUM / HIGH / URGENT | 低/中/高/紧急 | +| Status | PENDING / ASSIGNED / IN_PROGRESS / COMPLETED / VERIFIED / CANCELLED | 见状态机 | + +**partsUsed JSONB 结构**: + +```json +[ + { + "partsId": "uuid", + "partsName": "备件名称", + "quantity": 2, + "unitPrice": 100.00, + "totalPrice": 200.00 + } +] +``` + +**索引**: + +| 索引名 | 字段 | 用途 | +|--------|------|------| +| idx_mt_equipment_status | equipment_id, status | 设备+状态查询 | +| idx_mt_project_status | project_id, status | 项目+状态查询 | +| idx_mt_plan_createdat | plan_id, created_at | 计划关联查询 | +| idx_mt_status_assigneddate | status, assigned_date | 状态+派单日期查询 | + +### 2.5 InspectionTemplate(巡检模板) + +**表名**: `ops_inspection_template` +**实体类**: `com.ether.pms.mdm.entity.InspectionTemplate`(位于module-mdm) + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| templateCode | VARCHAR(50) | 是 | 自动生成 | 模板编码(UNIQUE) | +| templateName | VARCHAR(200) | 是 | - | 模板名称 | +| description | VARCHAR(2000) | 否 | - | 描述 | +| projectId | UUID | 否 | - | 所属项目 | +| spaceId | UUID | 否 | - | 关联空间 | +| category | VARCHAR(50) | 否 | - | 分类 | +| status | ENUM | 否 | ACTIVE | 模板状态(2种) | +| createdAt | TIMESTAMP | 否 | now() | 创建时间 | +| updatedAt | TIMESTAMP | 否 | now() | 更新时间 | +| createdBy | VARCHAR(200) | 否 | - | 创建人 | +| items | List\ | - | - | 检查项列表(@Transient,非持久化) | + +**枚举定义**: + +| 枚举 | 值 | 中文 | +|------|-----|------| +| TemplateStatus | ACTIVE | 启用 | +| | INACTIVE | 停用 | + +### 2.6 InspectionItem(巡检项) + +**表名**: `ops_inspection_item` +**实体类**: `com.ether.pms.mdm.entity.InspectionItem`(位于module-mdm) + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| templateId | UUID | 是 | - | 所属模板ID | +| itemName | VARCHAR(200) | 是 | - | 检查项名称 | +| description | VARCHAR(2000) | 否 | - | 描述 | +| checkMethod | VARCHAR(2000) | 否 | - | 检查方法 | +| standard | VARCHAR(2000) | 否 | - | 检查标准 | +| isMandatory | BOOLEAN | 否 | true | 是否必检 | +| isNormalRequired | BOOLEAN | 否 | true | 是否需要正常判定 | +| sortOrder | INTEGER | 否 | 0 | 排序序号 | +| createdAt | TIMESTAMP | 否 | now() | 创建时间 | + +### 2.7 SparePart(备件) + +**表名**: `ops_spare_part` +**实体类**: `com.ether.pms.mdm.entity.SparePart`(位于module-mdm) + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| projectId | UUID | 是 | - | 所属项目 | +| sparePartCode | VARCHAR | 是 | 自动生成 | 备件编码(UNIQUE),格式: SP+yyyyMMddHHmmss | +| sparePartName | VARCHAR | 是 | - | 备件名称 | +| categoryId | UUID | 否 | - | 分类ID | +| specification | VARCHAR(500) | 否 | - | 规格 | +| unit | VARCHAR(50) | 是 | - | 单位 | +| safeStock | INTEGER | 否 | 0 | 安全库存 | +| currentStock | INTEGER | 否 | 0 | 当前库存 | +| unitPrice | DECIMAL(10,2) | 否 | - | 单价 | +| supplier | VARCHAR(200) | 否 | - | 供应商 | +| supplierContact | VARCHAR | 否 | - | 供应商联系方式 | +| location | VARCHAR(200) | 否 | - | 存放位置 | +| remarks | VARCHAR(1000) | 否 | - | 备注 | +| status | ENUM | 是 | ACTIVE | 状态(2种) | +| createdAt | TIMESTAMP | 否 | now() | 创建时间 | +| updatedAt | TIMESTAMP | 否 | now() | 更新时间 | + +**枚举定义**: + +| 枚举 | 值 | 中文 | +|------|-----|------| +| Status | ACTIVE | 启用 | +| | INACTIVE | 停用 | + +**索引**: + +| 索引名 | 字段 | 用途 | +|--------|------|------| +| idx_sp_project_status | project_id, status | 项目+状态查询 | + +### 2.8 SparePartCategory(备件分类) + +**表名**: `ops_spare_part_category` +**实体类**: `com.ether.pms.mdm.entity.SparePartCategory`(位于module-mdm) + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| parentId | UUID | 否 | - | 父分类ID(支持树形结构) | +| categoryCode | VARCHAR | 是 | 自动生成 | 分类编码(UNIQUE),格式: CC+yyyyMMddHHmmss | +| categoryName | VARCHAR | 是 | - | 分类名称 | +| description | VARCHAR(500) | 否 | - | 描述 | +| sortOrder | INTEGER | 否 | - | 排序序号 | +| createdAt | TIMESTAMP | 否 | now() | 创建时间 | + +### 2.9 SparePartRecord(备件出入库记录) + +**表名**: `ops_spare_part_record` +**实体类**: `com.ether.pms.mdm.entity.SparePartRecord`(位于module-mdm) + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| recordCode | VARCHAR | 是 | 自动生成 | 记录编码(UNIQUE),格式: REC+类型+yyyyMMddHHmmss | +| recordType | ENUM | 是 | - | 记录类型(4种) | +| sparePartId | UUID | 是 | - | 备件ID | +| quantity | INTEGER | 是 | - | 数量 | +| balance | INTEGER | 是 | - | 操作后余额 | +| relatedOrderId | UUID | 否 | - | 关联工单ID | +| recordedBy | UUID | 否 | - | 操作人 | +| recordDate | TIMESTAMP | 否 | now() | 操作时间 | +| remarks | VARCHAR(1000) | 否 | - | 备注 | + +**枚举定义**: + +| 枚举 | 值 | 中文 | 说明 | +|------|-----|------|------| +| RecordType | IN | 入库 | 库存增加 | +| | OUT | 出库 | 库存减少,可关联工单 | +| | CHECK | 盘点 | 库存校准 | +| | ADJUST | 调整 | 库存调整 | + +### 2.10 EnergyMeter(能源计量点) + +**表名**: `ops_energy_meter` +**实体类**: `com.ether.pms.mdm.entity.EnergyMeter`(位于module-mdm) + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| projectId | UUID | 是 | - | 所属项目 | +| meterCode | VARCHAR | 是 | 自动生成 | 计量点编码(UNIQUE) | +| meterName | VARCHAR | 是 | - | 计量点名称 | +| energyType | ENUM | 是 | - | 能源类型(6种) | +| spaceNodeId | UUID | 否 | - | 关联空间节点 | +| installationLocation | VARCHAR | 否 | - | 安装位置 | +| ratedCapacity | DECIMAL(10,2) | 否 | - | 额定容量 | +| unitPrice | DECIMAL(10,4) | 否 | - | 单价 | +| status | ENUM | 是 | ACTIVE | 状态(2种) | +| createdAt | TIMESTAMP | 否 | now() | 创建时间 | +| updatedAt | TIMESTAMP | 否 | now() | 更新时间 | + +**枚举定义**: + +| 枚举 | 值 | 中文 | 说明 | +|------|-----|------|------| +| EnergyType | LIGHTING | 照明插座用电 | 建筑分项能耗分类 | +| | HVAC | 空调用电 | 建筑分项能耗分类 | +| | POWER | 动力用电 | 建筑分项能耗分类 | +| | SPECIAL | 特殊用电 | 建筑分项能耗分类 | +| | WATER | 给排水 | 水耗计量 | +| | GAS | 燃气 | 气耗计量 | +| Status | ACTIVE | 启用 | 可正常抄表 | +| | INACTIVE | 停用 | 不可抄表 | + +### 2.11 EnergyConsumption(能耗记录) + +**表名**: `ops_energy_consumption` +**实体类**: `com.ether.pms.mdm.entity.EnergyConsumption`(位于module-mdm) + +| 字段 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| id | UUID | 是 | 自动生成 | 主键 | +| projectId | UUID | 是 | - | 项目ID | +| meterId | UUID | 是 | - | 计量点ID | +| consumptionDate | DATE | 是 | - | 消耗日期 | +| previousReading | DECIMAL(12,2) | 否 | - | 上次读数 | +| currentReading | DECIMAL(12,2) | 否 | - | 当前读数 | +| consumption | DECIMAL(12,2) | 是 | - | 消耗量 = currentReading - previousReading | +| amount | DECIMAL(10,2) | 否 | - | 费用 = consumption x unitPrice | +| recordedBy | UUID | 否 | - | 抄表人 | +| recordMethod | ENUM | 否 | MANUAL | 抄表方式(2种) | +| remarks | VARCHAR(1000) | 否 | - | 备注 | +| createdAt | TIMESTAMP | 否 | now() | 创建时间 | + +**枚举定义**: + +| 枚举 | 值 | 中文 | 说明 | +|------|-----|------|------| +| RecordMethod | MANUAL | 手动录入 | 人工抄表 | +| | IOT | IoT自动采集 | 自动采集(预留) | + +**索引**: + +| 索引名 | 字段 | 用途 | +|--------|------|------| +| idx_ec_meter_date | meter_id, consumption_date | 计量点+日期查询 | +| idx_ec_project_date | project_id, consumption_date | 项目+日期查询 | + +### 2.12 ER关系图 + +```mermaid +erDiagram + MaintenancePlan ||--o{ MaintenanceTask : "1:N planId" + MaintenancePlan }o--|| Equipment : "equipmentId" + WorkOrder ||--o{ WorkOrderItem : "1:N workOrderId" + WorkOrder }o--o| MaintenancePlan : "planId" + WorkOrder }o--o| Equipment : "equipmentId" + InspectionTemplate ||--o{ InspectionItem : "1:N templateId" + SparePartCategory ||--o{ SparePart : "1:N categoryId" + SparePart ||--o{ SparePartRecord : "1:N sparePartId" + EnergyMeter ||--o{ EnergyConsumption : "1:N meterId" + + WorkOrder { + UUID id PK + String workNo UK + Source source + Type type + Priority priority + Status status + Status previousStatus + UUID projectId FK + UUID equipmentId FK + UUID planId FK + } + + WorkOrderItem { + UUID id PK + UUID workOrderId FK + ItemType itemType + String itemName + BigDecimal quantity + BigDecimal totalPrice + } + + MaintenancePlan { + UUID id PK + String planCode UK + String planName + PlanType planType + Integer cycleDays + PlanStatus status + UUID equipmentId FK + } + + MaintenanceTask { + UUID id PK + String taskNo UK + UUID planId FK + UUID equipmentId FK + TaskType taskType + TriggerType triggerType + Priority priority + Status status + } + + InspectionTemplate { + UUID id PK + String templateCode UK + String templateName + String category + TemplateStatus status + } + + InspectionItem { + UUID id PK + UUID templateId FK + String itemName + String checkMethod + String standard + Boolean isMandatory + } + + SparePart { + UUID id PK + String sparePartCode UK + String sparePartName + UUID categoryId FK + Integer currentStock + Integer safeStock + } + + SparePartRecord { + UUID id PK + String recordCode UK + RecordType recordType + UUID sparePartId FK + Integer quantity + Integer balance + UUID relatedOrderId FK + } + + EnergyMeter { + UUID id PK + String meterCode UK + String meterName + EnergyType energyType + BigDecimal unitPrice + } + + EnergyConsumption { + UUID id PK + UUID meterId FK + LocalDate consumptionDate + BigDecimal consumption + BigDecimal amount + } +``` + +--- + +## 三、API设计 + +### 3.1 WorkOrderController + +**基础路径**: `/api/wo/work-orders` +**控制器类**: `com.ether.pms.ops.controller.WorkOrderController` + +#### 工单 CRUD + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| POST | `/` | 创建工单 | WorkOrder | ApiResponse\ | OPS-001 | +| GET | `/` | 查询工单列表(分页) | QueryParams | ApiResponse\\> | OPS-002 | +| GET | `/{id}` | 获取工单详情 | - | ApiResponse\ | OPS-003 | +| PUT | `/{id}` | 更新工单 | WorkOrder | ApiResponse\ | OPS-004 | +| DELETE | `/{id}` | 删除工单(逻辑删除) | - | ApiResponse\ | OPS-005 | + +**查询参数**(GET /): + +| 参数 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| projectId | UUID | 否 | - | 按项目筛选 | +| equipmentId | UUID | 否 | - | 按设备筛选 | +| source | Source | 否 | - | 按来源筛选 | +| type | Type | 否 | - | 按类型筛选 | +| status | Status | 否 | - | 按状态筛选 | +| priority | Priority | 否 | - | 按优先级筛选 | +| assignedTo | String | 否 | - | 按负责人筛选 | +| keyword | String | 否 | - | 关键词搜索 | +| page | int | 否 | 0 | 页码 | +| size | int | 否 | 20 | 每页大小 | + +#### 工单状态流转 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| POST | `/{id}/assign` | 派单 | AssignRequest | ApiResponse\ | OPS-006 | +| POST | `/{id}/start` | 开始执行 | - | ApiResponse\ | OPS-007 | +| POST | `/{id}/complete` | 完成工单 | WorkOrder | ApiResponse\ | OPS-008 | +| POST | `/{id}/verify` | 验收工单 | VerifyRequest | ApiResponse\ | OPS-009 | +| POST | `/{id}/cancel` | 取消工单 | - | ApiResponse\ | OPS-010 | +| POST | `/{id}/suspend` | 挂起工单 | - | ApiResponse\ | OPS-011 | +| POST | `/{id}/resume` | 恢复工单 | - | ApiResponse\ | OPS-012 | +| POST | `/{id}/return` | 退回工单 | - | ApiResponse\ | OPS-013 | + +**请求体定义**: + +```java +// 派单请求 +AssignRequest { + String assignedTo; // 负责人 + String assignedVendor; // 服务商 + LocalDate assignedDate; // 派单日期 +} + +// 验收请求 +VerifyRequest { + String verifiedBy; // 验收人 + String remark; // 备注 + Integer rating; // 评分(1-5) +} +``` + +#### 工单统计与明细 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| GET | `/stats` | 工单统计 | - | ApiResponse\ | OPS-014 | +| GET | `/{id}/items` | 获取工单明细 | - | ApiResponse\\> | OPS-015 | +| POST | `/{id}/items` | 批量添加工单明细 | List\ | ApiResponse\ | OPS-016 | + +**WorkOrderStatsDTO**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| total | long | 工单总数 | +| pending | long | 待分配数 | +| assigned | long | 已派单数 | +| inProgress | long | 执行中数 | +| completed | long | 已完成数 | +| verified | long | 已验收数 | +| cancelled | long | 已取消数 | +| suspended | long | 已挂起数 | +| returned | long | 已退回数 | +| completedToday | long | 今日完成数 | +| createdToday | long | 今日创建数 | +| overdue | long | 逾期数 | +| bySource | Map\ | 按来源分布 | +| byType | Map\ | 按类型分布 | +| byPriority | Map\ | 按优先级分布 | + +### 3.2 MaintenanceTaskController + +**基础路径**: `/api/ops/maintenance-tasks` +**控制器类**: `com.ether.pms.ops.controller.MaintenanceTaskController` + +#### 维保任务 CRUD + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| POST | `/` | 创建维保任务 | MaintenanceTask | ApiResponse\ | OPS-017 | +| GET | `/` | 查询维保任务列表 | QueryParams | ApiResponse\\> | OPS-018 | +| GET | `/{id}` | 获取任务详情 | - | ApiResponse\ | OPS-018 | +| PUT | `/{id}` | 更新任务 | MaintenanceTask | ApiResponse\ | OPS-018 | +| DELETE | `/{id}` | 删除任务(逻辑删除,设为CANCELLED) | - | ApiResponse\ | OPS-024 | + +**查询参数**(GET /): + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| equipmentId | UUID | 否 | 按设备筛选 | +| planId | UUID | 否 | 按计划筛选 | +| status | Status | 否 | 按状态筛选 | +| priority | Priority | 否 | 按优先级筛选 | +| assignedTo | String | 否 | 按负责人筛选 | +| overdueDate | LocalDate | 否 | 逾期截止日期 | + +> 查询参数互斥优先级:equipmentId > planId > status > priority > assignedTo > overdueDate + +#### 维保任务状态流转 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| POST | `/{id}/assign` | 分配任务 | AssignRequest | ApiResponse\ | OPS-019 | +| POST | `/{id}/start` | 开始执行 | - | ApiResponse\ | OPS-020 | +| POST | `/{id}/complete` | 完成任务(简版) | CompleteRequest | ApiResponse\ | OPS-021 | +| POST | `/{id}/complete-details` | 完成任务(详版) | MaintenanceTask | ApiResponse\ | OPS-022 | +| POST | `/{id}/verify` | 验收任务 | VerifyRequest | ApiResponse\ | OPS-023 | +| POST | `/{id}/cancel` | 取消任务 | - | ApiResponse\ | OPS-024 | +| POST | `/{id}/rate` | 评分 | rating (query param) | ApiResponse\ | OPS-025 | + +**请求体定义**: + +```java +// 分配请求 +AssignRequest { + String assignedTo; + LocalDate assignedDate; +} + +// 完成请求(简版) +CompleteRequest { + String result; + BigDecimal actualHours; + BigDecimal cost; + String completedBy; +} + +// 验收请求 +VerifyRequest { + String verifiedBy; + String remark; + Integer rating; +} +``` + +#### 维保任务统计 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| GET | `/stats` | 任务统计 | - | ApiResponse\ | OPS-026 | + +**MaintenanceTaskStatsDTO**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| total | long | 任务总数 | +| pending | long | 待分配数 | +| assigned | long | 已派单数 | +| inProgress | long | 执行中数 | +| completed | long | 已完成数 | +| verified | long | 已验收数 | +| cancelled | long | 已取消数 | +| completedToday | long | 今日完成数 | +| createdToday | long | 今日创建数 | +| overdue | long | 逾期数 | +| avgCompleteHours | BigDecimal | 平均完成工时 | +| avgRating | BigDecimal | 平均评分 | +| byPriority | Map\ | 按优先级分布 | +| byTriggerType | Map\ | 按触发类型分布 | + +### 3.3 InspectionTemplateController + +**基础路径**: `/api/ops/inspection-templates` +**控制器类**: `com.ether.pms.mdm.controller.InspectionTemplateController`(位于module-mdm) + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| GET | `/` | 按项目查询模板 | projectId | ApiResponse\\> | OPS-028 | +| POST | `/` | 创建模板 | InspectionTemplate | ApiResponse\ | OPS-028 | +| GET | `/{id}` | 获取模板详情 | - | ApiResponse\ | OPS-028 | +| PUT | `/{id}` | 更新模板 | InspectionTemplate | ApiResponse\ | OPS-028 | +| POST | `/{id}/copy` | 复制模板 | newName (query param) | ApiResponse\ | OPS-029 | +| GET | `/by-type/{equipmentType}` | 按设备类型查询 | - | ApiResponse\\> | OPS-028 | + +### 3.4 SparePartController + +**基础路径**: `/api/ops/spare-parts` +**控制器类**: `com.ether.pms.mdm.controller.SparePartController`(位于module-mdm) + +#### 分类管理 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| GET | `/categories` | 获取所有分类 | - | ApiResponse\\> | OPS-031 | +| POST | `/categories` | 创建分类 | SparePartCategory | ApiResponse\ | OPS-031 | + +#### 备件管理 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| GET | `/` | 查询备件列表 | projectId, categoryId(可选) | ApiResponse\\> | OPS-030 | +| GET | `/{id}` | 获取备件详情 | - | ApiResponse\ | OPS-030 | +| POST | `/` | 创建备件 | SparePart | ApiResponse\ | OPS-030 | +| PUT | `/{id}` | 更新备件 | SparePart | ApiResponse\ | OPS-030 | +| DELETE | `/{id}` | 删除备件(设为INACTIVE) | - | ApiResponse\ | OPS-030 | +| GET | `/low-stock` | 低库存预警 | projectId | ApiResponse\\> | OPS-034 | + +#### 库存操作 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| POST | `/in-stock` | 入库 | StockRequest | ApiResponse\ | OPS-032 | +| POST | `/out-stock` | 出库 | OutStockRequest | ApiResponse\ | OPS-033 | +| GET | `/{id}/records` | 获取出入库记录 | - | ApiResponse\\> | OPS-035 | + +**请求体定义**: + +```java +// 入库请求 +StockRequest { + @NotNull UUID sparePartId; + @NotNull Integer quantity; + UUID recordedBy; + String remarks; +} + +// 出库请求 +OutStockRequest { + @NotNull UUID sparePartId; + @NotNull Integer quantity; + UUID relatedOrderId; // 关联工单ID + UUID recordedBy; + String remarks; +} +``` + +### 3.5 EnergyController + +**基础路径**: `/api/ops/energy` +**控制器类**: `com.ether.pms.mdm.controller.EnergyController`(位于module-mdm) + +#### 计量点管理 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| POST | `/meters` | 创建计量点 | EnergyMeter | ApiResponse\ | OPS-036 | +| GET | `/meters` | 查询计量点 | projectId, energyType(可选) | ApiResponse\\> | OPS-036 | +| GET | `/meters/{id}` | 获取计量点详情 | - | ApiResponse\ | OPS-036 | +| PUT | `/meters/{id}` | 更新计量点 | EnergyMeter | ApiResponse\ | OPS-036 | +| DELETE | `/meters/{id}` | 删除计量点 | - | ApiResponse\ | OPS-036 | + +#### 能耗记录 + +| 方法 | 路径 | 说明 | 请求体 | 响应 | 功能ID | +|------|------|------|--------|------|--------| +| POST | `/consumption` | 抄表记录 | RecordConsumptionRequest | ApiResponse\ | OPS-037 | +| GET | `/consumption/{meterId}` | 获取能耗记录 | startDate, endDate(可选) | ApiResponse\\> | OPS-037 | + +#### 能耗统计 + +| 方法 | 路径 | 说明 | 参数 | 响应 | 功能ID | +|------|------|------|------|------|--------| +| GET | `/statistics/by-type` | 按类型统计 | projectId, month | ApiResponse\\> | OPS-038 | +| GET | `/statistics/unit-consumption` | 单位面积能耗 | projectId, month | ApiResponse\ | OPS-039 | + +**请求体定义**: + +```java +// 抄表请求 +RecordConsumptionRequest { + @NotNull UUID meterId; + @NotNull BigDecimal currentReading; + UUID recordedBy; +} +``` + +--- + +## 四、业务规则 + +### 4.1 工单状态机 + +#### 完整状态流转图 + +```mermaid +stateDiagram-v2 + [*] --> PENDING : 创建工单 + + PENDING --> ASSIGNED : 派单(assign) + PENDING --> CANCELLED : 取消(cancel) + + ASSIGNED --> IN_PROGRESS : 开始执行(start) + ASSIGNED --> SUSPENDED : 挂起(suspend) + ASSIGNED --> RETURNED : 退回(return) + ASSIGNED --> CANCELLED : 取消(cancel) + + IN_PROGRESS --> COMPLETED : 完成(complete) + IN_PROGRESS --> SUSPENDED : 挂起(suspend) + IN_PROGRESS --> CANCELLED : 取消(cancel) + + SUSPENDED --> IN_PROGRESS : 恢复(resume)\n[默认回到IN_PROGRESS] + SUSPENDED --> ASSIGNED : 恢复(resume)\n[previousStatus=ASSIGNED时] + + RETURNED --> PENDING : 退回后重新变为待分配 + + COMPLETED --> VERIFIED : 验收(verify) + + VERIFIED --> [*] + CANCELLED --> [*] +``` + +#### 8种状态说明 + +| 状态 | 中文 | 说明 | 可转入状态 | +|------|------|------|-----------| +| PENDING | 待分配 | 工单创建后的初始状态 | ASSIGNED, CANCELLED | +| ASSIGNED | 已派单 | 已分配负责人/服务商 | IN_PROGRESS, SUSPENDED, RETURNED, CANCELLED | +| IN_PROGRESS | 执行中 | 正在处理 | COMPLETED, SUSPENDED, CANCELLED | +| SUSPENDED | 已挂起 | 暂停处理,记录前一状态 | IN_PROGRESS, ASSIGNED(取决于previousStatus) | +| RETURNED | 已退回 | 被退回重新分配 | PENDING | +| COMPLETED | 已完成 | 处理完毕 | VERIFIED | +| VERIFIED | 已验收 | 验收通过 | 终态 | +| CANCELLED | 已取消 | 工单取消 | 终态 | + +#### 每个流转的前置条件与自动处理 + +| 当前状态 | 操作 | 目标状态 | 前置条件 | 自动处理 | +|---------|------|---------|---------|---------| +| 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 | status 非 COMPLETED/VERIFIED | - | +| ASSIGNED/IN_PROGRESS | suspend | SUSPENDED | - | 保存 previousStatus = 当前状态 | +| SUSPENDED | resume | previousStatus | status == SUSPENDED | 恢复到 previousStatus,默认 IN_PROGRESS | +| ASSIGNED | return | PENDING | status == ASSIGNED | 清空 assignedTo/assignedVendor/assignedDate | + +#### previousStatus回溯机制 + +``` +挂起时: + workOrder.previousStatus = workOrder.status // 保存当前状态 + workOrder.status = SUSPENDED + +恢复时: + targetStatus = workOrder.previousStatus != null + ? workOrder.previousStatus + : IN_PROGRESS // 默认恢复到IN_PROGRESS + workOrder.previousStatus = null // 清空 + workOrder.status = targetStatus +``` + +#### 工单编号生成规则 + +``` +格式: WO-YYYYMMDD-XXXX +示例: WO-20260518-0001 + +生成算法: +1. 日期前缀: "WO-" + LocalDate.now().format("yyyyMMdd") + "-" +2. 查询当天最大编号: findMaxWorkNoByPrefix(prefix + "%") +3. 序号递增: maxWorkNo存在时取后4位+1,否则从1开始 +4. 格式化: String.format("%04d", sequence) +``` + +#### 完成工单自动计算工时 + +```java +actualHours = Duration.between(actualStart, actualEnd).toMinutes() / 60.0 +// 精度:保留2位小数,四舍五入 +``` + +### 4.2 维保任务触发机制 + +#### 4种触发类型 + +| 触发类型 | 枚举值 | 说明 | 优先级自动判定 | +|---------|--------|------|---------------| +| 计划触发 | PLAN | 由维保计划周期性触发 | MEDIUM | +| 巡检触发 | INSPECTION | 巡检发现异常时触发 | HIGH | +| 故障触发 | FAULT | 设备故障时触发 | HIGH(含紧急关键词时URGENT) | +| 手动创建 | MANUAL | 人工手动创建 | MEDIUM | + +#### 自动优先级判定算法 + +``` +输入: MaintenanceTask (taskType, triggerType, title, description) + +1. IF taskType == EMERGENCY + → return URGENT + +2. IF triggerType == FAULT + → 检查 title + description 是否包含紧急关键词 + → 紧急关键词: [困人, 漏水, 停电, 火灾, 爆炸, 漏电, 冒烟, 故障停机] + → IF 包含任一关键词 → return URGENT + → ELSE → return HIGH + +3. IF triggerType == INSPECTION + → return HIGH + +4. IF triggerType == PLAN + → return MEDIUM + +5. 默认 → return MEDIUM +``` + +#### 维保任务状态机 + +```mermaid +stateDiagram-v2 + [*] --> PENDING : 创建任务 + + PENDING --> ASSIGNED : 分配(assign) + PENDING --> CANCELLED : 取消(cancel) + + ASSIGNED --> IN_PROGRESS : 开始执行(start) + ASSIGNED --> CANCELLED : 取消(cancel) + + IN_PROGRESS --> COMPLETED : 完成(complete/complete-details) + IN_PROGRESS --> CANCELLED : 取消(cancel) + + COMPLETED --> VERIFIED : 验收(verify) + + VERIFIED --> [*] + CANCELLED --> [*] +``` + +#### 维保任务与工单状态机差异 + +| 差异点 | WorkOrder | MaintenanceTask | +|--------|-----------|-----------------| +| 状态数量 | 8种(含SUSPENDED/RETURNED) | 6种(无SUSPENDED/RETURNED) | +| 完成方式 | 仅一种 complete | 两种:complete(简版)+ complete-details(详版) | +| 评分方式 | 验收时评分 | 验收时评分 + 独立 rate 接口 | +| 删除行为 | 逻辑删除(isDeleted=true) | 逻辑删除(设为CANCELLED) | +| 完成后联动 | 无 | 自动更新设备维保记录 | +| 挂起/恢复 | 支持 | 不支持 | +| 退回 | 支持(ASSIGNED→PENDING) | 不支持 | + +#### 维保任务完成后联动 + +维保任务完成(completeTaskWithDetails)时自动更新设备信息: + +``` +1. 更新设备维保商: + equipment.maintenanceVendor = task.assignedVendor + +2. 更新下次巡检日期(仅预防性维护): + IF task.taskType == PREVENTIVE + THEN equipment.nextInspectionDate = now() + equipment.inspectionCycle + 默认周期: 30天 + +3. 异常容错: + 更新设备失败不影响工单完成(catch异常仅打印日志) +``` + +#### 任务编号生成规则 + +``` +格式: EQ-YYYYMMDD-XXXX +示例: EQ-20260518-0001 + +生成算法: 与工单编号类似,前缀为 EQ- +``` + +### 4.3 巡检模板管理 + +**模板创建规则**: +- templateCode 自动生成(UNIQUE约束) +- 模板创建时可同时配置检查项列表(@Transient字段,需单独持久化) +- 模板状态默认为 ACTIVE + +**模板复制规则**: +- 复制模板时创建新模板,名称使用传入的 newName +- 检查项同时复制到新模板 +- 新模板的 templateCode 自动生成 + +**检查项配置**: +- InspectionItem 通过 templateId 关联模板 +- isMandatory = true 的检查项为必检项,不可跳过 +- isNormalRequired = true 的检查项需要判定正常/异常 +- sortOrder 控制检查项的展示顺序 + +### 4.4 备件库存管理 + +#### 入库流程 + +``` +1. 校验: quantity > 0 +2. 查询备件: getSparePartById(sparePartId) +3. 更新库存: currentStock += quantity +4. 创建入库记录: RecordType=IN, balance=更新后库存 +5. 生成记录编码: REC + IN + yyyyMMddHHmmss +``` + +#### 出库流程 + +``` +1. 校验: quantity > 0 +2. 查询备件: getSparePartById(sparePartId) +3. 校验库存: currentStock >= quantity + → 不满足: 抛出 BusinessException("库存不足,当前库存:X,需要出库:Y") +4. 更新库存: currentStock -= quantity +5. 创建出库记录: RecordType=OUT, balance=更新后库存, relatedOrderId=关联工单 +6. 生成记录编码: REC + OUT + yyyyMMddHHmmss +``` + +#### 低库存预警 + +``` +预警条件: currentStock < safeStock +查询方式: sparePartRepository.findLowStockParts(projectId) +``` + +#### 库存余额计算 + +``` +每次出入库操作后: + record.balance = sparePart.currentStock (操作后的库存余额) +``` + +#### 关联工单 + +- 出库时可指定 relatedOrderId 关联工单 +- 用于追踪工单的备件消耗 + +#### 并发场景考虑 + +> 当前实现未使用乐观锁或悲观锁,存在并发安全问题: + +| 并发场景 | 风险 | 当前处理 | 建议方案 | +|----------|------|----------|---------| +| 同时出库 | 库存超卖 | 无保护 | 添加 @Version 乐观锁或 SELECT FOR UPDATE 悲观锁 | +| 同时入库 | 库存计算错误 | 无保护 | 添加 @Version 乐观锁 | +| 并发读写 | 读到中间状态 | 无保护 | 使用数据库事务隔离级别 | + +**建议并发控制方案**: + +```java +// 方案1: 乐观锁(推荐) +@Version +private Long version; + +// 出库时: +SparePart sp = repository.findById(id); +if (sp.getCurrentStock() < quantity) throw ...; +sp.setCurrentStock(sp.getCurrentStock() - quantity); +repository.save(sp); // version不匹配时抛出OptimisticLockException + +// 方案2: 悲观锁 +@Lock(LockModeType.PESSIMISTIC_WRITE) +@Query("SELECT sp FROM SparePart sp WHERE sp.id = :id") +SparePart findByIdForUpdate(@Param("id") UUID id); +``` + +### 4.5 能耗计量管理 + +#### 抄表流程 + +``` +1. 校验计量点: meterId 存在且 status == ACTIVE +2. 获取上次抄表记录: findTopByMeterIdOrderByConsumptionDateDesc +3. 计算消耗量: consumption = currentReading - previousReading +4. 校验读数: consumption >= 0(当前读数不能小于上次读数) +5. 计算费用: amount = consumption x meter.unitPrice +6. 创建能耗记录: recordMethod=MANUAL +``` + +#### 消耗量计算 + +``` +consumption = currentReading - previousReading + +特殊情况: +- 首次抄表: previousReading = 0 +- 读数倒退: 抛出 BusinessException("当前读数不能小于上次读数") +``` + +#### 费用计算 + +``` +amount = consumption x meter.unitPrice + +条件: +- meter.unitPrice 不为空 +- consumption > 0 +``` + +#### 按类型统计 + +``` +输入: projectId, month (LocalDate) +1. 计算月份范围: startDate = month.withDayOfMonth(1), endDate = month.withDayOfMonth(length) +2. 初始化所有EnergyType为0 +3. 查询项目总消耗: sumConsumptionByProjectAndDateRange +4. 当前实现: 将总消耗分配给LIGHTING类型(TODO: 应按meter.energyType汇总) +``` + +> 当前按类型统计实现不完整,所有消耗归入LIGHTING类型,需改为按meter.energyType分别汇总。 + +#### 单位面积能耗 + +``` +输入: projectId, month +1. 获取项目所有ACTIVE状态的计量点 +2. 遍历每个计量点,查询当月消耗量 +3. 累加总消耗量 + +注意: 当前实现未除以建筑面积,仅返回总消耗量 +TODO: 需要获取项目建筑面积,计算 单位面积能耗 = 总消耗量 / 建筑面积 +``` + +### 4.6 工单明细管理 + +#### 3种明细类型 + +| 类型 | 枚举值 | 中文 | 用途 | +|------|--------|------|------| +| PART | 备件 | 记录工单使用的备件及费用 | +| INSPECTION_ITEM | 巡检项 | 记录工单的巡检检查项 | +| CHECKPOINT | 检查点 | 记录工单的关键检查点 | + +#### 费用计算 + +``` +前端计算: + item.totalPrice = item.quantity x item.unitPrice + partsCost = SUM(item.totalPrice) WHERE item.itemType == PART + totalCost = laborCost + partsCost +``` + +#### 批量添加 + +- 支持批量添加工单明细 +- 受 BatchOperationValidator 限制(校验批量操作大小) +- 每条明细的 workOrderId 自动设置为当前工单ID +- sortOrder 控制明细展示顺序 + +--- + +## 五、执行约束 + +### 5.1 工单编号唯一性 + +- workNo 字段在数据库层设置 UNIQUE 约束 +- 生成算法基于日期前缀+4位序号,按日期递增 +- 编号格式: `WO-YYYYMMDD-XXXX` +- 同一天内序号从0001开始递增 + +### 5.2 任务编号唯一性 + +- taskNo 字段在数据库层设置 UNIQUE 约束 +- 编号格式: `EQ-YYYYMMDD-XXXX` +- 生成算法与工单编号类似 + +### 5.3 备件库存非负约束 + +- 出库时校验: `currentStock >= quantity` +- 不满足时抛出 BusinessException(6104, "库存不足,当前库存:X,需要出库:Y") +- 入库数量必须 > 0,否则抛出 BusinessException(6102, "入库数量必须大于0") +- 出库数量必须 > 0,否则抛出 BusinessException(6103, "出库数量必须大于0") + +### 5.4 能耗读数递增约束 + +- 抄表时校验: `currentReading >= previousReading` +- 不满足时抛出 BusinessException(6103, "当前读数不能小于上次读数") +- 首次抄表时 previousReading = 0 + +### 5.5 工单状态流转约束 + +- 只能按合法路径流转,非法流转抛出 RuntimeException +- COMPLETED/VERIFIED 状态不可取消 +- SUSPENDED 状态只能恢复(resume),不能直接转为其他状态 +- RETURNED 状态自动转为 PENDING + +**合法流转矩阵**: + +| 当前\目标 | PENDING | ASSIGNED | IN_PROGRESS | SUSPENDED | RETURNED | COMPLETED | VERIFIED | CANCELLED | +|----------|---------|----------|-------------|-----------|----------|-----------|----------|-----------| +| PENDING | - | V | - | - | - | - | - | V | +| ASSIGNED | - | - | V | V | V | - | - | V | +| IN_PROGRESS | - | - | - | V | - | V | - | V | +| SUSPENDED | - | V* | V* | - | - | - | - | - | +| RETURNED | V | - | - | - | - | - | - | - | +| COMPLETED | - | - | - | - | - | - | V | - | +| VERIFIED | - | - | - | - | - | - | - | - | +| CANCELLED | - | - | - | - | - | - | - | - | + +> V* 表示 SUSPENDED 恢复时回到 previousStatus + +### 5.6 维保计划周期约束 + +- cycleDays 必须 > 0(周期天数必须为正数) +- nextDate = lastDate + cycleDays(下次执行日期由周期推算) +- 计划状态变更:ACTIVE 可转为 INACTIVE 或 SUSPENDED + +--- + +## 六、权限控制 + +| API路径 | 操作 | 所需权限 | 说明 | +|---------|------|----------|------| +| POST /api/wo/work-orders | 创建工单 | wo:work-order:create | 需项目级权限 | +| GET /api/wo/work-orders | 查询工单 | wo:work-order:read | 需项目级权限 | +| PUT /api/wo/work-orders/{id} | 更新工单 | wo:work-order:update | 需项目级权限 | +| DELETE /api/wo/work-orders/{id} | 删除工单 | wo:work-order:delete | 需项目级权限 | +| POST /api/wo/work-orders/{id}/assign | 派单 | wo:work-order:assign | 需项目级权限 | +| POST /api/wo/work-orders/{id}/start | 开始执行 | wo:work-order:execute | 需项目级权限 | +| POST /api/wo/work-orders/{id}/complete | 完成工单 | wo:work-order:execute | 需项目级权限 | +| POST /api/wo/work-orders/{id}/verify | 验收工单 | wo:work-order:verify | 需项目级权限 | +| POST /api/wo/work-orders/{id}/cancel | 取消工单 | wo:work-order:cancel | 需项目级权限 | +| POST /api/wo/work-orders/{id}/suspend | 挂起工单 | wo:work-order:suspend | 需项目级权限 | +| POST /api/wo/work-orders/{id}/resume | 恢复工单 | wo:work-order:resume | 需项目级权限 | +| POST /api/wo/work-orders/{id}/return | 退回工单 | wo:work-order:return | 需项目级权限 | +| POST /api/ops/maintenance-tasks | 创建维保任务 | ops:maintenance-task:create | 需项目级权限 | +| POST /api/ops/maintenance-tasks/{id}/assign | 分配任务 | ops:maintenance-task:assign | 需项目级权限 | +| POST /api/ops/maintenance-tasks/{id}/start | 开始执行 | ops:maintenance-task:execute | 需项目级权限 | +| POST /api/ops/maintenance-tasks/{id}/complete | 完成任务 | ops:maintenance-task:execute | 需项目级权限 | +| POST /api/ops/maintenance-tasks/{id}/verify | 验收任务 | ops:maintenance-task:verify | 需项目级权限 | +| POST /api/ops/spare-parts/in-stock | 入库 | ops:spare-part:stock-in | 需项目级权限 | +| POST /api/ops/spare-parts/out-stock | 出库 | ops:spare-part:stock-out | 需项目级权限 | +| POST /api/ops/energy/consumption | 抄表 | ops:energy:record | 需项目级权限 | + +> 当前实现中权限控制尚未集成Spring Security,以上为设计预期权限矩阵。 + +--- + +## 七、例外情况处理 + +| 例外场景 | 触发条件 | 处理方式 | 错误码/信息 | +|----------|----------|----------|------------| +| 工单状态流转非法 | 从PENDING直接complete | 抛出RuntimeException | "只能完成进行中的工单" | +| 工单已完成后取消 | status=COMPLETED/VERIFIED时cancel | 抛出RuntimeException | "无法取消已完成的工单" | +| 挂起非ASSIGNED/IN_PROGRESS工单 | status不合法时suspend | 抛出RuntimeException | "只能挂起已派单或执行中的工单" | +| 恢复非SUSPENDED工单 | status!=SUSPENDED时resume | 抛出RuntimeException | "只能恢复已挂起的工单" | +| 退回非ASSIGNED工单 | status!=ASSIGNED时return | 抛出RuntimeException | "只能退回已派单的工单" | +| 备件库存不足 | 出库数量>当前库存 | 抛出BusinessException | 6104 "库存不足,当前库存:X,需要出库:Y" | +| 入库数量非法 | quantity <= 0 | 抛出BusinessException | 6102 "入库数量必须大于0" | +| 出库数量非法 | quantity <= 0 | 抛出BusinessException | 6103 "出库数量必须大于0" | +| 能耗读数倒退 | currentReading < previousReading | 抛出BusinessException | 6103 "当前读数不能小于上次读数" | +| 能源仪表不存在 | meterId无效 | 抛出BusinessException | 6101 "能源仪表不存在" | +| 仪表非ACTIVE状态 | 抄表时meter.status!=ACTIVE | 抛出BusinessException | 6102 "只能对ACTIVE状态的仪表进行抄表" | +| 维保计划关联设备不存在 | equipmentId无效 | 数据库外键约束 | 需添加业务校验 | +| 维保任务不存在 | taskId无效 | 抛出BusinessException | ErrorCode.NOT_FOUND "维保工单不存在" | +| 工单不存在 | workOrderId无效或已删除 | 抛出RuntimeException | "工单不存在: {id}" | +| 维保任务评分越界 | rating < 1 或 rating > 5 | 抛出BusinessException | 6006 "评分必须在1-5之间" | +| 维保任务评分状态非法 | 非COMPLETED/VERIFIED状态评分 | 抛出BusinessException | 6005 "只有已完成或已验收的工单才能评分" | +| 巡检模板被引用不能删除 | 模板被巡检记录引用 | - | 当前无校验,建议增加 | +| 备件不存在 | sparePartId无效 | 抛出BusinessException | 6101 "备件不存在" | + +--- + +**附录:已知代码问题与改进建议** + +| 问题 | 位置 | 影响 | 建议 | +|------|------|------|------| +| 备件出入库无并发控制 | SparePartServiceImpl | 高并发下可能库存超卖 | 添加 @Version 乐观锁或 SELECT FOR UPDATE | +| 能耗按类型统计实现不完整 | EnergyConsumptionServiceImpl | 所有消耗归入LIGHTING类型 | 按 meter.energyType 分别汇总 | +| 单位面积能耗未除以建筑面积 | EnergyConsumptionServiceImpl | 返回的是总消耗量而非单位面积能耗 | 获取项目建筑面积做除法 | +| 工单查询参数互斥 | WorkOrderController/MaintenanceTaskController | 多条件同时传入只生效一个 | 改为 Specification 动态查询 | +| 维保任务查询无分页 | MaintenanceTaskController | 数据量大时可能OOM | 改为分页查询 | +| 工单删除/退回使用RuntimeException | WorkOrderServiceImpl | 异常信息不够结构化 | 改为 BusinessException + ErrorCode | +| 维保任务删除实际是取消 | MaintenanceTaskServiceImpl | deleteTask 实际设置 status=CANCELLED | 命名与行为不一致,建议重命名 | +| 前后端状态枚举不一致 | maintenance.ts vs MaintenanceTask | 前端含ACCEPTED,后端含ASSIGNED/VERIFIED | 统一前后端状态定义 | +| 前端维保计划两套API | maintenance.ts + maintenance-plan.ts | 接口重复,维护困难 | 统一为一套API | +| 工单无流转记录 | WorkOrderServiceImpl | 无法追溯工单状态变更历史 | 新增 WorkOrderFlow 实体 | diff --git a/02-DESIGN/detail/DETAIL-SPACE.md b/02-DESIGN/detail/DETAIL-SPACE.md new file mode 100644 index 0000000..5c1a01b --- /dev/null +++ b/02-DESIGN/detail/DETAIL-SPACE.md @@ -0,0 +1,1008 @@ +# 空间与项目域 - 详细设计 + +**文档版本**: 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结构**: +```json +[ + {"itemId": "uuid", "itemName": "检查项", "value": "实测值", "result": "PASS/FAIL", "remark": "备注"} +] +``` + +**problems JSONB结构**: +```json +[ + {"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) + +```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}` | 已登录 | 分页参数越界 | +| 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}` | 项目成员 | 项目不存在 | +| 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**: + +```typescript +// 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}` | 已登录 | 分页参数越界 | +| 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**: + +```typescript +// 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}` | 管理员 | 标准项不存在; 已被模板引用 | + +**查询逻辑优先级**: +1. activeOnly=true → 仅返回ACTIVE状态项 +2. equipmentType + systemType → 双条件过滤 +3. equipmentType → 单条件过滤 +4. systemType → 系统类型过滤 +5. 无参数 → 返回全部 + +--- + +### 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}` | 项目成员 | 记录不存在; 已完成 | + +**查询逻辑优先级**: +1. equipmentId + 日期范围 → 设备+日期 +2. equipmentId → 设备 +3. planId → 计划 +4. inspector → 巡检人 +5. status → 状态 +6. 日期范围 → 时间段 +7. 无参数 → 全部 + +--- + +### 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}` | 项目成员 | 项目不存在 | +| 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 树形路径维护 + +- **treePath**: 物理路径,格式 `id1.id2.id3`,用于快速查询所有子孙节点 + - 创建节点时: `父节点.treePath + "." + 当前节点.id`(根节点为自身id) + - 支持通过 `LIKE 'parentId.%'` 快速查询所有子孙 + +- **treePathName**: 名称路径,格式 `项目/楼栋/单元/房间`,用于展示 + - 创建节点时: `父节点.treePathName + "/" + 当前节点.name` + +- **level**: 层级深度,根节点为0,每层+1 + - 创建节点时: `父节点.level + 1` + +- **sortOrder**: 同级排序,默认0 + +#### 4.1.3 删除规则 + +1. **删除前检查**: 调用 `GET /{id}/delete-check` 获取子节点信息 + - 返回: `{nodeId, nodeName, childCount, childTypeCount, totalDescendantCount}` + +2. **普通删除**: `DELETE /{id}` + - 有子节点时拒绝删除 + - 软删除: 设置 `isDeleted=true` + +3. **级联删除**: `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 创建巡检记录流程 + +1. 选择设备(`equipmentId`) +2. 填写巡检日期(`inspectionDate`)和巡检人(`inspector`) +3. 逐项填写检查结果(`items` JSONB) +4. 上报异常问题(`problems` JSONB) +5. 签到: 记录时间、位置、照片 + +#### 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校验 | diff --git a/02-DESIGN/domains/03-ASSET.md b/02-DESIGN/domains/03-ASSET.md deleted file mode 100644 index 9ea82ec..0000000 --- a/02-DESIGN/domains/03-ASSET.md +++ /dev/null @@ -1,370 +0,0 @@ -# 设施设备领域技术方案 - -**领域编号**: 4.3 -**微服务**: ether-asset -**最后更新**: 2026-02-14 - ---- - -## 一、领域概述 - -### 1.1 领域职责 - -设施设备领域负责管理物业资产全生命周期: - -- 设备台账管理(电梯、空调、消防、给排水、供配电等) -- 设备维保计划与记录 -- 设备故障管理 -- 设备与工单联动 - -### 1.2 核心概念 - -| 概念 | 说明 | 对应实体 | -| ------------ | ---------------------------- | ----------------- | -| **设备台账** | 设备基础信息和运行状态 | Equipment | -| **维保计划** | 定期保养计划 | MaintenancePlan | -| **维保记录** | 保养执行记录 | MaintenanceRecord | -| **设备类型** | 设备分类(电梯/空调/消防等) | EquipmentType | - ---- - -## 二、领域模型 - -### 2.1 聚合根设计 - -#### Equipment(设备台账) - -```java -@Entity -@Table(name = "asset_equipment") -@Data -public class Equipment { - @Id - private UUID id; - private UUID projectId; - - private String code; // 设备编码 - private String name; // 设备名称 - - // 分类 - private EquipmentType type; // ELEVATOR/AC/FIRE/PLUMBING/POWER - private EquipmentStatus status; // NORMAL/FAULT/MAINTAINING/SCRAPPED - - // 品牌型号 - private String brand; - private String model; - private String specifications; - private String serialNumber; - - // 厂商信息 - private String manufacturer; - private String supplier; - private String supplierPhone; - - // 位置 - private UUID spaceNodeId; // 关联空间节点 - private String locationDesc; // 位置描述 - - // 时间 - private LocalDate purchaseDate; - private LocalDate installDate; - private LocalDate warrantyDate; - private Integer lifespanYears; // 设计寿命(年) - - // 维保 - private Integer maintenanceCycle; // 保养周期(天) - private LocalDate lastMaintenanceDate; - private LocalDate nextMaintenanceDate; - - // 负责人 - private UUID managerId; - private String managerName; - private String contactPhone; - - // 二维码 - private String qrCode; - - // 扩展属性(JSONB) - private String attributes; - - // 审计字段 - private LocalDateTime createdAt; - private LocalDateTime updatedAt; -} -``` - -**设备类型枚举**: - -```java -public enum EquipmentType { - ELEVATOR("电梯"), - AIR_CONDITIONER("空调"), - FIRE_FIGHTING("消防设备"), - PLUMBING("给排水"), - POWER_SUPPLY("供配电"), - SECURITY("安防设备"), - PARKING("停车设备"), - OTHER("其他"); -} -``` - -#### MaintenancePlan(维保计划) - -```java -@Entity -@Table(name = "asset_maintenance_plan") -@Data -public class MaintenancePlan { - @Id - private UUID id; - private UUID projectId; - private UUID equipmentId; // 关联设备 - - private String name; - private MaintenanceType type; // DAILY/WEEKLY/MONTHLY/QUARTERLY/YEARLY - - // 周期 - private Integer cycleDays; // 周期天数 - private String cronExpression; // Cron表达式 - - // 内容 - private String content; // 保养内容描述 - private String checkItems; // 检查项清单(JSON) - - // 负责人 - private UUID maintainerId; - private String maintainerName; - - // 提醒 - private Integer remindDays; // 提前提醒天数 - - // 状态 - private Boolean enabled; - - // 审计字段 - private LocalDateTime createdAt; - private LocalDateTime updatedAt; -} -``` - -#### MaintenanceRecord(维保记录) - -```java -@Entity -@Table(name = "asset_maintenance_record") -@Data -public class MaintenanceRecord { - @Id - private UUID id; - private UUID equipmentId; - private UUID planId; // 关联计划 - - // 维保信息 - private LocalDate maintenanceDate; - private MaintenanceType type; - private String content; - - // 执行人 - private UUID maintainerId; - private String maintainerName; - - // 结果 - private MaintenanceResult result; // NORMAL/ABNORMAL - private String remark; - private String images; - - // 检查项结果 - private String checkResults; // [{"item":"检查A","result":"PASS"}] - - // 关联工单 - private UUID workOrderId; // 异常时关联的维修工单 - - // 下次维保 - private LocalDate nextMaintenanceDate; - - // 审计字段 - private LocalDateTime createdAt; - private LocalDateTime updatedAt; -} -``` - ---- - -## 三、设备与工单联动 - -### 3.1 故障自动报修 - -```java -@Component -public class EquipmentFaultHandler { - - @Autowired - private WorkOrderService workOrderService; - - @EventListener - public void onEquipmentFault(EquipmentFaultEvent event) { - Equipment equipment = event.getEquipment(); - - // 创建设备维修工单 - WorkOrder workOrder = new WorkOrder(); - workOrder.setOrderType(WorkOrderType.REPAIR); - workOrder.setTitle("设备故障: " + equipment.getName()); - workOrder.setDescription(event.getFaultDescription()); - workOrder.setEquipmentId(equipment.getId()); - workOrder.setSpaceNodeId(equipment.getSpaceNodeId()); - workOrder.setPriority(WorkOrderPriority.HIGH); - workOrder.setSource(WorkOrderSource.IOT); - - workOrderService.create(workOrder); - - // 更新设备状态 - equipment.setStatus(EquipmentStatus.FAULT); - equipmentRepository.save(equipment); - } -} -``` - -### 3.2 维保到期提醒 - -```java -@Component -public class MaintenanceReminderJob { - - @Scheduled(cron = "0 0 9 * * ?") // 每天9点执行 - public void remindUpcomingMaintenance() { - // 查询7天内到期的维保计划 - LocalDate targetDate = LocalDate.now().plusDays(7); - - List equipments = equipmentRepository - .findByNextMaintenanceDateBeforeAndStatus(targetDate, EquipmentStatus.NORMAL); - - for (Equipment equipment : equipments) { - notificationService.sendMaintenanceReminder(equipment); - } - } -} -``` - ---- - -## 四、实现状态与差异 - -### 4.1 实现状态 - -| 功能模块 | 实现状态 | 备注 | -| ----------------- | ----------- | ---------------------- | -| Equipment | 🟢 已实现 | 基础CRUD、二维码生成 | -| MaintenancePlan | 🟢 已实现 | 已迁移至 ether-asset | -| MaintenanceRecord | 🟢 已实现 | 已迁移至 ether-asset | -| 设备二维码 | 🟢 已实现 | 支持扫码查看设备信息 | -| 故障自动工单 | 🟡 部分实现 | 设备状态变更可关联工单 | -| IoT集成 | 🔴 未实现 | 待开发 | - -### 4.2 与设计方案的差异 - -| 设计项 | 设计方案 | 现有实现 | 差异分析 | -| ------------ | ------------ | ----------- | --------------------------------------- | -| **维保归属** | ether-asset | ether-asset | ✅ 已修正:维保实体已统一在 ether-asset | -| **设备管理** | ether-asset | ether-asset | ✅ 已修正:设备实体已统一在 ether-asset | -| **设备联动** | 故障自动工单 | 部分实现 | 🟡 功能完善中 | - -### 4.3 改进计划 - -| 优先级 | 改进项 | 说明 | -| ------ | ------------------ | ---------------------------------------------------- | -| ~~P1~~ | ~~迁移维保实体~~ | ~~将MaintenancePlan/Record迁移到ether-asset~~ 已完成 | -| P2 | 完善故障自动工单 | 设备状态变更时自动创建维修工单 | -| P2 | 完善设备二维码功能 | 支持扫码报修 | -| P3 | IoT传感器接入 | 对接传感器数据 | - ---- - -## 五、数据库表结构 - -```sql --- 设备台账表 -CREATE TABLE asset_equipment ( - id UUID PRIMARY KEY, - project_id UUID NOT NULL, - code VARCHAR(50) NOT NULL, - name VARCHAR(100) NOT NULL, - type VARCHAR(20) NOT NULL, - status VARCHAR(20) NOT NULL DEFAULT 'NORMAL', - brand VARCHAR(100), - model VARCHAR(100), - specifications VARCHAR(500), - serial_number VARCHAR(100), - manufacturer VARCHAR(100), - supplier VARCHAR(100), - supplier_phone VARCHAR(20), - space_node_id UUID, - location_desc VARCHAR(255), - purchase_date DATE, - install_date DATE, - warranty_date DATE, - lifespan_years INTEGER, - maintenance_cycle INTEGER, - last_maintenance_date DATE, - next_maintenance_date DATE, - manager_id UUID, - manager_name VARCHAR(100), - contact_phone VARCHAR(20), - qr_code VARCHAR(255), - attributes JSONB, - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - updated_at TIMESTAMP NOT NULL DEFAULT NOW(), - UNIQUE(project_id, code) -); - --- 维保计划表 -CREATE TABLE asset_maintenance_plan ( - id UUID PRIMARY KEY, - project_id UUID NOT NULL, - equipment_id UUID NOT NULL, - name VARCHAR(100) NOT NULL, - type VARCHAR(20) NOT NULL, - cycle_days INTEGER, - cron_expression VARCHAR(50), - content TEXT, - check_items JSONB, - maintainer_id UUID, - maintainer_name VARCHAR(100), - remind_days INTEGER DEFAULT 3, - enabled BOOLEAN DEFAULT TRUE, - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - updated_at TIMESTAMP NOT NULL DEFAULT NOW() -); - --- 维保记录表 -CREATE TABLE asset_maintenance_record ( - id UUID PRIMARY KEY, - equipment_id UUID NOT NULL, - plan_id UUID, - maintenance_date DATE NOT NULL, - type VARCHAR(20) NOT NULL, - content TEXT, - maintainer_id UUID, - maintainer_name VARCHAR(100), - result VARCHAR(20), - remark VARCHAR(500), - images TEXT, - check_results JSONB, - work_order_id UUID, - next_maintenance_date DATE, - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - updated_at TIMESTAMP NOT NULL DEFAULT NOW() -); - --- 创建索引 -CREATE INDEX idx_equipment_project ON asset_equipment(project_id); -CREATE INDEX idx_equipment_type ON asset_equipment(type); -CREATE INDEX idx_equipment_status ON asset_equipment(status); -CREATE INDEX idx_equipment_space ON asset_equipment(space_node_id); -CREATE INDEX idx_equipment_next_maintenance ON asset_equipment(next_maintenance_date); -CREATE INDEX idx_maintenance_plan_equipment ON asset_maintenance_plan(equipment_id); -CREATE INDEX idx_maintenance_record_equipment ON asset_maintenance_record(equipment_id); -``` - ---- - -**文档维护**: 本领域技术方案由 ether-asset 服务负责人维护 diff --git a/02-DESIGN/overview/OVERVIEW-DESIGN.md b/02-DESIGN/overview/OVERVIEW-DESIGN.md new file mode 100644 index 0000000..dbdcffd --- /dev/null +++ b/02-DESIGN/overview/OVERVIEW-DESIGN.md @@ -0,0 +1,1335 @@ +# 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 + `