# 设施设备管理增强功能开发计划 > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** 实现非居物业设施设备管理增强功能(M02-13~18),包括设备技术参数扩展、预防性维护引擎、能耗监控、备件库存、故障预测、点检标准库 **Architecture:** 基于现有 ether-pms 单体架构,在 module-mdm 模块新增设备管理相关实体和服务,前端新增设备管理页面。能耗监控和故障预测作为独立功能模块开发。 **Tech Stack:** Spring Boot 3.x + JPA + PostgreSQL + Vue3 + TypeScript + Ant Design Vue --- ## 开发阶段总览 ``` 阶段一(M02-13):设备技术参数扩展 - 约3天 阶段二(M02-14):预防性维护引擎 - 约5天 阶段三(M02-15):能耗监控管理 - 约4天 阶段四(M02-16):备件库存管理 - 约4天 阶段五(M02-17):设备故障预测 - 约3天 阶段六(M02-18):设备点检标准库 - 约3天 ───────────────────────────────────────── 总计 约22天 ``` --- ## 阶段一:M02-13 设备技术参数扩展 ### 数据库迁移 **文件:** `ether-pms/src/main/resources/db/migration/V10__add_equipment_extension_fields.sql` ```sql -- 添加设备扩展字段到 mdm_space_node 表(设备关联空间节点) ALTER TABLE mdm_space_node ADD COLUMN design_life_years INTEGER; ALTER TABLE mdm_space_node ADD COLUMN rated_power DECIMAL(10,2); ALTER TABLE mdm_space_node ADD COLUMN rated_voltage VARCHAR(20); ALTER TABLE mdm_space_node ADD COLUMN rated_current DECIMAL(10,2); ALTER TABLE mdm_space_node ADD COLUMN maintenance_vendor VARCHAR(100); ALTER TABLE mdm_space_node ADD COLUMN maintenance_vendor_contact VARCHAR(50); ALTER TABLE mdm_space_node ADD COLUMN maintenance_vendor_phone VARCHAR(20); ALTER TABLE mdm_space_node ADD COLUMN maintenance_contract_no VARCHAR(50); ALTER TABLE mdm_space_node ADD COLUMN maintenance_contract_start DATE; ALTER TABLE mdm_space_node ADD COLUMN maintenance_contract_end DATE; ALTER TABLE mdm_space_node ADD COLUMN special_equipment_type VARCHAR(50); ALTER TABLE mdm_space_node ADD COLUMN special_equipment_cert VARCHAR(100); ALTER TABLE mdm_space_node ADD COLUMN inspection_cycle INTEGER; ALTER TABLE mdm_space_node ADD COLUMN next_inspection_date DATE; ALTER TABLE mdm_space_node ADD COLUMN last_inspection_date DATE; ALTER TABLE mdm_space_node ADD COLUMN last_inspection_result VARCHAR(20); ALTER TABLE mdm_space_node ADD COLUMN common_spare_parts JSONB; ALTER TABLE mdm_space_node ADD COLUMN energy_consumption_standard DECIMAL(12,2); ALTER TABLE mdm_space_node ADD COLUMN installation_environment VARCHAR(50); ALTER TABLE mdm_space_node ADD COLUMN protection_level VARCHAR(20); ``` --- ### Task 1.1: 创建设备分类枚举 **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/enums/EquipmentCategory.java` ```java package com.ether.pms.mdm.enums; public enum EquipmentCategory { HVAC("暖通空调"), ELECTRICAL("电气设备"), FIRE("消防设备"), ELEVATOR("电梯设备"), SECURITY("安防设备"), 给排水("给排水设备"), LIGHTING("照明设备"), SPECIAL("特种设备"); private final String desc; EquipmentCategory(String desc) { this.desc = desc; } public String getDesc() { return desc; } } ``` **Step 1:** 创建枚举类 **Step 2:** 验证编译通过 **Step 3:** Commit: `feat: add equipment category enum` --- ### Task 1.2: 创建设备类型枚举 **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/enums/EquipmentType.java` ```java package com.ether.pms.mdm.enums; public enum EquipmentType { CENTRAL_AC("中央空调", EquipmentCategory.HVAC), AIR_CONDITIONER("分体空调", EquipmentCategory.HVAC), AIR_HANDLING_UNIT("空气处理机组", EquipmentCategory.HVAC), FAN_COIL("风机盘管", EquipmentCategory.HVAC), LOW_VOLTAGE_CABINET("低压配电柜", EquipmentCategory.ELECTRICAL), TRANSFORMER("变压器", EquipmentCategory.ELECTRICAL), GENERATOR("发电机", EquipmentCategory.ELECTRICAL), UPS("不间断电源", EquipmentCategory.ELECTRICAL), FIRE_PUMP("消防泵", EquipmentCategory.FIRE), SPRINKLER("喷淋系统", EquipmentCategory.FIRE), FIRE_ALARM("火灾报警系统", EquipmentCategory.FIRE), ELEVATOR("电梯", EquipmentCategory.ELEVATOR), CCTV("监控系统", EquipmentCategory.SECURITY), ACCESS_CONTROL("门禁系统", EquipmentCategory.SECURITY), WATER_PUMP("给水泵", EquipmentCategory.WATER_DRAINAGE), DRAINAGE_PUMP("排水泵", EquipmentCategory.WATER_DRAINAGE), LED_LIGHT("LED灯具", EquipmentCategory.LIGHTING), HIGH_BAY_LIGHT("工矿灯", EquipmentCategory.LIGHTING); private final String desc; private final EquipmentCategory category; EquipmentType(String desc, EquipmentCategory category) { this.desc = desc; this.category = category; } public String getDesc() { return desc; } public EquipmentCategory getCategory() { return category; } } ``` **Step 1:** 创建枚举类 **Step 2:** 验证编译通过 **Step 3:** Commit: `feat: add equipment type enum` --- ### Task 1.3: 更新SpaceNode实体 **Files:** - Modify: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/SpaceNode.java` 在现有实体末尾添加扩展字段: ```java // ========== 设备扩展字段 ========== @Column(name = "is_equipment") private Boolean isEquipment = false; @Column(name = "design_life_years") private Integer designLifeYears; @Column(name = "rated_power", precision = 10, scale = 2) private BigDecimal ratedPower; @Column(name = "rated_voltage", length = 20) private String ratedVoltage; @Column(name = "rated_current", precision = 10, scale = 2) private BigDecimal ratedCurrent; @Column(name = "maintenance_vendor", length = 100) private String maintenanceVendor; @Column(name = "maintenance_vendor_contact", length = 50) private String maintenanceVendorContact; @Column(name = "maintenance_vendor_phone", length = 20) private String maintenanceVendorPhone; @Column(name = "maintenance_contract_no", length = 50) private String maintenanceContractNo; @Column(name = "maintenance_contract_start") private LocalDate maintenanceContractStart; @Column(name = "maintenance_contract_end") private LocalDate maintenanceContractEnd; @Column(name = "special_equipment_type", length = 50) private String specialEquipmentType; @Column(name = "special_equipment_cert", length = 100) private String specialEquipmentCert; @Column(name = "inspection_cycle") private Integer inspectionCycle; @Column(name = "next_inspection_date") private LocalDate nextInspectionDate; @Column(name = "last_inspection_date") private LocalDate lastInspectionDate; @Column(name = "last_inspection_result", length = 20) private String lastInspectionResult; @Column(name = "common_spare_parts", columnDefinition = "TEXT") private String commonSpareParts; @Column(name = "energy_consumption_standard", precision = 12, scale = 2) private BigDecimal energyConsumptionStandard; @Column(name = "installation_environment", length = 50) private String installationEnvironment; @Column(name = "protection_level", length = 20) private String protectionLevel; // ========== 设备扩展字段结束 ========== ``` **Step 1:** 添加扩展字段到 SpaceNode 实体 **Step 2:** 运行 `cd ether-pms && mvn compile` 验证 **Step 3:** Commit: `feat: add equipment extension fields to SpaceNode` --- ### Task 1.4: 创建SpaceNode DTO **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/dto/SpaceNodeEquipmentDTO.java` ```java package com.ether.pms.mdm.dto; import lombok.Data; import lombok.EqualsAndHashCode; import java.math.BigDecimal; import java.time.LocalDate; import java.util.List; @Data @EqualsAndHashCode(callSuper = true) public class SpaceNodeEquipmentDTO extends SpaceNodeDTO { private Boolean isEquipment; private Integer designLifeYears; private BigDecimal ratedPower; private String ratedVoltage; private BigDecimal ratedCurrent; private String maintenanceVendor; private String maintenanceVendorContact; private String maintenanceVendorPhone; private String maintenanceContractNo; private LocalDate maintenanceContractStart; private LocalDate maintenanceContractEnd; private String specialEquipmentType; private String specialEquipmentCert; private Integer inspectionCycle; private LocalDate nextInspectionDate; private LocalDate lastInspectionDate; private String lastInspectionResult; private List commonSpareParts; private BigDecimal energyConsumptionStandard; private String installationEnvironment; private String protectionLevel; @Data public static class SparePartInfo { private String name; private String model; private Integer quantity; } } ``` **Step 1:** 创建 DTO 类 **Step 2:** 运行 `cd ether-pms && mvn compile` 验证 **Step 3:** Commit: `feat: add SpaceNodeEquipmentDTO` --- ### Task 1.5: 更新SpaceNodeService **Files:** - Modify: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/service/SpaceNodeService.java` 添加设备相关方法: ```java public SpaceNodeEquipmentDTO getEquipmentById(UUID id) { SpaceNode node = spaceNodeRepository.findById(id) .orElseThrow(() -> new BusinessException("EQUIPMENT_NOT_FOUND")); return convertToEquipmentDTO(node); } public List getSpecialEquipmentList(UUID projectId) { List list = spaceNodeRepository.findByProjectIdAndIsEquipmentAndSpecialEquipmentTypeIsNotNull( projectId, true); return list.stream().map(this::convertToEquipmentDTO).collect(Collectors.toList()); } public List getExpiringInspectionEquipment(UUID projectId, Integer daysAhead) { LocalDate threshold = LocalDate.now().plusDays(daysAhead); List list = spaceNodeRepository .findByProjectIdAndIsEquipmentAndNextInspectionDateBefore(projectId, true, threshold); return list.stream().map(this::convertToEquipmentDTO).collect(Collectors.toList()); } ``` **Step 1:** 添加服务方法 **Step 2:** 添加单元测试 **Step 3:** 运行测试验证 **Step 4:** Commit: `feat: add equipment service methods` --- ### Task 1.6: 更新SpaceNodeController **Files:** - Modify: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/controller/SpaceNodeController.java` 添加设备相关API: ```java @GetMapping("/{id}/equipment") public ApiResponse getEquipment(@PathVariable UUID id) { return ApiResponse.success(spaceNodeService.getEquipmentById(id)); } @GetMapping("/special-equipment") public ApiResponse> getSpecialEquipment( @RequestParam UUID projectId) { return ApiResponse.success(spaceNodeService.getSpecialEquipmentList(projectId)); } @GetMapping("/expiring-inspection") public ApiResponse> getExpiringInspection( @RequestParam UUID projectId, @RequestParam(defaultValue = "90") Integer daysAhead) { return ApiResponse.success(spaceNodeService.getExpiringInspectionEquipment(projectId, daysAhead)); } ``` **Step 1:** 添加控制器端点 **Step 2:** 使用 curl 测试端点 **Step 3:** Commit: `feat: add equipment API endpoints` --- ### Task 1.7: 前端 - 创建设备管理页面 **Files:** - Create: `ether-admin/src/views/equipment/EquipmentList.vue` - Create: `ether-admin/src/views/equipment/EquipmentDetail.vue` - Create: `ether-admin/src/api/equipment.ts` **Step 1:** 创建 API 模块 `ether-admin/src/api/equipment.ts` ```typescript import request from '@/utils/request' export interface EquipmentForm { id?: string code: string name: string nodeType: string locationDesc?: string designLifeYears?: number ratedPower?: number ratedVoltage?: string maintenanceVendor?: string maintenanceVendorPhone?: string specialEquipmentType?: string inspectionCycle?: number nextInspectionDate?: string [key: string]: any } export function getEquipmentList(params: any) { return request({ url: '/api/v1/mdm/space-nodes/equipment', method: 'get', params }) } export function getEquipmentDetail(id: string) { return request({ url: `/api/v1/mdm/space-nodes/${id}/equipment`, method: 'get' }) } export function getSpecialEquipment(projectId: string) { return request({ url: '/api/v1/mdm/space-nodes/special-equipment', method: 'get', params: { projectId } }) } export function getExpiringInspection(projectId: string, daysAhead?: number) { return request({ url: '/api/v1/mdm/space-nodes/expiring-inspection', method: 'get', params: { projectId, daysAhead } }) } ``` **Step 2:** 创建设备列表页面 `EquipmentList.vue` **Step 3:** 创建设备详情页面 `EquipmentDetail.vue` **Step 4:** 运行 `npm run build` 验证前端编译 **Step 5:** Commit: `feat: add equipment management pages` --- ## 阶段二:M02-14 预防性维护引擎 ### Task 2.1: 创建维保计划实体 **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/MaintenancePlan.java` ```java package com.ether.pms.mdm.entity; import jakarta.persistence.*; import lombok.Data; import java.time.LocalDateTime; import java.util.UUID; @Entity @Table(name = "ops_maintenance_plan") @Data public class MaintenancePlan { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; @Column(name = "project_id", nullable = false) private UUID projectId; @Column(name = "plan_code", nullable = false, unique = true) private String planCode; @Column(name = "plan_name", nullable = false) private String planName; @Column(name = "equipment_type") private String equipmentType; @Column(name = "trigger_type", nullable = false) @Enumerated(EnumType.STRING) private TriggerType triggerType; public enum TriggerType { TIME_BASED, // 时间触发 HOURS_BASED, // 运行小时触发 CYCLES_BASED, // 次数触发 CONDITION_BASED // 条件触发 } @Column(name = "trigger_value") private Integer triggerValue; @Column(name = "trigger_unit") private String triggerUnit; @Column(name = "maintenance_items", columnDefinition = "TEXT") private String maintenanceItems; @Column(name = "estimated_duration") private Integer estimatedDuration; @Column(name = "assigned_to") private UUID assignedTo; @Column(name = "sla_response_hours") private Integer slaResponseHours; @Column(name = "sla_complete_hours") private Integer slaCompleteHours; @Column(nullable = false) @Enumerated(EnumType.STRING) private Status status = Status.ACTIVE; public enum Status { ACTIVE, INACTIVE } @Column(name = "created_at") private LocalDateTime createdAt; @Column(name = "updated_at") private LocalDateTime updatedAt; @PrePersist public void prePersist() { createdAt = LocalDateTime.now(); updatedAt = LocalDateTime.now(); } @PreUpdate public void preUpdate() { updatedAt = LocalDateTime.now(); } } ``` **Step 1:** 创建实体类 **Step 2:** 运行 `mvn compile` 验证 **Step 3:** Commit: `feat: add MaintenancePlan entity` --- ### Task 2.2: 创建维保任务实体 **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/MaintenanceTask.java` ```java package com.ether.pms.mdm.entity; import jakarta.persistence.*; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.UUID; @Entity @Table(name = "ops_maintenance_task") @Data public class MaintenanceTask { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; @Column(name = "project_id", nullable = false) private UUID projectId; @Column(name = "task_code", nullable = false, unique = true) private String taskCode; @Column(name = "plan_id") private UUID planId; @Column(name = "equipment_id") private UUID equipmentId; @Column(name = "task_type") @Enumerated(EnumType.STRING) private TaskType taskType = TaskType.PREVENTIVE; public enum TaskType { PREVENTIVE, // 预防性维护 CORRECTIVE // 纠正性维护 } @Column(name = "trigger_type") @Enumerated(EnumType.STRING) private MaintenancePlan.TriggerType triggerType; @Column(name = "maintenance_items", columnDefinition = "TEXT") private String maintenanceItems; @Column(nullable = false) @Enumerated(EnumType.STRING) private Status status = Status.PENDING; public enum Status { PENDING, // 待接受 ACCEPTED, // 已接受 IN_PROGRESS, // 执行中 COMPLETED, // 已完成 CANCELLED // 已取消 } @Column(name = "assigned_to") private UUID assignedTo; @Column(name = "scheduled_date") private LocalDateTime scheduledDate; @Column(name = "actual_start_date") private LocalDateTime actualStartDate; @Column(name = "actual_end_date") private LocalDateTime actualEndDate; @Column(name = "labor_hours", precision = 10, scale = 2) private BigDecimal laborHours; @Column(name = "materials_cost", precision = 12, scale = 2) private BigDecimal materialsCost; @Column(columnDefinition = "TEXT") private String remarks; @Column(name = "created_at") private LocalDateTime createdAt; @Column(name = "updated_at") private LocalDateTime updatedAt; @PrePersist public void prePersist() { createdAt = LocalDateTime.now(); updatedAt = LocalDateTime.now(); } @PreUpdate public void preUpdate() { updatedAt = LocalDateTime.now(); } } ``` **Step 1:** 创建实体类 **Step 2:** 运行 `mvn compile` 验证 **Step 3:** Commit: `feat: add MaintenanceTask entity` --- ### Task 2.3: 创建维保Repository **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/repository/MaintenancePlanRepository.java` - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/repository/MaintenanceTaskRepository.java` ```java package com.ether.pms.mdm.repository; public interface MaintenancePlanRepository extends JpaRepository { List findByProjectIdAndStatus(UUID projectId, MaintenancePlan.Status status); Optional findByPlanCode(String planCode); } public interface MaintenanceTaskRepository extends JpaRepository { List findByProjectIdAndStatus(UUID projectId, MaintenanceTask.Status status); List findByAssignedToAndStatus(UUID assignedTo, MaintenanceTask.Status status); List findByEquipmentIdAndStatusNot(UUID equipmentId, MaintenanceTask.Status status); Optional findByTaskCode(String taskCode); } ``` **Step 1:** 创建 Repository **Step 2:** 添加查询方法单元测试 **Step 3:** Commit: `feat: add maintenance repositories` --- ### Task 2.4: 创建维保Service **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/service/MaintenancePlanService.java` - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/service/MaintenanceTaskService.java` - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/service/impl/MaintenancePlanServiceImpl.java` - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/service/impl/MaintenanceTaskServiceImpl.java` **Service核心方法:** ```java // MaintenancePlanService UUID createPlan(MaintenancePlan plan); List getActivePlansByProject(UUID projectId); void updatePlan(UUID id, MaintenancePlan plan); void deactivatePlan(UUID id); // MaintenanceTaskService List getTasksByAssignee(UUID assignee, String status); void acceptTask(UUID taskId, UUID userId); void startTask(UUID taskId); void completeTask(UUID taskId, BigDecimal laborHours, BigDecimal materialsCost, String remarks); List getOverdueTasks(); ``` **Step 1:** 创建 Service 接口和实现 **Step 2:** 添加单元测试 **Step 3:** 运行测试验证 **Step 4:** Commit: `feat: add maintenance service` --- ### Task 2.5: 创建维保Controller **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/controller/MaintenancePlanController.java` - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/controller/MaintenanceTaskController.java` **API 设计:** | 方法 | 路径 | 描述 | |------|------|------| | POST | /api/v1/ops/maintenance-plans | 创建维保计划 | | GET | /api/v1/ops/maintenance-plans | 获取维保计划列表 | | PUT | /api/v1/ops/maintenance-plans/{id} | 更新维保计划 | | DELETE | /api/v1/ops/maintenance-plans/{id} | 删除维保计划 | | GET | /api/v1/ops/maintenance-tasks | 获取维保任务列表 | | POST | /api/v1/ops/maintenance-tasks/{id}/accept | 接受任务 | | POST | /api/v1/ops/maintenance-tasks/{id}/start | 开始执行 | | POST | /api/v1/ops/maintenance-tasks/{id}/complete | 完成维保 | **Step 1:** 创建 Controller **Step 2:** 使用 curl 测试 API **Step 3:** Commit: `feat: add maintenance controllers` --- ### Task 2.6: 创建定时任务 - 维保触发检查 **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/scheduler/MaintenanceScheduler.java` ```java package com.ether.pms.mdm.scheduler; @Component @RequiredArgsConstructor public class MaintenanceScheduler { private final MaintenancePlanService planService; private final MaintenanceTaskService taskService; @Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点 public void checkTimeBasedMaintenance() { log.info("检查时间触发的维保计划..."); List plans = planService.getTimeBasedActivePlans(); plans.forEach(plan -> taskService.generateTasksFromPlan(plan)); } @Scheduled(cron = "0 0 * * * ?") // 每小时 public void checkHourlyMaintenance() { log.info("检查运行小时触发的维保计划..."); // 检查运行小时触发的计划 } } ``` **Step 1:** 创建定时任务类 **Step 2:** 在 PmsApplication 添加 `@EnableScheduling` **Step 3:** 测试定时任务执行 **Step 4:** Commit: `feat: add maintenance scheduler` --- ## 阶段三:M02-15 能耗监控管理 ### Task 3.1: 创建能耗相关实体 **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/EnergyMeter.java` - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/EnergyConsumption.java` ```java @Entity @Table(name = "ops_energy_meter") @Data public class EnergyMeter { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; @Column(name = "project_id", nullable = false) private UUID projectId; @Column(name = "meter_code", nullable = false, unique = true) private String meterCode; @Column(name = "meter_name", nullable = false) private String meterName; @Column(name = "energy_type", nullable = false) @Enumerated(EnumType.STRING) private EnergyType energyType; public enum EnergyType { LIGHTING, // 照明插座用电 HVAC, // 空调用电 POWER, // 动力用电 SPECIAL, // 特殊用电 WATER, // 给排水 GAS // 燃气 } @Column(name = "space_node_id") private UUID spaceNodeId; @Column(name = "installation_location") private String installationLocation; @Column(name = "unit_price", precision = 10, scale = 4) private BigDecimal unitPrice; @Column(nullable = false) @Enumerated(EnumType.STRING) private Status status = Status.ACTIVE; public enum Status { ACTIVE, INACTIVE } } @Entity @Table(name = "ops_energy_consumption") @Data public class EnergyConsumption { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; @Column(name = "project_id", nullable = false) private UUID projectId; @Column(name = "meter_id", nullable = false) private UUID meterId; @Column(name = "consumption_date", nullable = false) private LocalDate consumptionDate; @Column(name = "previous_reading", precision = 12, scale = 2) private BigDecimal previousReading; @Column(name = "current_reading", precision = 12, scale = 2) private BigDecimal currentReading; @Column(nullable = false, precision = 12, scale = 2) private BigDecimal consumption; @Column(precision = 10, scale = 2) private BigDecimal amount; @Column(name = "recorded_by") private UUID recordedBy; @Column(name = "record_method") @Enumerated(EnumType.STRING) private RecordMethod recordMethod = RecordMethod.MANUAL; public enum RecordMethod { MANUAL, IOT } } ``` **Step 1:** 创建实体 **Step 2:** 运行编译验证 **Step 3:** Commit: `feat: add energy management entities` --- ### Task 3.2: 创建能耗Service **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/service/EnergyMeterService.java` - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/service/EnergyConsumptionService.java` - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/service/EnergyStatisticsService.java` ```java // EnergyConsumptionService void recordConsumption(UUID meterId, BigDecimal currentReading, UUID recordedBy); List getConsumptionByMeter(UUID meterId, LocalDate startDate, LocalDate endDate); // EnergyStatisticsService Map getConsumptionByType(UUID projectId, LocalDate month); BigDecimal getUnitConsumption(UUID projectId, LocalDate month); // kWh/m² Map getConsumptionTrend(UUID projectId, Integer months); List getAlerts(UUID projectId); ``` **Step 1:** 创建 Service **Step 2:** 添加单元测试 **Step 3:** Commit: `feat: add energy service` --- ### Task 3.3: 创建能耗Controller **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/controller/EnergyController.java` **API 设计:** | 方法 | 路径 | 描述 | |------|------|------| | GET | /api/v1/ops/energy-meters | 获取计量点列表 | | POST | /api/v1/ops/energy-meters | 创建计量点 | | POST | /api/v1/ops/energy-consumption | 录入能耗数据 | | GET | /api/v1/ops/energy-statistics/by-type | 按分项统计 | | GET | /api/v1/ops/energy-statistics/unit-consumption | 单方能耗统计 | | GET | /api/v1/ops/energy-statistics/trend | 能耗趋势 | **Step 1:** 创建 Controller **Step 2:** 测试 API **Step 3:** Commit: `feat: add energy controller` --- ## 阶段四:M02-16 备件库存管理 ### Task 4.1: 创建备件实体 **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/SparePart.java` - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/SparePartRecord.java` ```java @Entity @Table(name = "ops_spare_part") @Data public class SparePart { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; @Column(name = "project_id", nullable = false) private UUID projectId; @Column(name = "spare_part_code", nullable = false, unique = true) private String sparePartCode; @Column(name = "spare_part_name", nullable = false) private String sparePartName; @Column(name = "category_id") private UUID categoryId; @Column private String specification; @Column(nullable = false) private String unit; @Column(name = "safe_stock") private Integer safeStock; @Column(name = "current_stock") private Integer currentStock; @Column(name = "unit_price", precision = 10, scale = 2) private BigDecimal unitPrice; @Column private String supplier; @Column(name = "location") private String location; @Column @Enumerated(EnumType.STRING) private Status status = Status.ACTIVE; } @Entity @Table(name = "ops_spare_part_record") @Data public class SparePartRecord { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; @Column(name = "record_code", nullable = false, unique = true) private String recordCode; @Column(name = "record_type", nullable = false) @Enumerated(EnumType.STRING) private RecordType recordType; public enum RecordType { IN, // 入库 OUT, // 出库 CHECK, // 盘点 ADJUST // 调整 } @Column(name = "spare_part_id", nullable = false) private UUID sparePartId; @Column(nullable = false) private Integer quantity; @Column(nullable = false) private Integer balance; @Column(name = "related_order_id") private UUID relatedOrderId; @Column(name = "recorded_by") private UUID recordedBy; @Column(name = "record_date") private LocalDateTime recordDate; } ``` **Step 1:** 创建实体 **Step 2:** 编译验证 **Step 3:** Commit: `feat: add spare part entities` --- ### Task 4.2: 创建备件Service **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/service/SparePartService.java` ```java public interface SparePartService { SparePart create(SparePart sparePart); void updateStock(UUID sparePartId, Integer quantity, SparePartRecord.RecordType type, UUID relatedOrderId); List getLowStockParts(UUID projectId); List getRecordsByPart(UUID sparePartId); } ``` **Step 1:** 创建 Service **Step 2:** 添加库存扣减逻辑(事务处理) **Step 3:** 添加单元测试 **Step 4:** Commit: `feat: add spare part service` --- ### Task 4.3: 创建备件Controller **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/controller/SparePartController.java` **API 设计:** | 方法 | 路径 | 描述 | |------|------|------| | GET | /api/v1/ops/spare-parts | 获取备件列表 | | POST | /api/v1/ops/spare-parts | 创建备件 | | GET | /api/v1/ops/spare-parts/{id} | 获取备件详情 | | GET | /api/v1/ops/spare-parts/low-stock | 获取低库存备件 | | POST | /api/v1/ops/spare-part-records | 创建备件记录 | | GET | /api/v1/ops/spare-part-records/{sparePartId} | 获取备件记录 | **Step 1:** 创建 Controller **Step 2:** 测试 API **Step 3:** Commit: `feat: add spare part controller` --- ## 阶段五:M02-17 设备故障预测 ### Task 5.1: 创建故障历史实体 **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/EquipmentFailureHistory.java` - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/EquipmentHealthScore.java` ```java @Entity @Table(name = "ops_equipment_failure_history") @Data public class EquipmentFailureHistory { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; @Column(name = "equipment_id", nullable = false) private UUID equipmentId; @Column(name = "failure_date", nullable = false) private LocalDateTime failureDate; @Column(name = "failure_type") private String failureType; @Column(name = "failure_cause") private String failureCause; @Column(name = "failure_description", columnDefinition = "TEXT") private String failureDescription; @Column(name = "repair_start_time") private LocalDateTime repairStartTime; @Column(name = "repair_end_time") private LocalDateTime repairEndTime; @Column(name = "repair_duration", precision = 10, scale = 2) private BigDecimal repairDuration; @Column(name = "repair_cost", precision = 12, scale = 2) private BigDecimal repairCost; @Column(name = "spare_parts_used", columnDefinition = "TEXT") private String sparePartsUsed; @Column(name = "work_order_id") private UUID workOrderId; } @Entity @Table(name = "ops_equipment_health_score") @Data public class EquipmentHealthScore { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; @Column(name = "equipment_id", nullable = false) private UUID equipmentId; @Column(name = "score_date", nullable = false) private LocalDate scoreDate; @Column(name = "health_score", precision = 5, scale = 2) private BigDecimal healthScore; @Column(precision = 10, scale = 2) private BigDecimal mtbf; @Column(precision = 10, scale = 2) private BigDecimal mttr; @Column(name = "failure_count") private Integer failureCount; @Column(name = "maintenance_completion_rate", precision = 5, scale = 2) private BigDecimal maintenanceCompletionRate; @Column(name = "alert_level") @Enumerated(EnumType.STRING) private AlertLevel alertLevel; public enum AlertLevel { NORMAL, WARNING, CRITICAL } } ``` **Step 1:** 创建实体 **Step 2:** 编译验证 **Step 3:** Commit: `feat: add equipment health entities` --- ### Task 5.2: 创建故障预测Service **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/service/EquipmentHealthService.java` ```java public interface EquipmentHealthService { // 计算设备健康度 EquipmentHealthScore calculateHealthScore(UUID equipmentId); // 获取设备MTBF BigDecimal calculateMTBF(UUID equipmentId, Integer days); // 获取设备MTTR BigDecimal calculateMTTR(UUID equipmentId, Integer days); // 获取设备健康度历史 List getHealthHistory(UUID equipmentId); // 获取预警设备列表 List getAlertEquipmentIds(UUID projectId, AlertLevel level); // 记录故障 void recordFailure(EquipmentFailureHistory failure); } ``` **健康度算法实现:** ```java public EquipmentHealthScore calculateHealthScore(UUID equipmentId) { // 基础分 = 100 BigDecimal baseScore = new BigDecimal("100"); // 故障率扣分 = 故障次数 × 5(近30天) int failureCount = failureHistoryRepository.countByEquipmentIdAndFailureDateAfter( equipmentId, LocalDateTime.now().minusDays(30)); BigDecimal failureDeduction = new BigDecimal(failureCount * 5); // 维保扣分 = (1 - 维保完成率) × 20 BigDecimal maintenanceRate = maintenanceTaskService.getCompletionRate(equipmentId); BigDecimal maintenanceDeduction = new BigDecimal("1").subtract(maintenanceRate) .multiply(new BigDecimal("20")); // 年龄扣分 = 投入使用年限 × 2(最高扣10分) SpaceNode equipment = spaceNodeRepository.findById(equipmentId).orElseThrow(); int yearsInService = calculateYearsInService(equipment.getCreatedAt()); BigDecimal ageDeduction = new BigDecimal(Math.min(yearsInService * 2, 10)); BigDecimal healthScore = baseScore .subtract(failureDeduction) .subtract(maintenanceDeduction) .subtract(ageDeduction); return healthScore; } ``` **Step 1:** 创建 Service 接口 **Step 2:** 实现计算逻辑 **Step 3:** 添加单元测试 **Step 4:** Commit: `feat: add equipment health service` --- ## 阶段六:M02-18 设备点检标准库 ### Task 6.1: 创建点检模板实体 **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/InspectionTemplate.java` ```java @Entity @Table(name = "ops_inspection_template") @Data public class InspectionTemplate { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; @Column(name = "project_id", nullable = false) private UUID projectId; @Column(name = "template_code", nullable = false, unique = true) private String templateCode; @Column(name = "template_name", nullable = false) private String templateName; @Column(name = "equipment_type") private String equipmentType; @Column(name = "inspection_items", columnDefinition = "TEXT") private String inspectionItems; @Column(name = "estimated_duration") private Integer estimatedDuration; @Column(nullable = false) @Enumerated(EnumType.STRING) private Status status = Status.ACTIVE; public enum Status { ACTIVE, INACTIVE } @Column private Integer version; @Column(name = "created_by") private UUID createdBy; @Column(name = "created_at") private LocalDateTime createdAt; @PrePersist public void prePersist() { createdAt = LocalDateTime.now(); if (version == null) version = 1; } } ``` **Step 1:** 创建实体 **Step 2:** 编译验证 **Step 3:** Commit: `feat: add inspection template entity` --- ### Task 6.2: 创建点检Service **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/service/InspectionTemplateService.java` ```java public interface InspectionTemplateService { InspectionTemplate create(InspectionTemplate template); InspectionTemplate copyTemplate(UUID templateId, String newName); List getTemplatesByType(String equipmentType); void updateTemplate(UUID id, InspectionTemplate template); } ``` **点检项目JSON结构:** ```json { "items": [ { "itemCode": "I001", "itemName": "电机温度检查", "inspectionPoint": "电机外壳", "inspectionMethod": "HAND_FEEL", "standard": "温度正常,无明显过热", "normalValue": "≤60°C", "abnormalValue": ">70°C", "handlingMethod": "停机检查,联系维修", "isRequired": true } ] } ``` **Step 1:** 创建 Service **Step 2:** 添加 JSON 解析/存储逻辑 **Step 3:** Commit: `feat: add inspection template service` --- ### Task 6.3: 创建点检Controller **Files:** - Create: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/controller/InspectionTemplateController.java` **API 设计:** | 方法 | 路径 | 描述 | |------|------|------| | GET | /api/v1/ops/inspection-templates | 获取点检模板列表 | | POST | /api/v1/ops/inspection-templates | 创建点检模板 | | GET | /api/v1/ops/inspection-templates/{id} | 获取模板详情 | | PUT | /api/v1/ops/inspection-templates/{id} | 更新模板 | | POST | /api/v1/ops/inspection-templates/{id}/copy | 复制模板 | | GET | /api/v1/ops/inspection-templates/by-type/{equipmentType} | 按设备类型获取 | **Step 1:** 创建 Controller **Step 2:** 测试 API **Step 3:** Commit: `feat: add inspection template controller` --- ## 前端页面开发(统一在最后) ### Task F.1: 设备管理页面 **Files:** - Create: `ether-admin/src/views/equipment/EquipmentList.vue` - Create: `ether-admin/src/views/equipment/EquipmentDetail.vue` - Create: `ether-admin/src/views/equipment/EquipmentForm.vue` - Modify: `ether-admin/src/views/Layout.vue` - 添加设备管理菜单 **Step 1:** 创建设备列表页面 **Step 2:** 创建设备详情页面 **Step 3:** 添加设备表单组件 **Step 4:** 更新 Layout 添加菜单 **Step 5:** 运行 `npm run build` 验证 **Step 6:** Commit: `feat: add equipment management frontend` --- ### Task F.2: 维保管理页面 **Files:** - Create: `ether-admin/src/views/maintenance/PlanList.vue` - Create: `ether-admin/src/views/maintenance/TaskList.vue` **Step 1:** 创建维保计划列表 **Step 2:** 创建维保任务列表 **Step 3:** 编译验证 **Step 4:** Commit: `feat: add maintenance management frontend` --- ### Task F.3: 能耗监控页面 **Files:** - Create: `ether-admin/src/views/energy/EnergyMeterList.vue` - Create: `ether-admin/src/views/energy/EnergyStatistics.vue` **Step 1:** 创建计量点管理页面 **Step 2:** 创建能耗统计页面 **Step 3:** 编译验证 **Step 4:** Commit: `feat: add energy monitoring frontend` --- ### Task F.4: 备件管理页面 **Files:** - Create: `ether-admin/src/views/sparepart/SparePartList.vue` - Create: `ether-admin/src/views/sparepart/SparePartRecord.vue` **Step 1:** 创建备件列表页面 **Step 2:** 创建备件记录页面 **Step 3:** 编译验证 **Step 4:** Commit: `feat: add spare part management frontend` --- ## 测试验证 ### Task T.1: 后端单元测试 为每个 Service 创建单元测试: - `SpaceNodeServiceTest` - `MaintenancePlanServiceTest` - `MaintenanceTaskServiceTest` - `EnergyConsumptionServiceTest` - `SparePartServiceTest` - `EquipmentHealthServiceTest` ```bash cd ether-pms && mvn test -Dtest=*ServiceTest ``` --- ### Task T.2: 后端集成测试 ```bash # 使用 H2 内存数据库进行集成测试 cd ether-pms && mvn verify ``` --- ### Task T.3: 前端构建验证 ```bash cd ether-admin && npm run build ``` --- ### Task T.4: E2E 测试 使用 Playwright 进行 E2E 测试: ```bash # 设备管理 E2E npx playwright test tests/equipment.spec.ts # 维保管理 E2E npx playwright test tests/maintenance.spec.ts ``` --- ## 数据库迁移脚本汇总 **文件列表:** 1. `V10__add_equipment_extension_fields.sql` - 设备扩展字段 2. `V11__create_maintenance_tables.sql` - 维保计划和任务表 3. `V12__create_energy_tables.sql` - 能耗表 4. `V13__create_spare_part_tables.sql` - 备件表 5. `V14__create_equipment_health_tables.sql` - 设备健康表 6. `V15__create_inspection_template_table.sql` - 点检模板表 --- ## 部署检查清单 - [ ] 数据库迁移脚本执行 - [ ] 后端编译通过 - [ ] 后端单元测试通过 - [ ] 前端编译通过 - [ ] API 端点验证 - [ ] E2E 测试通过 - [ ] 文档更新 --- ## 风险与缓解 | 风险 | 影响 | 缓解措施 | |------|------|----------| | 定时任务性能 | 维保检查任务执行慢 | 使用 @Async 或分布式任务 | | 能耗数据量大 | 查询性能问题 | 添加索引,分区表 | | IoT 数据接入 | 协议兼容问题 | 预留适配器接口 | | 历史数据不足 | 故障预测不准 | 初期使用规则判断 | --- *计划版本: v1.0* *最后更新: 2026-03-23*