41 KiB
设施设备管理增强功能开发计划
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
-- 添加设备扩展字段到 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
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
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
在现有实体末尾添加扩展字段:
// ========== 设备扩展字段 ==========
@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
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<SparePartInfo> 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
添加设备相关方法:
public SpaceNodeEquipmentDTO getEquipmentById(UUID id) {
SpaceNode node = spaceNodeRepository.findById(id)
.orElseThrow(() -> new BusinessException("EQUIPMENT_NOT_FOUND"));
return convertToEquipmentDTO(node);
}
public List<SpaceNodeEquipmentDTO> getSpecialEquipmentList(UUID projectId) {
List<SpaceNode> list = spaceNodeRepository.findByProjectIdAndIsEquipmentAndSpecialEquipmentTypeIsNotNull(
projectId, true);
return list.stream().map(this::convertToEquipmentDTO).collect(Collectors.toList());
}
public List<SpaceNodeEquipmentDTO> getExpiringInspectionEquipment(UUID projectId, Integer daysAhead) {
LocalDate threshold = LocalDate.now().plusDays(daysAhead);
List<SpaceNode> 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:
@GetMapping("/{id}/equipment")
public ApiResponse<SpaceNodeEquipmentDTO> getEquipment(@PathVariable UUID id) {
return ApiResponse.success(spaceNodeService.getEquipmentById(id));
}
@GetMapping("/special-equipment")
public ApiResponse<List<SpaceNodeEquipmentDTO>> getSpecialEquipment(
@RequestParam UUID projectId) {
return ApiResponse.success(spaceNodeService.getSpecialEquipmentList(projectId));
}
@GetMapping("/expiring-inspection")
public ApiResponse<List<SpaceNodeEquipmentDTO>> 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
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
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
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
package com.ether.pms.mdm.repository;
public interface MaintenancePlanRepository extends JpaRepository<MaintenancePlan, UUID> {
List<MaintenancePlan> findByProjectIdAndStatus(UUID projectId, MaintenancePlan.Status status);
Optional<MaintenancePlan> findByPlanCode(String planCode);
}
public interface MaintenanceTaskRepository extends JpaRepository<MaintenanceTask, UUID> {
List<MaintenanceTask> findByProjectIdAndStatus(UUID projectId, MaintenanceTask.Status status);
List<MaintenanceTask> findByAssignedToAndStatus(UUID assignedTo, MaintenanceTask.Status status);
List<MaintenanceTask> findByEquipmentIdAndStatusNot(UUID equipmentId, MaintenanceTask.Status status);
Optional<MaintenanceTask> 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核心方法:
// MaintenancePlanService
UUID createPlan(MaintenancePlan plan);
List<MaintenancePlan> getActivePlansByProject(UUID projectId);
void updatePlan(UUID id, MaintenancePlan plan);
void deactivatePlan(UUID id);
// MaintenanceTaskService
List<MaintenanceTask> 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<MaintenanceTask> 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
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<MaintenancePlan> 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
@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
// EnergyConsumptionService
void recordConsumption(UUID meterId, BigDecimal currentReading, UUID recordedBy);
List<EnergyConsumption> getConsumptionByMeter(UUID meterId, LocalDate startDate, LocalDate endDate);
// EnergyStatisticsService
Map<EnergyType, BigDecimal> getConsumptionByType(UUID projectId, LocalDate month);
BigDecimal getUnitConsumption(UUID projectId, LocalDate month); // kWh/m²
Map<String, BigDecimal> getConsumptionTrend(UUID projectId, Integer months);
List<EnergyAlert> 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
@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
public interface SparePartService {
SparePart create(SparePart sparePart);
void updateStock(UUID sparePartId, Integer quantity, SparePartRecord.RecordType type, UUID relatedOrderId);
List<SparePart> getLowStockParts(UUID projectId);
List<SparePartRecord> 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
@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
public interface EquipmentHealthService {
// 计算设备健康度
EquipmentHealthScore calculateHealthScore(UUID equipmentId);
// 获取设备MTBF
BigDecimal calculateMTBF(UUID equipmentId, Integer days);
// 获取设备MTTR
BigDecimal calculateMTTR(UUID equipmentId, Integer days);
// 获取设备健康度历史
List<EquipmentHealthScore> getHealthHistory(UUID equipmentId);
// 获取预警设备列表
List<UUID> getAlertEquipmentIds(UUID projectId, AlertLevel level);
// 记录故障
void recordFailure(EquipmentFailureHistory failure);
}
健康度算法实现:
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
@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
public interface InspectionTemplateService {
InspectionTemplate create(InspectionTemplate template);
InspectionTemplate copyTemplate(UUID templateId, String newName);
List<InspectionTemplate> getTemplatesByType(String equipmentType);
void updateTemplate(UUID id, InspectionTemplate template);
}
点检项目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 创建单元测试:
SpaceNodeServiceTestMaintenancePlanServiceTestMaintenanceTaskServiceTestEnergyConsumptionServiceTestSparePartServiceTestEquipmentHealthServiceTest
cd ether-pms && mvn test -Dtest=*ServiceTest
Task T.2: 后端集成测试
# 使用 H2 内存数据库进行集成测试
cd ether-pms && mvn verify
Task T.3: 前端构建验证
cd ether-admin && npm run build
Task T.4: E2E 测试
使用 Playwright 进行 E2E 测试:
# 设备管理 E2E
npx playwright test tests/equipment.spec.ts
# 维保管理 E2E
npx playwright test tests/maintenance.spec.ts
数据库迁移脚本汇总
文件列表:
V10__add_equipment_extension_fields.sql- 设备扩展字段V11__create_maintenance_tables.sql- 维保计划和任务表V12__create_energy_tables.sql- 能耗表V13__create_spare_part_tables.sql- 备件表V14__create_equipment_health_tables.sql- 设备健康表V15__create_inspection_template_table.sql- 点检模板表
部署检查清单
- 数据库迁移脚本执行
- 后端编译通过
- 后端单元测试通过
- 前端编译通过
- API 端点验证
- E2E 测试通过
- 文档更新
风险与缓解
| 风险 | 影响 | 缓解措施 |
|---|---|---|
| 定时任务性能 | 维保检查任务执行慢 | 使用 @Async 或分布式任务 |
| 能耗数据量大 | 查询性能问题 | 添加索引,分区表 |
| IoT 数据接入 | 协议兼容问题 | 预留适配器接口 |
| 历史数据不足 | 故障预测不准 | 初期使用规则判断 |
计划版本: v1.0 最后更新: 2026-03-23