纳入范围: - backend/ Spring Boot 多模块后端(iot-common/device/rule/data/alarm/auth) - frontend/admin/ Vue3 + Element Plus 管理后台 - Android/ 涂鸦原生 APP (com.xhai.iot) - backend/sql/ Flyway 风格迁移脚本(V2~V21) - docs/ 对接文档与设计文档 排除范围: - 构建产物: node_modules/ target/ build/ dist/ .gradle/ - 签名密钥: *.jks *.keystore (严禁入库) - 本机配置: Android/local.properties - 运行日志: logs/ *.log - AI 协作本地数据 .codebuddy/ 与第三方 demo tuya-demo/ .gitattributes 统一以 LF 存储, Windows 批处理保留 CRLF
17 KiB
中控屏直连 MumIot 协议 · 平台方评审回复与落地方案
| 项目 | 内容 |
|---|---|
| 对应文档 | 04-中控屏直连MumIot对接协议-评审稿.html(v0.1,2026-10-16) |
| 本稿版本 | v1.0(平台方评审回复) |
| 日期 | 2026-10-22 |
| 评审范围 | 协议 13 章 / 24 个接口(21 个新增建议 + 3 个扩展建议) |
| 评审方 | MumIot 平台 |
一、总体结论
采纳本协议作为中控屏接入的主体框架。
协议提出的"屏 = 本地执行网关 + 命令单据化 + 三重版本号(归属/物模型/状态)"设计,与平台演进方向一致。落地后不只中控屏受益,平台现有手机端 / 网页端也会同步补上四项长期欠账:
| 现状欠账 | 落地后改善 |
|---|---|
| 设备命令纯同步、无记录、不可追溯 | 命令单据化 + 状态机,全程可查 |
设备状态无版本号、/latest 是空壳 |
属性级状态 + 序号,最新值可用 |
| 无面向客户端的事件通道 | 家庭增量事件 + 长轮询,多端一致 |
| OTA 只有任务级聚合、无单设备追踪 | 单设备升级尝试(attempt),进度可追踪 |
但需分期落地,不建议一次全量实施。 协议中有三块属全新子系统建设(命令状态机、事件通道、状态版本号),另有 OTA 模型需重构,一次性上线的风险与回归成本过高。
二、P0 前置事项结论
2.1 DDL 缺列排查 —— ✅ 已澄清,非阻塞
协议要求"本地设备挂载到屏(父网关)",依赖 Device.parentDeviceId 与 Device.isGateway。此前发现 backend/sql/ 全部迁移脚本中均无这两列,疑为阻塞项。已核查生产库,结论如下:
parent_device_id varchar(64) YES NULL
is_gateway tinyint YES 0
生产库 device 表实际已存在这两列,非生产阻塞项。
遗留事项(降为 P1):
sql/目录缺少对应迁移脚本,导致「从脚本重建的新环境」会缺列。需补一个迁移脚本登记这两列,保证环境可重建。详见第五章 P1。
2.2 三项契约冲突 —— 我方裁决
协议第 2 章通用约定与平台现行约定存在三处冲突,我方裁决如下:
冲突 1:HTTP 状态码
- 平台现状:全局约定「业务异常 HTTP 200 + JSON 业务码」。例:
/api/v1/auth/token凭证无效返回200 + {"code":401}。 - 协议要求:错误使用对应非 2xx(含 202/409/410/429/503),且明确「屏不能仅凭 HTTP 200 判定设备控制成功」。
裁决:分区处理,双方约定不冲突。
- 协议新增的
/api/v1/panels/**及声明X-MUM-Protocol: panel-v1的请求 → 采用标准 HTTP 语义(202 已接受 / 4xx / 5xx)。 - 存量接口(手机 App、网页、涂鸦回调)→ 保持 200 + 业务码不动,避免打断现有客户端。
- 我方提供《ResultCode ↔ HTTP 状态 + errorCode》映射表(见 2.3),屏端按映射表处理。
协议第 2 章已预留此口子:「平台既有错误码可映射保留,但同一含义应稳定一致」。
冲突 2:成功响应的 code 值
- 平台现状:成功
code = 200(非 0)。 - 协议示例:
{"code": 0, "message": "success"}。
裁决:沿用平台 code = 200,并明确判定方式。
屏端请不要硬编码 code === 0。建议统一按下述优先级判定:
- HTTP 状态码(panel 接口);
- 响应体
success布尔字段(平台Result已带); - 业务
code(成功为 200,非 0/200 即异常)。
冲突 3:幂等键与新增必填字段的强制范围
- 协议要求:所有 POST 必带
Idempotency-Key;控制命令必带schemaRevision。 - 平台现状:现有 App / 网页 / 涂鸦回调均无这些字段。
裁决:仅对声明 X-MUM-Protocol: panel-v1 的请求强制。 存量客户端保持原契约,内部归入同一设备路由与命令记录,由平台补齐默认版本。
协议第 2 章已认可:「不能直接把新必填字段施加到所有旧客户端」。
2.3 错误码映射表(我方提供,供屏端实现)
| HTTP | 业务 code | errorCode | 屏端处理 |
|---|---|---|---|
| 400 | 400 | INVALID_ARGUMENT | 修正参数,不自动重试 |
| 400 | 400 | UNSUPPORTED_CAPABILITY | 刷新能力,关闭无效操作 |
| 401 | 401 | TOKEN_EXPIRED | 刷新后重试同一逻辑请求 |
| 401 | 401 | INVALID_CREDENTIAL | 停止循环,上报身份错误 |
| 403 | 403 | HOME_ACCESS_REVOKED | 停止该家庭云操作,清缓存 |
| 403 | 403 | DEVICE_OUT_OF_SCOPE | 不执行跨家庭/非本屏任务 |
| 409 | 409 | IDEMPOTENCY_CONFLICT | 不生成新键掩盖冲突 |
| 409 | 409 | SCHEMA_CHANGED | 重读物模型,重新确认目标 |
| 409 | 409 | ROUTE_CHANGED | 刷新归属,旧屏不执行 |
| 409 | 409 | DELIVERY_EXPIRED | 本次投递无效,不执行 |
| 409 | 409 | EXECUTION_ALREADY_STARTED | 查原命令,不重复操作 |
| 409 | 409 | DEVICE_OFFLINE | 显示设备/执行屏离线 |
| 409 | 409 | STALE_SESSION | 停止领取 |
| 410 | 410 | CURSOR_EXPIRED | 重拉完整快照 |
| 410 | 410 | SNAPSHOT_EXPIRED | 放弃分页,重新开始 |
| 410 | 410 | COMMAND_EXPIRED | 不执行 |
| 429 | 429 | RATE_LIMITED | 按 Retry-After 退避 |
| 503 | 503 | SERVICE_UNAVAILABLE | 读请求退避;控制先查原结果 |
errorCode为语义稳定字符串,屏端按errorCode分支;code数值仅供日志。
2.4 时间参数默认值(我方确认按协议实现)
心跳 30s / 在线租期 90s / 长轮询 20s / 命令总超时 30s(允许 1–120s)/ 投递租期 10s / 快照缓存 120s / 事件游标保留 24h / 幂等记录保留 7 天。
全部由会话响应下发,屏端以响应值为准,不硬编码。
三、逐条评审回执
图例:接受 = 按协议实现;修改后接受 = 采纳思路但有调整;暂缓 = 延后到后续期;需澄清 = 待屏方确认后定。
第 3 章 · 中控身份与会话
| # | 接口 | 标记 | 结论 | 说明 |
|---|---|---|---|---|
| 1 | POST /api/v1/panels/session |
新增 | 接受 | 新增 panel_terminal 表(serialNumber 唯一 + BCrypt 凭证 + homeId/memberId/role/enabled);JwtAuthenticationFilter 增加 panel → ROLE_PANEL 分支;复用现有 JwtUtil 签发 |
| 2 | POST /api/v1/panels/session/refresh |
新增 | 接受 | 协议要求「轮换 + 120s 重放窗口 + 可撤销」。平台现有 refresh 为无状态 JWT、无法撤销,故新增刷新令牌表实现轮换与撤销(同时修复会员侧同一隐患) |
| 3 | GET /api/v1/panels/{panelId} |
新增 | 接受 | 按协议返回,panelId 强制为自身 |
| 4 | POST /api/v1/panels/{panelId}/heartbeat |
新增 | 接受 | 按协议;租期自收到有效心跳起算,其它调用不隐式替代心跳 |
第 4 章 · 家庭快照与事件同步
| # | 接口 | 标记 | 结论 | 说明 |
|---|---|---|---|---|
| 5 | GET /api/v1/homesGET /api/v1/homes/{homeId}/rooms |
扩展 | 接受(关键安全项) | 平台现有接口以 memberId 入参过滤。屏调用时必须忽略入参、以会话绑定家庭为准。我方新增 PanelContextResolver 强制覆盖,杜绝越权选择器 |
| 6 | GET /api/v1/panels/{panelId}/snapshot |
新增 | 修改后接受 | 一期单页返回不分页(家庭设备量级有限),二期再上 pageToken 分页与快照一致性游标。字段按协议(含 executionSource/executorPanelId/routeRevision/schemaRevision/stateVersion) |
| 7 | GET /api/v1/panels/{panelId}/events |
新增 | 修改后接受(P2) | 采用协议的 DB 事件表 + 长轮询方案(平台无 SSE/WebSocket 端点基础设施,不另起炉灶)。新增 panel_event 表 + 不透明游标。一期先以快照 + 状态上报保证可用 |
第 5 章 · 设备登记与标准控制模型
| # | 接口 | 标记 | 结论 | 说明 |
|---|---|---|---|---|
| 8 | POST /api/v1/panels/{panelId}/devices/register |
新增 | 接受 | 映射良好:屏 = 网关设备(isGateway),本地设备挂 parentDeviceId;platform 已支持 local/matter。(panelId, localDeviceId) 唯一,重复登记返回同一映射 |
| 9 | PUT /api/v1/panels/{panelId}/devices/{deviceId}/schema |
新增 | 接受 | 物模型现状与协议 PropertySpec 高度吻合(code/type/access/min/max/step/unit/enumList/dpId 均已具备)。需新增 semantic、channel、completionMode 三字段,并引入设备级 schemaRevision |
| 10 | GET /api/v1/devices/{deviceId}/control-schemaGET /api/v1/products/{id}/spec |
扩展 | 修改后接受 | 现 control-schema 只返回 properties,未用 ProductAction/ProductEvent。按协议补齐 actions(如 stop)。版本号平台内部称 specVersion(产品级),对外统一暴露为 schemaRevision(设备级) |
第 6 章 · 控制命令与执行回执(核心)
| # | 接口 | 标记 | 结论 | 说明 |
|---|---|---|---|---|
| 11 | POST /api/v1/devices/{deviceId}/commands |
扩展 | 修改后接受(核心改造) | 现为同步阻塞、无命令记录(CommandStatus 常量有定义但零引用)。按协议改造为「先落库生成 commandId → 202 → 异步执行」。双轨兼容:云设备(涂鸦等)同步执行后直落终态,旧客户端仍返回 200 + 原结构;仅 executionSource=LOCAL_PANEL 且声明 panel-v1 的请求走 202 异步 |
| 12 | GET /api/v1/device-commands/{commandId} |
新增 | 接受 | 新增 device_command 表承载 10 态状态机;仅本命令关联家庭的合法调用者可查 |
| 13 | POST /api/v1/panels/{panelId}/commands/pull |
新增 | 接受 | 按协议:bootId 必填、投递租期 10s、deliveryToken 一次性 |
| 14 | POST /api/v1/panels/{panelId}/commands/{commandId}/start |
新增 | 接受 | 原子校验状态/有效期/归属/token,成功转 EXECUTING 并返回 executionToken |
| 15 | POST /api/v1/panels/{panelId}/commands/{commandId}/report |
新增 | 接受 | reportId + 递增 reportSequence 去重排序;平台按证据判定 CONFIRMED,不凭旧缓存值 |
第 7 章 · 状态与活动上报
| # | 接口 | 标记 | 结论 | 说明 |
|---|---|---|---|---|
| 16 | POST /api/v1/panels/{panelId}/reports |
新增 | 修改后接受 | 现设备实时状态唯一来源是 device.capabilities.dps(JSON 列),/data/latest 为 TODO 空壳,全项目无 stateVersion/sequence。按协议新增 device_state(deviceId + 属性码 + sequence + stateEpoch + quality),按 (epoch, seq) 比较决定 appliedToCurrentState。顺带补上 /latest 空壳,手机端同样受益 |
第 8 章 · 多屏、场景与 AI
| # | 接口 | 标记 | 结论 | 说明 |
|---|---|---|---|---|
| 17 | GET /api/v1/scene-runs/{sceneRunId} |
新增 | 暂缓(P3) | 一期云端场景经「场景 → 设备命令 → 执行屏」即可跑通;场景执行单据化延后,避免与命令状态机同期上线放大回归面 |
| 18 | POST /api/v1/panels/{panelId}/scene-runs/report |
新增 | 暂缓(P3) | 同上。离线场景历史记录二期补 |
| 19 | POST /api/v1/ai/command |
扩展 | 暂缓(P3) | 平台现有二次确认字段(targetType/targetId)可覆盖基本需求;confirmationId + expiresAt + status 随屏端语音排期再上 |
第 9 章 · 配置、内容、日志和 OTA
| # | 接口 | 标记 | 结论 | 说明 |
|---|---|---|---|---|
| 20 | GET /api/v1/panels/{panelId}/configuration |
新增 | 暂缓(P2) | 一期屏用本地默认配置即可启动;配置下发二期随运营需求上 |
| 21 | POST /api/v1/panels/{panelId}/diagnostics |
新增 | 暂缓(P3) | 诊断事件延后;期间用服务端日志定位 |
| 22 | GET /api/v1/panels/{panelId}/ota-tasks/next |
新增 | 修改后接受(P2) | 平台 OTA 现为「产品固件 + 任务级聚合计数」,缺 packageType/targetModel/minVersion、无单设备升级记录、重复上报会重复计数、非 running 或不在目标集合时静默丢弃上报(屏会误判成功)。按协议改造:新增 ota_attempt 单设备升级尝试,聚合计数改为由 attempt 统计 |
| 23 | POST /api/v1/ota-tasks/{id}/report |
扩展 | 修改后接受(P2) | 现请求体仅 deviceId/success/version 三字段。按协议增加 attemptId/reportId/reportSequence/stage/progress/errorCode/message,并做幂等 |
第 2 章 · 通用约定
| # | 约定 | 结论 | 说明 |
|---|---|---|---|
| 24 | 幂等键 / X-MUM-Protocol / 错误响应 / 标识与时间 |
修改后接受 | 见 2.2 三项裁决。权限清单(panel.read、gateway.execute 等 7 项)我方接受,签发时写入 JWT scopes,用 hasAuthority 做细粒度校验(平台已有 scopes 机制) |
四、数据模型改造清单
新增表
| 表 | 用途 | 期次 |
|---|---|---|
panel_terminal |
屏终端身份(序列号、凭证哈希、家庭绑定、角色) | P1 |
panel_refresh_token |
刷新令牌轮换与撤销(兼修会员侧隐患) | P1 |
device_command |
命令单据 + 10 态状态机(核心) | P1 |
device_state |
设备属性级状态 + 序号(补 /latest 空壳) |
P1 |
idempotency_record |
幂等记录(保留 7 天) | P1 |
panel_event |
家庭增量事件 + 游标 | P2 |
ota_attempt |
单设备升级尝试(attempt/stage/progress) | P2 |
panel_configuration |
屏配置下发 | P2 |
scene_run |
场景执行单据 | P3 |
字段扩展
device表新增:executor_panel_id、route_revision、schema_revision、local_device_id、protocol、execution_sourceproduct_property新增:semantic、channel、completionModefirmware新增:package_type、target_model、min_version
迁移脚本补齐(P1)
parent_device_id / is_gateway 两列生产库已存在但 sql/ 无迁移脚本,需补脚本登记,保证新环境可重建。
五、分期落地计划
P0 · 前置(已完成 / 进行中)
- 排查
parent_device_id/is_gateway生产库列 → 已确认存在,阻塞解除 - 三项契约冲突裁决 → 见 2.2,待屏方确认映射表
P1 · 一期:打通主链路
目标:屏能注册、能看家、能执行云下发的命令。完成即可覆盖约 90% 屏控场景。
- 屏终端身份 +
ROLE_PANEL+ session / refresh(含刷新令牌表) PanelContextResolver:家庭范围强制覆盖(安全关键)device执行归属字段 +devices/register+schema更新device_command状态机 + pull / start / report + 命令查询(双轨兼容云同步)device_state+reports批量上报snapshot(一期单页)- 补
parent_device_id/is_gateway迁移脚本
P2 · 二期:实时性与完整性
panel_event+events长轮询control-schema补 actions / events + 设备级schemaRevision- OTA 改造:
ota_attempt+Firmware扩展 +ota-tasks/next snapshot分页与一致性游标configuration下发
P3 · 三期:体验与运营
scene_run/scene-runs/report- AI
confirmationId+status diagnostics诊断事件- 多屏归属交接流程(
routeRevision迁移)
六、需屏方确认的问题清单
对应贵方第 12 章提出的 5 点,我方反馈如下并补充需确认项:
- 推送方式:我方一期采用协议建议的 DB 事件表 + 长轮询(平台无 SSE/WebSocket 端点基础设施)。请确认屏端「长轮询 20s / 心跳 30s / 租期 90s」三路并发调度互不阻塞。
- 一期范围:
scene-runs、AI confirmationId、diagnostics我方建议延后。请确认屏端一期是否强依赖其中任何一项。 - CONFIRMED 的证据来源:屏端能否提供「写后读回」校验?若否,命令只能到达
ACCEPTED/UNKNOWN,界面须如实显示「已接受 / 结果未知」,不得显示为已确认(此点协议亦强调)。 - 多屏归属交接:一期是否仅单屏?多屏场景下
routeRevision交接由谁触发(管理员后台操作 / 平台自动)? - 配网:确认屏端 Matter/Zigbee/BLE 配网完成后走设备登记,平台不下发任何协议密钥(与协议一致)。
- OTA 下载:我方提供平台自有下载域名,无需屏端 Bearer Token 转发第三方(与协议一致)。
- 错误码映射表(见 2.3):请确认按
errorCode字符串分支、code数值仅用于日志,且不硬编码code === 0。
七、下一步
- 双方就本回复的「修改后接受 / 暂缓」项达成一致,输出定稿版协议(路径与字段维护在同一份文档中)。
- 我方按第五章 P1 启动一期开发;屏端同步适配。
- 定稿前请勿按评审稿开发——评审稿中标注「新增建议」的路径与字段,以定稿为准。