# 执行计划 — 设备模拟模块

> **文档类型**: 执行计划文档
> **创建日期**: 2026-07-29
> **关联 PRD**: `_PRD_需求文档_设备模拟模块.md` v1.0
> **负责人**: czj
> **优先级**: P0
> **状态**: 待评审

---

## 一、执行概述

### 1.1 需求概要

新增"设备模拟"父级模块，通过 MQTT 协议模拟设备行为，实现模拟设备绑定平台。核心变更：

1. **侧边栏新增菜单**：新增"设备模拟"父级菜单，包含 5 个子菜单（环境配置、门口屏模拟、无纸化模拟、中控设备模拟、集控客户端模拟）
2. **MQTT 连接管理**：实现 MQTT Broker 连接池管理，支持多环境配置
3. **设备模拟引擎**：实现 5 种设备类型的模拟逻辑（设备注册、状态上报、心跳、指令响应）
4. **数据持久化**：环境配置、模拟设备、上报记录的数据表及 CRUD API
5. **前端页面**：5 个管理页面 + 设备列表组件 + 上报记录查看

### 1.2 技术变更范围

| 层级 | 影响文件 | 变更类型 |
|------|---------|---------|
| 后端依赖 | `backend/requirements.txt` | 新增：`paho-mqtt` |
| 后端模型 | `backend/app/models/device_sim.py` | 新增：3 个 ORM 模型 |
| 后端 Schema | `backend/app/schemas/device_sim.py` | 新增：Pydantic 校验 |
| 后端路由 | `backend/app/routers/device_sim.py` | 新增：CRUD + 控制 API |
| 后端服务 | `backend/app/services/device_sim_service.py` | 新增：业务逻辑层 |
| 后端服务 | `backend/app/services/mqtt_manager.py` | 新增：MQTT 连接管理器 |
| 后端模拟器 | `backend/app/simulators/base_simulator.py` | 新增：模拟设备基类 |
| 后端模拟器 | `backend/app/simulators/door_simulator.py` | 新增：门口屏模拟器 |
| 后端模拟器 | `backend/app/simulators/paperless_simulator.py` | 新增：无纸化模拟器 |
| 后端模拟器 | `backend/app/simulators/central_simulator.py` | 新增：中控模拟器 |
| 后端模拟器 | `backend/app/simulators/client_simulator.py` | 新增：集控客户端模拟器 |
| 后端入口 | `backend/app/main.py` | 修改：注册路由 |
| 后端配置 | `backend/app/config.py` | 修改：新增设备模拟配置项 |
| 后端数据库 | `backend/app/database.py` | 修改：表初始化 |
| 前端类型 | `frontend/src/types/device.ts` | 新增：设备模拟类型定义 |
| 前端 API | `frontend/src/api/deviceSim.ts` | 新增：API 调用封装 |
| 前端路由 | `frontend/src/router/index.ts` | 修改：新增 5 个路由 |
| 前端布局 | `frontend/src/App.vue` | 修改：侧边栏新增菜单 |
| 前端页面 | `frontend/src/views/device-sim/EnvConfig.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` | 新增：集控客户端模拟页 |
| 前端组件 | `frontend/src/components/device/DeviceList.vue` | 新增：模拟设备列表组件 |
| 前端组件 | `frontend/src/components/device/ReportLog.vue` | 新增：上报记录组件 |

### 1.3 预计工期

| 阶段 | 内容 | 预计工时 |
|------|------|---------|
| 阶段一 | 后端数据模型 + Schema + 基础 CRUD API | 2 天 |
| 阶段二 | MQTT 管理器 + 模拟设备基类 | 2 天 |
| 阶段三 | 5 种设备类型模拟逻辑 | 3 天 |
| 阶段四 | 前端页面（路由 + 侧边栏 + 5 个子页面） | 3 天 |
| 阶段五 | 上报记录 + 实时状态 + 异常场景 | 1.5 天 |
| 阶段六 | 集成测试 + 部署 | 1 天 |
| **总计** | | **12.5 天** |

---

## 二、任务分解

### 2.1 阶段一：后端数据模型与基础 API（2 天）

#### 任务清单

