# HANDOFF — 设备模拟模块

> **生成时间**: 2026-08-13
> **当前分支**: `platform-auto-test`
> **最近提交**: `26cc89c5` fix(executor): 登录检测多信号优化与设备模拟按类型批量启停
> **未提交改动**: 设备模拟相关 6 个前端文件 + 文档（见下方"会话 C"修改文件清单）

---

## 📊 会话进度记录

### 2026-08-13 会话 C：环境筛选与统计卡片联动修复（已全部完成并部署）

**会话目标**：修复设备模拟页面筛选 5.202 环境时统计卡片仍显示 540 的问题

**状态**：✅ 全部完成 — 文档 + 代码实现 + 构建 + 部署到 192.168.5.60 + 验证通过

**问题现象**：门口屏模拟页面顶部环境下拉框选择 **192.168.5.202**（0 台设备）后，统计卡片【总设备】仍显示 540。

**根因**（详见问题分析文档）：每台设备模拟页面存在**两套互不联动的环境筛选控件**：
1. **页面顶部下拉框**（`currentEnvId`）→ 变更只刷新统计卡片
2. **DeviceList 内部筛选栏**（`filterEnvConfigId`）→ 变更只刷新设备列表

且 `DeviceList.vue` 未声明 `envConfigId` prop，页面传入的 `:env-config-id` 被静默忽略，页面顶部环境选择无法传递到设备列表。

**后端验证**（确认后端无问题）：
- `door + env_328d4859a59a430ca25835f8d3c06c29(5.202)` → `total:0` ✅
- `door + env_98aea2c545b24aa29e9072e99fbef61c(5.44)` → `total:540` ✅
- `door + 无环境` → `total:540` ✅

**已修复内容**（统一为单一环境源，双向联动）：

1. **`DeviceList.vue`**（公共组件）：
   - props 增加 `envConfigId?: string`（可选，不传不影响独立使用）
   - emit 增加 `(e: 'env-change', envId: string)`
   - `watch(() => props.envConfigId)` 同步到内部 `filterEnvConfigId` 并刷新列表
   - 内部筛选栏 `@change` 由 `loadDevices` 改为 `onInternalEnvChange`（刷新列表 + emit `env-change`）

2. **四个 Sim 页面**（`DoorSim.vue` / `PaperlessSim.vue` / `CentralSim.vue` / `ClientSim.vue`）：
   - 模板新增 `@env-change="onDeviceEnvChange"`
   - 新增 `onDeviceEnvChange(envId)` 方法：同步 `currentEnvId` + `loadStats()`

**联带修复**：`frontend/src/views/functional-report/TemplateConfig.vue` 第 103 行 el-switch v-model 索引类型错误（TS7053），构建 `vue-tsc` 阻塞，已加 `as any` 修复。

**构建部署**：前端 `npm run build` 成功（29.34s，80 个文件），`deploy_frontend_only.py` 部署到 5.60，容器健康。

**部署后验证**（服务器端 grep 构建产物确认）：
- `DeviceList-8m7X4mUz.js`：`emits:["device-changed","env-change"]` + `watch(()=>ee.envConfigId)` 同步逻辑 ✅
- `DoorSim-B__vCye8.js`：VNode 渲染含 `"env-config-id":k.value` + `onEnvChange` 事件绑定 ✅
- `index.html` 引用 `index-Dxn_XUMs.js`（与本地产物 hash 一致，部署为最新）

**修改文件清单（本次会话）**：

| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `Docs/PRD/设备模拟/问题处理/问题分析_筛选环境统计卡片不跟随.md` | 新增 | 问题分析文档（两套环境筛选不联动根因） |
| `Docs/PRD/设备模拟/执行计划/执行计划_环境筛选统一联动修复.md` | 新增 | 3 阶段执行计划文档 |
| `frontend/src/components/device/DeviceList.vue` | 修改 | envConfigId prop + watch + env-change emit + onInternalEnvChange |
| `frontend/src/views/device-sim/DoorSim.vue` | 修改 | @env-change 绑定 + onDeviceEnvChange |
| `frontend/src/views/device-sim/PaperlessSim.vue` | 修改 | 同上 |
| `frontend/src/views/device-sim/CentralSim.vue` | 修改 | 同上 |
| `frontend/src/views/device-sim/ClientSim.vue` | 修改 | 同上 |
| `frontend/src/views/functional-report/TemplateConfig.vue` | 修改 | el-switch v-model 索引类型修复（构建阻塞） |
| `deploy_frontend_only.py` | 新增 | 仅前端 dist 部署脚本（会话 B 会话中新增） |

