iotsaas/docs/中控屏对接设计.md
2995840376 0ce191c448 chore: 初始化 iotsaas 版本库基线
纳入范围:
- 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
2026-10-27 12:00:41 +08:00

13 KiB
Raw Blame History

10.1 寸中控屏对接设计文档

状态:讨论定稿(待开发) 日期:2026-09-18 范围:中控屏(家庭内常驻大屏)与现有 IoT SaaS 后端的对接方案,涵盖认证、接口、实时推送、安全。


1. 背景与目标

现有系统(后端 iot-device 9081)已具备完整的 家庭/房间/设备/涂鸦/动态 能力,手机 APP 通过会员账号(JWT userType=member)访问,家庭权限靠 homeMember 表校验。

现在要对接一块 10.1 寸中控屏,功能与手机 APP 类似:家庭/房间管理、设备管理/控制、场景、近期动态。目标是让中控屏以安全、隔离、实时的方式复用现有业务能力,同时不破坏手机端现有逻辑。


2. 决策摘要(已确认方案)

维度 决策 说明
身份模型 专属设备凭证(非会员账号登录) 中控屏是家庭设备,不存个人密码;出厂 SN + 激活码,APP 扫码绑定家庭后签发 panel JWT
接口组织 独立命名空间 /api/v1/panel 与手机接口清晰隔离,便于限流、审计、差异化(聚合接口)
实时推送 WebSocket 推送 新增 WSS 端点,按 homeId 分组推送状态/场景结果,手机后续也可复用
家庭归属 单一固定家庭 激活后绑定一个家庭,所有接口强制限定该 homeId

3. 整体架构

