纳入范围: - 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
241 lines
13 KiB
Markdown
241 lines
13 KiB
Markdown
# 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 推送消息结构
|
||
|
||
```json
|
||
{
|
||
"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 锁定)
|