refactor(iot): 移除 MQTT 传输层,仅保留设备开放接口与门禁写操作策略

- 依赖:删除根 pom 的 paho.version 与 org.eclipse.paho.client.mqttv3 依赖声明,
  同步移除 ruoyi-iot 模块中的 Paho 依赖,模块描述改为“iot设备数据与开放接口”
- MQTT 协议栈整体删除:IotMqttConfig、IotMqttProperties、MqttClientManager、
  MqttMessageDispatcher、MqttCallbackHandler、MqttConnectionCallback、
  IMqttMessageListener、MqttMessageDTO、MqttPublishRequest、MqttMessageEvent、
  IMqttService/MqttServiceImpl、IotMqttController,以及 lock 侧 topic 订阅与
  LockMessageListener、AccessRecordHandler、CommandRespHandler、
  GatewayOnlineHandler、LockSignalHandler
- MQTT 报文留痕链路删除:LockMqttLog 实体、LockMqttLogMapper(+XML)、
  ILockMqttLogService/LockMqttLogServiceImpl、LockMqttLogController,
  以及对应的前端页面 build_ui/src/views/iot/mqttlog 与 API build_ui/src/api/iot/mqttlog.js
- 命令下发改为仅落库:LockCommandServiceImpl 派发时直接置 TRANSPORT_DISABLED 失败,
  retryPending 返回 0;删除 GatewayOfflineCheckTask、LockCommandRetryTask、
  LockDeviceOfflineCheckTask 等依赖 MQTT 的补偿与离线检查定时任务
- 保留与传输无关的门禁写操作策略:新增 IotLockSecurityProperties(iot.lock-security),
  RealIotSecurityGate 及其测试改用该配置类
- 配置与调用处清理:application*.yml 中 iot.mqtt 段迁移为 iot.lock-security,
  删除 broker-url/client-id/username/password/topics/will/qos 等连接参数,
  修正 iot.face 环境标识注释引用;QA 工具启动参数改用 --iot.lock-security.*,
  并移除 browser_regression.js 的 mqttlog 路由
- 测试同步:删除依赖 MQTT 与传输层的 CommandRespHandlerMockTest、
  Round5sEgressIsolationTest、Round5sExecutionIntegrationTest、
  Round5v4BrowserFixture、Round5v4KingbaseIntegrationTest;
  更新 RealIotSecurityGateTest、FaceExtractClientMockTest、
  TrainIotLockAuthBridgeMockTest

说明:数据库侧 lock_mqtt_log 表结构与历史验收文档保留,未做数据迁移;
前端若仍配置了“MQTT报文日志”菜单,需在后台菜单管理中一并移除。
This commit is contained in:
buliping committed 2026-11-10 16:06:23 +08:00
1 parent f5e8d7f564
commit 1def568f24
54 files changed
+690 -4734

No files matched your search

