You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
zs_aucma-mes-back/aucma-dms/DmsBillsFaultInstance_重构方案.md

909 lines
33 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# DmsMobileController 故障报修重构方案
> **文档版本**: 3.1
> **创建日期**: 2026-02-05
> **最后更新**: 2026-02-05
> **作者**: Claude AI
> **项目**: AUCMA-MES 设备管理系统
> **模块**: DMS 设备管理 - PDA故障报修
> **重要说明**
> 1. 本方案仅关注 DmsMobileController 的重构保持PDA完美兼容内部补全完整业务流程
> 2. DmsBillsFaultInstanceController 为Web端接口不在本次重构范围
> 3. **设备信息改为使用 BaseDeviceLedgerbase模块但返回类型为 DmsBaseDeviceLedgerdms模块需要进行字段转换**
---
## 一、重构背景
### 1.1 当前问题分析
#### 1.1.1 DmsMobileController 故障报修方法现状
**现有方法**
| 方法 | 行号范围 | 当前问题 |
|------|---------|----------|
| insertFaultInstsanceActivity | ~ | 基本正常,创建故障报修 |
| startRepair | ~ | **关键问题**:缺少维修工单和派工计划创建逻辑 |
| completeRepair | ~ | **关键问题**:缺少维修记录创建逻辑 |
| getBillsFaultInstance4Repair | ~ | 方法复杂,包含知识库匹配逻辑 |
#### 1.1.2 业务流程不完整
**PDA端当前流程**
```
PDA创建故障报修 → dms_bills_fault_instance 表
PDA开始维修 → ❌ 缺少创建维修工单dms_repair_work_order
❌ 缺少创建派工计划dms_dispatch_plan
❌ 缺少触发断点1故障报修状态→维修中
PDA完成维修 → ❌ 缺少触发断点3创建维修记录 dms_repair_record
❌ 缺少触发断点3故障报修状态→维修完成
```
**Web端完整流程**(参考):
```
故障报修 (dms_bills_fault_instance)
维修工单 (dms_repair_work_order) ← 【断点1】更新故障报修状态
派工计划 (dms_dispatch_plan) ← 【断点2】派工时自动创建
维修完成
维修记录 (dms_repair_record) ← 【断点3】自动创建更新故障报修状态
```
#### 1.1.3 问题影响
- **数据完整性缺失**PDA操作后Web端的维修工单和派工计划模块看不到数据
- **状态不一致**:故障报修、维修工单、派工计划、维修记录之间的状态无法同步
- **业务流程断裂**3个断点修复机制在PDA端无法工作
### 1.2 重构目标
1. **PDA完美兼容**PDA端不需要做任何修改接口路径和参数格式完全不变
2. **业务流程补全**DmsMobileController 内部补全维修工单和派工计划的创建
3. **数据一致性**确保4个Service之间的数据完全同步
4. **最小改动**:仅修改 DmsMobileController不涉及 Web 端的 DmsBillsFaultInstanceController
---
## 二、架构设计
### 2.1 DmsMobileController 重构架构
```
┌─────────────────────────────────────────────────────────────┐
│ PDA 端Android
└────────────────────────┬────────────────────────────────────┘
│ HTTP/Multipart
┌─────────────────────────────────────────────────────────────┐
│ DmsMobileController代理模式
├─────────────────────────────────────────────────────────────┤
│ 保持所有 /dms/mobile/* 接口路径不变 │
│ 保持所有参数格式不变 │
│ 内部转换为业务逻辑调用 │
├─────────────────────────────────────────────────────────────┤
│ 依赖注入的4个Service
│ - DmsBillsFaultInstanceService故障报修服务
│ - DmsRepairWorkOrderService维修工单服务
│ - DmsDispatchPlanService派工计划服务
│ - DmsRepairRecordService维修记录服务
└────────────────────────┬────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 业务服务层现有4个Service
├─────────────────────────────────────────────────────────────┤
│ DmsBillsFaultInstanceServiceImpl │
│ DmsRepairWorkOrderServiceImpl │
│ DmsDispatchPlanServiceImpl │
│ DmsRepairRecordServiceImpl │
└─────────────────────────────────────────────────────────────┘
```
### 2.2 核心方法改造方案
#### 2.2.1 startRepair 方法改造
**改造前**
```java
@PostMapping("/startRepair")
public AjaxResult startRepair(@RequestBody DmsFaultInstanceActivity activity) {
// 直接更新操作记录
// ❌ 缺少维修工单创建
// ❌ 缺少派工计划创建
// ❌ 缺少状态同步
}
```
**改造后**
```java
@PostMapping("/startRepair")
public AjaxResult startRepair(@RequestBody DmsFaultInstanceActivity activity) {
// 1. 查询故障报修
// 2. 检查是否已有维修工单,没有则创建
// 3. 【断点1】更新故障报修状态为"维修中"
// 4. 创建派工计划
// 5. 【断点2】自动接收派工
// 6. 更新维修工单状态为"维修中"
// 7. 更新操作记录
}
```
#### 2.2.2 completeRepair 方法改造
**改造前**
```java
@PostMapping("/completeRepair")
public AjaxResult completeRepair(...) {
// 直接更新操作记录
// ❌ 缺少维修记录创建
// ❌ 缺少状态同步
}
```
**改造后**
```java
@PostMapping("/completeRepair")
public AjaxResult completeRepair(...) {
// 1. 查询故障报修和维修工单
// 2. 更新维修工单状态为"已完成"
// 3. 【断点3】创建维修记录
// 4. 【断点3】更新故障报修状态为"维修完成"
// 5. 处理配件消耗
// 6. 更新操作记录
}
```
---
## 三、需要新建的DTO类
### 3.1 DTO清单可选用于内部转换
| 序号 | DTO类名 | 文件路径 | 用途 |
|------|---------|----------|------|
| 1 | DmsFaultRepairContext | aucma-dms/src/main/java/com/aucma/dms/dto/ | 维修上下文信息(内部使用) |
**说明**DTO类主要用于内部参数传递也可以直接使用现有的实体类。
---
## 四、DmsMobileController 重构方案
### 4.1 文件路径
**文件**: `aucma-dms/src/main/java/com/aucma/dms/controller/DmsMobileController.java`
### 4.2 依赖注入(新增)
```java
@RestController
@RequestMapping("/dms/mobile")
public class DmsMobileController extends BaseController {
// 现有依赖
@Autowired
private IDmsBillsFaultInstanceService dmsBillsFaultInstanceService;
// 【新增】维修工单服务
@Autowired
private IDmsRepairWorkOrderService dmsRepairWorkOrderService;
// 【新增】派工计划服务
@Autowired
private IDmsDispatchPlanService dmsDispatchPlanService;
// 【新增】维修记录服务
@Autowired
private IDmsRepairRecordService dmsRepairRecordService;
// 【新增】维修知识库服务(可选,用于 getBillsFaultInstance4Repair
@Autowired
private IDmsKnowledgeRepairService dmsKnowledgeRepairService;
}
```
### 4.3 startRepair 方法完整重构
```java
/**
* 开始维修
*
* 【重构说明】
* - 保持接口路径和参数格式不变
* - 内部补全维修工单和派工计划的创建逻辑
* - 触发断点1和断点2确保数据完整性
*/
@PostMapping("/startRepair")
public AjaxResult startRepair(@RequestBody DmsFaultInstanceActivity activity) {
try {
Long faultInstanceId = activity.getRepairInstanceId();
String repairer = activity.getRepairer();
log.info("PDA开始维修故障报修ID: {}, 维修人: {}", faultInstanceId, repairer);
// ========== 步骤1查询故障报修 ==========
DmsBillsFaultInstance fault = dmsBillsFaultInstanceService
.selectDmsBillsFaultInstanceByRepairInstanceId(faultInstanceId);
if (fault == null) {
return AjaxResult.error("故障报修不存在");
}
// ========== 步骤2检查是否已有维修工单 ==========
DmsRepairWorkOrder existingOrder = dmsRepairWorkOrderService
.selectByFaultInstanceId(faultInstanceId);
DmsRepairWorkOrder order;
if (existingOrder == null) {
// ========== 步骤3创建维修工单 ==========
order = new DmsRepairWorkOrder();
order.setFaultInstanceId(faultInstanceId);
order.setOrderStatus("0"); // 待派工
order.setRepairer(repairer);
order.setCreateTime(new Date());
dmsRepairWorkOrderService.insertRepairWorkOrder(order);
log.info("自动创建维修工单工单ID: {}", order.getRepairOrderId());
// 【断点1】更新故障报修状态为"维修中"
fault.setBillsStatus("1");
fault.setUpdateTime(new Date());
dmsBillsFaultInstanceService.updateDmsBillsFaultInstance(fault);
log.info("【断点1】故障报修状态已更新为维修中");
} else {
order = existingOrder;
}
// ========== 步骤4创建派工计划 ==========
DmsDispatchPlan plan = new DmsDispatchPlan();
plan.setRepairOrderId(order.getRepairOrderId());
plan.setRepairer(repairer);
plan.setReceiveStatus("0"); // 待接收
plan.setExecutionStatus("0"); // 未开始
plan.setCreateTime(new Date());
dmsDispatchPlanService.insertDispatchPlan(plan);
// 【断点2】派工时自动创建派工计划自动接收
plan.setReceiveStatus("1"); // 已接收
plan.setExecutionStatus("1"); // 维修中
plan.setReceiveTime(new Date());
plan.setUpdateTime(new Date());
dmsDispatchPlanService.updateDispatchPlan(plan);
log.info("【断点2】派工计划已创建并自动接收");
// ========== 步骤5更新维修工单状态为"维修中" ==========
order.setOrderStatus("2"); // 维修中
order.setRealBeginTime(new Date());
order.setUpdateTime(new Date());
dmsRepairWorkOrderService.updateRepairWorkOrder(order);
log.info("维修工单状态已更新为维修中");
// ========== 步骤6更新操作记录原有逻辑 ==========
activity.setProcessHandleStatus("1"); // 执行中
activity.setStartTime(new Date());
dmsBillsFaultInstanceService.updateDmsFaultInstanceActivity(activity);
return AjaxResult.success("开始维修成功");
} catch (Exception e) {
log.error("开始维修失败", e);
return AjaxResult.error("开始维修失败:" + e.getMessage());
}
}
```
### 4.4 completeRepair 方法完整重构
```java
/**
* 完成维修
*
* 【重构说明】
* - 保持接口路径和参数格式不变
* - 内部补全维修记录的创建逻辑
* - 触发断点3确保数据完整性
*/
@PostMapping("/completeRepair")
public AjaxResult completeRepair(
@RequestParam("repairInstanceId") Long repairInstanceId,
@RequestParam("instanceActivityId") Long instanceActivityId,
@RequestParam(value = "checkedFault", required = false) String checkedFault,
@RequestParam(value = "repairContent", required = false) String repairContent,
@RequestParam(value = "protectedMethod", required = false) String protectedMethod,
@RequestParam(value = "repairer", required = false) String repairer,
@RequestParam(value = "parts1", required = false) String parts1,
@RequestParam(value = "files", required = false) List<MultipartFile> files) {
try {
log.info("PDA完成维修故障报修ID: {}, 维修人: {}", repairInstanceId, repairer);
// ========== 步骤1查询故障报修和维修工单 ==========
DmsBillsFaultInstance fault = dmsBillsFaultInstanceService
.selectDmsBillsFaultInstanceByRepairInstanceId(repairInstanceId);
if (fault == null) {
return AjaxResult.error("故障报修不存在");
}
DmsRepairWorkOrder order = dmsRepairWorkOrderService
.selectByFaultInstanceId(repairInstanceId);
if (order == null) {
return AjaxResult.error("维修工单不存在,请先开始维修");
}
// ========== 步骤2更新维修工单状态为"已完成" ==========
order.setOrderStatus("3"); // 已完成
order.setRealEndTime(new Date());
order.setUpdateTime(new Date());
dmsRepairWorkOrderService.updateRepairWorkOrder(order);
log.info("维修工单状态已更新为已完成");
// ========== 步骤3【断点3】创建维修记录 ==========
DmsRepairRecord record = new DmsRepairRecord();
record.setRepairOrderId(order.getRepairOrderId());
record.setFaultInstanceId(repairInstanceId);
record.setRepairContent(repairContent);
record.setRepairer(repairer);
record.setCheckedFault(checkedFault);
record.setProtectedMethod(protectedMethod);
record.setIsArchived("0"); // 未存档
record.setRepairEndTime(new Date());
record.setCreateTime(new Date());
dmsRepairRecordService.insertDmsRepairRecord(record);
log.info("【断点3】维修记录已创建记录ID: {}", record.getRecordId());
// ========== 步骤4【断点3】更新故障报修状态为"维修完成" ==========
fault.setBillsStatus("2"); // 维修完成
fault.setRealEndTime(new Date());
fault.setUpdateTime(new Date());
dmsBillsFaultInstanceService.updateDmsBillsFaultInstance(fault);
log.info("【断点3】故障报修状态已更新为维修完成");
// ========== 步骤5处理配件消耗 ==========
if (parts1 != null && !parts1.isEmpty()) {
List<DmsFaultCompentsParts> parts = parsePartsJson(parts1);
for (DmsFaultCompentsParts part : parts) {
part.setFaultId(repairInstanceId);
// 保存配件信息
}
log.info("配件信息已保存,共{}个", parts.size());
}
// ========== 步骤6更新操作记录原有逻辑 ==========
DmsFaultInstanceActivity activity = new DmsFaultInstanceActivity();
activity.setInstanceActivityId(instanceActivityId);
activity.setCheckedFault(checkedFault);
activity.setRepairContent(repairContent);
activity.setProtectedMethod(protectedMethod);
activity.setRepairer(repairer);
activity.setProcessHandleStatus("2"); // 已完成
activity.setEndTime(new Date());
activity.setRepairConfirm(2); // 已确认
activity.setConfirmTime(new Date());
dmsBillsFaultInstanceService.updateDmsFaultInstanceActivity(activity);
return AjaxResult.success("完成维修成功");
} catch (Exception e) {
log.error("完成维修失败", e);
return AjaxResult.error("完成维修失败:" + e.getMessage());
}
}
/**
* 解析配件JSON字符串
*/
private List<DmsFaultCompentsParts> parsePartsJson(String parts1) {
try {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(parts1,
new TypeReference<List<DmsFaultCompentsParts>>() {});
} catch (Exception e) {
log.error("解析配件JSON失败", e);
return new ArrayList<>();
}
}
```
### 4.5 设备信息字段转换(重要)
#### 4.5.1 字段差异说明
**需求**DmsMobileController 中所有设备信息改为使用 `BaseDeviceLedger`aucma-base模块但返回类型为 `DmsBaseDeviceLedger`aucma-dms模块需要进行字段转换。
**字段映射表**
| 业务含义 | BaseDeviceLedger | DmsBaseDeviceLedger | 转换规则 |
|---------|------------------|---------------------|----------|
| 主键ID | `objId` | `deviceId` | `objId → deviceId` |
| 设备编号 | `deviceCode` | `deviceCode` | 直接映射 |
| 设备名称 | `deviceName` | `deviceName` | 直接映射 |
| 设备类型 | `deviceType` | `deviceTypeId` | `deviceType → deviceTypeId` |
| 设备型号 | `deviceModel` | `deviceSpec` | `deviceModel → deviceSpec` |
| 设备状态 | `deviceStatus` | `deviceStatus` | 直接映射 |
| 设备位置 | - | `deviceLocation` | 可为空 |
| 资产编号 | - | `assetNumber` | 可为空 |
| 供应商 | `manufacturer` | - | 需映射到 `suplierName` |
#### 4.5.2 字段转换方法
```java
/**
* 将 BaseDeviceLedger 转换为 DmsBaseDeviceLedger
* 用于PDA接口返回值转换
*/
private DmsBaseDeviceLedger convertToDmsBaseDeviceLedger(BaseDeviceLedger baseLedger) {
if (baseLedger == null) {
return null;
}
DmsBaseDeviceLedger dmsLedger = new DmsBaseDeviceLedger();
// 主键映射
dmsLedger.setDeviceId(baseLedger.getObjId());
// 直接映射字段
dmsLedger.setDeviceCode(baseLedger.getDeviceCode());
dmsLedger.setDeviceName(baseLedger.getDeviceName());
dmsLedger.setDeviceStatus(baseLedger.getDeviceStatus());
// 字段名称转换
dmsLedger.setDeviceTypeId(baseLedger.getDeviceType()); // deviceType → deviceTypeId
dmsLedger.setDeviceSpec(baseLedger.getDeviceModel()); // deviceModel → deviceSpec
dmsLedger.setSuplierName(baseLedger.getManufacturer()); // manufacturer → suplierName
// 可为空字段
dmsLedger.setDeviceLocation(baseLedger.getUsedDepartment()); // 使用部门 → 设备位置
// 复制审计字段
dmsLedger.setCreateTime(baseLedger.getCreateTime());
dmsLedger.setCreateBy(baseLedger.getCreateBy());
dmsLedger.setUpdateTime(baseLedger.getUpdateTime());
dmsLedger.setUpdateBy(baseLedger.getUpdateBy());
dmsLedger.setIsFlag(baseLedger.getIsFlag());
return dmsLedger;
}
/**
* 批量转换 BaseDeviceLedger 列表
*/
private List<DmsBaseDeviceLedger> convertToDmsBaseDeviceLedgerList(List<BaseDeviceLedger> baseLedgerList) {
if (CollectionUtils.isEmpty(baseLedgerList)) {
return new ArrayList<>();
}
List<DmsBaseDeviceLedger> dmsLedgerList = new ArrayList<>();
for (BaseDeviceLedger baseLedger : baseLedgerList) {
dmsLedgerList.add(convertToDmsBaseDeviceLedger(baseLedger));
}
return dmsLedgerList;
}
```
#### 4.5.3 需要修改的方法
**方法1getDeviceByDeviceCodeLine 75-79**
**修改前**
```java
@GetMapping("/getDeviceByDeviceCode/{deviceCode}")
public AjaxResult getDeviceByDeviceCode(@PathVariable("deviceCode") String deviceCode) {
DmsBaseDeviceLedger dmsBaseDeviceLedger = baseDeviceLedgerService.selectDmsBaseDeviceLedgerByDeviceCode(deviceCode);
return success(dmsBaseDeviceLedger);
}
```
**修改后**
```java
@Autowired
private IBaseDeviceLedgerService baseDeviceLedgerService; // 改为使用 base 模块的 Service
@GetMapping("/getDeviceByDeviceCode/{deviceCode}")
public AjaxResult getDeviceByDeviceCode(@PathVariable("deviceCode") String deviceCode) {
// 1. 使用 BaseDeviceLedger 查询
BaseDeviceLedger baseLedger = baseDeviceLedgerService.selectBaseDeviceLedgerByDeviceCode(deviceCode);
// 2. 转换为 DmsBaseDeviceLedger 返回
DmsBaseDeviceLedger dmsBaseDeviceLedger = convertToDmsBaseDeviceLedger(baseLedger);
return success(dmsBaseDeviceLedger);
}
```
**方法2likeDeviceNameLine 102-106**
**修改前**
```java
@GetMapping("/likeDeviceName")
public AjaxResult likeDeviceName(String deviceName) {
List<DmsBaseDeviceLedger> deviceLedgerList = baseDeviceLedgerService.likeDeviceName(deviceName);
return success(deviceLedgerList);
}
```
**修改后**
```java
@GetMapping("/likeDeviceName")
public AjaxResult likeDeviceName(String deviceName) {
// 1. 使用 BaseDeviceLedger 查询
List<BaseDeviceLedger> baseLedgerList = baseDeviceLedgerService.likeBaseDeviceName(deviceName);
// 2. 批量转换为 DmsBaseDeviceLedger 返回
List<DmsBaseDeviceLedger> dmsLedgerList = convertToDmsBaseDeviceLedgerList(baseLedgerList);
return success(dmsLedgerList);
}
```
### 4.6 其他方法保持不变
以下方法保持原有逻辑不变:
- insertFaultInstsanceActivity - 创建故障报修
- getBillsFaultInstances - 查询故障列表
- getBillsFaultInstanceByRepairInstanceId - 查询故障详情
- updateFaultInstanceActivity - 更新故障信息
- scanDevice4Repair - 扫码设备验证
---
## 五、数据流图
### 5.1 startRepair 数据流(重构后)
```
PDA调用 startRepair(faultInstanceId, repairer)
DmsMobileController.startRepair()
1. 查询故障报修
2. 检查是否已有维修工单
├→ 没有 → 创建维修工单
│ └→ 【断点1】更新故障报修状态为"维修中"
└→ 已有 → 使用现有工单
3. 创建派工计划
└→ 【断点2】自动接收派工
4. 更新维修工单状态为"维修中"
5. 更新操作记录
返回成功
```
### 5.2 completeRepair 数据流(重构后)
```
PDA调用 completeRepair(faultInstanceId, repairContent, parts)
DmsMobileController.completeRepair()
1. 查询故障报修和维修工单
2. 更新维修工单状态为"已完成"
3. 【断点3】创建维修记录
4. 【断点3】更新故障报修状态为"维修完成"
5. 处理配件消耗
6. 更新操作记录
返回成功
```
---
## 六、测试方案
### 6.1 单元测试
```java
@SpringBootTest
class DmsMobileControllerTest {
@Autowired
private DmsMobileController dmsMobileController;
@Test
void testStartRepair() {
// 准备测试数据
DmsFaultInstanceActivity activity = new DmsFaultInstanceActivity();
activity.setRepairInstanceId(1L);
activity.setRepairer("张三");
// 调用接口
AjaxResult result = dmsMobileController.startRepair(activity);
// 验证结果
assertEquals("开始维修成功", result.get("msg"));
// 验证:维修工单已创建
DmsRepairWorkOrder order = repairWorkOrderService.selectByFaultInstanceId(1L);
assertNotNull(order);
assertEquals("2", order.getOrderStatus()); // 维修中
// 验证:派工计划已创建
DmsDispatchPlan plan = dispatchPlanService.selectByRepairOrderId(order.getRepairOrderId());
assertNotNull(plan);
assertEquals("1", plan.getReceiveStatus()); // 已接收
// 验证:故障报修状态已更新
DmsBillsFaultInstance fault = faultInstanceService.selectByRepairInstanceId(1L);
assertEquals("1", fault.getBillsStatus()); // 维修中
}
@Test
void testCompleteRepair() {
// 准备测试数据
// ...
// 调用接口
AjaxResult result = dmsMobileController.completeRepair(...);
// 验证结果
assertEquals("完成维修成功", result.get("msg"));
// 验证:维修记录已创建
DmsRepairRecord record = repairRecordService.selectByRepairOrderId(1L);
assertNotNull(record);
// 验证:故障报修状态已更新
DmsBillsFaultInstance fault = faultInstanceService.selectByRepairInstanceId(1L);
assertEquals("2", fault.getBillsStatus()); // 维修完成
}
}
```
### 6.2 PDA兼容性测试关键
| 测试场景 | 测试步骤 | 预期结果 |
|---------|----------|----------|
| PDA开始维修 | 调用 /startRepair | 1. 维修工单自动创建<br>2. 派工计划自动创建<br>3. 故障报修状态→维修中<br>4. 返回格式不变 |
| PDA完成维修 | 调用 /completeRepair | 1. 维修记录自动创建<br>2. 故障报修状态→维修完成<br>3. 返回格式不变 |
| PDA完整流程 | 创建→开始维修→完成维修 | 4个表数据完整状态同步正确 |
### 6.3 Web端数据验证
| 模块 | 验证内容 | 验证方法 |
|------|----------|----------|
| 维修工单 | PDA创建的工单可见 | 查询 dms_repair_work_order 表 |
| 派工计划 | PDA自动创建的派工可见 | 查询 dms_dispatch_plan 表 |
| 维修记录 | PDA自动创建的记录可见 | 查询 dms_repair_record 表 |
| 故障报修 | 状态同步正确 | 查询 dms_bills_fault_instance.bills_status |
---
## 七、文件清单
### 7.1 需要修改的文件1个
| 序号 | 文件路径 | 修改内容 | 影响范围 |
|------|----------|----------|----------|
| 1 | aucma-dms/src/main/java/com/aucma/dms/controller/DmsMobileController.java | **1. 新增依赖注入**<br> - IBaseDeviceLedgerServicebase模块<br> - IDmsRepairWorkOrderService<br> - IDmsDispatchPlanService<br> - IDmsRepairRecordService<br><br> **2. 新增字段转换方法**<br> - convertToDmsBaseDeviceLedger() - 单个对象转换<br> - convertToDmsBaseDeviceLedgerList() - 批量转换<br><br> **3. 修改设备查询方法**<br> - getDeviceByDeviceCode() - 改用 BaseDeviceLedger<br> - likeDeviceName() - 改用 BaseDeviceLedger<br><br> **4. 重构核心方法**<br> - startRepair() - 补全工单和派工创建<br> - completeRepair() - 补全维修记录创建<br><br> **5. 新增辅助方法**<br> - parsePartsJson() - 解析配件JSON | 仅PDA端Web端不受影响 |
**修改详细说明**
**新增导入**
```java
import com.aucma.base.domain.BaseDeviceLedger;
import com.aucma.base.service.IBaseDeviceLedgerService;
import org.springframework.beans.BeanUtils;
```
**字段转换关键点**
- 主键:`objId` → `deviceId`
- 设备类型:`deviceType` → `deviceTypeId`
- 设备型号:`deviceModel` → `deviceSpec`
- 供应商:`manufacturer` → `suplierName`
### 7.2 需要新建的文件0个
**说明**本次重构不需要新建DTO类直接使用现有实体类 + 字段转换方法即可。
### 7.3 不需要修改的文件
| 文件 | 说明 |
|------|------|
| DmsBillsFaultInstanceController.java | Web端接口不在本次重构范围 |
| DmsBillsFaultInstanceServiceImpl.java | 现有服务,直接调用 |
| DmsRepairWorkOrderServiceImpl.java | 现有服务,直接调用 |
| DmsDispatchPlanServiceImpl.java | 现有服务,直接调用 |
| DmsRepairRecordServiceImpl.java | 现有服务,直接调用 |
---
## 八、实施步骤
### 阶段1准备工作半天
- [ ] 备份 DmsMobileController.java
- [ ] 创建开发分支
- [ ] 确认4个Service的可用性
- [ ] 准备测试数据
### 阶段2代码重构1天
- [ ] 新增4个Service依赖注入
- [ ] 重构 startRepair 方法
- [ ] 重构 completeRepair 方法
- [ ] 新增 parsePartsJson 辅助方法
- [ ] 添加日志记录
### 阶段3单元测试半天
- [ ] 编写 startRepair 测试用例
- [ ] 编写 completeRepair 测试用例
- [ ] 验证数据一致性
### 阶段4集成测试1天
- [ ] PDA端联调测试
- [ ] Web端数据验证
- [ ] 完整流程测试
### 阶段5部署和验证半天
- [ ] 部署到测试环境
- [ ] PDA端功能验证
- [ ] Web端数据验证
- [ ] 性能测试
**总计3-4天**
---
## 九、风险控制
### 9.1 风险识别
| 风险 | 影响 | 概率 | 应对措施 |
|------|------|------|----------|
| PDA接口变更导致PDA无法使用 | 高 | 低 | 严格保持接口路径和参数不变 |
| 业务流程补全导致数据错误 | 高 | 中 | 充分的单元测试和集成测试 |
| 性能下降 | 中 | 低 | 优化查询,添加必要的日志 |
| 事务回滚导致数据不一致 | 中 | 低 | 添加事务注解,确保原子性 |
### 9.2 回滚方案
1. **代码备份**:重构前备份 DmsMobileController.java
2. **快速回滚**:如出现问题,立即恢复备份文件
3. **灰度发布**先在部分PDA设备上测试
4. **监控观察**:部署后密切关注日志和数据
---
## 十、成功标准
### 10.1 功能标准
- [ ] PDA端功能完全正常无需修改
- [ ] startRepair 后自动创建维修工单和派工计划
- [ ] completeRepair 后自动创建维修记录
- [ ] Web端4个模块能看到完整的PDA操作数据
- [ ] 3个断点修复机制正常工作
### 10.2 质量标准
- [ ] 单元测试覆盖率 > 80%
- [ ] 集成测试全部通过
- [ ] 数据一致性检查无错误
- [ ] PDA兼容性测试全部通过
### 10.3 性能标准
- [ ] startRepair 接口响应时间 < 500ms
- [ ] completeRepair 接口响应时间 < 500ms
- [ ] 系统吞吐量无明显下降
---
## 十一、附录
### 11.1 状态流转图
```
故障报修状态 (bills_status)
0 (待维修) ──────────────→ 1 (维修中) ──────────────→ 2 (维修完成)
↑ ↓ ↓
└──── 创建报修 └─ startRepair └─ completeRepair
维修工单状态 (order_status)
0 (待派工) ──→ 1 (已派工) ──→ 2 (维修中) ──→ 3 (已完成)
↑ ↓ ↓ ↓
└─ startRepair创建 └─ 派工 └─ 接收派工 └─ completeRepair
计划
派工计划状态 (receive_status / execution_status)
0 (待接收) ──→ 1 (已接收)
↓ ↓
└─ startRepair创建 └─ 自动接收
维修记录状态 (is_archived)
0 (未存档)
└─ completeRepair 自动创建
```
### 11.2 依赖注入清单
```java
// DmsMobileController 需要注入的Service
// 【原有】故障报修服务
@Autowired
private IDmsBillsFaultInstanceService dmsBillsFaultInstanceService;
// 【重构新增】设备台账服务base模块- 用于设备信息查询和转换
@Autowired
private IBaseDeviceLedgerService baseDeviceLedgerService;
// 【重构新增】维修工单服务 - 用于PDA端自动创建维修工单
@Autowired
private IDmsRepairWorkOrderService dmsRepairWorkOrderService;
// 【重构新增】派工计划服务 - 用于PDA端自动创建派工计划
@Autowired
private IDmsDispatchPlanService dmsDispatchPlanService;
// 【重构新增】维修记录服务 - 用于PDA端自动创建维修记录
@Autowired
private IDmsRepairRecordService dmsRepairRecordService;
```
**说明**
- `IBaseDeviceLedgerService` 来自 `com.aucma.base.service`
- 其他Service来自 `com.aucma.dms.service`
### 11.3 核心方法签名
```java
// startRepair
@PostMapping("/startRepair")
public AjaxResult startRepair(@RequestBody DmsFaultInstanceActivity activity)
// completeRepair
@PostMapping("/completeRepair")
public AjaxResult completeRepair(
@RequestParam("repairInstanceId") Long repairInstanceId,
@RequestParam("instanceActivityId") Long instanceActivityId,
@RequestParam(value = "checkedFault", required = false) String checkedFault,
@RequestParam(value = "repairContent", required = false) String repairContent,
@RequestParam(value = "protectedMethod", required = false) String protectedMethod,
@RequestParam(value = "repairer", required = false) String repairer,
@RequestParam(value = "parts1", required = false) String parts1,
@RequestParam(value = "files", required = false) List<MultipartFile> files)
```
---
## 文档变更记录
| 版本 | 日期 | 变更内容 | 作者 |
|------|------|----------|------|
| 1.0 | 2025-02-05 | 初始版本 | Claude AI |
| 2.0 | 2026-02-05 | 基于4Service分析,完全重写 | Claude AI |
| 3.0 | 2026-02-05 | **聚焦 DmsMobileController 重构,不涉及 Web 端** | Claude AI |
| 3.1 | 2026-02-05 | **新增设备信息字段转换方案(BaseDeviceLedger DmsBaseDeviceLedger** | Claude AI |
---
**文档结束**
> 本方案仅关注 DmsMobileController 的重构保持PDA完美兼容内部补全完整业务流程。DmsBillsFaultInstanceController 为Web端接口不在本次重构范围。
>
> **重要更新**:设备信息查询改为使用 BaseDeviceLedgerbase模块通过字段转换方法转换为 DmsBaseDeviceLedgerdms模块返回给PDA端。