**待办**：
- 本次改动尚未提交 git（连同会话 B 的一起），下次会话可通过 `/GitCommit` 提交
- 建议浏览器硬刷新（Ctrl+F5）验证：选 5.202 → 统计 0；选 5.44 → 统计 540；不选 → 540；DeviceList 内部切换环境与统计卡片同步

---

### 2026-08-13 会话 B：统计卡片修复 + 文档体系重构（已全部完成并部署）

**会话目标**：修复设备模拟页面顶部统计卡片（总设备/运行中/已停止）显示为 0 的问题

**状态**：✅ 全部完成 — 代码实现 + 构建 + 部署到 192.168.5.60 + API 验证通过

**已完成内容**：

#### ✅ 问题分析文档

- **`Docs/PRD/设备模拟/问题处理/问题分析_统计卡片显示为0.md`**
- 4 个根因分析：
  1. 后端无专用统计 API（`list_simulators()` 返回分页数据，无法聚合统计）
  2. 四个页面的 `onDeviceChanged()` 均为空函数，不响应设备变更
  3. `DoorSim.vue` 的 `total`/`runningCount` ref 初始化为 0 且从未更新
  4. `PaperlessSim.vue`、`CentralSim.vue`、`ClientSim.vue` 三个页面完全缺失统计卡片 UI

#### ✅ 执行计划文档

- **`Docs/PRD/设备模拟/执行计划/执行计划_统计卡片修复.md`**
- 4 阶段修复方案：
  1. 后端新增 `GET /api/device-sim/devices/stats` 统计 API
  2. 前端新增 `getDeviceStats()` API 封装和类型定义
  3. 统一 4 个页面的统计卡片组件，改为响应式更新
  4. 验证部署

#### ✅ 文档体系重组

- 所有设备模拟文档从 `Docs/PRD/需求文档/设备模拟/` 迁移到 `Docs/PRD/设备模拟/`
- 三级子目录：`需求文档/`、`执行计划/`、`问题处理/`
- 共 11 个文档已归类存放

#### ✅ Phase 1：后端统计 API（已完成）

- **`backend/app/schemas/device_sim.py`** — 新增 `DeviceStatsResponse`：
  - `total` / `running` / `stopped` 三字段 + `by_type: Dict[str, dict]`（按设备类型分组统计）
- **`backend/app/services/device_sim_service.py`** — 新增 `get_device_stats()` 方法：
  - 仅查询 `id, device_type, status` 三列（不加载完整 ORM 对象，性能优化）
  - 从 `_running_simulators` 内存字典读取实时运行状态（与 `list_simulators()` 同模式）
  - 单次遍历聚合 total/running/stopped + `by_type` 分组
- **`backend/app/routers/device_sim.py`** — 新增 `GET /devices/stats` 端点：
  - 参数：`device_type`（枚举 door/paperless/central/client）+ `env_config_id`
  - ⚠️ **路由顺序**：`/devices/stats`（第 233 行）必须在 `/devices/{device_id}`（第 472 行）之前注册，否则 `stats` 会被捕获为 device_id

#### ✅ Phase 2：前端 API/类型（已完成）

- **`frontend/src/types/device.ts`** — 新增 `DeviceStats` 接口（total/running/stopped/byType）
- **`frontend/src/api/deviceSim.ts`** — 新增 `getDeviceStats()` API 调用

#### ✅ Phase 3：四页面统一统计卡片（已完成）

