# 执行计划 — 门口屏模拟器 Token 获取

> **文档类型**: 执行计划文档
> **创建日期**: 2026-08-19
> **作者**: czj
> **关联文档**: `_PRD_需求优化_门口屏模拟器Token获取.md`、`_PRD_需求文档_设备模拟模块.md`
> **预计工期**: 2.5 天

---

## 一、执行概述

### 1.1 项目背景

真实门口屏设备（UbGview 应用）启动后第一个动作是调用 HTTP 接口 `POST /exapi/api-android/token/getTokenInfoByToken` 获取 token。本计划为门口屏模拟器补充该接口调用链，使模拟器启动流程与真实设备一致——先获取 token，为后续 MQTT 主题解析和上报做准备。

### 1.2 执行目标

1. 门口屏模拟器启动时第一步调用 `getTokenInfoByToken` 接口
2. 使用日志中的固定 X-SIGN（已验证可调用成功）
3. 多设备场景下 regUdid 由 device_id 派生，保证唯一
4. 成功获取并保存 Authorization / token / conference / company 信息
5. 将 companyNumber / cnum 回填到 topic_params

### 1.3 范围界定

| 范围 | 内容 |
|------|------|
| ✅ 本期执行 | HTTP 接口调用、签名头构造、regUdid 生成、响应解析、token 保存、参数回填 |
| ❌ 本期排除 | MQTT 主题解析、会议服务主题订阅、MQTT 周期上报、动态 X-SIGN 算法 |

---

## 二、任务分解与实施计划

### 阶段 1：接口调研与数据确认（0.5 天）

| # | 任务 | 说明 | 产出 |
|---|------|------|------|
| 1.1 | 确认接口调用细节 | 复核日志中的 URL / Header / Body / Response 结构 | 接口调用手顺（已确认：HTTP 200 + OPERATION_SUCCESSFUL） |
| 1.2 | 确认目标环境地址 | 从 EnvConfig 中获取目标系统 host（如 192.168.5.48） | host 配置项 |
| 1.3 | 确认 X-SIGN 固定值 | 从日志中提取固定 X-SIGN 作为常量 | 常量定义 |
| 1.4 | 确认 appToken 固定值 | 从日志 body 中提取 `AND-34H-0101` | 常量定义 |

**阶段 1 完成标准**：接口调用所需的最小常量集合（host / X-SIGN / appToken）已确认并记录。

### 阶段 2：Token 客户端实现（1 天）

| # | 任务 | 说明 | 产出 |
|---|------|------|------|
| 2.1 | 新增 `DoorTokenClient` 类 | 文件：`backend/app/simulators/door_token_client.py` | 客户端类 |
| 2.2 | 实现 `_build_headers()` | X-SIGN 固定值 + X-TIMESTAMP（秒级）+ X-RANDOM（16 位） | 请求头构造 |
| 2.3 | 实现 `_generate_reg_udid()` | `{device_id}_{8位随机}`，保证多设备唯一 | regUdid 生成 |
| 2.4 | 实现 `get_token()` | POST 调用 + 超时控制（5s）+ 重试（3 次, 指数退避） | 接口调用 |
| 2.5 | 实现 `_parse_response()` | 解析 Authorization / token / conference / company | 响应解析 |
| 2.6 | 实现 `_get_mqtt_config()`（预留） | 解析日志中的 mqttSavedConfig（本期仅预留，不做订阅） | 预留方法 |

**阶段 2 完成标准**：`DoorTokenClient` 可独立调用接口成功，单元验证返回完整 token 数据。

### 阶段 3：模拟器启动流程集成（0.5 天）

| # | 任务 | 说明 | 产出 |
|---|------|------|------|
| 3.1 | `DoorSimulator.start()` 接入 token 获取 | 在 start() 最前面调用 `DoorTokenClient.get_token()` | 启动流程改造 |
| 3.2 | 保存 token 信息 | 新增 `self._token_info` 属性保存完整响应 | token 保存 |
| 3.3 | topic_params 回填 | company_id ← companyNumber, room_id ← cnum, app_token ← appToken | 参数回填 |
| 3.4 | 失败处理 | token 获取失败 → 记录错误日志 → 启动终止 → 设备状态 error | 异常处理 |

**阶段 3 完成标准**：启动门口屏设备，日志显示接口调用成功，token 已保存，topic_params 已回填。

### 阶段 4：验证与部署（0.5 天）

| # | 任务 | 说明 | 产出 |
|---|------|------|------|
| 4.1 | 单设备验证 | 创建 1 台门口屏设备，启动，确认 token 获取成功 | 验证记录 |
| 4.2 | 多设备验证 | 创建 3-5 台门口屏设备，启动，确认 regUdid 均不重复 | 验证记录 |
| 4.3 | 失败场景验证 | 模拟网络不可达，确认重试 3 次后启动终止 | 验证记录 |
| 4.4 | 回归验证 | 其他设备类型（paperless/central）启动不受影响 | 回归记录 |
| 4.5 | 部署 | 提交代码，部署到测试服务器（192.168.5.60 双容器） | 部署完成 |

**阶段 4 完成标准**：单设备 + 多设备 + 失败场景全部验证通过，线上部署完成。

