# 2026-06-10 工作日志

## MQTT 分发重构 & 设备下发业务层

### 9. MqttMessageDispatcher 策略模式重构

**问题**：Dispatcher 硬编码 7 个 Regex Pattern 的 if-else 链，新增 Topic 必须改 Dispatcher 类。

**方案**：
- 创建 `MqttTopicHandler` 接口（`topicPattern()` + `handle(deviceNo, payload)`）
- 每个 Handler 实现该接口并声明自己的 Topic 正则
- `MqttMessageDispatcher` 改为注入 `List<MqttTopicHandler>`，启动时自动收集，循环匹配
- 新增 Topic：只需新增 Handler Bean，Dispatcher 零改动

**修复的 Bug**：
- `DeviceCommandAckHandler` 原来是空壳（只打日志），现在委托到 `IDeviceCommandAckHandler` → `MqttCommandAckService`
- `DeviceStatusHandler` 原来存在但 Dispatcher 从未路由到它（缺少 PATTERN），现已补全
- 新增 `ScheduleFinishHandler`、`KeyFinishHandler`、`DeviceCommandReceiptHandler` 替代原来只打日志的空处理

**文件变更**：
| 文件 | 变更 |
|---|---|
| `app/mqtt/MqttTopicHandler.java` | **新增** 策略接口 |
| `app/mqtt/MqttMessageDispatcher.java` | **重写** 策略模式 |
| `app/handler/DeviceDataHandler.java` | 实现 MqttTopicHandler |
| `app/handler/DeviceRegisterHandler.java` | 实现 MqttTopicHandler |
| `app/handler/DeviceStatusHandler.java` | 实现 MqttTopicHandler（新增 /status Topic） |
| `app/handler/ErromesHandler.java` | 实现 MqttTopicHandler |
| `app/handler/DeviceCommandAckHandler.java` | 实现 MqttTopicHandler + 委托 IDeviceCommandAckHandler |
| `app/handler/ScheduleFinishHandler.java` | **新增** |
| `app/handler/KeyFinishHandler.java` | **新增** |
| `app/handler/DeviceCommandReceiptHandler.java` | **新增** |

### 10. 新增设备下发业务层 DeviceCommandService

**问题**：`AppDeviceServiceImpl.switchDevice()` 直接拼装 `DeviceCommand` 并调用底层 `IDeviceCommandPublisher`，缺少统一的下发入口。

**方案**：
- 创建 `IDeviceCommandService` 接口
- `DeviceCommandServiceImpl` 封装命令构造、设备校验、下发生效、浇水日志
- 提供类型化方法：`sendSwitchCommand()`、`sendCustomCommand()`、`sendCommand()`
- `AppDeviceServiceImpl.switchDevice()` 改为调用 `deviceCommandService.sendSwitchCommand()`

**文件变更**：
| 文件 | 变更 |
|---|---|
| `app/service/IDeviceCommandService.java` | **新增** 接口 |
| `app/service/impl/DeviceCommandServiceImpl.java` | **新增** 实现 |
| `app/service/impl/AppDeviceServiceImpl.java` | 重构 switchDevice，注入 IDeviceCommandService，移除废弃的私有方法 |

### 11. 修复循环依赖根因

`MqttCommandAckService.mqttClientManager` 字段保留 `final` 导致 Lombok `@RequiredArgsConstructor` 仍将其放入构造器，`@Lazy` 注解在字段上不生效。已去掉 `final`，确保字段注入 + @Lazy 代理正确工作。

### 12. 删除冗余 /publish/status Topic

设备上传电量数据本身就证明在线，单独的 `/publish/status` Topic 没必要。删除 `DeviceStatusHandler` 和 `AppMqttDeviceStatusProperties`。策略模式下 Dispatcher 自动感知，零改动。

### 13. 修复两处循环依赖（构造器注入导致）

**大循环**（6个Bean）：
```
DeviceMqttCommandPublisher → MqttClientManager → MqttMessageDispatcher
  → DeviceRegisterHandler → AppDeviceServiceImpl → DeviceCommandServiceImpl
  → DeviceMqttCommandPublisher（闭合）
```
修复：`DeviceCommandServiceImpl.commandPublisher` 去掉 `final`，改为 `@Lazy @Autowired` 字段注入。

**双向循环**（2个Bean）：
```
AppDeviceServiceImpl ↔ DeviceCommandServiceImpl
```
修复：`AppDeviceServiceImpl.deviceCommandService` 去掉 `final`，改为 `@Lazy @Autowired` 字段注入。