- **`DoorSim.vue`** — `loadStats()` 接入 `getDeviceStats({ deviceType: 'door' })`，`onDeviceChanged()` / `onEnvChange()` 响应式刷新
- **`PaperlessSim.vue`** — **完整重写**：原 53 行无统计卡片，现补齐统计卡片 UI + 响应式刷新
- **`CentralSim.vue`** — **完整重写**：同上
- **`ClientSim.vue`** — **完整重写**：同上
- 四页面统一模式：统计卡片 + 环境选择器 + `loadStats()` + `onDeviceChanged()` + `onEnvChange()` + `onMounted`

#### ✅ Phase 4：构建 + 部署验证（已完成）

- 前端 `npm run build` 构建成功（34.80s，76 个文件）
- 部署脚本：**`deploy_to_560_stats.py`**（paramiko SFTP 上传 3 个后端文件 + dist，docker compose restart app）
- 已部署到 192.168.5.60，验证结果：
  - 容器状态 `Up (healthy)`，健康检查 `{"status":"healthy"}`
  - stats API 路由注册成功：`/api/device-sim/devices/stats`
  - **API 实际返回**：`{"total":1080,"running":0,"stopped":1080,"byType":{"door":{"total":540,...},"paperless":{"total":540,...}}}`
  - 数据库 1080 台设备（540 door + 540 paperless）统计正确，卡片不再显示 0

**修改文件清单**：

| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `backend/app/schemas/device_sim.py` | 修改 | 新增 `DeviceStatsResponse` |
| `backend/app/services/device_sim_service.py` | 修改 | 新增 `get_device_stats()` |
| `backend/app/routers/device_sim.py` | 修改 | 新增 `GET /devices/stats` 端点（路由顺序在 `/devices/{device_id}` 之前） |
| `frontend/src/types/device.ts` | 修改 | 新增 `DeviceStats` 类型 |
| `frontend/src/api/deviceSim.ts` | 修改 | 新增 `getDeviceStats()` |
| `frontend/src/views/device-sim/DoorSim.vue` | 修改 | 统计卡片接入真实数据 + 响应式刷新 |
| `frontend/src/views/device-sim/PaperlessSim.vue` | 重写 | 补齐统计卡片 UI + 响应式刷新 |
| `frontend/src/views/device-sim/CentralSim.vue` | 重写 | 补齐统计卡片 UI + 响应式刷新 |
| `frontend/src/views/device-sim/ClientSim.vue` | 重写 | 补齐统计卡片 UI + 响应式刷新 |
| `deploy_to_560_stats.py` | 新增 | 统计卡片修复部署脚本 |
| `Docs/PRD/设备模拟/问题处理/问题分析_统计卡片显示为0.md` | 新增 | 统计卡片问题分析 |
| `Docs/PRD/设备模拟/执行计划/执行计划_统计卡片修复.md` | 新增 | 4 阶段修复计划 |
| `Docs/PRD/需求文档/设备模拟/` (原目录) | 移动 | 全部移到 `Docs/PRD/设备模拟/` 子目录 |

**待办**：本次改动（9 个后端/前端文件 + 部署脚本）尚未提交 git，下次会话可通过 `/GitCommit` 提交。

---

### 2026-08-13 会话 A：全部启动/停止改为操作全量设备

**会话目标**：将【全部启动】/【全部停止】按钮从操作当前分页改为操作该模块下所有设备

**已完成内容**：

#### ✅ 后端 Service 新增全量查询方法

- **`start_all_by_device_type(device_type, env_config_id)`** — 按设备类型查询数据库全量设备 ID，调用 `batch_start_simulators`
- **`stop_all_by_device_type(device_type, env_config_id)`** — 同上，调用 `batch_stop_simulators`
- 支持可选 `env_config_id` 筛选（配合前端筛选栏的环境过滤）

#### ✅ 后端 Schema 新增

- **`BatchAllOperationRequest`** — 含 `device_type`（必填，枚举 door/paperless/central/client）和 `env_config_id`（可选）

#### ✅ 后端 API 路由新增

- `POST /api/device-sim/devices/batch/start-all` — 按设备类型全部启动
- `POST /api/device-sim/devices/batch/stop-all` — 按设备类型全部停止

