# DmsMobileController 故障报修重构方案

> **文档版本**: 3.1
> **创建日期**: 2026-02-05
> **最后更新**: 2026-02-05
> **作者**: Claude AI
> **项目**: AUCMA-MES 设备管理系统
> **模块**: DMS 设备管理 - PDA故障报修

> **重要说明**：
> 1. 本方案仅关注 DmsMobileController 的重构，保持PDA完美兼容，内部补全完整业务流程
> 2. DmsBillsFaultInstanceController 为Web端接口，不在本次重构范围
> 3. **设备信息改为使用 BaseDeviceLedger（base模块），但返回类型为 DmsBaseDeviceLedger（dms模块），需要进行字段转换**

---

## 一、重构背景

### 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 需要修改的方法

**方法1：getDeviceByDeviceCode（Line 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);
}
```

**方法2：likeDeviceName（Line 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>   - IBaseDeviceLedgerService（base模块）<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 | 基于4个Service分析，完全重写 | Claude AI |
| 3.0 | 2026-02-05 | **聚焦 DmsMobileController 重构，不涉及 Web 端** | Claude AI |
| 3.1 | 2026-02-05 | **新增设备信息字段转换方案（BaseDeviceLedger → DmsBaseDeviceLedger）** | Claude AI |

---

**文档结束**

> 本方案仅关注 DmsMobileController 的重构，保持PDA完美兼容，内部补全完整业务流程。DmsBillsFaultInstanceController 为Web端接口，不在本次重构范围。
>
> **重要更新**：设备信息查询改为使用 BaseDeviceLedger（base模块），通过字段转换方法转换为 DmsBaseDeviceLedger（dms模块）返回给PDA端。