+276
View File
@@ -0,0 +1,276 @@
# 从项目移除 MQTT——文件级改造清单
> **适用范围**:RuoYi-Vue(`railwaysystem`)全部模块
> **方案定位**:方案 A(推荐)——只拆除"MQTT 传输 + 订阅回调 + 调度 + 留痕",保留数据层、协议构造层与 HTTP 能力
> **红线约束**:报到机**人脸采集**与**门锁入住** HTTP 链路必须完整保留
> **编制时间**:2026-11-10
---
## 目录
- [0. 关键前提](#0-关键前提)
- [1. 严禁删除、严禁修改的保留项](#1-严禁删除严禁修改的保留项)
- [1.1 报到机 HTTP 链路(红线)](#11-报到机-http-链路红线)
- [1.2 人脸能力(HTTP,非 MQTT)](#12-人脸能力http非-mqtt)
- [1.3 门锁主数据与 DB 层](#13-门锁主数据与-db-层)
- [1.4 状态机与协议构造层](#14-状态机与协议构造层)
- [1.5 HTTP 开放接口](#15-http-开放接口)
- [1.6 容易误删的隐形依赖](#16-容易误删的隐形依赖)
- [2. 需删除的文件(36 个)](#2-需删除的文件36-个)
- [3. 需修改的文件(7 个)](#3-需修改的文件7-个)
- [4. 改造后行为说明](#4-改造后行为说明)
- [5. 方案 B(彻底清理,仅供评估)](#5-方案-b彻底清理仅供评估)
- [6. 执行顺序建议](#6-执行顺序建议)
---
## 0. 关键前提
**不能直接删除 `ruoyi-iot` 模块。** 该模块除 MQTT 外还承载三类被强依赖的能力:
| 能力 | 被谁依赖 | 说明 |
|---|---|---|
| 报到机 HTTP(人脸采集 / 门锁入住) | `ruoyi-admin` | 控制器直接 `import com.ruoyi.iot.lock.*`(`LockDeviceMapper`、`ILockAuthService`、`LockAuth`) |
| 人脸特征提取(HTTP 调外部服务) | `ruoyi-train` | `TrainStudentServiceImpl` 使用 `FaceExtractClient`、`LockFace`、`LockAuthMapper` |
| 门锁主数据 + HTTP 开放接口 | `ruoyi-admin`、`ruoyi-train` | 设备/网关/分组/授权/开门记录 CRUD、`/openapi/**` |
**结论**:采用方案 A——仅移除 MQTT 传输侧,保留数据层、协议构造层、开放接口。授权/设备/网关三个业务服务**无需改动**。
---
## 1. 严禁删除、严禁修改的保留项
### 1.1 报到机 HTTP 链路(红线)
| 文件 | 说明 |
|---|---|
| `ruoyi-admin/.../controller/api/TrainRegisterApiController.java` | 人脸采集、门锁入住(`callStayCheckin`)、报到重置;**方案 A 下零改动** |
| `ruoyi-admin/.../controller/api/TrainFaceApiController.java` | 被报到机控制器复用(`deleteEmployee` 解绑人脸) |
| `ruoyi-train/.../integration/lock/client/TrainLockStayApiClient.java` | HTTP 门锁入住/退宿开放平台客户端 |
| `ruoyi-train/.../integration/lock/dto/LockStayCheckinResult.java` | 入住结果 DTO |
| `ruoyi-train/.../integration/lock/dto/LockAuthBridgeResult.java` | 授权桥接结果 DTO |
> `TrainRegisterApiController` 依赖的 `ILockAuthService`、`LockDeviceMapper` 在本方案中均保留,因此该文件**不需要任何改动**。
### 1.2 人脸能力(HTTP,非 MQTT)
```
ruoyi-iot/.../lock/remote/FaceExtractClient.java
ruoyi-iot/.../config/FaceExtractProperties.java
ruoyi-iot/.../lock/domain/LockFace.java
ruoyi-iot/.../lock/mapper/LockFaceMapper.java (+ resources/mapper/lock/LockFaceMapper.xml)
ruoyi-iot/.../lock/service/ILockFaceService.java
ruoyi-iot/.../lock/service/impl/LockFaceServiceImpl.java
ruoyi-iot/.../lock/controller/LockFaceController.java
ruoyi-train/.../service/impl/TrainStudentServiceImpl.java
ruoyi-train/.../mapper/TrainStudentMapper.java
```
### 1.3 门锁主数据与 DB 层
```
lock/domain/LockDevice.java LockGateway.java LockGroup.java LockAuth.java
LockAccessLog.java ExecutionRecord.java LockServiceConfig.java
lock/mapper/LockDeviceMapper.java LockGatewayMapper.java LockGroupMapper.java
LockAuthMapper.java LockAccessLogMapper.java LockServiceConfigMapper.java
resources/mapper/lock/Lock{Device,Gateway,Group,Auth,AccessLog,ServiceConfig}Mapper.xml
lock/service/ILock{Auth,Device,Gateway,Group,AccessLog,ServiceConfig}Service.java + impl
lock/controller/Lock{Auth,Device,Gateway,Group,AccessLog,ServiceConfig}Controller.java
```
> 三个业务实现类(`LockAuthServiceImpl`、`LockDeviceServiceImpl`、`LockGatewayServiceImpl`)只间接依赖命令服务,本方案中**不改**。
### 1.4 状态机与协议构造层
```
lock/security/RealIotSecurityGate.java (无任何 MQTT 引用,被 train 授权桥接直接用)
lock/security/ExecutionStates.java
lock/service/LockExecutionTransactions.java
lock/LockConstants.java
lock/protocol/YdlinkCodec.java YdlinkFrameDTO.java YdlinkOp.java
YdlinkTopicBuilder.java YdlinkTopicDTO.java YdlinkTopicType.java
LockAuthPayloadFactory.java
```
**原因**:上述协议类被 `LockAuthServiceImpl`、`LockDeviceServiceImpl`、`LockGatewayServiceImpl` 用于生成 `lock_command.payload/topic` 并落库(outbox)。删除会连带 4 个业务类和大量测试改动,违背最小改动原则。
### 1.5 HTTP 开放接口
```
ruoyi-iot/.../openapi/**(OpenAuthApiController、OpenFaceApiController、OpenQueryApiController、
OpenApiAuthFilter、OpenApiProperties、Open*Request)
```
### 1.6 容易误删的隐形依赖
| 文件 | 为什么不能删 |
|---|---|
| `ruoyi-iot/.../lock/task/LockScheduleConfig.java` | 全项目**唯一**的 `@EnableScheduling`;`ruoyi-train` 的 `DmsOperationPoller`(`@Scheduled`)依赖它,删除会静默停掉 DMS 轮询 |
---
## 2. 需删除的文件(36 个)
### 2.1 MQTT 连接与传输核心(13)
| 文件 | 删除理由 |
|---|---|
| `config/IotMqttConfig.java` | MQTT 客户端 Bean 装配与生命周期初始化 |
| `config/IotMqttProperties.java` | `iot.mqtt.*` 全部配置项 |
| `core/MqttClientManager.java` | 连接、重连、健康检查、订阅恢复、发布 |
| `core/MqttCallbackHandler.java` | Paho 回调入口 |
| `core/MqttConnectionCallback.java` | 连接状态回调接口 |
| `core/MqttMessageDispatcher.java` | 上行消息分发 |
| `core/IMqttMessageListener.java` | 业务监听器 SPI |
| `domain/MqttMessageDTO.java` | 消息载体 |
| `domain/MqttPublishRequest.java` | 发布请求 DTO |
| `event/MqttMessageEvent.java` | 消息事件(无其他监听者) |
| `service/IMqttService.java` | 发布/订阅抽象 |
| `service/impl/MqttServiceImpl.java` | 唯一实现 |
| `controller/IotMqttController.java` | `/iot/mqtt/status|publish|subscribe` 运维接口 |
### 2.2 订阅与上行回调处理(6)
| 文件 | 删除理由 |
|---|---|
| `lock/mqtt/LockTopicSubscriber.java` | 启动订阅 5 个 `ydlink` 主题(`ApplicationRunner`) |
| `lock/mqtt/LockMessageListener.java` | 消息路由 + 报文留痕 |
| `lock/mqtt/handler/GatewayOnlineHandler.java` | 上下线回调 |
| `lock/mqtt/handler/CommandRespHandler.java` | 三类回执处理 |
| `lock/mqtt/handler/AccessRecordHandler.java` | 开门记录回调 |
| `lock/mqtt/handler/LockSignalHandler.java` | 信号/电量回调 |
### 2.3 MQTT 相关调度任务(3)
| 文件 | 删除理由 |
|---|---|
| `lock/task/LockCommandRetryTask.java` | 依赖 `retryPending → dispatch → publish`,无传输后无意义 |
| `lock/task/GatewayOfflineCheckTask.java` | 依据 MQTT 心跳判定离线,移除后会误判全员离线 |
| `lock/task/LockDeviceOfflineCheckTask.java` | 同上 |
### 2.4 报文留痕(6)
```
lock/domain/LockMqttLog.java
lock/mapper/LockMqttLogMapper.java
resources/mapper/lock/LockMqttLogMapper.xml
lock/service/ILockMqttLogService.java
lock/service/impl/LockMqttLogServiceImpl.java
lock/controller/LockMqttLogController.java
```
> DB 表 `lock_mqtt_log` 在仓库内无 DDL,可保留空表或手工 `DROP`(不在本次代码范围)。
### 2.5 QA 模拟(1)
```
ruoyi-admin/.../controller/tool/QaMockMqttApiController.java
```
### 2.6 测试(3)
```
ruoyi-iot/src/test/.../lock/mqtt/handler/CommandRespHandlerMockTest.java
ruoyi-iot/src/test/.../lock/security/Round5sExecutionIntegrationTest.java (依赖 MqttClientManager/IMqttService)
ruoyi-iot/src/test/.../lock/security/Round5sEgressIsolationTest.java (以传输层断言为主)
```
---
## 3. 需修改的文件(7 个)
### 3.1 `ruoyi-iot/.../lock/service/impl/LockCommandServiceImpl.java`(唯一业务改动)
| 点位 | 改动 | 目的 |
|---|---|---|
| 第 18 行 | 删除 `import ...IMqttService` | 解除传输依赖 |
| 第 37 行 | 删除 `@Lazy @Autowired IMqttService mqttService` | 同上 |
| 留痕块(约 90-115 行) | 删除 `mqttLogMapper` 字段与 `writeMqttLog` 逻辑 | 留痕层已删除 |
| `dispatch()` 169-174 行 | 去掉 `isConnected()` 判断,改为**直接**置 `STATUS_FAIL` + `errorMsg="TRANSPORT_DISABLED"` 并返回 `null` | 保留断连语义,避免上游状态悬挂 |
| `dispatch()` 180-203 行 | 删除 `publishCommand()` 与 `AWAITING_ACK` 迁移分支,改为 `return false` | 移除发布逻辑 |
| `retryPending()` 206-210 行 | 改为 `return 0`(或随 RetryTask 一并删除) | 无补偿下发 |
> **替代方案**:若完全不想碰业务文件,可保留 `IMqttService`/`MqttServiceImpl` 作为空实现,但 MQTT 依赖仍在,不满足"移除"目标。二者取其一。
### 3.2 `ruoyi-iot/pom.xml`
- 删除 `org.eclipse.paho:org.eclipse.paho.client.mqttv3` 依赖(第 22-26 行)
- `<description>` 由"iot设备接入(MQTT)"改为"iot设备数据与开放接口"
### 3.3 根 `pom.xml`
- 删除 `paho.version` 属性
- 删除 `dependencyManagement` 中 Paho 条目(第 148-153 行)
### 3.4 `ruoyi-admin/src/main/resources/application.yml`
- **整段删除** `iot.mqtt`(第 195-259 行:enabled / mock / broker-url / username / topics / will 等)
- **保留** `iot:` 下其他节点:`hmac`、`openapi`、`face`、`lock_endpoint`、`api_key`、`avatar_profile`、`face_api_key`
### 3.5 三个环境变体配置
```
application_localhost.yml
application_localhost_24.yml
application_localhost_230.yml
```
同样只删 `mqtt:` 子树,保留其余 `iot.*`。
### 3.6 `ruoyi-iot/src/test/.../lock/security/RealIotSecurityGateTest.java`
- 删除使用 `new MqttClientManager(p, null).publish(...)` 的断言(约第 38 行)
- 保留其余门禁策略断言(该类本身保留)
### 3.7 文档(可选)
`docs/mqtt_usage_and_status.md` 标注"已移除",或移入归档目录。
---
## 4. 改造后行为说明
| 场景 | 改造后行为 |
|---|---|
| 应用启动 | 不再创建 MQTT 连接、不再订阅主题、无 `iot-mqtt-health` 线程、无连接失败日志 |
| 业务触发授权/开关锁 | 仍生成 `lock_command` 记录,但状态直接落 `FAIL / TRANSPORT_DISABLED`,不产生任何网络请求 |
| 报到机人脸采集 | 完全不变(HTTP + 本地文件 + 人脸服务 HTTP) |
| 报到机门锁入住 | 完全不变(`TrainLockStayApiClient` HTTP) |
| 管理端门锁/网关/分组/人脸页面 | 查询与维护功能保留;"下发/远程控制"类操作返回失败提示 |
| DMS 轮询任务 | 正常(`LockScheduleConfig` 保留) |
---
## 5. 方案 B(彻底清理,仅供评估)
若要求连同 `lock_command` outbox 一并移除,需追加修改:
- `LockAuthServiceImpl`、`LockDeviceServiceImpl`、`LockGatewayServiceImpl`:删除命令构造与 `commandService.send(...)`
- `LockCommandServiceImpl`、`ILockCommandService`、`LockCommand`、`LockCommandMapper(+XML)`、`LockCommandController`:整体删除
- `RealIotSecurityGate`:删除 `requireDispatchAllowed` / payload 校验分支
- `ruoyi-train/.../TrainIotLockAuthBridgeServiceImpl`:改为仅维护授权记录,不再生成命令
- 相关测试:`Round5v4KingbaseIntegrationTest`、`Round5v4BrowserFixture`、`TrainIotLockAuthBridgeMockTest` 需重写
- `protocol/**` 可随之删除
**风险**:触及 10+ 业务文件与 5+ 测试,且与保留链路相交(`LockAuthMapper` 仍在授权桥接中使用),回归成本高。
---
## 6. 执行顺序建议
1. 先改 `LockCommandServiceImpl`(去 `IMqttService`、去留痕)——此时传输层仍存在但不被引用
2. 再删 2.2 订阅/回调、2.3 调度、2.4 留痕、2.1 传输核心、2.5 QA
3. 删依赖(3.2、3.3)与配置(3.4、3.5)
4. 清理测试(2.6、3.6)
5. 验证:
```bash
mvn -o -pl ruoyi-iot,ruoyi-train,ruoyi-admin -am test-compile
```
启动冒烟后回归三条 HTTP 链路:**人脸上传 → 门锁入住 → 报到重置**
> **再次提醒**:`LockScheduleConfig`、`protocol/**`、`RealIotSecurityGate`、`openapi/**`、`FaceExtractClient`、`Lock*Mapper` 六类文件**不在删除范围**;删除会直接破坏保留功能或导致其它模块编译失败。
---
*文档结束。*
+259
View File
@@ -0,0 +1,259 @@
# MQTT 使用情况与生效状态分析
> **分析对象**:RuoYi-Vue(`railwaysystem`)全部模块
> **分析时间**:2026-11-10
> **分析方式**:源码静态梳理 + 运行时探测(进程、端口、Broker 连通性、日志),**未修改任何代码**
---
## 目录
- [1. 结论摘要](#1-结论摘要)
- [2. MQTT 功能清单](#2-mqtt-功能清单)
- [2.1 连接与消息通道](#21-连接与消息通道)
- [2.2 上行:设备 → 平台(5 个订阅主题)](#22-上行设备--平台5-个订阅主题)
- [2.3 下行:平台 → 设备](#23-下行平台--设备)
- [2.4 运维与辅助](#24-运维与辅助)
- [3. 相关代码文件清单](#3-相关代码文件清单)
- [4. 生效状态分析](#4-生效状态分析)
- [4.1 配置层](#41-配置层)
- [4.2 运行时证据](#42-运行时证据)
- [4.3 日志证据](#43-日志证据)
- [4.4 断连时的代码行为](#44-断连时的代码行为)
- [4.5 功能可用性对照](#45-功能可用性对照)
- [5. 判断依据与恢复方向](#5-判断依据与恢复方向)
---
## 1. 结论摘要
| 维度 | 结论 |
|---|---|
| 配置状态 | **已启用**:`enabled=true`、`mock=false`、`protocol-verified=true`、`real-device-write-enabled=true` |
| 运行状态 | **当前未生效(连接断开)**:应用进程对 Broker 处于 `SYN_SENT`,健康检查每约 30s 重连并持续失败 |
| 消息收发 | **上行不可用**(未订阅,收不到任何上行报文);**下行不可用**(命令派发前即判失败) |
| 代码可用性 | **链路完好**:2026-11-09 日志中 27 次连接成功,并有真实上行报文处理记录 |
| 根因定位 | **环境问题,非代码缺陷**:配置的 Broker `192.168.1.202:1883` 网络不可达;本机 `127.0.0.1:1883` 实际运行着 mosquitto,但配置未指向 |
> 一句话结论:**MQTT 代码与配置都已就绪,但当前连不上配置的 Broker,所有门禁上下行功能处于停摆状态。**
---
## 2. MQTT 功能清单
### 2.1 连接与消息通道
| # | 功能点 | 说明 | 关键代码 |
|---|---|---|---|
| 1 | 客户端建立与重连 | 启动异步连接;失败后由 `iot-mqtt-health` 线程每 30s 兜底重连 | `MqttClientManager#connect / startHealthExecutor` |
| 2 | 启动自动订阅 | 5 个上行主题,QoS 1;重连后 `resubscribe()` 恢复订阅 | `MqttClientManager#resubscribe` |
| 3 | 消息分发 | 按主题分发至业务监听器,并发布 `MqttMessageEvent`;单监听器异常隔离 | `MqttMessageDispatcher#dispatch` |
| 4 | 报文留痕 | 收/发报文写入 `lock_mqtt_log`,payload 脱敏为 `[redacted]` | `LockMqttLogServiceImpl` |
### 2.2 上行:设备 → 平台(5 个订阅主题)
| # | 功能点 | 主题 | 处理结果 |
|---|---|---|---|
| 5 | 网关上下线 | `ydlink/+/thing/onoff_line` | 更新网关在线状态 |
| 6 | 属性设置响应 | `ydlink/+/thing/property/set_response` | 命令状态机推进 |
| 7 | 远程开关锁响应 | `ydlink/+/thing/action/execute_response` | 命令状态机推进 |
| 8 | 授权批量下发回执 | `ydlink/+/thing/data/batch_report` | 授权命令确认 / 拒绝 |
| 9 | 开门记录上报 | `ydlink/+/thing/event/trigger` | 写入开门记录 |
| 10 | 信号/电量上报 | `ydlink/+/thing/event/trigger`(op=RSSI) | 更新设备信号值 |
### 2.3 下行:平台 → 设备
| # | 功能点 | 触发入口 | 说明 |
|---|---|---|---|
| 11 | 门锁授权下发 / 撤销 | 学员入住、退宿、换宿 | 事务提交后派发(outbox 模式) |
| 12 | 远程开锁 / 关锁 | 管理端门锁操作 | 同上 |
| 13 | 网关属性设置(校时、网络参数) | 管理端网关管理 | 同上 |
| 14 | 命令补偿重发 | `LockCommandRetryTask`(每 60s) | **`retry-task-enabled=false`,当前不执行** |
| 15 | 派发安全门禁 | `RealIotSecurityGate` | 环境隔离、SN 白名单、协议审批、真实写入总开关;不通过则 `POLICY_BLOCKED_NOT_ATTEMPTED` |
### 2.4 运维与辅助
| # | 功能点 | 说明 |
|---|---|---|
| 16 | `/iot/mqtt/status|publish|subscribe` | 仅 `mock=true` 或 `qa-api-enabled=true` 时加载,**当前不加载** |
| 17 | `/qa/mock/**` 模拟接口 | 同上,**当前不可用** |
| 18 | 网关离线判定 | `GatewayOfflineCheckTask`、`LockDeviceOfflineCheckTask`(定时任务,遗嘱消息已关闭) |
> **不属于 MQTT 的部分**:报到机人脸上传后的"门锁入住"走 HTTP 开放平台(`TrainLockStayApiClient#callStayCheckin`),不受本次断连影响。
---
## 3. 相关代码文件清单
### 3.1 连接核心与配置(`ruoyi-iot`)
```
com/ruoyi/iot/config/IotMqttProperties.java
com/ruoyi/iot/config/IotMqttConfig.java
com/ruoyi/iot/core/MqttClientManager.java
com/ruoyi/iot/core/MqttCallbackHandler.java
com/ruoyi/iot/core/MqttConnectionCallback.java
com/ruoyi/iot/core/MqttMessageDispatcher.java
com/ruoyi/iot/core/IMqttMessageListener.java
com/ruoyi/iot/domain/MqttMessageDTO.java
com/ruoyi/iot/domain/MqttPublishRequest.java
com/ruoyi/iot/event/MqttMessageEvent.java
com/ruoyi/iot/service/IMqttService.java
com/ruoyi/iot/service/impl/MqttServiceImpl.java
com/ruoyi/iot/controller/IotMqttController.java
```
### 3.2 门禁协议与消息处理
```
com/ruoyi/iot/lock/mqtt/LockTopicSubscriber.java
com/ruoyi/iot/lock/mqtt/LockMessageListener.java
com/ruoyi/iot/lock/mqtt/handler/GatewayOnlineHandler.java
com/ruoyi/iot/lock/mqtt/handler/CommandRespHandler.java
com/ruoyi/iot/lock/mqtt/handler/AccessRecordHandler.java
com/ruoyi/iot/lock/mqtt/handler/LockSignalHandler.java
com/ruoyi/iot/lock/protocol/YdlinkCodec.java
com/ruoyi/iot/lock/protocol/YdlinkFrameDTO.java
com/ruoyi/iot/lock/protocol/YdlinkOp.java
com/ruoyi/iot/lock/protocol/YdlinkTopicBuilder.java
com/ruoyi/iot/lock/protocol/YdlinkTopicDTO.java
com/ruoyi/iot/lock/protocol/YdlinkTopicType.java
```
### 3.3 门禁业务
```
com/ruoyi/iot/lock/security/RealIotSecurityGate.java
com/ruoyi/iot/lock/service/impl/LockCommandServiceImpl.java
com/ruoyi/iot/lock/service/impl/LockAuthServiceImpl.java
com/ruoyi/iot/lock/service/impl/LockDeviceServiceImpl.java
com/ruoyi/iot/lock/service/impl/LockGatewayServiceImpl.java
com/ruoyi/iot/lock/service/impl/LockMqttLogServiceImpl.java
com/ruoyi/iot/lock/service/ILockMqttLogService.java
com/ruoyi/iot/lock/domain/LockMqttLog.java
com/ruoyi/iot/lock/mapper/LockMqttLogMapper.java
com/ruoyi/iot/lock/controller/LockMqttLogController.java
com/ruoyi/iot/lock/task/LockCommandRetryTask.java
com/ruoyi/iot/lock/task/GatewayOfflineCheckTask.java
com/ruoyi/iot/lock/task/LockDeviceOfflineCheckTask.java
```
### 3.4 业务桥接(`ruoyi-train`)
```
com/ruoyi/train/integration/lock/service/impl/TrainIotLockAuthBridgeServiceImpl.java
com/ruoyi/train/integration/lock/service/ITrainLockAuthBridgeService.java
com/ruoyi/train/integration/lock/dto/LockAuthBridgeResult.java
```
> 同目录 `client/TrainLockStayApiClient.java`、`dto/LockStayCheckinResult.java` 为 **HTTP** 门锁开放平台调用,不走 MQTT。
### 3.5 QA 与测试
```
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/QaMockMqttApiController.java
ruoyi-iot/src/test/java/...(MqttClientManagerTest、MqttMessageDispatcherTest、LockMessageListenerTest 等)
```
### 3.6 配置文件
```
ruoyi-admin/src/main/resources/application.yml # iot.mqtt.*(195-259 行,当前生效)
ruoyi-admin/src/main/resources/application_localhost.yml # broker 指向 202/24
ruoyi-admin/src/main/resources/application_localhost_24.yml
ruoyi-admin/src/main/resources/application_localhost_230.yml
```
> 激活 profile 为 `druid`,其 `application-druid.yml` 无 `iot.mqtt` 覆盖项,最终以 `application.yml` 为准。
---
## 4. 生效状态分析
### 4.1 配置层
```yaml
mqtt:
enabled: true # 创建连接
mock: false # 真实收发
protocol-verified: true # 允许真实下发
real-device-write-enabled: true
environment-id: railway-iot-01
retry-task-enabled: false # 补偿任务关闭
broker-url: tcp://192.168.1.202:1883
username: admin3
password: admin3
automatic-reconnect: true
health-check-interval: 30000
topics: 5 个上行主题(QoS 1)
```
### 4.2 运行时证据
| 检查项 | 结果 | 含义 |
|---|---|---|
| 应用进程 | `java.exe` PID 26688 监听 `0.0.0.0:8096` | 应用正在运行 |
| MQTT socket | 对 `192.168.1.202:1883` 处于 **`SYN_SENT`** | TCP 连接未建立,正在重试 |
| Broker 连通性 | `Test-NetConnection 192.168.1.202:1883` → TCP 失败、Ping 超时 | Broker 不可达 |
| 本机 1883 | `127.0.0.1:1883` 由 `mosquitto.exe`(PID 6032)监听 | 本地有可用 Broker,配置未指向 |
### 4.3 日志证据
| 日志 | 证据 |
|---|---|
| `logs/sys-error.log` | `[iot-mqtt-health] ERROR MqttClientManager - MQTT 连接失败, broker=tcp://192.168.1.202:1883`,累计 **448 次**,间隔约 30s;今日 13:52:50 仍有新记录 |
| `logs/sys-info.2026-11-09.log` | `MQTT 连接成功` **27 次**;含真实上行处理(网关状态更新、开门记录落库) |
| `logs/sys-info.log`(今日) | 无任何 MQTT 行 —— 今日连接从未成功,失败仅写 error 日志 |
### 4.4 断连时的代码行为
```java
// LockCommandServiceImpl#dispatch
if (!mqttService.isConnected()) {
log.warn("[TRACE-DISPATCH] cid={} isConnected=false -> TRANSPORT_DISCONNECTED", commandId);
command.setCommandStatus(LockConstants.STATUS_FAIL);
command.setErrorMsg("TRANSPORT_DISCONNECTED_NOT_ATTEMPTED");
commandMapper.updateCommand(command);
return null;
}
```
断连时命令**在发送前即被标记失败**,不会进入 `AWAITING_ACK`,因此不存在"平台显示已下发、设备未收到"的假成功。
### 4.5 功能可用性对照
| 功能 | 状态 | 依据 |
|---|---|---|
| MQTT 连接 | ❌ 未连接 | `SYN_SENT` + 448 次失败日志 + Broker 不可达 |
| 上行 5 个主题 | ❌ 无订阅 | 订阅在 `connect()` 成功后执行,当前未连接 |
| 网关状态 / 开门记录 / 回执处理 | ❌ 停止 | 依赖上行报文 |
| 授权下发 / 远程开关锁 / 网关设置 | ❌ 直接失败 | `isConnected=false` → `STATUS_FAIL` |
| 命令补偿重发 | ❌ 不执行 | `retry-task-enabled=false` |
| `/iot/mqtt/status`、`/qa/mock/**` | ❌ 未加载 | `@ConditionalOnProperty` 条件不满足,**无法用接口自证状态** |
| 报到机人脸采集与门锁入住(HTTP) | ✅ 正常 | 走 HTTP 开放平台,与 MQTT 无关 |
---
## 5. 判断依据与恢复方向
### 5.1 判断依据
1. **配置齐备**:开关、Mock、协议审批、真实写入、白名单均已就位 → 具备生效条件。
2. **运行证据否证**:应用存活但 TCP 未连通,健康检查持续失败 → 连接未建立。
3. **产物证据缺失**:无订阅即无上行;命令侧有明确的失败状态码 → 收发均不可用。
4. **历史反证代码无缺陷**:曾出现 27 次连接成功与真实报文处理 → 问题在环境与配置指向,而非实现。
### 5.2 恢复方向(无需改代码)
1. 排查 `192.168.1.202:1883`:主机连通性、防火墙、Broker 进程、网段隔离;
2. 若现场已有本机 Broker,将 `iot.mqtt.broker-url` 改为 `tcp://127.0.0.1:1883`,并核对 `username/password`;
3. 恢复后按序验证:
- 日志出现 `MQTT 连接成功`
- 5 个主题订阅成功
- 收到网关 `onoff_line` 上行
- 对白名单内 SN 下发一次授权,确认回执进入 `CONFIRMED`
4. 如需离线补偿能力,再评估开启 `retry-task-enabled=true`(会周期性重发至真实设备,属生产变更,需审批)。
---
*文档结束。如需补充时序图或按模块拆分,可在此基础上扩展。*