#### ✅ 前端 API 封装

- `startAllDevices(deviceType, envConfigId?)` / `stopAllDevices(deviceType, envConfigId?)`

#### ✅ 前端 DeviceList.vue 改造

- `handleStartAll()` / `handleStopAll()` 改为调用新 API，不再取当前分页的 `devices` 列表
- 同步传递 `filterEnvConfigId`，配合环境筛选栏实现按环境过滤
- 四个设备页面（门口屏/无纸化/中控/集控客户端）共用该组件，自动统一生效

**修改文件清单**：

| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `backend/app/services/device_sim_service.py` | 修改 | 新增 `start_all_by_device_type()` / `stop_all_by_device_type()` |
| `backend/app/schemas/device_sim.py` | 修改 | 新增 `BatchAllOperationRequest` Schema |
| `backend/app/routers/device_sim.py` | 修改 | 新增 `POST /devices/batch/start-all` 和 `stop-all` 端点 |
| `frontend/src/api/deviceSim.ts` | 修改 | 新增 `startAllDevices()` / `stopAllDevices()` API |
| `frontend/src/components/device/DeviceList.vue` | 修改 | 全部启动/停止改为调用全量 API |

**验证情况**：后端 Python 语法检查通过，前端 TypeScript 检查通过，`npm run build` 构建成功（31.20s）。

---

### 2026-08-06 会话 B：中控设备多类型主题上报 — 代码实现

**会话目标**：按执行计划实现中控设备 7 种主题类型上报的 4 个阶段

**已完成内容**：

#### ✅ 阶段一：后端主题模板 + 模拟器消息体

1. **`topic_templates.py`** — 中控主题模板从 2 种扩展为 7 种
   - 新增 `template_subscribe` + `template_publish` 双模板结构（替代旧的 `template` + `direction`）
   - 每种模板带 `device_type` 标识，用于按类型筛选
   - 新增 `resolve_topics_by_device_type()` 函数，根据 `topic_params.device_type` 筛选对应主题
   - `resolve_all_topics()` 兼容新旧两种模板结构

2. **`central_simulator.py`** — 新增 7 种消息体构建方法
   - `_build_room_online_payload()` — 会议室在线（action: online）
   - `_build_device_online_payload()` — 设备在线（action: _updatestatus）
   - `_build_audio_payload()` — 音频系统（action: _updateaudio）
   - `_build_video_payload()` — 视频系统（action: _updatevideo）
   - `_build_control_payload()` — 控制系统（action: _updatecontrol）
   - `_build_network_payload()` — 网络系统（action: _updatenetwork）
   - `_build_power_payload()` — 电源系统（action: _updatepower）
   - `build_status_payload()` 根据 `topic_params.device_type` 动态选择方法

3. **`base_simulator.py`** — `_resolve_topics()` 支持按 `device_type` 筛选中控主题
   - 中控设备且 `topic_params` 含 `device_type` 时调用 `resolve_topics_by_device_type()`
   - 其他设备类型保持原有逻辑

#### ✅ 阶段二：后端 Excel 导入 + API 适配

1. **`device_sim_service.py`** — Excel 导入支持主题类型
   - 列映射新增 "主题类型" → `central_device_type`（兼容旧版 "设备类型"）
   - 中控设备自动存入 `topic_params.device_number`（= device_id）
   - 中控设备自动存入 `topic_params.device_type`（默认 `device_online`）
   - 新增 `switch_device_type()` 方法：更新 topic_params + 运行中自动重启

2. **`device_sim.py`** — Excel 模板 + 切换 API
   - 中控模板新增"会议室编号"和"主题类型"两列
   - 新增 `PATCH /api/device-sim/devices/{device_id}/type` API
   - API 参数 `device_type_name` 支持 7 种类型标识

3. **`device_sim.py` (schemas)** — `TopicTemplateResponse` 新增 `device_type` 字段

#### ✅ 阶段三：前端设备类型显示 + 切换 UI