| 任务ID | 任务名称 | 文件 | 说明 | 优先级 |
|--------|---------|------|------|--------|
| 1.1 | 新增 `paho-mqtt` 依赖 | `backend/requirements.txt` | 新增 `paho-mqtt==2.1.0` | P0 |
| 1.2 | 创建设备模拟 ORM 模型 | `backend/app/models/device_sim.py` | 3 个模型：EnvConfig / DeviceSimulator / ReportLog | P0 |
| 1.3 | 注册模型到 database.py | `backend/app/database.py` | 导入模型确保表自动创建 | P0 |
| 1.4 | 创建设备模拟 Pydantic Schema | `backend/app/schemas/device_sim.py` | 请求/响应 Schema + 配置校验 | P0 |
| 1.5 | 新增 config.py 配置项 | `backend/app/config.py` | 新增设备模拟相关配置（默认上报间隔等） | P0 |
| 1.6 | 实现环境配置 CRUD 服务 | `backend/app/services/device_sim_service.py` | 环境配置的增删改查 + 连接测试 | P0 |
| 1.7 | 实现模拟设备 CRUD 服务 | `backend/app/services/device_sim_service.py` | 设备创建/启停/删除 | P0 |
| 1.8 | 实现上报记录服务 | `backend/app/services/device_sim_service.py` | 上报记录查询 + 清理 | P1 |
| 1.9 | 实现设备模拟控制路由 | `backend/app/routers/device_sim.py` | 所有 CRUD + 控制 API 路由 | P0 |
| 1.10 | 注册路由到 main.py | `backend/app/main.py` | 添加 `app.include_router(device_sim_router)` | P0 |

#### API 接口清单

**环境配置 API**

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/device-sim/env-configs` | 获取环境配置列表 |
| POST | `/api/device-sim/env-configs` | 新增环境配置 |
| GET | `/api/device-sim/env-configs/{id}` | 获取环境配置详情 |
| PUT | `/api/device-sim/env-configs/{id}` | 更新环境配置 |
| DELETE | `/api/device-sim/env-configs/{id}` | 删除环境配置 |
| POST | `/api/device-sim/env-configs/{id}/test` | 测试 MQTT 连接 |

**模拟设备 API**

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/device-sim/devices` | 获取模拟设备列表 |
| POST | `/api/device-sim/devices` | 创建模拟设备 |
| GET | `/api/device-sim/devices/{id}` | 获取设备详情 |
| PUT | `/api/device-sim/devices/{id}` | 更新设备配置 |
| DELETE | `/api/device-sim/devices/{id}` | 删除模拟设备 |
| POST | `/api/device-sim/devices/{id}/start` | 启动模拟设备 |
| POST | `/api/device-sim/devices/{id}/stop` | 停止模拟设备 |
| POST | `/api/device-sim/devices/{id}/report` | 手动触发上报 |

**上报记录 API**

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/device-sim/report-logs` | 获取上报记录列表（支持设备筛选、时间范围、分页） |
| DELETE | `/api/device-sim/report-logs/cleanup` | 清理过期记录（保留 7 天） |

---

### 2.2 阶段二：MQTT 管理器与模拟设备基类（2 天）

#### 任务清单

| 任务ID | 任务名称 | 文件 | 说明 | 优先级 |
|--------|---------|------|------|--------|
| 2.1 | 实现 MQTT 连接管理器 | `backend/app/services/mqtt_manager.py` | 连接池管理、断线重连（指数退避） | P0 |
| 2.2 | 实现模拟设备基类 | `backend/app/simulators/base_simulator.py` | 抽象基类，定义通用接口 | P0 |
| 2.3 | 实现设备注册与心跳逻辑 | `backend/app/simulators/base_simulator.py` | 注册流程 + 定时心跳上报 | P0 |
| 2.4 | 实现指令回调处理 | `backend/app/simulators/base_simulator.py` | 订阅平台下发主题，回调分发 | P0 |
| 2.5 | 编写 MQTT 管理器单元测试 | `backend/tests/test_mqtt_manager.py` | 连接/断开/重连测试 | P1 |

#### MQTT 管理器核心设计

```python
class MqttConnection:
    """单个 MQTT 连接"""
    client: mqtt.Client
    env_config_id: str
    connected: bool
    reconnect_attempts: int
    last_connected_at: datetime