┌────────────┐    HTTPS/WSS     ┌──────────────────────────────┐
│ 中控屏      │ ──────────────▶ │  iot-device (9081)            │
│ (10.1"屏)  │                  │  /api/v1/panel/*  控制器       │
│            │ ◀──── WSS 推送 ── │  /panel/ws  WebSocket 端点     │
└────────────┘                  │      │                         │
        │ 激活(扫码)             │      ▼                         │
        ▼                       │  PanelAuth 拦截器(deviceType+  │
┌────────────┐                  │  homeId+session 校验)          │
│ 手机 APP    │ ── 扫码绑定 ───▶ │      │                         │
└────────────┘                  │      ▼                         │
                                │  复用现有 Service 层            │
                                │  (Home/Room/Device/Tuya/       │
                                │   Activity Service)            │
                                │      │                         │
                                │      ▼                         │
                                │  DeviceEventPublisher ──▶ RabbitMQ
                                │  (设备数据变化)          │      │
                                │                          ▼      │
                                │                  WS 广播(按homeId)
                                └──────────────────────────────┘

核心思路:数据模型与业务 Service 完全复用,新增的只是「设备身份鉴权层」+「panel 专属控制器(含聚合接口)」+「WebSocket 推送通道」。


4. 认证与安全设计

4.1 设备激活流程(出厂 SN + 激活码 + APP 扫码绑定)

  1. 出厂阶段:每台中控屏烧录唯一 SN,并预置一次性 激活码(服务端可校验的凭证,哈希存储)。
  2. 首次开机:中控屏展示 二维码(含 SN + 激活码),提示"用手机 APP 扫码绑定家庭"。
  3. 手机 APP 扫码 → 调用后端绑定接口,选择一个家庭(homeId)→ 后端:
    • 校验激活码有效且未使用;
    • 创建/更新 panel_device 记录,写入 home_id、bound_member_id、status=已绑定;
    • 标记激活码已消费(一次性)。
  4. 中控屏用 SN + 激活码 调用 /panel/auth/token 换取 panel JWT(access + refresh)。
  5. 后续所有请求携带 panel JWT;access 过期用 refresh 静默续期(与手机机制一致)。

安全优势:中控屏从不接触任何个人会员密码,只持有设备凭证 + 短期 JWT,即使设备被物理获取,也只能访问已绑定家庭的有限接口。

4.2 Panel Token 设计

在现有 com.iot.common.util.JwtUtil 基础上扩展 panel token 生成,claims 增加:

claim 值 用途
sub panel_device.id 设备主键
deviceType panel 与 userType=member/admin 区分
homeId 绑定家庭 ID 强制 home 隔离
scope home:read,home:control 权限最小化
sessionId 本次会话 ID 支持远程吊销
userType panel(兼容现有解析) —

access 有效期建议 30 分钟,refresh 滚动续期;refresh 接口校验 deviceType=panel 且 session 仍有效。

4.3 鉴权隔离

新增 PanelAuth 拦截器(或在现有 JwtAuthFilter 扩展分支):

  • 校验 deviceType=panel;
  • 从 token 取出 homeId,所有 /panel 接口请求参数中的 homeId 必须以 token 中的 homeId 为准,禁止越权访问其它家庭;
  • 校验 sessionId 未被吊销(panel_device.status 非禁用);
  • 禁止 panel token 访问 /auth/member、/admin、/users、/roles 等会员/后台接口(路径白名单隔离)。

4.4 安全清单 / 风险缓解

  • 传输安全:全链路 HTTPS;中控屏出厂内置服务端证书指纹(证书固定 / pinning),防局域网中间人。
  • 凭证安全:激活码一次性、可吊销、限尝试次数;中控屏仅加密存储 refresh token,不存任何密码。
  • 权限最小化:panel 仅 home-scoped,不能访问会员/管理员接口;homeId 由 token 锁定。
  • 设备吊销:手机 APP「中控屏管理」可解绑/禁用某 SN → panel_device.status=disabled 且 session 失效,已签发 JWT 在下一次校验被拒。
  • 限流:激活、换 token、控制等敏感操作独立限流(如 5 次/分钟/IP)。
  • 审计:所有中控屏控制写入 device_activity_log,source=central,并关联 panel_id,可追溯。
  • 防重放(可选二期):敏感写操作要求 nonce + timestamp 签名。
  • 远程禁用:管理员可在后台按 SN 拉黑设备。

5. 接口设计(/api/v1/panel)

5.1 认证类

方法 路径 说明
POST /api/v1/panel/auth/activate 提交 SN + 激活码,校验有效性,返回待绑定态
POST /api/v1/panel/auth/token SN + 激活码 换取 access/refresh(绑定完成后)
POST /api/v1/panel/auth/refresh 刷新 token(滚动续期,校验 session)
POST /api/v1/panel/auth/unbind 解绑/注销(使 session 失效)

手机 APP 侧的「扫码绑定家庭」接口:复用现有家庭接口,新增一个绑定确认端点(如 /api/v1/homes/{homeId}/bind-panel),仅 owner/admin 可操作。

5.2 家庭 / 房间 / 设备(复用现有 Service)

方法 路径 复用
GET /api/v1/panel/homes/{homeId}/summary 新增聚合接口:首页一屏拉取(房间、设备+状态、场景、近期动态)
GET /api/v1/panel/homes/{homeId}/rooms RoomService
GET /api/v1/panel/homes/{homeId}/devices DeviceService(含在线状态)
GET /api/v1/panel/homes/{homeId}/devices/{deviceId} DeviceService.getDeviceInfo
POST /api/v1/panel/devices/{deviceId}/commands DeviceService.sendCommand / TuyaCloudService.sendCommand(与 DeviceController.sendCommand 同源逻辑)
GET /api/v1/panel/homes/{homeId}/activities DeviceActivityService(近期动态)

注:现有控制接口在 DeviceController.sendCommand(/api/v1/devices/{deviceId}/commands)与 TuyaController.sendCommand(/api/v1/tuya/devices/{deviceId}/commands)。panel 控制器内部复用同一 DeviceService/TuyaCloudService,仅替换鉴权维度为 panel+homeId。

5.3 场景(当前无 SceneController,需新建,与首页场景对齐)

方法 路径 说明
GET /api/v1/panel/homes/{homeId}/scenes 场景列表(推荐场景)
POST /api/v1/panel/homes/{homeId}/scenes/{sceneId}/execute 执行场景

待确认:首页「推荐场景 / 灯光 / 空调 / 安防」当前后端落地程度(是否已有场景实体与执行链路),需在开发前与首页场景功能一并规划。

5.4 近期动态

复用 DeviceActivityController(/api/v1/devices/activities),panel 下限定 homeId 过滤;首页聚合接口 summary 直接内联最近 N 条动态,点击进入查看完整列表(复用 activities 列表 + 详情页)。


6. WebSocket 实时推送设计

6.1 端点与鉴权

  • 端点:/api/v1/panel/ws?token={panelJWT}(WS 握手无法自定义 Header,token 放 query;配合 WSS 加密)。
  • 握手时校验 panel JWT(deviceType + homeId + session),失败立即关闭。
  • 连接成功后,将该 Session 加入内存 homeId → Set<Session> 路由表。

6.2 推送消息结构

{
  "type": "device.status | device.online | device.offline | scene.executed | home.updated",
  "homeId": 123,
  "payload": { "deviceId": "...", "dps": {...}, "ts": 1234567890 }
}

6.3 与现有事件机制衔接

现有 DeviceEventPublisher 已通过 RabbitMQ(EXCHANGE + ROUTING_DEVICE_DATA_CHANGE)发布设备数据变化事件。

  • 新增一个 WS 广播监听器:消费设备数据变化事件 → 根据设备归属 homeId → 推送给该 home 所有在线 panel(及后续手机)WS 连接。
  • 控制成功(DeviceService.sendCommand)后同样 publishDeviceDataChange → 同 home 所有客户端实时刷新。
  • 上下线(涂鸦云推送 TuyaMessageSubscribeService)也纳入广播。

这样中控屏与手机控制/状态天然一致,无需各自轮询。

6.4 客户端职责

心跳保活 + 断线指数退避重连;重连后增量拉取(调用 summary/devices)补齐离线期间变更。


7. 数据模型变更

新增表 panel_device(MySQL):

字段 类型 说明
id bigint PK 设备主键
sn varchar(64) UNIQUE 出厂序列号
activation_code_hash varchar(128) 激活码哈希(一次性)
home_id bigint 绑定家庭(单一固定)
bound_member_id bigint 操作绑定的会员(审计/解绑)
name varchar(64) 中控屏昵称
status tinyint 0 未激活 / 1 已绑定 / 2 已禁用
session_id varchar(64) 当前会话(用于吊销)
last_active_at datetime 最后活跃
create_time / update_time datetime —

现有 device_activity_log 已有 source 字段(app/panel/voice/scene/cloud_push),新增 central 取值并加 panel_id 列用于审计关联。


8. 与手机端一致性 & 不破坏现有

  • 不改动现有 /api/v1/homes、/rooms、/devices、/tuya、/devices/activities 及其鉴权(member 维度)。
  • 新增 /panel 命名空间与 PanelAuth 拦截器,是叠加而非替换。
  • 业务 Service 层复用,避免逻辑分叉。
  • WebSocket 推送对手机端是可选增强:手机后续可接同一 WS 通道,移除轮询,逐步收敛到统一实时模型。

9. 分阶段实施计划

  • P0 基础:panel_device 表 + 激活/换 token + PanelAuth 拦截器 + 证书固定准备。
  • P1 核心接口:/panel 家庭/房间/设备/控制/动态(复用 Service),中控屏可完成基础操控。
  • P2 实时:WebSocket 端点 + RabbitMQ 事件 → WS 广播,首页聚合 summary 实时刷新。
  • P3 场景:场景列表/执行接口(与首页场景功能对齐落地)。
  • P4 安全加固 & 管理:限流、审计、远程禁用;手机 APP「中控屏管理」页面(绑定/解绑/禁用)。

10. 待确认 / 开放问题

  1. 激活码的生成与下发方式(出厂预置固件 vs 后台按 SN 生成)。
  2. 首页「推荐场景 / 灯光 / 空调 / 安防」后端当前落地程度 —— 需先与首页场景功能统一规划(P3 依赖)。
  3. 是否允许同一家庭绑定多台中控屏(建议允许 N 台,均 home-scoped)。
  4. 中控屏是否要做「局域网内直连网关/设备」低延迟控制(本期先走云端,局域网优化留二期)。
  5. 设备重置(恢复出厂)后激活码失效策略:需后台支持重新生成激活码。

附录:现有可复用代码锚点

  • 控制:DeviceController.sendCommand(/api/v1/devices/{deviceId}/commands)、TuyaController.sendCommand(/api/v1/tuya/devices/{deviceId}/commands)
  • 事件:DeviceEventPublisher.publishDeviceDataChange(RabbitMQ 设备数据变化)
  • 鉴权:com.iot.common.util.JwtUtil(现有 JWT,已含 userType claim,可扩展 deviceType/homeId/sessionId)
  • 动态:DeviceActivityController + device_activity_log(含 source)
  • 家庭权限:HomeService / RoomServiceImpl 的 homeMember 校验逻辑(panel 改为 homeId 锁定)