---

## 三、验收标准

### 3.1 功能验收

| 编号 | 验收项 | 预期结果 |
|------|--------|---------|
| F01 | 门口屏启动时调用 token 接口 | 日志显示 `POST /exapi/api-android/token/getTokenInfoByToken` 记录 |
| F02 | 接口调用成功 | `code=200, success=true`，Authorization 已保存 |
| F03 | token 信息保存 | `_token_info` 包含 Authorization / token / conference / company |
| F04 | topic_params 回填 | company_id / room_id 自动回填为接口返回值 |
| F05 | regUdid 唯一性 | 多设备启动时 regUdid 各不相同 |
| F06 | 接口调用失败 | 启动终止，日志显示错误原因，设备状态 error |

### 3.2 边界验收

| 编号 | 边界场景 | 预期结果 |
|------|---------|---------|
| B01 | 网络不可达 | 3 次重试后失败，启动终止 |
| B02 | 接口返回 success: false | 立即失败，不重试 |
| B03 | 响应缺少 Authorization | 解析异常，启动终止 |
| B04 | 单设备启动 | regUdid 正常生成，token 获取成功 |
| B05 | 多设备（10 台）同时启动 | 各设备独立获取 token，regUdid 均不重复 |

---

## 四、测试计划

| 测试类型 | 范围 | 方式 |
|---------|------|------|
| 单元测试 | `DoorTokenClient` 的 header/body/regUdid/解析逻辑 | pytest，mock 网络 |
| 集成测试 | 真实调用目标环境接口 | 手工验证（已验证 HTTP 200 可调用成功） |
| 手动验证 | 单设备 / 多设备 / 失败场景 | 前端界面操作 + 后端日志 |

---

## 五、风险评估

| 风险 | 等级 | 影响 | 应对措施 |
|------|------|------|---------|
| 固定 X-SIGN 后续失效 | 中 | 接口调用 401 | 短期限固定值，后续逆向 APK 生成动态签名 |
| X-SIGN 与时间戳校验 | 中 | 当前时间戳可能导致签名失效 | 已实际调用验证成功，若失效回退用日志时间戳 |
| 服务端 regUdid 唯一校验严格 | 低 | 多设备启动部分失败 | regUdid 由 device_id 派生 + 随机后缀，冲突概率极低 |
| 目标环境网络不可达 | 中 | 启动失败 | 重试 3 次 + 明确错误日志 |
| 多设备并发调用目标环境 | 中 | 目标环境压力 | 每设备独立请求，接口轻量，风险可控 |

---

## 六、实施记录

| 日期 | 任务 | 状态 | 备注 |
|------|------|------|------|
| 2026-08-19 | 梳理需求，确认决策 | ✅ 完成 | 与用户确认 X-SIGN 固定值、regUdid 派生、只存 token |
| 2026-08-19 | 接口实际调用验证 | ✅ 完成 | HTTP 200 + OPERATION_SUCCESSFUL，token 完整返回 |
| 2026-08-19 | 撰写 PRD 需求文档 | ✅ 完成 | `_PRD_需求优化_门口屏模拟器Token获取.md` |
| 2026-08-19 | 撰写执行计划 | ⏳ 进行中 | 本文档 |
| - | 阶段 1 接口调研与数据确认 | ⬜ 待执行 | |
| - | 阶段 2 Token 客户端实现 | ⬜ 待执行 | |
| - | 阶段 3 模拟器启动流程集成 | ⬜ 待执行 | |
| - | 阶段 4 验证与部署 | ⬜ 待执行 | |

---

## 七、后续工作（不在本期）

| 后续项 | 说明 | 优先级 |
|--------|------|--------|
| MQTT 主题解析 | 根据回填后的 company_id + room_id 解析 MQTT 主题 | P1 |
| 会议服务主题订阅 | 订阅 `/iot/v1/conference/service/request/{company_id}/{room_id}/` | P1 |
| MQTT 周期上报 | 启动周期上报任务 | P1 |
| 动态 X-SIGN 算法 | 逆向 APK 确认签名算法，替换固定值 | P2 |
| 其他设备类型接入 | 无纸化屏等设备复用 TokenClient | P2 |

---

## 八、附录

### 8.1 涉及代码文件

| 文件 | 操作 | 说明 |
|------|------|------|
| `backend/app/simulators/door_token_client.py` | 新增 | Token 获取客户端 |
| `backend/app/simulators/door_simulator.py` | 修改 | start() 接入 token 获取 |
| `backend/app/config.py` | 修改（可选） | token 接口相关配置项 |

### 8.2 接口调用手顺（已验证）

```
POST https://192.168.5.48/exapi/api-android/token/getTokenInfoByToken
Headers:
  Content-Type: application/json; charset=utf-8
  X-SIGN: {日志固定值}
  X-TIMESTAMP: {当前秒级时间戳}
  X-RANDOM: {16 位随机}
Body:
  {"appToken": "AND-34H-0101", "regUdid": "{device_id}_{8位随机}"}
Response:
  code=200, success=true
  result.Authorization → 保存
  result.token.companyNumber → topic_params.company_id
  result.token.cnum → topic_params.room_id
```

---

*文档结束*