class MqttManager:
    """MQTT 连接管理器（单例）"""
    
    _connections: dict[str, MqttConnection]
    _simulators: dict[str, BaseSimulator]
    
    def connect(self, env_config_id: str) -> bool
    def disconnect(self, env_config_id: str)
    def publish(self, env_config_id: str, topic: str, payload: dict) -> bool
    def subscribe(self, env_config_id: str, topic: str, callback) -> bool
    def start_simulator(self, device_id: str, env_config_id: str, sim_type: str)
    def stop_simulator(self, device_id: str)
    def get_connection_status(self, env_config_id: str) -> str
```

#### 模拟设备基类设计

```python
class BaseSimulator(ABC):
    """模拟设备基类"""
    
    def __init__(self, device_id: str, env_config_id: str, mqtt: MqttManager):
        self.device_id = device_id
        self.env_config_id = env_config_id
        self.mqtt = mqtt
        self._running = False
        self._thread: Optional[threading.Thread] = None
        self._report_interval = 30  # 默认 30 秒上报一次
    
    @abstractmethod
    def get_topic_prefix(self) -> str: ...
    @abstractmethod
    def build_register_payload(self) -> dict: ...
    @abstractmethod
    def build_status_payload(self) -> dict: ...
    
    def start(self):
        """启动模拟设备：注册 + 订阅指令 + 开启定时上报"""
        # 1. 发送注册消息
        self.mqtt.publish(self.env_config_id, 
            f"{self.get_topic_prefix()}/{self.device_id}/register",
            self.build_register_payload())
        # 2. 订阅平台下发指令主题
        self.mqtt.subscribe(self.env_config_id,
            f"{self.get_topic_prefix()}/{self.device_id}/command",
            self._on_command)
        # 3. 开启定时上报线程
        self._running = True
        self._thread = threading.Thread(target=self._auto_report_loop, daemon=True)
        self._thread.start()
    
    def stop(self):
        """停止模拟设备：发送离线 + 断开"""
        if self._running:
            self._running = False
            # 发送离线状态
            self.mqtt.publish(self.env_config_id,
                f"{self.get_topic_prefix()}/{self.device_id}/status",
                {"status": "offline", "device_id": self.device_id})
    
    def report(self, topic: str, payload: dict) -> bool:
        """手动触发上报"""
        return self.mqtt.publish(self.env_config_id, topic, payload)
    
    def _auto_report_loop(self):
        """定时上报循环"""
        while self._running:
            payload = self.build_status_payload()
            self.mqtt.publish(self.env_config_id,
                f"{self.get_topic_prefix()}/{self.device_id}/status",
                payload)
            time.sleep(self._report_interval)
    
    @abstractmethod
    def _on_command(self, topic: str, payload: dict):
        """处理平台下发的指令"""
        pass
