# 中控屏直连 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`**。建议统一按下述优先级判定: 1. HTTP 状态码(panel 接口); 2. 响应体 `success` 布尔字段(平台 `Result` 已带); 3. 业务 `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/homes`
`GET /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-schema`
`GET /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_source` - **`product_property` 新增**:`semantic`、`channel`、`completionMode` - **`firmware` 新增**:`package_type`、`target_model`、`min_version` ### 迁移脚本补齐(P1) `parent_device_id` / `is_gateway` 两列生产库已存在但 `sql/` 无迁移脚本,需补脚本登记,保证新环境可重建。 --- ## 五、分期落地计划 ### P0 · 前置(已完成 / 进行中) - [x] 排查 `parent_device_id` / `is_gateway` 生产库列 → **已确认存在,阻塞解除** - [x] 三项契约冲突裁决 → 见 2.2,待屏方确认映射表 ### P1 · 一期:打通主链路 目标:**屏能注册、能看家、能执行云下发的命令**。完成即可覆盖约 90% 屏控场景。 1. 屏终端身份 + `ROLE_PANEL` + session / refresh(含刷新令牌表) 2. `PanelContextResolver`:家庭范围强制覆盖(安全关键) 3. `device` 执行归属字段 + `devices/register` + `schema` 更新 4. **`device_command` 状态机 + pull / start / report + 命令查询**(双轨兼容云同步) 5. `device_state` + `reports` 批量上报 6. `snapshot`(一期单页) 7. 补 `parent_device_id` / `is_gateway` 迁移脚本 ### P2 · 二期:实时性与完整性 8. `panel_event` + `events` 长轮询 9. `control-schema` 补 actions / events + 设备级 `schemaRevision` 10. OTA 改造:`ota_attempt` + `Firmware` 扩展 + `ota-tasks/next` 11. `snapshot` 分页与一致性游标 12. `configuration` 下发 ### P3 · 三期:体验与运营 13. `scene_run` / `scene-runs/report` 14. AI `confirmationId` + `status` 15. `diagnostics` 诊断事件 16. 多屏归属交接流程(`routeRevision` 迁移) --- ## 六、需屏方确认的问题清单 对应贵方第 12 章提出的 5 点,我方反馈如下并补充需确认项: 1. **推送方式**:我方一期采用协议建议的 **DB 事件表 + 长轮询**(平台无 SSE/WebSocket 端点基础设施)。请确认屏端「长轮询 20s / 心跳 30s / 租期 90s」三路并发调度互不阻塞。 2. **一期范围**:`scene-runs`、`AI confirmationId`、`diagnostics` 我方建议**延后**。请确认屏端一期是否强依赖其中任何一项。 3. **CONFIRMED 的证据来源**:屏端能否提供「写后读回」校验?若否,命令只能到达 `ACCEPTED` / `UNKNOWN`,界面须如实显示「已接受 / 结果未知」,不得显示为已确认(此点协议亦强调)。 4. **多屏归属交接**:一期是否仅单屏?多屏场景下 `routeRevision` 交接由谁触发(管理员后台操作 / 平台自动)? 5. **配网**:确认屏端 Matter/Zigbee/BLE 配网完成后走设备登记,**平台不下发任何协议密钥**(与协议一致)。 6. **OTA 下载**:我方提供平台自有下载域名,**无需屏端 Bearer Token 转发第三方**(与协议一致)。 7. **错误码映射表**(见 2.3):请确认按 `errorCode` 字符串分支、`code` 数值仅用于日志,且不硬编码 `code === 0`。 --- ## 七、下一步 1. 双方就本回复的「修改后接受 / 暂缓」项达成一致,输出**定稿版协议**(路径与字段维护在同一份文档中)。 2. 我方按第五章 P1 启动一期开发;屏端同步适配。 3. 定稿前请勿按评审稿开发——评审稿中标注「新增建议」的路径与字段,以定稿为准。