1. **`device.ts`** — 新增 `CENTRAL_DEVICE_TYPES` 映射 + `TopicTemplate.deviceType` 字段
2. **`deviceSim.ts`** — 新增 `switchDeviceType()` API 调用
3. **`DeviceList.vue`** — 
   - 中控设备列表新增"主题类型"列（带 el-tag 标签）
   - 操作列新增"切换类型"下拉框（7 种选项，当前类型禁用+勾选）
   - 切换确认弹窗提示运行中设备将自动重启
   - 导入弹窗中控提示更新（会议室编号 + 主题类型说明）
   - **表格各列增加 tooltip 说明**：
     - 设备名称：设备的显示名称，可自定义
     - 设备 ID：设备的唯一标识符，用于 MQTT 消息关联
     - 主题类型：中控设备上报的 MQTT 主题类型，支持 7 种切换
     - 状态：设备的运行状态：运行中/已停止/异常
     - 上报次数：设备累计发送 MQTT 消息的次数
     - 最后上报：设备最后一次发送 MQTT 消息的时间

#### ✅ 阶段四：联调测试 + 部署验证

- Python 语法检查通过
- 前端 `npm run build` 构建成功
- 已部署到 192.168.5.60（SFTP 上传 + docker restart）

#### ✅ 文案优化

- 用户可见的"设备类型"统一改为"主题类型"
- Excel 表头、前端 UI、API 错误提示均已更新
- 后端列映射兼容旧版"设备类型"列名

**中控 7 种主题类型**：

| 类型 | action | 发布主题 | client_udid 来源 |
|------|--------|---------|------------------|
| 会议室在线 | `online` | `/maintain/room/online/{会议室编号}/` | -（udid 字段） |
| 设备在线 | `_updatestatus` | `/maintain/room/master/client/` | 会议室编号 |
| 音频系统 | `_updateaudio` | `/maintain/room/master/client/` | 设备编号 |
| 视频系统 | `_updatevideo` | `/maintain/room/master/client/` | 设备编号 |
| 控制系统 | `_updatecontrol` | `/maintain/room/master/client/` | 设备编号 |
| 网络系统 | `_updatenetwork` | `/maintain/room/master/client/` | 设备编号 |
| 电源系统 | `_updatepower` | `/maintain/room/master/client/` | 设备编号 |

**本次会话修改文件清单**：

| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `backend/app/simulators/topic_templates.py` | 修改 | 中控模板扩展为 7 种 + 新增 resolve_topics_by_device_type() |
| `backend/app/simulators/central_simulator.py` | 修改 | 新增 7 种消息体构建方法 + 动态选择逻辑 |
| `backend/app/simulators/base_simulator.py` | 修改 | _resolve_topics() 支持按 device_type 筛选 |
| `backend/app/routers/device_sim.py` | 修改 | Excel 模板新增主题类型列 + PATCH 切换类型 API |
| `backend/app/services/device_sim_service.py` | 修改 | Excel 导入支持主题类型 + switch_device_type() |
| `backend/app/schemas/device_sim.py` | 修改 | TopicTemplateResponse 新增 device_type 字段 |
| `frontend/src/types/device.ts` | 修改 | 新增 CENTRAL_DEVICE_TYPES 映射 |
| `frontend/src/api/deviceSim.ts` | 修改 | 新增 switchDeviceType() API |
| `frontend/src/components/device/DeviceList.vue` | 修改 | 主题类型列 + 切换下拉框 + tooltip + 导入提示 |

---

### 2026-08-06 会话 A：中控设备多类型主题上报 — PRD + 计划

**会话目标**：输出 PRD 需求文档和执行计划文档

**已完成内容**：
1. ✅ PRD 需求文档 — `Docs/PRD/设备模拟/需求文档/_PRD_需求优化_中控设备多类型主题上报.md`
2. ✅ 执行计划文档 — `Docs/PRD/设备模拟/执行计划/_执行计划_中控设备多类型主题上报.md`
3. ✅ 初步代码实现（已部署到 192.168.5.60 但需按新计划重构）

---

### 2026-08-05 会话：Excel 批量导入 + 设备列表增强

## 一、本次会话完成的工作