```

---

### 2.3 阶段三：5 种设备类型模拟逻辑（3 天）

#### 任务清单

| 任务ID | 任务名称 | 文件 | 说明 | 优先级 |
|--------|---------|------|------|--------|
| 3.1 | 门口屏模拟器 | `backend/app/simulators/door_simulator.py` | 继承 BaseSimulator，实现门禁事件 | P0 |
| 3.2 | 无纸化模拟器 | `backend/app/simulators/paperless_simulator.py` | 继承 BaseSimulator，实现签到/文件同步 | P0 |
| 3.3 | 中控设备模拟器 | `backend/app/simulators/central_simulator.py` | 继承 BaseSimulator，实现子设备控制 | P0 |
| 3.4 | 集控客户端模拟器 | `backend/app/simulators/client_simulator.py` | 继承 BaseSimulator，实现心跳/远程控制 | P0 |
| 3.5 | 模拟器工厂 | `backend/app/simulators/__init__.py` | 设备类型到模拟器类的映射 | P0 |

#### 各模拟器差异点

| 模拟器 | 独特上报类型 | 特殊处理 |
|--------|-------------|---------|
| 门口屏 DoorSimulator | 门禁事件(event)、呼叫记录(call) | 随机模拟刷卡开门/密码开门/异常开门 |
| 无纸化 PaperlessSimulator | 签到(signin)、文件同步(file_sync)、投屏(screen) | 模拟会议阶段变化 |
| 中控 CentralSimulator | 子设备状态(sub_status)、场景(scene) | 管理子设备列表，模拟联动控制 |
| 集控客户端 ClientSimulator | 心跳(heartbeat)、日志(log)、版本(version) | 高频率心跳（5秒间隔），模拟日志上报 |

---

### 2.4 阶段四：前端页面（3 天）

#### 任务清单

| 任务ID | 任务名称 | 文件 | 说明 | 优先级 |
|--------|---------|------|------|--------|
| 4.1 | 设备模拟类型定义 | `frontend/src/types/device.ts` | TypeScript 接口定义 | P0 |
| 4.2 | 设备模拟 API 封装 | `frontend/src/api/deviceSim.ts` | axios 调用封装 | P0 |
| 4.3 | 侧边栏新增"设备模拟"菜单 | `frontend/src/App.vue` | 新增父菜单 + 5 个子菜单项 | P0 |
| 4.4 | 新增 5 个路由 | `frontend/src/router/index.ts` | 设备模拟子页面路由 | P0 |
| 4.5 | 环境配置页面 | `frontend/src/views/device-sim/EnvConfig.vue` | 环境配置 CRUD + 连接测试 | P0 |
| 4.6 | 门口屏模拟页面 | `frontend/src/views/device-sim/DoorSim.vue` | 设备列表 + 操作控制 | P0 |
| 4.7 | 无纸化模拟页面 | `frontend/src/views/device-sim/PaperlessSim.vue` | 设备列表 + 操作控制 | P0 |
| 4.8 | 中控设备模拟页面 | `frontend/src/views/device-sim/CentralSim.vue` | 设备列表 + 子设备管理 | P0 |
| 4.9 | 集控客户端模拟页面 | `frontend/src/views/device-sim/ClientSim.vue` | 设备列表 + 心跳监控 | P0 |
| 4.10 | 模拟设备列表组件 | `frontend/src/components/device/DeviceList.vue` | 公共设备列表组件（启停 + 状态 + 手动上报） | P0 |
| 4.11 | 上报记录组件 | `frontend/src/components/device/ReportLog.vue` | 上报记录查看 + 筛选 | P1 |

#### 路由设计

```typescript
{
  path: '/device-sim',
  name: 'DeviceSim',
  redirect: '/device-sim/settings',
  meta: { title: '设备模拟' },
  children: [
    { path: 'settings',  name: 'EnvConfig',    component: () => import('@/views/device-sim/EnvConfig.vue'),    meta: { title: '环境配置' } },
    { path: 'door',      name: 'DoorSim',      component: () => import('@/views/device-sim/DoorSim.vue'),      meta: { title: '门口屏模拟' } },
    { path: 'paperless', name: 'PaperlessSim',  component: () => import('@/views/device-sim/PaperlessSim.vue'), meta: { title: '无纸化模拟' } },
    { path: 'central',   name: 'CentralSim',   component: () => import('@/views/device-sim/CentralSim.vue'),   meta: { title: '中控设备模拟' } },
    { path: 'client',    name: 'ClientSim',    component: () => import('@/views/device-sim/ClientSim.vue'),    meta: { title: '集控客户端模拟' } },
  ]
}
```

#### 侧边栏菜单设计

```html
<!-- 设备模拟（父菜单） -->
<el-sub-menu index="/device-sim">
  <template #title>
    <el-icon><Connection /></el-icon>
    <span>设备模拟</span>
  </template>
  <el-menu-item index="/device-sim/settings">环境配置</el-menu-item>
  <el-menu-item index="/device-sim/door">门口屏模拟</el-menu-item>
  <el-menu-item index="/device-sim/paperless">无纸化模拟</el-menu-item>
  <el-menu-item index="/device-sim/central">中控设备模拟</el-menu-item>
  <el-menu-item index="/device-sim/client">集控客户端模拟</el-menu-item>
