提交 311403f6 authored 作者: 陈泽健's avatar 陈泽健

docs(device-sim): 更新 HANDOFF - 记录 P0 消息流问题排查进展

Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 f670fbd1
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
> **生成时间**: 2026-08-05 > **生成时间**: 2026-08-05
> **当前分支**: `platform-auto-test` > **当前分支**: `platform-auto-test`
> **最近提交**: `63547417` feat(device-sim): 页面优化 - 批量操作 + 实时消息流 + 环境切换 > **最近提交**: `11ddf7e9` fix(device-sim): 消息记录架构重构 - 消息队列解耦模拟器线程与数据库
--- ---
...@@ -13,9 +13,9 @@ ...@@ -13,9 +13,9 @@
**需求**: 无纸化设备上报消息格式与真实设备一致。 **需求**: 无纸化设备上报消息格式与真实设备一致。
**实现**: **实现**:
- 新增 `build_reboot_response_payload()` 方法 - 新增 `build_reboot_response_payload()` 方法(无纸化专属字段:appName="无纸化2.0", deviceModel="Pad-10Pro", buildInfo="" 等)
- 新增 `build_heartbeat_payload()` 方法 - 新增 `build_heartbeat_payload()` 方法
- `deviceModel`: 无纸化 `Pad-10Pro`,门口屏 `YT-10` - `deviceModel`: 门口屏 `YT-10`,无纸化 `Pad-10Pro`
- 授权码逻辑: 无纸化使用 `paperless_token_prefix` 或将门口屏 `AND-` 替换为 `ANP-` - 授权码逻辑: 无纸化使用 `paperless_token_prefix` 或将门口屏 `AND-` 替换为 `ANP-`
**修改文件**: **修改文件**:
...@@ -43,20 +43,32 @@ ...@@ -43,20 +43,32 @@
- `backend/app/services/device_sim_service.py` - `backend/app/services/device_sim_service.py`
- `backend/app/schemas/device_sim.py` - `backend/app/schemas/device_sim.py`
### 4. ✅ WebSocket 实时消息推送(已实现,待修复) ### 4. ✅ WebSocket 实时消息推送
**新增端点**: `WS /api/device-sim/ws/messages` **新增端点**: `WS /api/device-sim/ws/messages`
**当前状态**: 已实现但存在架构问题,暂时禁用 **实现**: 消息队列消费成功后,通过回调推送消息到前端
**修改文件**: **修改文件**:
- `backend/app/routers/device_sim.py` — WebSocket 端点 - `backend/app/routers/device_sim.py` — WebSocket 端点 + 回调注册
- `backend/app/services/device_sim_service.py`消息回调(已注释) - `backend/app/services/device_sim_service.py`推送回调接口
### 5. ✅ 前端页面优化 ### 5. ✅ 消息记录架构重构(消息队列)
**实现方案**:
- 引入 `asyncio.Queue(maxsize=10000)` 消息队列
- 模拟器线程只做入队(`enqueue_message()`),不涉及数据库
- 主线程异步消费循环(`message_consumer_loop()`)写入数据库 + WebSocket 推送
- 消费循环在 `main.py``lifespan` 中启动
**修改文件**:
- `backend/app/services/device_sim_service.py` — 消息队列 + 消费循环
- `backend/app/main.py` — lifespan 启动消费循环
### 6. ✅ 前端页面优化
**新组件**: **新组件**:
- `MessageStream.vue` — 实时消息流组件(终端风格) - `MessageStream.vue` — 实时消息流组件(终端风格,支持方向过滤/暂停/搜索
**增强组件**: **增强组件**:
- `DeviceList.vue` — 批量勾选和批量操作按钮 - `DeviceList.vue` — 批量勾选和批量操作按钮
...@@ -67,9 +79,23 @@ ...@@ -67,9 +79,23 @@
- `CentralSim.vue` — 同上 - `CentralSim.vue` — 同上
- `ClientSim.vue` — 同上 - `ClientSim.vue` — 同上
### 6. ✅ 代码提交 ### 7. ✅ 文档
**新增文档**:
- `_PRD_需求优化_消息记录架构重构与实时消息流.md` — PRD 需求文档
- `_执行计划_消息记录架构重构与实时消息流.md` — 执行计划文档
**更新文档**:
- `HANDOFF_设备模拟.md` — 本文件
### 8. ✅ 代码提交
**提交**: `63547417` feat(device-sim): 页面优化 - 批量操作 + 实时消息流 + 环境切换 | 提交 | 说明 |
|------|------|
| `e4223169` | 无纸化设备消息体修复 + 环境配置支持无纸化授权码 |
| `63547417` | 页面优化 - 批量操作 + 实时消息流 + 环境切换 |
| `11ddf7e9` | 消息记录架构重构 - 消息队列解耦模拟器线程与数据库 |
| `22eb5e22` | 消息记录架构重构 PRD + 执行计划 + HANDOFF 更新 |
**已推送到远程**: `origin/platform-auto-test` **已推送到远程**: `origin/platform-auto-test`
...@@ -77,34 +103,58 @@ ...@@ -77,34 +103,58 @@
## 二、已知问题 ## 二、已知问题
### P0 — 消息记录架构问题 ### P0 — 消息流页面显示"暂无消息"
**现象**: 设备启动后显示 `running`,但 `totalReports: 0``lastReportedAt: null`,消息流页面无消息显示。
**现象**: 上报记录数据库写入失败,`totalReports` 始终为 0,消息流页面显示"暂无消息"。 **排查过程**:
1. 确认新代码已部署到容器(`enqueue_message` 存在于容器内)
2. 确认 MQTT 连接正常,主题订阅成功
3. 确认 `_sync_report_log` 被重写为入队操作
4. 确认 `_message_queue` 在消费循环中运行
5. 确认 `_message_callback` 已注册到 WebSocket 路由
6. 发现 `message_consumer_loop` 日志"消息消费循环已启动"正常输出
7.`_queue_stats` 显示 `enqueued: 0`,说明 `_sync_report_log` 没有被调用
**根因**: `_sync_report_log()` 在模拟器线程中运行,数据库 session 不兼容线程,导致 `Session's transaction has been rolled back` 错误。 **可能根因**(待验证):
- 模拟器的 `_on_report_callback` 没有被正确设置?但 `set_report_callback``start_simulator` 中被调用
- 回调函数中,旧代码 `report_callback(**kwargs)` 还存在(第 579-583 行),但它是 `async` 函数且在线程中调用,可能被忽略
- `_notify_report` 中的 `self._on_report_callback` 可能为 `None`
**临时方案**: WebSocket 推送已注释,不影响 MQTT 上报。 **建议排查方向**:
1.`_notify_report` 方法中添加日志,确认是否被调用
2.`_sync_report_log` 方法中添加日志,确认是否被触发
3. 检查 `set_report_callback` 中的 lambda 闭包是否正确传参
4. 删除旧的 `report_callback` 异步函数(第 579-583 行),避免混淆
**正式方案**: 参见 PRD 和执行计划文档,需要引入消息队列重构。 ### P1 — 中控/集控客户端主题确认
**说明**: 这两类设备使用的旧格式主题,需要确认真实 MQTT 主题。
--- ---
## 三、修改文件清单 ## 三、修改文件清单
### 已提交(commit 63547417) ### 已提交
| 文件 | 变更类型 | 说明 | | 文件 | 变更类型 | 说明 |
|------|----------|------| |------|----------|------|
| `backend/app/routers/device_sim.py` | 修改 | 批量操作 API + WebSocket 端点 | | `backend/app/main.py` | 修改 | lifespan 启动消息消费循环 |
| `backend/app/services/device_sim_service.py` | 修改 | 批量操作方法 + 消息回调(已注释) | | `backend/app/routers/device_sim.py` | 修改 | 批量操作 API + WebSocket 端点 + 回调注册 |
| `backend/app/services/device_sim_service.py` | 修改 | 消息队列 + 消费循环 + 批量操作 |
| `backend/app/schemas/device_sim.py` | 修改 | BatchOperationRequest/Response | | `backend/app/schemas/device_sim.py` | 修改 | BatchOperationRequest/Response |
| `backend/app/simulators/paperless_simulator.py` | 修改 | 无纸化专属消息体 |
| `backend/app/simulators/base_simulator.py` | 修改 | deviceModel 修正 |
| `backend/app/simulators/topic_templates.py` | 修改 | 无纸化新增主题 |
| `frontend/src/api/deviceSim.ts` | 修改 | 批量操作 API + WebSocket 连接 | | `frontend/src/api/deviceSim.ts` | 修改 | 批量操作 API + WebSocket 连接 |
| `frontend/src/components/device/DeviceList.vue` | 修改 | 批量勾选和操作 | | `frontend/src/components/device/DeviceList.vue` | 修改 | 批量勾选和操作 |
| `frontend/src/components/device/MessageStream.vue` | 新增 | 实时消息流组件 | | `frontend/src/components/device/MessageStream.vue` | 新增 | 实时消息流组件 |
| `frontend/src/types/device.ts` | 修改 | 无纸化授权码字段 |
| `frontend/src/views/device-sim/DoorSim.vue` | 修改 | 左右布局重构 | | `frontend/src/views/device-sim/DoorSim.vue` | 修改 | 左右布局重构 |
| `frontend/src/views/device-sim/PaperlessSim.vue` | 修改 | 同上 | | `frontend/src/views/device-sim/PaperlessSim.vue` | 修改 | 同上 |
| `frontend/src/views/device-sim/CentralSim.vue` | 修改 | 同上 | | `frontend/src/views/device-sim/CentralSim.vue` | 修改 | 同上 |
| `frontend/src/views/device-sim/ClientSim.vue` | 修改 | 同上 | | `frontend/src/views/device-sim/ClientSim.vue` | 修改 | 同上 |
| `frontend/src/views/device-sim/EnvConfig.vue` | 修改 | 无纸化授权码配置 |
--- ---
...@@ -112,7 +162,7 @@ ...@@ -112,7 +162,7 @@
| 优先级 | 任务 | 关联文档 | 说明 | | 优先级 | 任务 | 关联文档 | 说明 |
|--------|------|----------|------| |--------|------|----------|------|
| **P0** | 消息记录架构重构 | `_PRD_需求优化_消息记录架构重构与实时消息流.md` | 引入消息队列,修复数据库写入和 WebSocket 推送 | | **P0** | 排查消息流"暂无消息"问题 | `_PRD_需求优化_消息记录架构重构与实时消息流.md` | 见已知问题分析 |
| P1 | 中控/集控客户端主题确认 | — | 确认真实上报/订阅主题 | | P1 | 中控/集控客户端主题确认 | — | 确认真实上报/订阅主题 |
| P2 | 环境配置统计功能 | — | 统计各环境下的设备数量 | | P2 | 环境配置统计功能 | — | 统计各环境下的设备数量 |
...@@ -144,11 +194,24 @@ ...@@ -144,11 +194,24 @@
## 七、技术备注 ## 七、技术备注
### 模拟器线程模型约束 ### 消息队列架构
```
模拟器线程(入队) 主线程异步循环(消费)
┌──────────────────┐ ┌──────────────────────────┐
│ MQTT 上报 │ │ 消息队列 (asyncio.Queue) │
│ → 回调触发 │ ────────▶ │ → 写入数据库 │
│ → 入队(非阻塞) │ │ → WebSocket 推送 │
└──────────────────┘ └──────────────────────────┘
```
### 关键代码位置
- 模拟器必须使用 `sync_playwright` 同步 API(Windows 限制) - 消息队列定义: `device_sim_service.py` 第 34-60 行
- 模拟器在独立线程中运行,不能直接使用主线程的数据库 session - 入队函数: `device_sim_service.py` 第 62-76 行 (`enqueue_message`)
- 解决方案:使用消息队列解耦 - 消费循环: `device_sim_service.py` 末尾 (`message_consumer_loop`)
- 消费循环启动: `main.py` lifespan 中
- WebSocket 回调注册: `device_sim.py` 第 557-577 行 (`_register_ws_callback`)
### 消息流组件使用 ### 消息流组件使用
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论