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

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

Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 f670fbd1
......@@ -2,7 +2,7 @@
> **生成时间**: 2026-08-05
> **当前分支**: `platform-auto-test`
> **最近提交**: `63547417` feat(device-sim): 页面优化 - 批量操作 + 实时消息流 + 环境切换
> **最近提交**: `11ddf7e9` fix(device-sim): 消息记录架构重构 - 消息队列解耦模拟器线程与数据库
---
......@@ -13,9 +13,9 @@
**需求**: 无纸化设备上报消息格式与真实设备一致。
**实现**:
- 新增 `build_reboot_response_payload()` 方法
- 新增 `build_reboot_response_payload()` 方法(无纸化专属字段:appName="无纸化2.0", deviceModel="Pad-10Pro", buildInfo="" 等)
- 新增 `build_heartbeat_payload()` 方法
- `deviceModel`: 无纸化 `Pad-10Pro`,门口屏 `YT-10`
- `deviceModel`: 门口屏 `YT-10`,无纸化 `Pad-10Pro`
- 授权码逻辑: 无纸化使用 `paperless_token_prefix` 或将门口屏 `AND-` 替换为 `ANP-`
**修改文件**:
......@@ -43,20 +43,32 @@
- `backend/app/services/device_sim_service.py`
- `backend/app/schemas/device_sim.py`
### 4. ✅ WebSocket 实时消息推送(已实现,待修复)
### 4. ✅ WebSocket 实时消息推送
**新增端点**: `WS /api/device-sim/ws/messages`
**当前状态**: 已实现但存在架构问题,暂时禁用
**实现**: 消息队列消费成功后,通过回调推送消息到前端
**修改文件**:
- `backend/app/routers/device_sim.py` — WebSocket 端点
- `backend/app/services/device_sim_service.py`消息回调(已注释)
- `backend/app/routers/device_sim.py` — WebSocket 端点 + 回调注册
- `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` — 批量勾选和批量操作按钮
......@@ -67,9 +79,23 @@
- `CentralSim.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`
......@@ -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/services/device_sim_service.py` | 修改 | 批量操作方法 + 消息回调(已注释) |
| `backend/app/main.py` | 修改 | lifespan 启动消息消费循环 |
| `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/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/components/device/DeviceList.vue` | 修改 | 批量勾选和操作 |
| `frontend/src/components/device/MessageStream.vue` | 新增 | 实时消息流组件 |
| `frontend/src/types/device.ts` | 修改 | 无纸化授权码字段 |
| `frontend/src/views/device-sim/DoorSim.vue` | 修改 | 左右布局重构 |
| `frontend/src/views/device-sim/PaperlessSim.vue` | 修改 | 同上 |
| `frontend/src/views/device-sim/CentralSim.vue` | 修改 | 同上 |
| `frontend/src/views/device-sim/ClientSim.vue` | 修改 | 同上 |
| `frontend/src/views/device-sim/EnvConfig.vue` | 修改 | 无纸化授权码配置 |
---
......@@ -112,7 +162,7 @@
| 优先级 | 任务 | 关联文档 | 说明 |
|--------|------|----------|------|
| **P0** | 消息记录架构重构 | `_PRD_需求优化_消息记录架构重构与实时消息流.md` | 引入消息队列,修复数据库写入和 WebSocket 推送 |
| **P0** | 排查消息流"暂无消息"问题 | `_PRD_需求优化_消息记录架构重构与实时消息流.md` | 见已知问题分析 |
| P1 | 中控/集控客户端主题确认 | — | 确认真实上报/订阅主题 |
| P2 | 环境配置统计功能 | — | 统计各环境下的设备数量 |
......@@ -144,11 +194,24 @@
## 七、技术备注
### 模拟器线程模型约束
### 消息队列架构
```
模拟器线程(入队) 主线程异步循环(消费)
┌──────────────────┐ ┌──────────────────────────┐
│ MQTT 上报 │ │ 消息队列 (asyncio.Queue) │
│ → 回调触发 │ ────────▶ │ → 写入数据库 │
│ → 入队(非阻塞) │ │ → WebSocket 推送 │
└──────────────────┘ └──────────────────────────┘
```
### 关键代码位置
- 模拟器必须使用 `sync_playwright` 同步 API(Windows 限制)
- 模拟器在独立线程中运行,不能直接使用主线程的数据库 session
- 解决方案:使用消息队列解耦
- 消息队列定义: `device_sim_service.py` 第 34-60 行
- 入队函数: `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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论