</el-sub-menu>
```

#### 环境配置页面设计

```
┌─────────────────────────────────────────────────────────────┐
│  环境配置                                      [+ 新增配置]  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────────┐│
│  │ 配置名称: [________]  Broker: [________]  端口: [____]  ││
│  │ 用户名: [________]    密码: [________]    TLS: [☐]     ││
│  │ 主题前缀: [______________] 客户端前缀: [____________]    ││
│  │                                     [测试连接] [保存]   ││
│  └─────────────────────────────────────────────────────────┘│
│                                                             │
│  ┌──────┬──────────┬──────────┬────────┬──────┬───────────┐│
│  │ 名称 │ Broker   │ 端口     │ 状态   │ 操作  │ 最后连接  ││
│  ├──────┼──────────┼──────────┼────────┼──────┼───────────┤│
│  │ 测试 │ 10.0.0.1 │ 1883     │ ● 已连 │ [编辑]│ 2026-07-29││
│  │ 环境 │          │          │         │ [删除]│           ││
│  ├──────┼──────────┼──────────┼────────┼──────┼───────────┤│
│  │ 开发 │ 10.0.0.2 │ 1883     │ ○ 未连 │ [编辑]│ -         ││
│  │ 环境 │          │          │         │ [删除]│           ││
│  └──────┴──────────┴──────────┴────────┴──────┴───────────┘│
└─────────────────────────────────────────────────────────────┘
```

#### 模拟设备列表页面设计（以门口屏为例）

```
┌─────────────────────────────────────────────────────────────┐
│  门口屏模拟                      [+ 新增设备]               │
│  环境: [测试环境 ▼]  状态: [全部 ▼]                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌──────┬──────────┬──────────┬──────┬─────────┬──────────┐│
│  │ 设备 │ 设备 ID  │ 状态     │ 上报 │ 最后    │ 操作     ││
│  │ 名称 │          │          │ 次数 │ 上报时间 │          ││
│  ├──────┼──────────┼──────────┼──────┼─────────┼──────────┤│
│  │ 大门 │ door_001 │ 🟢 运行中│ 142  │ 10:32:15│ [停止]   ││
│  │ 门口屏│          │          │      │         │ [手动上报]││
│  ├──────┼──────────┼──────────┼──────┼─────────┼──────────┤│
│  │ 侧门 │ door_002 │ 🔴 已停止│ 0    │ -       │ [启动]   ││
│  │ 门口屏│          │          │      │         │ [删除]   ││
│  └──────┴──────────┴──────────┴──────┴─────────┴──────────┘│
│                                                             │
│  ┌─ 上报记录 ──────────────────────────────────────────────┐│
│  │ 时间: [最近1小时 ▼]  类型: [全部 ▼]                    ││
│  │ ┌─────────────────────────────────────────────────────┐ ││
│  │ │ 10:32:15  [上报] → device/door/door_001/status     │ ││
│  │ │ {"status":"online","door":"closed","battery":85}    │ ││
│  │ ├─────────────────────────────────────────────────────┤ ││
│  │ │ 10:31:00  [上报] → device/door/door_001/event      │ ││
│  │ │ {"event":"door_open","method":"card","card_id":"..."}│ ││
│  │ └─────────────────────────────────────────────────────┘ ││
│  └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
```

#### 页面标题映射

在 `App.vue` 的 `pageTitle` 计算属性中新增设备模拟的标题映射：

```typescript
const DEVICE_SIM_LABELS: Record<string, string> = {
  settings: '环境配置',
  door: '门口屏模拟',
  paperless: '无纸化模拟',
  central: '中控设备模拟',
  client: '集控客户端模拟',
}
```

---

### 2.5 阶段五：上报记录与实时状态（1.5 天）

#### 任务清单

| 任务ID | 任务名称 | 文件 | 说明 | 优先级 |
|--------|---------|------|------|--------|
| 5.1 | 上报记录自动清理 | `backend/app/services/device_sim_service.py` | 定时清理 7 天前记录 | P1 |
| 5.2 | 上报记录查询 API 优化 | `backend/app/routers/device_sim.py` | 支持时间范围、类型筛选、分页 | P1 |
| 5.3 | 设备状态实时更新 | `backend/app/services/mqtt_manager.py` | 连接状态变化时更新数据库 + 日志 | P1 |
| 5.4 | 异常场景模拟逻辑 | 各模拟器实现 | 模拟断线、上报失败、超时等 | P1 |
| 5.5 | 前端设备状态自动刷新 | `frontend/src/components/device/DeviceList.vue` | 定时轮询设备状态 | P1 |

---

### 2.6 阶段六：集成测试与部署（1 天）

#### 任务清单

| 任务ID | 任务名称 | 文件 | 说明 | 优先级 |
|--------|---------|------|------|--------|
| 6.1 | 后端单元测试 | `backend/tests/test_device_sim.py` | CRUD API + MQTT 管理测试 | P1 |
| 6.2 | 端到端测试 | 手动测试 | 创建环境 → 创建设备 → 启动 → 验证上报 → 停止 | P0 |
| 6.3 | 服务器部署 | 部署流程 | scp 更新代码 + 重启容器 | P0 |
| 6.4 | 编写交接文档 | `HANDOFF.md` 更新 | 记录设备模拟模块开发进度 | P1 |

---

## 三、技术要点

### 3.1 MQTT 连接管理

- **同步 API**：使用 `paho-mqtt` 同步客户端，与 Playwright 同步 API 风格一致
- **断线重连**：`on_disconnect` 回调中实现指数退避重连（1s → 2s → 4s → ... → 60s max）
- **连接池**：以一个环境配置为单位管理连接，同一环境下的所有模拟设备共享一个 MQTT 连接
- **线程安全**：使用 `threading.Lock` 保护连接池的并发访问

### 3.2 模拟设备生命周期

```
创建设备 (stopped) → 启动设备 (starting) → 注册成功 (running) → 停止设备 (stopped)
                         ↓                      ↓
                    注册失败 (error)     定时上报 ↻
                                           ↓
                                    收到平台指令 → 处理并响应