### 1. ✅ Excel 批量导入设备功能

**需求**: 通过 Excel 文件批量导入模拟设备，快速创建多台设备。

**实现**:
- **后端新增端点**:
  - `GET /api/device-sim/devices/import/template` — 下载导入模板
  - `POST /api/device-sim/devices/import` — 批量导入设备
- **Excel 表头格式**:
  - 环境配置名称（必填）— 后台按名称查找环境配置 ID
  - 设备名称（必填）
  - 设备ID（必填）— 同一环境下唯一
  - 授权码/app_token（可选，仅门口屏）— 填入 topic_params
  - 自动重连（可选）— 支持是/否、true/false
  - 启用定时上报（可选）
  - 上报间隔(秒)（可选）
- **前端**:
  - 新增"批量导入"按钮和导入弹窗
  - 支持拖拽上传 .xlsx 文件
  - 模板下载链接
  - 导入结果表格显示每行成功/失败及错误原因

**修改文件**:
- `backend/requirements.txt` — 添加 `openpyxl==3.1.2`
- `backend/app/schemas/device_sim.py` — 新增 `DeviceImportRowResult`、`DeviceImportResponse`
- `backend/app/services/device_sim_service.py` — 新增 `import_simulators_from_excel()` 方法
- `backend/app/routers/device_sim.py` — 新增导入和模板下载端点
- `frontend/src/types/device.ts` — 新增导入相关类型
- `frontend/src/api/deviceSim.ts` — 新增 `importDevices()`、`downloadImportTemplate()`
- `frontend/src/components/device/DeviceList.vue` — 新增导入按钮、弹窗、逻辑

### 2. ✅ 移除实时消息流组件

**需求**: 暂时移除实时消息流面板，简化界面。

**实现**:
- 四个设备模拟页面（门口屏/无纸化/中控/集控客户端）移除右侧消息流面板
- 只保留设备列表，全宽显示
- `MessageStream.vue` 组件保留但不再使用

**修改文件**:
- `frontend/src/views/device-sim/DoorSim.vue`
- `frontend/src/views/device-sim/PaperlessSim.vue`
- `frontend/src/views/device-sim/CentralSim.vue`
- `frontend/src/views/device-sim/ClientSim.vue`

### 3. ✅ 分页器增强

**需求**: 分页器支持选择每页条数。

**实现**:
- 新增每页条数选择：10/20/50/100 条/页
- 布局调整：总数 + 每页条数 + 分页 + 跳转
- 使用 `v-model:page-size` 替代固定的 `limit`

**修改文件**:
- `frontend/src/components/device/DeviceList.vue`

### 4. ✅ 全部启动/全部停止按钮

**需求**: 快捷操作当前页所有设备。

**实现**:
- 未勾选设备时显示"全部启动"、"全部停止"按钮
- 勾选设备后切换为"批量启动"、"批量停止"、"批量删除"
- 操作当前页显示的所有设备

**修改文件**:
- `frontend/src/components/device/DeviceList.vue`

### 5. ✅ 模板下载中文文件名修复

**问题**: 下载模板时，中文文件名导致 HTTP 头编码错误。

**修复**: 使用 `urllib.parse.quote()` 对文件名进行 URL 编码。

**修改文件**:
- `backend/app/routers/device_sim.py`

### 6. ✅ 部署到 192.168.5.60

**方式**: 使用 paramiko SSH + SFTP 上传前端 dist 和后端代码，容器内安装 openpyxl 依赖后重启。

---

## 二、已知问题

### P0 — 消息流页面显示"暂无消息"（已移除消息流 UI，问题暂缓）

**说明**: 实时消息流组件已移除，此问题暂不处理。后续如需恢复消息流功能，再排查消息队列入队问题。

---

## 三、后续待办

| 优先级 | 任务 | 说明 |
|--------|------|------|
| P1 | 中控 7 种主题类型联调测试 | 下载中控模板 → 填写 7 种类型 → 导入验证 → 运行中切换类型 → 消息体格式验证 |
| P1 | 中控/集控客户端主题确认 | 确认真实上报/订阅主题与实际系统一致 |
| P2 | 环境配置统计功能 | 统计各环境下的设备数量 |
| P2 | 旧版中控设备兼容性测试 | 已创建的中控设备（无 device_type）默认为"设备在线"，需验证 |
| P2 | 执行计划文档状态更新 | 更新 `_执行计划_中控设备多类型主题上报.md` 中 4 个阶段的执行记录 |

---

## 四、相关文档索引

| 文档 | 路径 |
|------|------|
| 设备模拟 PRD | `Docs/PRD/设备模拟/需求文档/_PRD_需求文档_设备模拟模块.md` |
| 中控多类型 PRD | `Docs/PRD/设备模拟/需求文档/_PRD_需求优化_中控设备多类型主题上报.md` |
| 中控多类型执行计划 | `Docs/PRD/设备模拟/执行计划/_执行计划_中控设备多类型主题上报.md` |
| 统计卡片问题分析 | `Docs/PRD/设备模拟/问题处理/问题分析_统计卡片显示为0.md` |
| 统计卡片修复计划 | `Docs/PRD/设备模拟/执行计划/执行计划_统计卡片修复.md` |
| 统计卡片部署脚本 | `deploy_to_560_stats.py` |
| 环境筛选问题分析 | `Docs/PRD/设备模拟/问题处理/问题分析_筛选环境统计卡片不跟随.md` |
| 环境筛选修复计划 | `Docs/PRD/设备模拟/执行计划/执行计划_环境筛选统一联动修复.md` |
| 前端部署脚本 | `deploy_frontend_only.py` |

---

## 五、部署信息

| 项目 | 值 |
|------|-----|
| 服务器 IP | `192.168.5.60` |
| SSH 用户/密码 | `ubains / Ubains@123` |
| 部署目录 | `/data/third_party/plat-auto-test/` |
| Docker 容器名 | `plat-auto-test-app` |
| MySQL 容器名 | `plat-auto-test-mysql` |
| 前端地址 | http://192.168.5.60 |

**部署方式**: 
- 后端：SFTP 上传 `backend/app/` 下修改的文件到服务器 `/data/third_party/plat-auto-test/backend/app/`
- 前端：本地 `npm run build` 后，SFTP 上传 `frontend/dist/` 到服务器 `/data/third_party/plat-auto-test/frontend/dist/`
- 重启容器：`docker restart plat-auto-test-app`

---

## 六、Excel 导入模板格式

### 门口屏模板

| 列 | 表头 | 必填 | 说明 |
|----|------|------|------|
| A | 环境配置名称 | 是 | 需与已创建的环境配置名称完全一致 |
| B | 设备名称 | 是 | |
| C | 设备编号 | 是 | 同一环境下唯一 |
| D | 授权码(app_token) | 否 | 仅门口屏需要，填入 topic_params |
| E | 自动重连 | 否 | 默认 true，支持 是/否、true/false |
| F | 启用定时上报 | 否 | 默认 true |
| G | 上报间隔(秒) | 否 | 默认 30，整数 >= 1 |

### 中控模板

| 列 | 表头 | 必填 | 说明 |
|----|------|------|------|
| A | 环境配置名称 | 是 | 需与已创建的环境配置名称完全一致 |
| B | 设备名称 | 是 | |
| C | 设备编号 | 是 | 同一环境下唯一，同时作为消息体 client_udid |
| D | 会议室编号 | 是 | 用于 MQTT 主题订阅和消息上报 |
| E | 主题类型 | 是 | 会议室在线/设备在线/音频系统/视频系统/控制系统/网络系统/电源系统 |
| F | 自动重连 | 否 | 默认 true |
| G | 启用定时上报 | 否 | 默认 true |
| H | 上报间隔(秒) | 否 | 默认 30 |

---

## 七、API 新增端点

| 方法 | 路径 | 说明 |
|------|------|------|
| `PATCH` | `/api/device-sim/devices/{device_id}/type` | 切换中控设备主题类型，参数 `device_type_name` |

---

*本文档记录设备模拟模块开发状态，供下次会话快速恢复上下文。*