```

### 3.3 数据隔离

- 不同环境配置使用不同的 MQTT 连接和主题前缀，完全隔离
- 模拟设备数据存储在本地数据库，不依赖被测平台
- 上报记录保留最近 7 天，自动清理

### 3.4 密码加密

```python
from cryptography.fernet import Fernet
# 使用项目配置的密钥加密 MQTT 密码
# API 响应中密码字段返回 "********" 脱敏
```

---

## 四、依赖安装

```bash
# 后端新增依赖
pip install paho-mqtt==2.1.0

# 更新 requirements.txt
echo "paho-mqtt==2.1.0" >> backend/requirements.txt
```

---

## 五、部署方案

### 5.1 本地开发

```bash
# 后端
cd backend
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8001

# 前端
cd frontend
npm run dev
```

### 5.2 服务器部署

```bash
# 更新后端代码
scp -r backend/app ubains@192.168.5.60:/data/third_party/plat-auto-test/backend/
ssh ubains@192.168.5.60 'cd /data/third_party/plat-auto-test/deploy && docker compose restart app'

# 更新前端
cd frontend && npm run build
scp -r dist/* ubains@192.168.5.60:/data/third_party/plat-auto-test/frontend/dist/
```

---

## 六、验收检查清单

| 验收项 | 检查方式 | 预期结果 |
|--------|---------|---------|
| 环境配置 CRUD | API 测试 | 增删改查全部正常 |
| MQTT 连接测试 | API 测试 | 连接成功/失败状态正确返回 |
| 模拟设备创建 | API 测试 | 5 种设备类型均可创建 |
| 设备启动/停止 | API 测试 | 状态切换正确，MQTT 消息发送 |
| 手动上报 | API 测试 | 指定主题和内容的上报成功 |
| 侧边栏导航 | 页面检查 | 5 个子菜单显示正常，可切换 |
| 环境配置页面 | 页面检查 | 列表展示、新增、编辑、删除、测试连接 |
| 门口屏模拟页面 | 页面检查 | 设备列表、启停、上报记录 |
| 无纸化模拟页面 | 页面检查 | 同上 |
| 中控模拟页面 | 页面检查 | 同上 |
| 集控客户端模拟页面 | 页面检查 | 同上 |
| 上报记录查看 | 页面检查 | 记录展示、筛选、分页 |
| 数据持久化 | 刷新测试 | 页面刷新后数据正确恢复 |
| 密码加密存储 | 代码审查 | 数据库存储加密，API 响应脱敏 |

---

## 七、风险与应对

| 风险 | 影响 | 概率 | 应对方案 |
|------|------|------|---------|
| 被测平台 MQTT 主题格式不明确 | 模拟数据无法被平台识别 | 中 | 先实现可配置主题格式，后续根据实际接口调整 |
| 无可用 MQTT Broker 测试环境 | 无法完成端到端验证 | 中 | 内置 Mosquitto 容器或在 Docker Compose 中增加 MQTT 服务 |
| 大量模拟设备并发导致线程耗尽 | 系统资源耗尽 | 低 | 限制最大并发模拟设备数（默认 50），使用线程池 |
| 与现有 Playwright 执行冲突 | 执行中心受影响 | 低 | 模拟设备使用独立线程池，与 Playwright 执行器隔离 |

---

*本文档由 Claude Code 生成，用于指导设备模拟模块的开发实施。*