Procházet zdrojové kódy

feat(camera, machine): enhance API functionality and improve data handling

- Introduced new endpoints for camera and machine management, including pagination and list retrieval.
- Updated existing API calls to use POST methods for fetching camera and machine lists, improving data handling.
- Added support for PTZ direct control and enhanced camera management features.
- Improved type definitions for API responses to ensure better type safety and clarity in data structures.
- Enhanced mock API handlers for testing, ensuring accurate simulation of API responses.

This commit significantly improves the API's flexibility and usability, enhancing the overall user experience in managing cameras and machines.
yb před 1 týdnem
rodič
revize
ee4725998b

+ 324 - 0
docs/camera-surveillance-system-design.md

@@ -0,0 +1,324 @@
+# 日本パチンコ店多摄像头监控+多角度同步回放系统
+
+> 版本: v2.0 日期: 2026-01-16 状态: 头脑风暴完成
+
+---
+
+## 1. 用例场景 (Use Cases)
+
+| 场景         | 描述                                    | 优先级 |
+| ------------ | --------------------------------------- | ------ |
+| **纠纷回溯** | 客户投诉机台故障/奖金争议,需多角度验证 | P0     |
+| **作弊检测** | 识别职业作弊团伙行为模式                | P0     |
+| **事故取证** | 打架斗殴、盗窃等治安事件                | P0     |
+| **员工监督** | 收银操作合规性、服务质量                | P1     |
+| **运营分析** | 热门机台、客流动线分析                  | P2     |
+| **合规审计** | 定期提交给监管部门的证据存档            | P1     |
+
+---
+
+## 2. 系统模块拆分
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│                      管理层 (Management)                     │
+├─────────────┬─────────────┬─────────────┬──────────────────┤
+│  用户权限   │  设备管理   │  告警中心   │   报表统计       │
+└─────────────┴─────────────┴─────────────┴──────────────────┘
+                              ↓
+┌─────────────────────────────────────────────────────────────┐
+│                     业务层 (Business)                        │
+├─────────────┬─────────────┬─────────────┬──────────────────┤
+│ 同步回放引擎│ 事件标签系统│ 智能检索   │  导出/分享       │
+└─────────────┴─────────────┴─────────────┴──────────────────┘
+                              ↓
+┌─────────────────────────────────────────────────────────────┐
+│                     数据层 (Data)                            │
+├─────────────┬─────────────┬─────────────┬──────────────────┤
+│  时间轴索引 │ 元数据存储  │ 热存储NVR  │  冷存储归档      │
+└─────────────┴─────────────┴─────────────┴──────────────────┘
+                              ↓
+┌─────────────────────────────────────────────────────────────┐
+│                    采集层 (Ingestion)                        │
+├─────────────┬─────────────┬─────────────┬──────────────────┤
+│ RTSP采集网关│ 时钟同步NTP │ 编码转码   │  边缘AI检测      │
+└─────────────┴─────────────┴─────────────┴──────────────────┘
+```
+
+### 核心模块职责
+
+| 模块             | 职责                     | 关键技术点          |
+| ---------------- | ------------------------ | ------------------- |
+| **采集网关**     | 统一接入各品牌摄像头     | RTSP/ONVIF 协议适配 |
+| **时钟同步**     | 确保多路视频帧级对齐     | NTP + PTP 精确时钟  |
+| **同步回放引擎** | 多画面联动播放/暂停/跳转 | 基于时间戳的帧对齐  |
+| **事件标签系统** | 人工/AI 标注关键时刻     | 标签分类+搜索索引   |
+| **热存储**       | 近期高频访问视频         | SSD/NVMe + 本地 NVR |
+| **冷存储**       | 长期归档合规保留         | 对象存储 + 分层迁移 |
+
+---
+
+## 3. 摄像头布局与多角度策略
+
+### 典型パチンコ店布局 (200 台机规模)
+
+| 区域         | 摄像头类型 | 数量  | 覆盖策略                   |
+| ------------ | ---------- | ----- | -------------------------- |
+| **机台区域** | 固定枪机   | 40-50 | 每 4-5 台机 1 个,俯视 45° |
+| **机台正面** | PTZ 球机   | 8-10  | 可调焦看客户手部操作       |
+| **走道通道** | 广角枪机   | 15-20 | 覆盖人员动线               |
+| **收银台**   | 高清枪机   | 4-6   | 正面+侧面+手部特写         |
+| **出入口**   | 人脸识别机 | 4-6   | 全景+人脸抓拍              |
+| **景品柜台** | 固定枪机   | 4-6   | 交易全过程                 |
+| **后场仓库** | 广角枪机   | 4-6   | 出入+操作台                |
+
+### 多角度同步分组策略
+
+```
+┌─────────────────────────────────────────┐
+│           机台区 Zone-A (1-50号机)       │
+│  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐    │
+│  │CAM01│  │CAM02│  │CAM03│  │PTZ01│    │
+│  │俯视 │  │俯视 │  │俯视 │  │特写 │    │
+│  └──┬──┘  └──┬──┘  └──┬──┘  └──┬──┘    │
+│     └────────┴────────┴────────┘        │
+│              同步组 SG-A01              │
+└─────────────────────────────────────────┘
+```
+
+**分组原则:**
+
+- 物理相邻的摄像头归入同一同步组
+- 每组 3-6 路视频为宜
+- 同组摄像头必须时钟严格同步(误差<100ms)
+
+---
+
+## 4. 同步回放 UX 设计
+
+### 4.1 时间轴界面
+
+```
+┌───────────────────────────────────────────────────────────┐
+│  [◀◀] [◀] [▶/‖] [▶▶] [1x▼]  │ 2024-01-15 14:32:15.234  │
+├───────────────────────────────────────────────────────────┤
+│ CAM-01 ████████░░░░░░░░░████████████░░░░░░░░░░░░░░░░░░░░ │
+│ CAM-02 ████████░░░░░░░░░████████████░░░░░░░░░░░░░░░░░░░░ │
+│ CAM-03 ████████░░░░░░░░████████████░░░░░░░░░░░░░░░░░░░░░ │
+│ PTZ-01 ████████░░░░░░░░░████████████░░░░░░░░░░░░░░░░░░░░ │
+├───────────────────────────────────────────────────────────┤
+│        ▲告警  ★书签  ●动检  ■事件标签                     │
+│   [14:00]----[▲]--[★]----[●●●]--[■纠纷]----[15:00]       │
+└───────────────────────────────────────────────────────────┘
+```
+
+### 4.2 事件标签体系
+
+| 一级分类     | 二级标签                   | 触发方式    |
+| ------------ | -------------------------- | ----------- |
+| **安全事件** | 打架/盗窃/可疑人员         | 人工+AI     |
+| **纠纷事件** | 机台故障/奖金争议/服务投诉 | 人工        |
+| **作弊嫌疑** | 异常手法/团伙配合/设备干扰 | AI+人工确认 |
+| **运营事件** | VIP 到店/高额中奖/设备维护 | 系统自动    |
+| **合规存档** | 定期抽检/监管备查          | 人工标记    |
+
+### 4.3 检索功能
+
+| 检索维度     | 示例                   | 实现方式       |
+| ------------ | ---------------------- | -------------- |
+| **时间范围** | 2024-01-15 14:00~15:00 | 时间戳索引     |
+| **摄像头**   | CAM-01, CAM-02         | 设备 ID 过滤   |
+| **区域**     | 收银台区、A 区机台     | 空间分组       |
+| **事件标签** | 纠纷+收银台            | 标签组合查询   |
+| **AI 检测**  | 人员聚集>5 人          | 边缘 AI 元数据 |
+| **全文搜索** | "客户投诉 50 号机"     | 备注文本索引   |
+
+### 4.4 同步播放交互
+
+| 操作         | 行为                    | 快捷键   |
+| ------------ | ----------------------- | -------- |
+| 点击任一画面 | 该画面放大/全部联动跳转 | 单击     |
+| 时间轴拖动   | 全部画面同步跳转        | 拖拽     |
+| 标签点击     | 跳转到事件起始时刻      | 单击标签 |
+| 播放速度     | 0.25x/0.5x/1x/2x/4x     | 数字键   |
+| 逐帧         | 前进/后退单帧           | ←/→      |
+| 书签         | 标记当前时刻            | B 键     |
+
+---
+
+## 5. 存储与保留策略
+
+### 5.1 分层存储架构
+
+```
+┌────────────────────────────────────────────────────────┐
+│                    访问频率                             │
+│  高 ▲                                                  │
+│     │  ┌──────────┐                                   │
+│     │  │ 热存储   │ SSD/NVR  7天  高码率原片           │
+│     │  │ (Hot)    │ 本地      快速随机访问             │
+│     │  └────┬─────┘                                   │
+│     │       ↓ 自动迁移                                 │
+│     │  ┌──────────┐                                   │
+│     │  │ 温存储   │ HDD/NAS  30天  中码率转码          │
+│     │  │ (Warm)   │ 本地/近端  顺序读取优化            │
+│     │  └────┬─────┘                                   │
+│     │       ↓ 自动迁移                                 │
+│     │  ┌──────────┐                                   │
+│     │  │ 冷存储   │ 对象存储 90天+ 低码率归档          │
+│     │  │ (Cold)   │ 云端     按需恢复(分钟级)         │
+│  低 │  └──────────┘                                   │
+└────────────────────────────────────────────────────────┘
+```
+
+### 5.2 保留策略明细
+
+| 层级     | 保留期 | 存储介质      | 码率        | 访问延迟 | 成本/TB/月 |
+| -------- | ------ | ------------- | ----------- | -------- | ---------- |
+| **热**   | 7 天   | 本地 SSD/NVR  | 原始 8Mbps  | <1 秒    | ¥¥¥¥       |
+| **温**   | 30 天  | 本地 HDD 阵列 | 转码 2Mbps  | <10 秒   | ¥¥         |
+| **冷**   | 90 天  | 云对象存储    | 归档 1Mbps  | 1-5 分钟 | ¥          |
+| **长期** | 1 年+  | 深度归档      | 关键帧 only | 数小时   | ¢          |
+
+### 5.3 容量估算 (80 路摄像头)
+
+| 参数          | 数值                                   |
+| ------------- | -------------------------------------- |
+| 摄像头数量    | 80 路                                  |
+| 平均码率      | 4Mbps                                  |
+| 每日数据量    | 80 × 4Mbps × 86400s ÷ 8 = **3.4TB/天** |
+| 热存储(7 天)  | ~24TB                                  |
+| 温存储(30 天) | ~50TB (转码后)                         |
+| 冷存储(90 天) | ~75TB (归档后)                         |
+
+---
+
+## 6. 日本隐私/合规清单
+
+### 6.1 法律框架
+
+| 法规               | 要求                   | 对策                  |
+| ------------------ | ---------------------- | --------------------- |
+| **個人情報保護法** | 个人信息收集须告知目的 | 店内明显位置张贴告示  |
+| **風営法**         | パチンコ店特定监控要求 | 依规保留期限+监管报备 |
+| **肖像権**         | 未经同意不得公开面部   | 导出时自动马赛克选项  |
+| **労働基準法**     | 员工监控须事先告知     | 劳动合同明确条款      |
+
+### 6.2 合规检查清单
+
+- [ ] **告知公示**: 入口处设置"防犯カメラ作動中"标识
+- [ ] **目的限定**: 明确"防犯・安全管理"用途,禁止营销分析
+- [ ] **保留期限**: 普通视频 ≤90 天,涉案视频按案件期限
+- [ ] **访问控制**: 分级权限,操作全程审计日志
+- [ ] **数据加密**: 存储加密(AES-256)+ 传输加密(TLS)
+- [ ] **马赛克功能**: 导出前非相关人员面部自动模糊
+- [ ] **删除机制**: 超期自动删除,删除日志可追溯
+- [ ] **跨境限制**: 数据不出日本境内(使用 AWS 东京/GCP 东京)
+- [ ] **员工培训**: 年度隐私合规培训记录
+- [ ] **应急预案**: 数据泄露 72 小时内报告流程
+
+### 6.3 监管报备
+
+| 监管方             | 报备内容               | 频率        |
+| ------------------ | ---------------------- | ----------- |
+| 都道府県公安委員会 | 摄像头位置图、保留策略 | 新增/变更时 |
+| 個人情報保護委員会 | 数据处理报告           | 年度        |
+| 店内掲示           | 监控告知、投诉窗口     | 常设        |
+
+---
+
+## 7. MVP vs Phase-2 路线图
+
+### MVP (3 个月)
+
+| 功能           | 优先级 | 说明                |
+| -------------- | ------ | ------------------- |
+| 多路 RTSP 接入 | P0     | 支持主流 NVR/摄像头 |
+| 时钟同步       | P0     | NTP 服务+时间戳校准 |
+| 基础同步回放   | P0     | 4 画面联动播放      |
+| 时间轴导航     | P0     | 拖拽跳转+播放控制   |
+| 手动书签       | P0     | 标记关键时刻        |
+| 基础权限       | P0     | 管理员/操作员两级   |
+| 热存储 7 天    | P0     | 本地 NVR 直接读取   |
+| 视频导出       | P1     | 片段下载(带水印)    |
+
+**MVP 交付物:**
+
+- 可部署的 Web 应用
+- 支持单店 80 路摄像头
+- 完成收银台区域全流程验证
+
+### Phase-2 (6 个月)
+
+| 功能         | 优先级 | 说明                 |
+| ------------ | ------ | -------------------- |
+| 分层存储     | P0     | 热 → 温 → 冷自动迁移 |
+| 事件标签系统 | P0     | 分类标签+检索        |
+| 高级检索     | P1     | 多条件组合查询       |
+| AI 动检告警  | P1     | 异常聚集/滞留检测    |
+| 多店管理     | P1     | 集团化部署支持       |
+| 审计日志     | P1     | 操作全程追溯         |
+| 马赛克导出   | P1     | 人脸自动模糊         |
+| API 开放     | P2     | 与现有系统集成       |
+| 移动端回放   | P2     | iPad/手机查看        |
+
+### 后续迭代方向
+
+- AI 作弊行为识别模型
+- 与 POS/会员系统数据关联
+- 跨店嫌疑人员追踪
+- 3D 空间重建回放
+
+---
+
+## 8. 关键取舍 (Trade-offs)
+
+### 8.1 架构决策
+
+| 决策点       | 选项 A   | 选项 B      | 建议         | 理由                                 |
+| ------------ | -------- | ----------- | ------------ | ------------------------------------ |
+| **存储位置** | 全云端   | 本地+云混合 | **混合**     | 热数据本地保证延迟,冷数据云端降成本 |
+| **视频编码** | 保持原始 | 统一转码    | **统一转码** | H.265 统一格式便于回放兼容           |
+| **时钟同步** | 软件 NTP | 硬件 PTP    | **软件 NTP** | MVP 阶段 100ms 精度够用,硬件成本高  |
+| **AI 部署**  | 云端推理 | 边缘推理    | **边缘**     | 减少带宽,保护隐私                   |
+
+### 8.2 功能取舍
+
+| 功能         | 收益           | 成本          | MVP 纳入 | 理由                         |
+| ------------ | -------------- | ------------- | -------- | ---------------------------- |
+| 人脸识别     | 高(追踪嫌疑人) | 高(合规风险)  | **否**   | 隐私法限制严格,Phase-2 评估 |
+| 实时 AI 告警 | 高(即时响应)   | 高(算力/误报) | **否**   | 先验证回放价值,再加实时     |
+| 语音拾取     | 中(纠纷还原)   | 高(隐私合规)  | **否**   | 日本对录音限制更严           |
+| 360° 全景    | 中(沉浸体验)   | 高(特殊设备)  | **否**   | ROI 不高,传统多角度够用     |
+
+### 8.3 风险与缓解
+
+| 风险               | 影响 | 概率 | 缓解措施                    |
+| ------------------ | ---- | ---- | --------------------------- |
+| 时钟漂移导致不同步 | 高   | 中   | 定期校准+漂移检测告警       |
+| 存储成本超预算     | 中   | 中   | 智能保留策略+压缩优化       |
+| 隐私投诉/监管处罚  | 高   | 低   | 严格合规+定期审计           |
+| 摄像头兼容性问题   | 中   | 高   | 提前测试主流品牌+ONVIF 标准 |
+| 网络带宽瓶颈       | 中   | 中   | 边缘转码+分时段传输         |
+
+### 8.4 成本结构预估 (单店 80 路)
+
+| 项目           | 初期投入       | 月度运营       |
+| -------------- | -------------- | -------------- |
+| 摄像头设备     | ¥3,000,000     | -              |
+| 本地服务器/NVR | ¥1,500,000     | ¥30,000 (维护) |
+| 网络改造       | ¥500,000       | ¥50,000 (专线) |
+| 软件授权       | ¥800,000       | ¥80,000 (订阅) |
+| 云存储(冷)     | -              | ¥100,000       |
+| **合计**       | **¥5,800,000** | **¥260,000**   |
+
+---
+
+## 总结
+
+本方案为日本パチンコ店设计了完整的多摄像头监控与同步回放系统:
+
+1. **核心价值**: 纠纷快速取证、作弊行为追溯、合规存档
+2. **技术重点**: 精确时钟同步、分层存储、事件标签检索
+3. **合规优先**: 严格遵循日本隐私法规,数据不出境
+4. **渐进演进**: MVP 聚焦同步回放核心,Phase-2 扩展 AI 能力

+ 3326 - 0
docs/export-1768527071618.md

@@ -0,0 +1,3326 @@
+# 文档
+
+## 摄像头控制 Controller
+
+MVP API 接口:
+
+- GET /camera/list 获取摄像头列表
+- GET /camera/{id} 获取摄像头信息
+- POST /camera/switch 切换摄像头通道
+- GET /camera/current 获取当前通道
+
+后台 PTZ 接口(可选):
+
+- POST /camera/{id}/ptz/start 开始 PTZ 控制
+- POST /camera/{id}/ptz/stop 停止 PTZ 控制
+
+### 获取摄像头列表
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/camera/list
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/camera/list
+
+描述:获取摄像头列表
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称      | 类型   | 必填 | 最大长度 | 描述            | 示例值      |
+| --------- | ------ | ---- | -------- | --------------- | ----------- |
+| machineId | String | 否   | -        | 机器 ID(可选) | machine_001 |
+
+#### 请求示例
+
+```
+{
+    "machineId": "machine_001"
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | array | 否 |  | 响应数据 (ActualType: List) |  |
+| └ cameraId | String | 否 | - | 摄像头 ID | cam_001 |
+| └ name | String | 否 | - | 摄像头名称 | 主摄像头 |
+| └ machineId | String | 否 | - | 所属机器 ID | machine_001 |
+| └ status | String | 否 | - | 摄像头状态: ONLINE, OFFLINE | ONLINE |
+| └ capability | String | 否 | - | 摄像头能力: switch_only, ptz_enabled | ptz_enabled |
+| └ ptzSupported | Boolean | 否 | - | 是否支持 PTZ | true |
+| └ channels | List<ChannelDTO> | 否 |  | 通道列表 (ActualType: ChannelDTO) |  |
+| └ channelId | String | 否 | - | 通道 ID | ch_001 |
+| └ name | String | 否 | - | 通道名称 | 主通道 |
+| └ rtspUrl | String | 否 | - | RTSP 地址 | rtsp://192.168.1.100:554/stream1 |
+| └ defaultView | Boolean | 否 | - | 是否默认视角 | true |
+| └ status | String | 否 | - | 通道状态: ONLINE, OFFLINE | ONLINE |
+| └ cameraId | String | 否 | - | 所属摄像头 ID | cam_001 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": [
+        {
+            "cameraId": "cam_001",
+            "name": "主摄像头",
+            "machineId": "machine_001",
+            "status": "ONLINE",
+            "capability": "ptz_enabled",
+            "ptzSupported": true,
+            "channels": [
+                {
+                    "channelId": "ch_001",
+                    "name": "主通道",
+                    "rtspUrl": "rtsp://192.168.1.100:554/stream1",
+                    "defaultView": true,
+                    "status": "ONLINE",
+                    "cameraId": "cam_001"
+                }
+            ]
+        }
+    ]
+}
+```
+
+#### 错误码
+
+无
+
+### 获取摄像头信息
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/camera/{cameraId}
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/camera/{cameraId}
+
+描述:获取摄像头信息
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### Path 参数
+
+| 名称     | 必填 | 描述      | 示例值     |
+| -------- | ---- | --------- | ---------- |
+| cameraId | 是   | 摄像头 ID | camera_001 |
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: CameraDTO) |  |
+| └ cameraId | String | 否 | - | 摄像头 ID | cam_001 |
+| └ name | String | 否 | - | 摄像头名称 | 主摄像头 |
+| └ machineId | String | 否 | - | 所属机器 ID | machine_001 |
+| └ status | String | 否 | - | 摄像头状态: ONLINE, OFFLINE | ONLINE |
+| └ capability | String | 否 | - | 摄像头能力: switch_only, ptz_enabled | ptz_enabled |
+| └ ptzSupported | Boolean | 否 | - | 是否支持 PTZ | true |
+| └ channels | List<ChannelDTO> | 否 |  | 通道列表 (ActualType: ChannelDTO) |  |
+| └ channelId | String | 否 | - | 通道 ID | ch_001 |
+| └ name | String | 否 | - | 通道名称 | 主通道 |
+| └ rtspUrl | String | 否 | - | RTSP 地址 | rtsp://192.168.1.100:554/stream1 |
+| └ defaultView | Boolean | 否 | - | 是否默认视角 | true |
+| └ status | String | 否 | - | 通道状态: ONLINE, OFFLINE | ONLINE |
+| └ cameraId | String | 否 | - | 所属摄像头 ID | cam_001 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "cameraId": "cam_001",
+        "name": "主摄像头",
+        "machineId": "machine_001",
+        "status": "ONLINE",
+        "capability": "ptz_enabled",
+        "ptzSupported": true,
+        "channels": [
+            {
+                "channelId": "ch_001",
+                "name": "主通道",
+                "rtspUrl": "rtsp://192.168.1.100:554/stream1",
+                "defaultView": true,
+                "status": "ONLINE",
+                "cameraId": "cam_001"
+            }
+        ]
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 切换摄像头通道(MVP 核心)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/camera/switch
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/camera/switch
+
+描述:切换摄像头通道(MVP 核心)
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称      | 类型   | 必填 | 最大长度 | 描述        | 示例值      |
+| --------- | ------ | ---- | -------- | ----------- | ----------- |
+| machineId | String | 是   | -        | 机器 ID     | machine_001 |
+| channelId | String | 是   | -        | 目标通道 ID | ch_001      |
+
+#### 请求示例
+
+```
+{
+    "machineId": "machine_001",
+    "channelId": "ch_001"
+}
+```
+
+#### 响应参数
+
+| 名称          | 类型    | 必填 | 最大长度 | 描述                              | 示例值                           |
+| ------------- | ------- | ---- | -------- | --------------------------------- | -------------------------------- |
+| success       | Boolean | 否   | -        | 请求是否成功                      | true                             |
+| errCode       | String  | 否   | -        | 错误码(失败时返回)              |                                  |
+| errMessage    | String  | 否   | -        | 错误信息(失败时返回)            |                                  |
+| data          | object  | 否   |          | 响应数据 (ActualType: ChannelDTO) |                                  |
+| └ channelId   | String  | 否   | -        | 通道 ID                           | ch_001                           |
+| └ name        | String  | 否   | -        | 通道名称                          | 主通道                           |
+| └ rtspUrl     | String  | 否   | -        | RTSP 地址                         | rtsp://192.168.1.100:554/stream1 |
+| └ defaultView | Boolean | 否   | -        | 是否默认视角                      | true                             |
+| └ status      | String  | 否   | -        | 通道状态: ONLINE, OFFLINE         | ONLINE                           |
+| └ cameraId    | String  | 否   | -        | 所属摄像头 ID                     | cam_001                          |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "channelId": "ch_001",
+        "name": "主通道",
+        "rtspUrl": "rtsp://192.168.1.100:554/stream1",
+        "defaultView": true,
+        "status": "ONLINE",
+        "cameraId": "cam_001"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 获取当前活动通道
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/camera/current
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/camera/current
+
+描述:获取当前活动通道
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称      | 类型   | 必填 | 最大长度 | 描述    | 示例值      |
+| --------- | ------ | ---- | -------- | ------- | ----------- |
+| machineId | string | 是   | -        | 机器 ID | machine_001 |
+
+#### 响应参数
+
+| 名称          | 类型    | 必填 | 最大长度 | 描述                              | 示例值                           |
+| ------------- | ------- | ---- | -------- | --------------------------------- | -------------------------------- |
+| success       | Boolean | 否   | -        | 请求是否成功                      | true                             |
+| errCode       | String  | 否   | -        | 错误码(失败时返回)              |                                  |
+| errMessage    | String  | 否   | -        | 错误信息(失败时返回)            |                                  |
+| data          | object  | 否   |          | 响应数据 (ActualType: ChannelDTO) |                                  |
+| └ channelId   | String  | 否   | -        | 通道 ID                           | ch_001                           |
+| └ name        | String  | 否   | -        | 通道名称                          | 主通道                           |
+| └ rtspUrl     | String  | 否   | -        | RTSP 地址                         | rtsp://192.168.1.100:554/stream1 |
+| └ defaultView | Boolean | 否   | -        | 是否默认视角                      | true                             |
+| └ status      | String  | 否   | -        | 通道状态: ONLINE, OFFLINE         | ONLINE                           |
+| └ cameraId    | String  | 否   | -        | 所属摄像头 ID                     | cam_001                          |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "channelId": "ch_001",
+        "name": "主通道",
+        "rtspUrl": "rtsp://192.168.1.100:554/stream1",
+        "defaultView": true,
+        "status": "ONLINE",
+        "cameraId": "cam_001"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 开始 PTZ 控制(后台专用)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/camera/{cameraId}/ptz/start
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/camera/{cameraId}/ptz/start
+
+描述:开始 PTZ 控制(后台专用)
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### Path 参数
+
+| 名称     | 必填 | 描述      | 示例值        |
+| -------- | ---- | --------- | ------------- |
+| cameraId | 是   | 摄像头 ID | hikvision_ptz |
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称   | 类型   | 必填 | 最大长度 | 描述        | 示例值 |
+| ------ | ------ | ---- | -------- | ----------- | ------ |
+| action | string | 是   | -        | PTZ 动作    | up     |
+| speed  | int32  | 是   | -        | 速度(1-100) | 50     |
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | object  | 否   | -        | 响应数据 (ActualType: Void) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {}
+}
+```
+
+#### 错误码
+
+无
+
+### 停止 PTZ 控制(后台专用)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/camera/{cameraId}/ptz/stop
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/camera/{cameraId}/ptz/stop
+
+描述:停止 PTZ 控制(后台专用)
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### Path 参数
+
+| 名称     | 必填 | 描述      | 示例值        |
+| -------- | ---- | --------- | ------------- |
+| cameraId | 是   | 摄像头 ID | hikvision_ptz |
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | object  | 否   | -        | 响应数据 (ActualType: Void) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {}
+}
+```
+
+#### 错误码
+
+无
+
+### PTZ 直接控制(兼容前端 pan/tilt/zoom 方式)
+
+与前端海康调试接口一致:直接传 pan/tilt/zoom 值
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/camera/{cameraId}/ptz/control
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/camera/{cameraId}/ptz/control
+
+描述:PTZ 直接控制(兼容前端 pan/tilt/zoom 方式)
+
+与前端海康调试接口一致:直接传 pan/tilt/zoom 值
+
+ContentType:`application/json`
+
+#### Path 参数
+
+| 名称     | 必填 | 描述      | 示例值        |
+| -------- | ---- | --------- | ------------- |
+| cameraId | 是   | 摄像头 ID | hikvision_ptz |
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称    | 类型    | 必填 | 最大长度 | 描述                                                   | 示例值 |
+| ------- | ------- | ---- | -------- | ------------------------------------------------------ | ------ |
+| pan     | Integer | 否   | 100      | 水平移动速度<br>负值向左,正值向右<br>范围: -100 ~ 100 | 0      |
+| tilt    | Integer | 否   | 100      | 垂直移动速度<br>负值向下,正值向上<br>范围: -100 ~ 100 | 0      |
+| zoom    | Integer | 否   | 100      | 缩放速度<br>负值缩小,正值放大<br>范围: -100 ~ 100     | 0      |
+| channel | Integer | 否   | -        | 通道号(可选,默认 1)                                 | 0      |
+
+#### 请求示例
+
+```
+{
+    "pan": 0,
+    "tilt": 0,
+    "zoom": 0,
+    "channel": 0
+}
+```
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | object  | 否   | -        | 响应数据 (ActualType: Void) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {}
+}
+```
+
+#### 错误码
+
+无
+
+## 摄像头管理
+
+### 获取摄像头列表(分页)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/list
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/list
+
+描述:获取摄像头列表(分页)
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称      | 类型    | 必填 | 最大长度 | 描述                                            | 示例值      |
+| --------- | ------- | ---- | -------- | ----------------------------------------------- | ----------- |
+| page      | Integer | 否   | -        | 页码 (从 1 开始)                                | 1           |
+| size      | Integer | 否   | -        | 每页条数                                        | 10          |
+| keyword   | String  | 否   | -        | 搜索关键词 (模糊匹配名称、ID 等)                | 摄像头      |
+| enabled   | Boolean | 否   | -        | 启用状态过滤 (null=全部, true=启用, false=禁用) | true        |
+| sortBy    | String  | 否   | -        | 排序字段                                        | createdAt   |
+| sortDir   | String  | 否   | -        | 排序方向 (ASC/DESC)                             | DESC        |
+| machineId | String  | 否   | -        | 机器 ID 过滤                                    | machine_001 |
+| status    | String  | 否   | -        | 在线状态过滤 (ONLINE/OFFLINE)                   | ONLINE      |
+
+#### 请求示例
+
+```
+{
+    "page": 1,
+    "size": 10,
+    "keyword": "摄像头",
+    "enabled": true,
+    "sortBy": "createdAt",
+    "sortDir": "DESC",
+    "machineId": "machine_001",
+    "status": "ONLINE"
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: PageResponse) |  |
+| └ list | List<T> | 否 |  | 数据列表 (ActualType: T) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ cameraId | String | 否 | - | 摄像头 ID | cam_001 |
+| └ name | String | 否 | - | 名称 | 主摄像头 |
+| └ ip | String | 否 | - | IP 地址 | 192.168.1.100 |
+| └ port | Integer | 否 | - | 端口 | 80 |
+| └ username | String | 否 | - | 用户名 | admin |
+| └ brand | String | 否 | - | 品牌 | hikvision |
+| └ capability | String | 否 | - | 能力: switch_only, ptz_enabled | ptz_enabled |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ machineId | String | 否 | - | 所属机器 ID | machine_001 |
+| └ machineName | String | 否 | - | 所属机器名称 | 1 号机 |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ channels | List<ChannelInfoDTO> | 否 |  | 通道列表 (ActualType: ChannelInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ channelId | String | 否 | - | 通道 ID | ch_001 |
+| └ name | String | 否 | - | 通道名称 | 主通道 |
+| └ rtspUrl | String | 否 | - | RTSP 地址 | rtsp://192.168.1.100:554/stream1 |
+| └ defaultView | Boolean | 否 | - | 是否默认视角 | true |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2026-01-07T10:00:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2026-01-07T10:00:00 |
+| └ page | Integer | 否 | - | 当前页码 (从 1 开始) | 1 |
+| └ size | Integer | 否 | - | 每页条数 | 10 |
+| └ total | Long | 否 | - | 总记录数 | 100 |
+| └ totalPages | Integer | 否 | - | 总页数 | 10 |
+| └ hasNext | Boolean | 否 | - | 是否有下一页 | true |
+| └ hasPrevious | Boolean | 否 | - | 是否有上一页 | false |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "list": [
+            {
+                "id": 1,
+                "cameraId": "cam_001",
+                "name": "主摄像头",
+                "ip": "192.168.1.100",
+                "port": 80,
+                "username": "admin",
+                "brand": "hikvision",
+                "capability": "ptz_enabled",
+                "status": "ONLINE",
+                "machineId": "machine_001",
+                "machineName": "1号机",
+                "enabled": true,
+                "channels": [
+                    {
+                        "id": 1,
+                        "channelId": "ch_001",
+                        "name": "主通道",
+                        "rtspUrl": "rtsp://192.168.1.100:554/stream1",
+                        "defaultView": true,
+                        "status": "ONLINE"
+                    }
+                ],
+                "createdAt": "2026-01-07T10:00:00",
+                "updatedAt": "2026-01-07T10:00:00"
+            }
+        ],
+        "page": 1,
+        "size": 10,
+        "total": 100,
+        "totalPages": 10,
+        "hasNext": true,
+        "hasPrevious": false
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 获取摄像头列表(全部,不分页)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/admin/cameras/listAll
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/admin/cameras/listAll
+
+描述:获取摄像头列表(全部,不分页)
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称      | 类型   | 必填 | 最大长度 | 描述            | 示例值      |
+| --------- | ------ | ---- | -------- | --------------- | ----------- |
+| machineId | string | 否   | -        | 机器 ID(可选) | machine_001 |
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | array | 否 |  | 响应数据 (ActualType: List) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ cameraId | String | 否 | - | 摄像头 ID | cam_001 |
+| └ name | String | 否 | - | 名称 | 主摄像头 |
+| └ ip | String | 否 | - | IP 地址 | 192.168.1.100 |
+| └ port | Integer | 否 | - | 端口 | 80 |
+| └ username | String | 否 | - | 用户名 | admin |
+| └ brand | String | 否 | - | 品牌 | hikvision |
+| └ capability | String | 否 | - | 能力: switch_only, ptz_enabled | ptz_enabled |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ machineId | String | 否 | - | 所属机器 ID | machine_001 |
+| └ machineName | String | 否 | - | 所属机器名称 | 1 号机 |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ channels | List<ChannelInfoDTO> | 否 |  | 通道列表 (ActualType: ChannelInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ channelId | String | 否 | - | 通道 ID | ch_001 |
+| └ name | String | 否 | - | 通道名称 | 主通道 |
+| └ rtspUrl | String | 否 | - | RTSP 地址 | rtsp://192.168.1.100:554/stream1 |
+| └ defaultView | Boolean | 否 | - | 是否默认视角 | true |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2026-01-07T10:00:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": [
+        {
+            "id": 1,
+            "cameraId": "cam_001",
+            "name": "主摄像头",
+            "ip": "192.168.1.100",
+            "port": 80,
+            "username": "admin",
+            "brand": "hikvision",
+            "capability": "ptz_enabled",
+            "status": "ONLINE",
+            "machineId": "machine_001",
+            "machineName": "1号机",
+            "enabled": true,
+            "channels": [
+                {
+                    "id": 1,
+                    "channelId": "ch_001",
+                    "name": "主通道",
+                    "rtspUrl": "rtsp://192.168.1.100:554/stream1",
+                    "defaultView": true,
+                    "status": "ONLINE"
+                }
+            ],
+            "createdAt": "2026-01-07T10:00:00",
+            "updatedAt": "2026-01-07T10:00:00"
+        }
+    ]
+}
+```
+
+#### 错误码
+
+无
+
+### 获取摄像头详情
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/admin/cameras/detail
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/admin/cameras/detail
+
+描述:获取摄像头详情
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述          | 示例值 |
+| ---- | ----- | ---- | -------- | ------------- | ------ |
+| id   | int64 | 是   | -        | 摄像头主键 ID | 1      |
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: CameraInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ cameraId | String | 否 | - | 摄像头 ID | cam_001 |
+| └ name | String | 否 | - | 名称 | 主摄像头 |
+| └ ip | String | 否 | - | IP 地址 | 192.168.1.100 |
+| └ port | Integer | 否 | - | 端口 | 80 |
+| └ username | String | 否 | - | 用户名 | admin |
+| └ brand | String | 否 | - | 品牌 | hikvision |
+| └ capability | String | 否 | - | 能力: switch_only, ptz_enabled | ptz_enabled |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ machineId | String | 否 | - | 所属机器 ID | machine_001 |
+| └ machineName | String | 否 | - | 所属机器名称 | 1 号机 |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ channels | List<ChannelInfoDTO> | 否 |  | 通道列表 (ActualType: ChannelInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ channelId | String | 否 | - | 通道 ID | ch_001 |
+| └ name | String | 否 | - | 通道名称 | 主通道 |
+| └ rtspUrl | String | 否 | - | RTSP 地址 | rtsp://192.168.1.100:554/stream1 |
+| └ defaultView | Boolean | 否 | - | 是否默认视角 | true |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2026-01-07T10:00:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "cameraId": "cam_001",
+        "name": "主摄像头",
+        "ip": "192.168.1.100",
+        "port": 80,
+        "username": "admin",
+        "brand": "hikvision",
+        "capability": "ptz_enabled",
+        "status": "ONLINE",
+        "machineId": "machine_001",
+        "machineName": "1号机",
+        "enabled": true,
+        "channels": [
+            {
+                "id": 1,
+                "channelId": "ch_001",
+                "name": "主通道",
+                "rtspUrl": "rtsp://192.168.1.100:554/stream1",
+                "defaultView": true,
+                "status": "ONLINE"
+            }
+        ],
+        "createdAt": "2026-01-07T10:00:00",
+        "updatedAt": "2026-01-07T10:00:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 添加摄像头
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/add
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/add
+
+描述:添加摄像头
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| cameraId | String | 是 | 50 | 摄像头 ID(唯一标识)<br>Validate[max: 50; ] | cam_001 |
+| name | String | 是 | 100 | 名称<br>Validate[max: 100; ] | 主摄像头 |
+| ip | String | 是 | - | IP 地址<br>Validate[regexp: ^((25[0-5] | 2[0-4]\\d | [01]?\\d\\d?)\\.){3}(25[0-5] | 2[0-4]\\d | [01]?\\d\\d?)$; ] | 192.168.1.100 |
+| port | Integer | 否 | - | 端口 | 80 |
+| username | String | 否 | 50 | 用户名<br>Validate[max: 50; ] | admin |
+| password | String | 否 | 100 | 密码<br>Validate[max: 100; ] | password123 |
+| brand | String | 否 | 30 | 品牌<br>Validate[max: 30; ] | hikvision |
+| capability | String | 否 | - | 能力: switch_only, ptz_enabled<br>Validate[regexp: ^(switch_only | ptz_enabled)$; ] | ptz_enabled |
+| machineId | String | 否 | - | 所属机器 ID | machine_001 |
+| channels | List<CameraAddRequest$ChannelAddRequest> | 否 |  | 通道列表 (ActualType: ChannelAddRequest) |  |
+| └ channelId | String | 是 | - | 通道 ID | ch_001 |
+| └ name | String | 是 | - | 通道名称 | 主通道 |
+| └ rtspUrl | String | 否 | - | RTSP 地址 | rtsp://192.168.1.100:554/stream1 |
+| └ defaultView | Boolean | 否 | - | 是否默认视角 | true |
+
+#### 请求示例
+
+```
+{
+    "cameraId": "cam_001",
+    "name": "主摄像头",
+    "ip": "192.168.1.100",
+    "port": 80,
+    "username": "admin",
+    "password": "password123",
+    "brand": "hikvision",
+    "capability": "ptz_enabled",
+    "machineId": "machine_001",
+    "channels": [
+        {
+            "channelId": "ch_001",
+            "name": "主通道",
+            "rtspUrl": "rtsp://192.168.1.100:554/stream1",
+            "defaultView": true
+        }
+    ]
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: CameraInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ cameraId | String | 否 | - | 摄像头 ID | cam_001 |
+| └ name | String | 否 | - | 名称 | 主摄像头 |
+| └ ip | String | 否 | - | IP 地址 | 192.168.1.100 |
+| └ port | Integer | 否 | - | 端口 | 80 |
+| └ username | String | 否 | - | 用户名 | admin |
+| └ brand | String | 否 | - | 品牌 | hikvision |
+| └ capability | String | 否 | - | 能力: switch_only, ptz_enabled | ptz_enabled |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ machineId | String | 否 | - | 所属机器 ID | machine_001 |
+| └ machineName | String | 否 | - | 所属机器名称 | 1 号机 |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ channels | List<ChannelInfoDTO> | 否 |  | 通道列表 (ActualType: ChannelInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ channelId | String | 否 | - | 通道 ID | ch_001 |
+| └ name | String | 否 | - | 通道名称 | 主通道 |
+| └ rtspUrl | String | 否 | - | RTSP 地址 | rtsp://192.168.1.100:554/stream1 |
+| └ defaultView | Boolean | 否 | - | 是否默认视角 | true |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2026-01-07T10:00:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "cameraId": "cam_001",
+        "name": "主摄像头",
+        "ip": "192.168.1.100",
+        "port": 80,
+        "username": "admin",
+        "brand": "hikvision",
+        "capability": "ptz_enabled",
+        "status": "ONLINE",
+        "machineId": "machine_001",
+        "machineName": "1号机",
+        "enabled": true,
+        "channels": [
+            {
+                "id": 1,
+                "channelId": "ch_001",
+                "name": "主通道",
+                "rtspUrl": "rtsp://192.168.1.100:554/stream1",
+                "defaultView": true,
+                "status": "ONLINE"
+            }
+        ],
+        "createdAt": "2026-01-07T10:00:00",
+        "updatedAt": "2026-01-07T10:00:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 更新摄像头
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/update
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/update
+
+描述:更新摄像头
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| id | Long | 否 | - | 摄像头数据库 ID | 1 |
+| name | String | 否 | - | 名称 | 主摄像头 |
+| ip | String | 否 | - | IP 地址 | 192.168.1.100 |
+| port | Integer | 否 | - | 端口 | 80 |
+| username | String | 否 | - | 用户名 | admin |
+| password | String | 否 | - | 密码(不传或为空则不修改) | password123 |
+| brand | String | 否 | - | 品牌 | hikvision |
+| capability | String | 否 | - | 能力: switch_only, ptz_enabled | ptz_enabled |
+| machineId | String | 否 | - | 所属机器 ID | machine_001 |
+| enabled | Boolean | 否 | - | 启用状态 | true |
+| channels | List<CameraUpdateRequest$ChannelUpdateRequest> | 否 |  | 通道列表 (ActualType: ChannelUpdateRequest) |  |
+| └ id | Long | 否 | - | 通道数据库 ID | 1 |
+| └ channelId | String | 否 | - | 通道 ID | ch_001 |
+| └ name | String | 否 | - | 通道名称 | 主通道 |
+| └ rtspUrl | String | 否 | - | RTSP 地址 | rtsp://192.168.1.100:554/stream1 |
+| └ defaultView | Boolean | 否 | - | 是否默认视角 | true |
+
+#### 请求示例
+
+```
+{
+    "id": 1,
+    "name": "主摄像头",
+    "ip": "192.168.1.100",
+    "port": 80,
+    "username": "admin",
+    "password": "password123",
+    "brand": "hikvision",
+    "capability": "ptz_enabled",
+    "machineId": "machine_001",
+    "enabled": true,
+    "channels": [
+        {
+            "id": 1,
+            "channelId": "ch_001",
+            "name": "主通道",
+            "rtspUrl": "rtsp://192.168.1.100:554/stream1",
+            "defaultView": true
+        }
+    ]
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: CameraInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ cameraId | String | 否 | - | 摄像头 ID | cam_001 |
+| └ name | String | 否 | - | 名称 | 主摄像头 |
+| └ ip | String | 否 | - | IP 地址 | 192.168.1.100 |
+| └ port | Integer | 否 | - | 端口 | 80 |
+| └ username | String | 否 | - | 用户名 | admin |
+| └ brand | String | 否 | - | 品牌 | hikvision |
+| └ capability | String | 否 | - | 能力: switch_only, ptz_enabled | ptz_enabled |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ machineId | String | 否 | - | 所属机器 ID | machine_001 |
+| └ machineName | String | 否 | - | 所属机器名称 | 1 号机 |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ channels | List<ChannelInfoDTO> | 否 |  | 通道列表 (ActualType: ChannelInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ channelId | String | 否 | - | 通道 ID | ch_001 |
+| └ name | String | 否 | - | 通道名称 | 主通道 |
+| └ rtspUrl | String | 否 | - | RTSP 地址 | rtsp://192.168.1.100:554/stream1 |
+| └ defaultView | Boolean | 否 | - | 是否默认视角 | true |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2026-01-07T10:00:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "cameraId": "cam_001",
+        "name": "主摄像头",
+        "ip": "192.168.1.100",
+        "port": 80,
+        "username": "admin",
+        "brand": "hikvision",
+        "capability": "ptz_enabled",
+        "status": "ONLINE",
+        "machineId": "machine_001",
+        "machineName": "1号机",
+        "enabled": true,
+        "channels": [
+            {
+                "id": 1,
+                "channelId": "ch_001",
+                "name": "主通道",
+                "rtspUrl": "rtsp://192.168.1.100:554/stream1",
+                "defaultView": true,
+                "status": "ONLINE"
+            }
+        ],
+        "createdAt": "2026-01-07T10:00:00",
+        "updatedAt": "2026-01-07T10:00:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 删除摄像头
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/delete
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/delete
+
+描述:删除摄像头
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述          | 示例值 |
+| ---- | ----- | ---- | -------- | ------------- | ------ |
+| id   | int64 | 是   | -        | 摄像头主键 ID | 1      |
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | object  | 否   | -        | 响应数据 (ActualType: Void) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {}
+}
+```
+
+#### 错误码
+
+无
+
+### 检测摄像头连通性
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/check
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/check
+
+描述:检测摄像头连通性
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述          | 示例值 |
+| ---- | ----- | ---- | -------- | ------------- | ------ |
+| id   | int64 | 是   | -        | 摄像头主键 ID | 1      |
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                           | 示例值 |
+| ---------- | ------- | ---- | -------- | ------------------------------ | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                   | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)           |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)         |        |
+| data       | boolean | 否   | -        | 响应数据 (ActualType: Boolean) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": false
+}
+```
+
+#### 错误码
+
+无
+
+### 获取摄像头快照
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/admin/cameras/snapshot
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/admin/cameras/snapshot
+
+描述:获取摄像头快照
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述          | 示例值 |
+| ---- | ----- | ---- | -------- | ------------- | ------ |
+| id   | int64 | 是   | -        | 摄像头主键 ID | 1      |
+
+#### 响应参数
+
+| 名称       | 类型          | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean       | 否   | -        | 请求是否成功                | true   |
+| errCode    | String        | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String        | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | array         | 否   |          | 响应数据 (ActualType: byte) |        |
+| └ -        | array[byte[]] | 否   | -        | array of byte[]             |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": [
+        {
+            " -": ""
+        }
+    ]
+}
+```
+
+#### 错误码
+
+无
+
+### PTZ 控制
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/ptz
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/ptz
+
+描述:PTZ 控制
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称   | 类型   | 必填 | 最大长度 | 描述             | 示例值 |
+| ------ | ------ | ---- | -------- | ---------------- | ------ |
+| id     | Long   | 否   | -        | 摄像头 ID        | 1      |
+| action | String | 否   | -        | PTZ 动作         | UP     |
+| speed  | Float  | 否   | -        | 速度 (0.0 - 1.0) | 0.5    |
+
+#### 请求示例
+
+```
+{
+    "id": 1,
+    "action": "UP",
+    "speed": 0.5
+}
+```
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                           | 示例值 |
+| ---------- | ------- | ---- | -------- | ------------------------------ | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                   | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)           |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)         |        |
+| data       | boolean | 否   | -        | 响应数据 (ActualType: Boolean) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": false
+}
+```
+
+#### 错误码
+
+无
+
+## 机器管理
+
+### 获取机器列表(分页)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/machines/list
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/machines/list
+
+描述:获取机器列表(分页)
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称    | 类型    | 必填 | 最大长度 | 描述                                            | 示例值    |
+| ------- | ------- | ---- | -------- | ----------------------------------------------- | --------- |
+| page    | Integer | 否   | -        | 页码 (从 1 开始)                                | 1         |
+| size    | Integer | 否   | -        | 每页条数                                        | 10        |
+| keyword | String  | 否   | -        | 搜索关键词 (模糊匹配名称、ID 等)                | 摄像头    |
+| enabled | Boolean | 否   | -        | 启用状态过滤 (null=全部, true=启用, false=禁用) | true      |
+| sortBy  | String  | 否   | -        | 排序字段                                        | createdAt |
+| sortDir | String  | 否   | -        | 排序方向 (ASC/DESC)                             | DESC      |
+
+#### 请求示例
+
+```
+{
+    "page": 1,
+    "size": 10,
+    "keyword": "摄像头",
+    "enabled": true,
+    "sortBy": "createdAt",
+    "sortDir": "DESC"
+}
+```
+
+#### 响应参数
+
+| 名称          | 类型          | 必填 | 最大长度 | 描述                                | 示例值              |
+| ------------- | ------------- | ---- | -------- | ----------------------------------- | ------------------- |
+| success       | Boolean       | 否   | -        | 请求是否成功                        | true                |
+| errCode       | String        | 否   | -        | 错误码(失败时返回)                |                     |
+| errMessage    | String        | 否   | -        | 错误信息(失败时返回)              |                     |
+| data          | object        | 否   |          | 响应数据 (ActualType: PageResponse) |                     |
+| └ list        | List<T>       | 否   |          | 数据列表 (ActualType: T)            |                     |
+| └ id          | Long          | 否   | -        | 主键 ID                             | 1                   |
+| └ machineId   | String        | 否   | -        | 机器 ID                             | machine_001         |
+| └ name        | String        | 否   | -        | 名称                                | 1 号机              |
+| └ location    | String        | 否   | -        | 位置                                | A 区 1 楼           |
+| └ description | String        | 否   | -        | 描述                                | 主力机器            |
+| └ enabled     | Boolean       | 否   | -        | 是否启用                            | true                |
+| └ cameraCount | Integer       | 否   | -        | 摄像头数量                          | 3                   |
+| └ createdAt   | LocalDateTime | 否   | -        | 创建时间                            | 2026-01-07T10:00:00 |
+| └ updatedAt   | LocalDateTime | 否   | -        | 更新时间                            | 2026-01-07T10:00:00 |
+| └ page        | Integer       | 否   | -        | 当前页码 (从 1 开始)                | 1                   |
+| └ size        | Integer       | 否   | -        | 每页条数                            | 10                  |
+| └ total       | Long          | 否   | -        | 总记录数                            | 100                 |
+| └ totalPages  | Integer       | 否   | -        | 总页数                              | 10                  |
+| └ hasNext     | Boolean       | 否   | -        | 是否有下一页                        | true                |
+| └ hasPrevious | Boolean       | 否   | -        | 是否有上一页                        | false               |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "list": [
+            {
+                "id": 1,
+                "machineId": "machine_001",
+                "name": "1号机",
+                "location": "A区1楼",
+                "description": "主力机器",
+                "enabled": true,
+                "cameraCount": 3,
+                "createdAt": "2026-01-07T10:00:00",
+                "updatedAt": "2026-01-07T10:00:00"
+            }
+        ],
+        "page": 1,
+        "size": 10,
+        "total": 100,
+        "totalPages": 10,
+        "hasNext": true,
+        "hasPrevious": false
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 获取机器列表(全部,不分页)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/admin/machines/listAll
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/admin/machines/listAll
+
+描述:获取机器列表(全部,不分页)
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称          | 类型          | 必填 | 最大长度 | 描述                        | 示例值              |
+| ------------- | ------------- | ---- | -------- | --------------------------- | ------------------- |
+| success       | Boolean       | 否   | -        | 请求是否成功                | true                |
+| errCode       | String        | 否   | -        | 错误码(失败时返回)        |                     |
+| errMessage    | String        | 否   | -        | 错误信息(失败时返回)      |                     |
+| data          | array         | 否   |          | 响应数据 (ActualType: List) |                     |
+| └ id          | Long          | 否   | -        | 主键 ID                     | 1                   |
+| └ machineId   | String        | 否   | -        | 机器 ID                     | machine_001         |
+| └ name        | String        | 否   | -        | 名称                        | 1 号机              |
+| └ location    | String        | 否   | -        | 位置                        | A 区 1 楼           |
+| └ description | String        | 否   | -        | 描述                        | 主力机器            |
+| └ enabled     | Boolean       | 否   | -        | 是否启用                    | true                |
+| └ cameraCount | Integer       | 否   | -        | 摄像头数量                  | 3                   |
+| └ createdAt   | LocalDateTime | 否   | -        | 创建时间                    | 2026-01-07T10:00:00 |
+| └ updatedAt   | LocalDateTime | 否   | -        | 更新时间                    | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": [
+        {
+            "id": 1,
+            "machineId": "machine_001",
+            "name": "1号机",
+            "location": "A区1楼",
+            "description": "主力机器",
+            "enabled": true,
+            "cameraCount": 3,
+            "createdAt": "2026-01-07T10:00:00",
+            "updatedAt": "2026-01-07T10:00:00"
+        }
+    ]
+}
+```
+
+#### 错误码
+
+无
+
+### 获取机器详情
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/admin/machines/detail
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/admin/machines/detail
+
+描述:获取机器详情
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述        | 示例值 |
+| ---- | ----- | ---- | -------- | ----------- | ------ |
+| id   | int64 | 是   | -        | 机器主键 ID | 1      |
+
+#### 响应参数
+
+| 名称          | 类型          | 必填 | 最大长度 | 描述                              | 示例值              |
+| ------------- | ------------- | ---- | -------- | --------------------------------- | ------------------- |
+| success       | Boolean       | 否   | -        | 请求是否成功                      | true                |
+| errCode       | String        | 否   | -        | 错误码(失败时返回)              |                     |
+| errMessage    | String        | 否   | -        | 错误信息(失败时返回)            |                     |
+| data          | object        | 否   |          | 响应数据 (ActualType: MachineDTO) |                     |
+| └ id          | Long          | 否   | -        | 主键 ID                           | 1                   |
+| └ machineId   | String        | 否   | -        | 机器 ID                           | machine_001         |
+| └ name        | String        | 否   | -        | 名称                              | 1 号机              |
+| └ location    | String        | 否   | -        | 位置                              | A 区 1 楼           |
+| └ description | String        | 否   | -        | 描述                              | 主力机器            |
+| └ enabled     | Boolean       | 否   | -        | 是否启用                          | true                |
+| └ cameraCount | Integer       | 否   | -        | 摄像头数量                        | 3                   |
+| └ createdAt   | LocalDateTime | 否   | -        | 创建时间                          | 2026-01-07T10:00:00 |
+| └ updatedAt   | LocalDateTime | 否   | -        | 更新时间                          | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "machineId": "machine_001",
+        "name": "1号机",
+        "location": "A区1楼",
+        "description": "主力机器",
+        "enabled": true,
+        "cameraCount": 3,
+        "createdAt": "2026-01-07T10:00:00",
+        "updatedAt": "2026-01-07T10:00:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 添加机器
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/machines/add
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/machines/add
+
+描述:添加机器
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称        | 类型   | 必填 | 最大长度 | 描述                                       | 示例值      |
+| ----------- | ------ | ---- | -------- | ------------------------------------------ | ----------- |
+| machineId   | String | 是   | 50       | 机器 ID(唯一标识)<br>Validate[max: 50; ] | machine_001 |
+| name        | String | 是   | 100      | 名称<br>Validate[max: 100; ]               | 1 号机      |
+| location    | String | 否   | 200      | 位置<br>Validate[max: 200; ]               | A 区 1 楼   |
+| description | String | 否   | 500      | 描述<br>Validate[max: 500; ]               | 主力机器    |
+
+#### 请求示例
+
+```
+{
+    "machineId": "machine_001",
+    "name": "1号机",
+    "location": "A区1楼",
+    "description": "主力机器"
+}
+```
+
+#### 响应参数
+
+| 名称          | 类型          | 必填 | 最大长度 | 描述                              | 示例值              |
+| ------------- | ------------- | ---- | -------- | --------------------------------- | ------------------- |
+| success       | Boolean       | 否   | -        | 请求是否成功                      | true                |
+| errCode       | String        | 否   | -        | 错误码(失败时返回)              |                     |
+| errMessage    | String        | 否   | -        | 错误信息(失败时返回)            |                     |
+| data          | object        | 否   |          | 响应数据 (ActualType: MachineDTO) |                     |
+| └ id          | Long          | 否   | -        | 主键 ID                           | 1                   |
+| └ machineId   | String        | 否   | -        | 机器 ID                           | machine_001         |
+| └ name        | String        | 否   | -        | 名称                              | 1 号机              |
+| └ location    | String        | 否   | -        | 位置                              | A 区 1 楼           |
+| └ description | String        | 否   | -        | 描述                              | 主力机器            |
+| └ enabled     | Boolean       | 否   | -        | 是否启用                          | true                |
+| └ cameraCount | Integer       | 否   | -        | 摄像头数量                        | 3                   |
+| └ createdAt   | LocalDateTime | 否   | -        | 创建时间                          | 2026-01-07T10:00:00 |
+| └ updatedAt   | LocalDateTime | 否   | -        | 更新时间                          | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "machineId": "machine_001",
+        "name": "1号机",
+        "location": "A区1楼",
+        "description": "主力机器",
+        "enabled": true,
+        "cameraCount": 3,
+        "createdAt": "2026-01-07T10:00:00",
+        "updatedAt": "2026-01-07T10:00:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 更新机器
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/machines/update
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/machines/update
+
+描述:更新机器
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称        | 类型    | 必填 | 最大长度 | 描述          | 示例值    |
+| ----------- | ------- | ---- | -------- | ------------- | --------- |
+| id          | Long    | 否   | -        | 机器数据库 ID | 1         |
+| name        | String  | 否   | -        | 名称          | 1 号机    |
+| location    | String  | 否   | -        | 位置          | A 区 1 楼 |
+| description | String  | 否   | -        | 描述          | 主力机器  |
+| enabled     | Boolean | 否   | -        | 启用状态      | true      |
+
+#### 请求示例
+
+```
+{
+    "id": 1,
+    "name": "1号机",
+    "location": "A区1楼",
+    "description": "主力机器",
+    "enabled": true
+}
+```
+
+#### 响应参数
+
+| 名称          | 类型          | 必填 | 最大长度 | 描述                              | 示例值              |
+| ------------- | ------------- | ---- | -------- | --------------------------------- | ------------------- |
+| success       | Boolean       | 否   | -        | 请求是否成功                      | true                |
+| errCode       | String        | 否   | -        | 错误码(失败时返回)              |                     |
+| errMessage    | String        | 否   | -        | 错误信息(失败时返回)            |                     |
+| data          | object        | 否   |          | 响应数据 (ActualType: MachineDTO) |                     |
+| └ id          | Long          | 否   | -        | 主键 ID                           | 1                   |
+| └ machineId   | String        | 否   | -        | 机器 ID                           | machine_001         |
+| └ name        | String        | 否   | -        | 名称                              | 1 号机              |
+| └ location    | String        | 否   | -        | 位置                              | A 区 1 楼           |
+| └ description | String        | 否   | -        | 描述                              | 主力机器            |
+| └ enabled     | Boolean       | 否   | -        | 是否启用                          | true                |
+| └ cameraCount | Integer       | 否   | -        | 摄像头数量                        | 3                   |
+| └ createdAt   | LocalDateTime | 否   | -        | 创建时间                          | 2026-01-07T10:00:00 |
+| └ updatedAt   | LocalDateTime | 否   | -        | 更新时间                          | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "machineId": "machine_001",
+        "name": "1号机",
+        "location": "A区1楼",
+        "description": "主力机器",
+        "enabled": true,
+        "cameraCount": 3,
+        "createdAt": "2026-01-07T10:00:00",
+        "updatedAt": "2026-01-07T10:00:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 删除机器
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/machines/delete
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/machines/delete
+
+描述:删除机器
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述        | 示例值 |
+| ---- | ----- | ---- | -------- | ----------- | ------ |
+| id   | int64 | 是   | -        | 机器主键 ID | 1      |
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | object  | 否   | -        | 响应数据 (ActualType: Void) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {}
+}
+```
+
+#### 错误码
+
+无
+
+## 推流通道管理
+
+### 获取推流通道列表(分页)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/stream-channels/list
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/stream-channels/list
+
+描述:获取推流通道列表(分页)
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称    | 类型    | 必填 | 最大长度 | 描述                                            | 示例值    |
+| ------- | ------- | ---- | -------- | ----------------------------------------------- | --------- |
+| page    | Integer | 否   | -        | 页码 (从 1 开始)                                | 1         |
+| size    | Integer | 否   | -        | 每页条数                                        | 10        |
+| keyword | String  | 否   | -        | 搜索关键词 (模糊匹配名称、ID 等)                | 摄像头    |
+| enabled | Boolean | 否   | -        | 启用状态过滤 (null=全部, true=启用, false=禁用) | true      |
+| sortBy  | String  | 否   | -        | 排序字段                                        | createdAt |
+| sortDir | String  | 否   | -        | 排序方向 (ASC/DESC)                             | DESC      |
+
+#### 请求示例
+
+```
+{
+    "page": 1,
+    "size": 10,
+    "keyword": "摄像头",
+    "enabled": true,
+    "sortBy": "createdAt",
+    "sortDir": "DESC"
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: PageResponse) |  |
+| └ list | List<T> | 否 |  | 数据列表 (ActualType: T) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ channelId | String | 否 | - | 通道 ID | cf_channel_001 |
+| └ name | String | 否 | - | 通道名称 | 主推流通道 |
+| └ accountId | String | 否 | - | Cloudflare 账户 ID | 5544eac7cfb260d4fec9467d49513cea |
+| └ liveInputId | String | 否 | - | Cloudflare Live Input ID | b51e49994b6fd9e56b6f1fdfcd339fe6 |
+| └ customerSubdomain | String | 否 | - | 客户子域名 | customer-pj89kn2ke2tcuh19 |
+| └ mode | String | 否 | - | 推流模式: WHIP / RTMPS / SRT | WHIP |
+| └ whipUrl | String | 否 | - | WHIP 推流地址 | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/publish |
+| └ rtmpsUrl | String | 否 | - | RTMPS 推流地址 | rtmps://live.cloudflare.com:443/live/xxx |
+| └ hlsPlaybackUrl | String | 否 | - | HLS 播放地址 | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/manifest/video.m3u8 |
+| └ whepPlaybackUrl | String | 否 | - | WHEP 播放地址 (WebRTC) | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/play |
+| └ recordingEnabled | Boolean | 否 | - | 是否启用录制 | false |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2024-01-15T10:30:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2024-01-15T10:30:00 |
+| └ page | Integer | 否 | - | 当前页码 (从 1 开始) | 1 |
+| └ size | Integer | 否 | - | 每页条数 | 10 |
+| └ total | Long | 否 | - | 总记录数 | 100 |
+| └ totalPages | Integer | 否 | - | 总页数 | 10 |
+| └ hasNext | Boolean | 否 | - | 是否有下一页 | true |
+| └ hasPrevious | Boolean | 否 | - | 是否有上一页 | false |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "list": [
+            {
+                "id": 1,
+                "channelId": "cf_channel_001",
+                "name": "主推流通道",
+                "accountId": "5544eac7cfb260d4fec9467d49513cea",
+                "liveInputId": "b51e49994b6fd9e56b6f1fdfcd339fe6",
+                "customerSubdomain": "customer-pj89kn2ke2tcuh19",
+                "mode": "WHIP",
+                "whipUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/publish",
+                "rtmpsUrl": "rtmps://live.cloudflare.com:443/live/xxx",
+                "hlsPlaybackUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/manifest/video.m3u8",
+                "whepPlaybackUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/play",
+                "recordingEnabled": false,
+                "enabled": true,
+                "createdAt": "2024-01-15T10:30:00",
+                "updatedAt": "2024-01-15T10:30:00"
+            }
+        ],
+        "page": 1,
+        "size": 10,
+        "total": 100,
+        "totalPages": 10,
+        "hasNext": true,
+        "hasPrevious": false
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 获取推流通道列表(全部,不分页)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/admin/stream-channels/listAll
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/admin/stream-channels/listAll
+
+描述:获取推流通道列表(全部,不分页)
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | array | 否 |  | 响应数据 (ActualType: List) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ channelId | String | 否 | - | 通道 ID | cf_channel_001 |
+| └ name | String | 否 | - | 通道名称 | 主推流通道 |
+| └ accountId | String | 否 | - | Cloudflare 账户 ID | 5544eac7cfb260d4fec9467d49513cea |
+| └ liveInputId | String | 否 | - | Cloudflare Live Input ID | b51e49994b6fd9e56b6f1fdfcd339fe6 |
+| └ customerSubdomain | String | 否 | - | 客户子域名 | customer-pj89kn2ke2tcuh19 |
+| └ mode | String | 否 | - | 推流模式: WHIP / RTMPS / SRT | WHIP |
+| └ whipUrl | String | 否 | - | WHIP 推流地址 | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/publish |
+| └ rtmpsUrl | String | 否 | - | RTMPS 推流地址 | rtmps://live.cloudflare.com:443/live/xxx |
+| └ hlsPlaybackUrl | String | 否 | - | HLS 播放地址 | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/manifest/video.m3u8 |
+| └ whepPlaybackUrl | String | 否 | - | WHEP 播放地址 (WebRTC) | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/play |
+| └ recordingEnabled | Boolean | 否 | - | 是否启用录制 | false |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2024-01-15T10:30:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2024-01-15T10:30:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": [
+        {
+            "id": 1,
+            "channelId": "cf_channel_001",
+            "name": "主推流通道",
+            "accountId": "5544eac7cfb260d4fec9467d49513cea",
+            "liveInputId": "b51e49994b6fd9e56b6f1fdfcd339fe6",
+            "customerSubdomain": "customer-pj89kn2ke2tcuh19",
+            "mode": "WHIP",
+            "whipUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/publish",
+            "rtmpsUrl": "rtmps://live.cloudflare.com:443/live/xxx",
+            "hlsPlaybackUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/manifest/video.m3u8",
+            "whepPlaybackUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/play",
+            "recordingEnabled": false,
+            "enabled": true,
+            "createdAt": "2024-01-15T10:30:00",
+            "updatedAt": "2024-01-15T10:30:00"
+        }
+    ]
+}
+```
+
+#### 错误码
+
+无
+
+### 获取推流通道详情
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/admin/stream-channels/detail
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/admin/stream-channels/detail
+
+描述:获取推流通道详情
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述            | 示例值 |
+| ---- | ----- | ---- | -------- | --------------- | ------ |
+| id   | int64 | 是   | -        | 推流通道主键 ID | 1      |
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: StreamChannelInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ channelId | String | 否 | - | 通道 ID | cf_channel_001 |
+| └ name | String | 否 | - | 通道名称 | 主推流通道 |
+| └ accountId | String | 否 | - | Cloudflare 账户 ID | 5544eac7cfb260d4fec9467d49513cea |
+| └ liveInputId | String | 否 | - | Cloudflare Live Input ID | b51e49994b6fd9e56b6f1fdfcd339fe6 |
+| └ customerSubdomain | String | 否 | - | 客户子域名 | customer-pj89kn2ke2tcuh19 |
+| └ mode | String | 否 | - | 推流模式: WHIP / RTMPS / SRT | WHIP |
+| └ whipUrl | String | 否 | - | WHIP 推流地址 | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/publish |
+| └ rtmpsUrl | String | 否 | - | RTMPS 推流地址 | rtmps://live.cloudflare.com:443/live/xxx |
+| └ hlsPlaybackUrl | String | 否 | - | HLS 播放地址 | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/manifest/video.m3u8 |
+| └ whepPlaybackUrl | String | 否 | - | WHEP 播放地址 (WebRTC) | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/play |
+| └ recordingEnabled | Boolean | 否 | - | 是否启用录制 | false |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2024-01-15T10:30:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2024-01-15T10:30:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "channelId": "cf_channel_001",
+        "name": "主推流通道",
+        "accountId": "5544eac7cfb260d4fec9467d49513cea",
+        "liveInputId": "b51e49994b6fd9e56b6f1fdfcd339fe6",
+        "customerSubdomain": "customer-pj89kn2ke2tcuh19",
+        "mode": "WHIP",
+        "whipUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/publish",
+        "rtmpsUrl": "rtmps://live.cloudflare.com:443/live/xxx",
+        "hlsPlaybackUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/manifest/video.m3u8",
+        "whepPlaybackUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/play",
+        "recordingEnabled": false,
+        "enabled": true,
+        "createdAt": "2024-01-15T10:30:00",
+        "updatedAt": "2024-01-15T10:30:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 添加推流通道
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/stream-channels/add
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/stream-channels/add
+
+描述:添加推流通道
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| channelId | String | 是 | - | 通道 ID | cf_channel_001 |
+| name | String | 是 | - | 通道名称 | 主推流通道 |
+| accountId | String | 否 | - | Cloudflare 账户 ID | 5544eac7cfb260d4fec9467d49513cea |
+| apiToken | String | 否 | - | Cloudflare API Token | d_f4nGUKFlB66MMMIS3NfVlUbn0A0p4jlrW5BbZW |
+| liveInputId | String | 是 | - | Cloudflare Live Input ID | b51e49994b6fd9e56b6f1fdfcd339fe6 |
+| streamKey | String | 否 | - | 流密钥 (用于 RTMPS) | 8c108b4025d3278b188b443e8a6c5503kb51e49994b6fd9e56b6f1fdfcd339fe6 |
+| customerSubdomain | String | 是 | - | 客户子域名 | customer-pj89kn2ke2tcuh19 |
+| mode | String | 否 | - | 推流模式: WHIP / RTMPS / SRT | WHIP |
+| recordingEnabled | Boolean | 否 | - | 是否启用录制 | false |
+
+#### 请求示例
+
+```
+{
+    "channelId": "cf_channel_001",
+    "name": "主推流通道",
+    "accountId": "5544eac7cfb260d4fec9467d49513cea",
+    "apiToken": "d_f4nGUKFlB66MMMIS3NfVlUbn0A0p4jlrW5BbZW",
+    "liveInputId": "b51e49994b6fd9e56b6f1fdfcd339fe6",
+    "streamKey": "8c108b4025d3278b188b443e8a6c5503kb51e49994b6fd9e56b6f1fdfcd339fe6",
+    "customerSubdomain": "customer-pj89kn2ke2tcuh19",
+    "mode": "WHIP",
+    "recordingEnabled": false
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: StreamChannelInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ channelId | String | 否 | - | 通道 ID | cf_channel_001 |
+| └ name | String | 否 | - | 通道名称 | 主推流通道 |
+| └ accountId | String | 否 | - | Cloudflare 账户 ID | 5544eac7cfb260d4fec9467d49513cea |
+| └ liveInputId | String | 否 | - | Cloudflare Live Input ID | b51e49994b6fd9e56b6f1fdfcd339fe6 |
+| └ customerSubdomain | String | 否 | - | 客户子域名 | customer-pj89kn2ke2tcuh19 |
+| └ mode | String | 否 | - | 推流模式: WHIP / RTMPS / SRT | WHIP |
+| └ whipUrl | String | 否 | - | WHIP 推流地址 | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/publish |
+| └ rtmpsUrl | String | 否 | - | RTMPS 推流地址 | rtmps://live.cloudflare.com:443/live/xxx |
+| └ hlsPlaybackUrl | String | 否 | - | HLS 播放地址 | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/manifest/video.m3u8 |
+| └ whepPlaybackUrl | String | 否 | - | WHEP 播放地址 (WebRTC) | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/play |
+| └ recordingEnabled | Boolean | 否 | - | 是否启用录制 | false |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2024-01-15T10:30:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2024-01-15T10:30:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "channelId": "cf_channel_001",
+        "name": "主推流通道",
+        "accountId": "5544eac7cfb260d4fec9467d49513cea",
+        "liveInputId": "b51e49994b6fd9e56b6f1fdfcd339fe6",
+        "customerSubdomain": "customer-pj89kn2ke2tcuh19",
+        "mode": "WHIP",
+        "whipUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/publish",
+        "rtmpsUrl": "rtmps://live.cloudflare.com:443/live/xxx",
+        "hlsPlaybackUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/manifest/video.m3u8",
+        "whepPlaybackUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/play",
+        "recordingEnabled": false,
+        "enabled": true,
+        "createdAt": "2024-01-15T10:30:00",
+        "updatedAt": "2024-01-15T10:30:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 更新推流通道
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/stream-channels/update
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/stream-channels/update
+
+描述:更新推流通道
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| id | Long | 是 | - | 主键 ID | 1 |
+| name | String | 否 | - | 通道名称 | 主推流通道 |
+| accountId | String | 否 | - | Cloudflare 账户 ID | 5544eac7cfb260d4fec9467d49513cea |
+| apiToken | String | 否 | - | Cloudflare API Token | d_f4nGUKFlB66MMMIS3NfVlUbn0A0p4jlrW5BbZW |
+| liveInputId | String | 否 | - | Cloudflare Live Input ID | b51e49994b6fd9e56b6f1fdfcd339fe6 |
+| streamKey | String | 否 | - | 流密钥 (用于 RTMPS) | 8c108b4025d3278b188b443e8a6c5503kb51e49994b6fd9e56b6f1fdfcd339fe6 |
+| customerSubdomain | String | 否 | - | 客户子域名 | customer-pj89kn2ke2tcuh19 |
+| mode | String | 否 | - | 推流模式: WHIP / RTMPS / SRT | WHIP |
+| recordingEnabled | Boolean | 否 | - | 是否启用录制 | false |
+| enabled | Boolean | 否 | - | 是否启用 | true |
+
+#### 请求示例
+
+```
+{
+    "id": 1,
+    "name": "主推流通道",
+    "accountId": "5544eac7cfb260d4fec9467d49513cea",
+    "apiToken": "d_f4nGUKFlB66MMMIS3NfVlUbn0A0p4jlrW5BbZW",
+    "liveInputId": "b51e49994b6fd9e56b6f1fdfcd339fe6",
+    "streamKey": "8c108b4025d3278b188b443e8a6c5503kb51e49994b6fd9e56b6f1fdfcd339fe6",
+    "customerSubdomain": "customer-pj89kn2ke2tcuh19",
+    "mode": "WHIP",
+    "recordingEnabled": false,
+    "enabled": true
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: StreamChannelInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ channelId | String | 否 | - | 通道 ID | cf_channel_001 |
+| └ name | String | 否 | - | 通道名称 | 主推流通道 |
+| └ accountId | String | 否 | - | Cloudflare 账户 ID | 5544eac7cfb260d4fec9467d49513cea |
+| └ liveInputId | String | 否 | - | Cloudflare Live Input ID | b51e49994b6fd9e56b6f1fdfcd339fe6 |
+| └ customerSubdomain | String | 否 | - | 客户子域名 | customer-pj89kn2ke2tcuh19 |
+| └ mode | String | 否 | - | 推流模式: WHIP / RTMPS / SRT | WHIP |
+| └ whipUrl | String | 否 | - | WHIP 推流地址 | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/publish |
+| └ rtmpsUrl | String | 否 | - | RTMPS 推流地址 | rtmps://live.cloudflare.com:443/live/xxx |
+| └ hlsPlaybackUrl | String | 否 | - | HLS 播放地址 | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/manifest/video.m3u8 |
+| └ whepPlaybackUrl | String | 否 | - | WHEP 播放地址 (WebRTC) | https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/play |
+| └ recordingEnabled | Boolean | 否 | - | 是否启用录制 | false |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2024-01-15T10:30:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2024-01-15T10:30:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "channelId": "cf_channel_001",
+        "name": "主推流通道",
+        "accountId": "5544eac7cfb260d4fec9467d49513cea",
+        "liveInputId": "b51e49994b6fd9e56b6f1fdfcd339fe6",
+        "customerSubdomain": "customer-pj89kn2ke2tcuh19",
+        "mode": "WHIP",
+        "whipUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/publish",
+        "rtmpsUrl": "rtmps://live.cloudflare.com:443/live/xxx",
+        "hlsPlaybackUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/manifest/video.m3u8",
+        "whepPlaybackUrl": "https://customer-pj89kn2ke2tcuh19.cloudflarestream.com/b51e49994b6fd9e56b6f1fdfcd339fe6/webRTC/play",
+        "recordingEnabled": false,
+        "enabled": true,
+        "createdAt": "2024-01-15T10:30:00",
+        "updatedAt": "2024-01-15T10:30:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 删除推流通道
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/stream-channels/delete
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/stream-channels/delete
+
+描述:删除推流通道
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述            | 示例值 |
+| ---- | ----- | ---- | -------- | --------------- | ------ |
+| id   | int64 | 是   | -        | 推流通道主键 ID | 1      |
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | object  | 否   | -        | 响应数据 (ActualType: Void) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {}
+}
+```
+
+#### 错误码
+
+无
+
+## 管理员认证
+
+### 登录
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/auth/login
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/auth/login
+
+描述:登录
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称     | 类型   | 必填 | 最大长度 | 描述                          | 示例值 |
+| -------- | ------ | ---- | -------- | ----------------------------- | ------ |
+| username | String | 是   | 50       | 用户名<br>Validate[max: 50; ] | admin  |
+| password | String | 是   | 100      | 密码<br>Validate[max: 100; ]  | 123456 |
+
+#### 请求示例
+
+```
+{
+    "username": "admin",
+    "password": "123456"
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: LoginResponse) |  |
+| └ token | String | 否 | - | JWT Token | eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiJ9.xxx |
+| └ tokenType | String | 否 | - | Token 类型 | Bearer |
+| └ expiresIn | Long | 否 | - | 过期时间(秒) | 86400 |
+| └ admin | AdminInfoDTO | 否 |  | 用户信息 |  |
+| └ id | Long | 否 | - | 用户 ID | 1 |
+| └ username | String | 否 | - | 用户名 | admin |
+| └ nickname | String | 否 | - | 昵称 | 管理员 |
+| └ role | String | 否 | - | 角色 | ADMIN |
+| └ lastLoginAt | LocalDateTime | 否 | - | 最后登录时间 | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "token": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiJ9.xxx",
+        "tokenType": "Bearer",
+        "expiresIn": 86400,
+        "admin": {
+            "id": 1,
+            "username": "admin",
+            "nickname": "管理员",
+            "role": "ADMIN",
+            "lastLoginAt": "2026-01-07T10:00:00"
+        }
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 登出
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/auth/logout
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/auth/logout
+
+描述:登出
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | object  | 否   | -        | 响应数据 (ActualType: Void) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {}
+}
+```
+
+#### 错误码
+
+无
+
+### 获取当前用户信息(直接从 Token 解析,不查询数据库)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/admin/auth/info
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/admin/auth/info
+
+描述:获取当前用户信息(直接从 Token 解析,不查询数据库)
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称          | 类型          | 必填 | 最大长度 | 描述                                | 示例值              |
+| ------------- | ------------- | ---- | -------- | ----------------------------------- | ------------------- |
+| success       | Boolean       | 否   | -        | 请求是否成功                        | true                |
+| errCode       | String        | 否   | -        | 错误码(失败时返回)                |                     |
+| errMessage    | String        | 否   | -        | 错误信息(失败时返回)              |                     |
+| data          | object        | 否   |          | 响应数据 (ActualType: AdminInfoDTO) |                     |
+| └ id          | Long          | 否   | -        | 用户 ID                             | 1                   |
+| └ username    | String        | 否   | -        | 用户名                              | admin               |
+| └ nickname    | String        | 否   | -        | 昵称                                | 管理员              |
+| └ role        | String        | 否   | -        | 角色                                | ADMIN               |
+| └ lastLoginAt | LocalDateTime | 否   | -        | 最后登录时间                        | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "username": "admin",
+        "nickname": "管理员",
+        "role": "ADMIN",
+        "lastLoginAt": "2026-01-07T10:00:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 修改密码
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/auth/password
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/auth/password
+
+描述:修改密码
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称        | 类型   | 必填 | 最大长度 | 描述                                         | 示例值      |
+| ----------- | ------ | ---- | -------- | -------------------------------------------- | ----------- |
+| oldPassword | String | 是   | -        | 旧密码                                       | oldpassword |
+| newPassword | String | 是   | 100      | 新密码(6-100 字符)<br>Validate[max: 100; ] | newpassword |
+
+#### 请求示例
+
+```
+{
+    "oldPassword": "oldpassword",
+    "newPassword": "newpassword"
+}
+```
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | object  | 否   | -        | 响应数据 (ActualType: Void) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {}
+}
+```
+
+#### 错误码
+
+无
+
+## 统计
+
+### 获取仪表盘统计数据
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/admin/stats/dashboard
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/admin/stats/dashboard
+
+描述:获取仪表盘统计数据
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称             | 类型    | 必填 | 最大长度 | 描述                                     | 示例值 |
+| ---------------- | ------- | ---- | -------- | ---------------------------------------- | ------ |
+| success          | Boolean | 否   | -        | 请求是否成功                             | true   |
+| errCode          | String  | 否   | -        | 错误码(失败时返回)                     |        |
+| errMessage       | String  | 否   | -        | 错误信息(失败时返回)                   |        |
+| data             | object  | 否   |          | 响应数据 (ActualType: DashboardStatsDTO) |        |
+| └ machineTotal   | Long    | 否   | -        | 机器总数                                 | 10     |
+| └ machineEnabled | Long    | 否   | -        | 启用的机器数                             | 8      |
+| └ cameraTotal    | Long    | 否   | -        | 摄像头总数                               | 20     |
+| └ cameraOnline   | Long    | 否   | -        | 在线摄像头数                             | 15     |
+| └ cameraOffline  | Long    | 否   | -        | 离线摄像头数                             | 5      |
+| └ channelTotal   | Long    | 否   | -        | 通道总数                                 | 40     |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "machineTotal": 10,
+        "machineEnabled": 8,
+        "cameraTotal": 20,
+        "cameraOnline": 15,
+        "cameraOffline": 5,
+        "channelTotal": 40
+    }
+}
+```
+
+#### 错误码
+
+无
+
+## 本地视频推流 Controller
+
+本地视频直接推送到 Cloudflare WHIP:
+
+- POST /stream/local/start 启动本地视频推流
+- POST /stream/local/stop 停止本地视频推流
+- GET /stream/local/{name} 获取推流状态
+- GET /stream/local/list 获取所有本地视频推流
+
+### 启动本地视频推流
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/stream/local/start
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/stream/local/start
+
+描述:启动本地视频推流
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称            | 类型    | 必填 | 最大长度 | 描述                                        | 示例值 |
+| --------------- | ------- | ---- | -------- | ------------------------------------------- | ------ |
+| streamName      | String  | 否   | -        | 流名称(唯一标识)                          |        |
+| videoPath       | String  | 否   | -        | 视频文件路径                                |        |
+| loop            | Boolean | 否   | -        | 是否循环播放                                | true   |
+| targetChannelId | String  | 否   | -        | 目标推流通道 ID(可选,不传则使用默认通道) |        |
+
+#### 请求示例
+
+```
+{
+    "streamName": "string",
+    "videoPath": "string",
+    "loop": true,
+    "targetChannelId": "string"
+}
+```
+
+#### 响应参数
+
+| 名称           | 类型    | 必填 | 最大长度 | 描述                                       | 示例值 |
+| -------------- | ------- | ---- | -------- | ------------------------------------------ | ------ |
+| success        | Boolean | 否   | -        | 请求是否成功                               | true   |
+| errCode        | String  | 否   | -        | 错误码(失败时返回)                       |        |
+| errMessage     | String  | 否   | -        | 错误信息(失败时返回)                     |        |
+| data           | object  | 否   |          | 响应数据 (ActualType: LocalVideoStreamDTO) |        |
+| └ streamName   | String  | 否   | -        | 流名称                                     |        |
+| └ sourceType   | String  | 否   | -        | 源类型:local_video 或 rtsp_camera         |        |
+| └ sourcePath   | String  | 否   | -        | 源路径(视频文件路径或 RTSP URL)          |        |
+| └ rtspUrl      | String  | 否   | -        | MediaMTX 提供的 RTSP URL                   |        |
+| └ loop         | Boolean | 否   | -        | 是否循环播放                               | true   |
+| └ streamTaskId | String  | 否   | -        | 推流任务 ID(如果已推送到 Cloudflare)     |        |
+| └ playbackUrl  | String  | 否   | -        | 播放地址(Cloudflare HLS/WHEP)            |        |
+| └ status       | String  | 否   | -        | 状态                                       |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "streamName": "string",
+        "sourceType": "string",
+        "sourcePath": "string",
+        "rtspUrl": "string",
+        "loop": true,
+        "streamTaskId": "string",
+        "playbackUrl": "string",
+        "status": "string"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 停止本地视频推流
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/stream/local/stop
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/stream/local/stop
+
+描述:停止本地视频推流
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称       | 类型   | 必填 | 最大长度 | 描述               | 示例值 |
+| ---------- | ------ | ---- | -------- | ------------------ | ------ |
+| streamName | string | 是   | -        | No comments found. |        |
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | object  | 否   | -        | 响应数据 (ActualType: Void) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {}
+}
+```
+
+#### 错误码
+
+无
+
+### 获取本地视频推流状态
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/stream/local/{streamName}
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/stream/local/{streamName}
+
+描述:获取本地视频推流状态
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### Path 参数
+
+| 名称       | 必填 | 描述               | 示例值 |
+| ---------- | ---- | ------------------ | ------ |
+| streamName | 是   | No comments found. |        |
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称           | 类型    | 必填 | 最大长度 | 描述                                       | 示例值 |
+| -------------- | ------- | ---- | -------- | ------------------------------------------ | ------ |
+| success        | Boolean | 否   | -        | 请求是否成功                               | true   |
+| errCode        | String  | 否   | -        | 错误码(失败时返回)                       |        |
+| errMessage     | String  | 否   | -        | 错误信息(失败时返回)                     |        |
+| data           | object  | 否   |          | 响应数据 (ActualType: LocalVideoStreamDTO) |        |
+| └ streamName   | String  | 否   | -        | 流名称                                     |        |
+| └ sourceType   | String  | 否   | -        | 源类型:local_video 或 rtsp_camera         |        |
+| └ sourcePath   | String  | 否   | -        | 源路径(视频文件路径或 RTSP URL)          |        |
+| └ rtspUrl      | String  | 否   | -        | MediaMTX 提供的 RTSP URL                   |        |
+| └ loop         | Boolean | 否   | -        | 是否循环播放                               | true   |
+| └ streamTaskId | String  | 否   | -        | 推流任务 ID(如果已推送到 Cloudflare)     |        |
+| └ playbackUrl  | String  | 否   | -        | 播放地址(Cloudflare HLS/WHEP)            |        |
+| └ status       | String  | 否   | -        | 状态                                       |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "streamName": "string",
+        "sourceType": "string",
+        "sourcePath": "string",
+        "rtspUrl": "string",
+        "loop": true,
+        "streamTaskId": "string",
+        "playbackUrl": "string",
+        "status": "string"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 获取所有本地视频推流
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/stream/local/list
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/stream/local/list
+
+描述:获取所有本地视频推流
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称           | 类型    | 必填 | 最大长度 | 描述                                   | 示例值 |
+| -------------- | ------- | ---- | -------- | -------------------------------------- | ------ |
+| success        | Boolean | 否   | -        | 请求是否成功                           | true   |
+| errCode        | String  | 否   | -        | 错误码(失败时返回)                   |        |
+| errMessage     | String  | 否   | -        | 错误信息(失败时返回)                 |        |
+| data           | array   | 否   |          | 响应数据 (ActualType: List)            |        |
+| └ streamName   | String  | 否   | -        | 流名称                                 |        |
+| └ sourceType   | String  | 否   | -        | 源类型:local_video 或 rtsp_camera     |        |
+| └ sourcePath   | String  | 否   | -        | 源路径(视频文件路径或 RTSP URL)      |        |
+| └ rtspUrl      | String  | 否   | -        | MediaMTX 提供的 RTSP URL               |        |
+| └ loop         | Boolean | 否   | -        | 是否循环播放                           | true   |
+| └ streamTaskId | String  | 否   | -        | 推流任务 ID(如果已推送到 Cloudflare) |        |
+| └ playbackUrl  | String  | 否   | -        | 播放地址(Cloudflare HLS/WHEP)        |        |
+| └ status       | String  | 否   | -        | 状态                                   |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": [
+        {
+            "streamName": "string",
+            "sourceType": "string",
+            "sourcePath": "string",
+            "rtspUrl": "string",
+            "loop": true,
+            "streamTaskId": "string",
+            "playbackUrl": "string",
+            "status": "string"
+        }
+    ]
+}
+```
+
+#### 错误码
+
+无
+
+## 推流服务 Controller
+
+推流管理 API 接口:
+
+- POST /stream/start 启动推流任务
+- POST /stream/stop 停止推流任务
+- GET /stream/task/{taskId} 获取任务状态
+- GET /stream/tasks 获取机器推流任务列表
+- GET /stream/tasks/active 获取所有活动任务
+- GET /stream/channels 获取推流通道列表
+- POST /stream/task/{taskId}/switch 切换推流源
+
+### 启动推流任务
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/stream/start
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/stream/start
+
+描述:启动推流任务
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| machineId | String | 否 | - | 机器 ID(必填) | machine_001 |
+| cameraId | String | 否 | - | 摄像头 ID(必填) | hikvision_ptz |
+| cameraChannelId | String | 否 | - | 摄像头通道 ID(必填) | hik_main |
+| sourceRtspUrl | String | 否 | - | 源 RTSP 地址(可选,如不传则从摄像头服务获取) | rtsp://admin:password@192.168.1.101:554/stream1 |
+| targetChannelId | String | 否 | - | 目标推流通道 ID(可选,如不传则使用默认通道) | cf_channel_001 |
+
+#### 请求示例
+
+```
+{
+    "machineId": "machine_001",
+    "cameraId": "hikvision_ptz",
+    "cameraChannelId": "hik_main",
+    "sourceRtspUrl": "rtsp://admin:password@192.168.1.101:554/stream1",
+    "targetChannelId": "cf_channel_001"
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: StreamTaskDTO) |  |
+| └ taskId | String | 否 | - | 任务 ID | task_abc123def456 |
+| └ machineId | String | 否 | - | 机器 ID | machine_001 |
+| └ cameraId | String | 否 | - | 摄像头 ID | hikvision_ptz |
+| └ cameraChannelId | String | 否 | - | 摄像头通道 ID | hik_main |
+| └ sourceRtspUrl | String | 否 | - | 源 RTSP 地址 | rtsp://admin:password@192.168.1.101:554/stream1 |
+| └ status | String | 否 | - | 推流状态: IDLE, STARTING, STREAMING, STOPPED, ERROR | STREAMING |
+| └ statusDescription | String | 否 | - | 状态描述 | 推流中 |
+| └ hlsPlaybackUrl | String | 否 | - | HLS 播放地址 | https://customer-xxx.cloudflarestream.com/xxx/manifest/video.m3u8 |
+| └ errorMessage | String | 否 | - | 错误信息 |  |
+| └ retryCount | int | 否 | - | 重试次数 | 0 |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2024-01-15T10:30:00 |
+| └ startedAt | LocalDateTime | 否 | - | 开始推流时间 | 2024-01-15T10:30:05 |
+| └ stoppedAt | LocalDateTime | 否 | - | 停止推流时间 | yyyy-MM-dd HH:mm:ss |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "taskId": "task_abc123def456",
+        "machineId": "machine_001",
+        "cameraId": "hikvision_ptz",
+        "cameraChannelId": "hik_main",
+        "sourceRtspUrl": "rtsp://admin:password@192.168.1.101:554/stream1",
+        "status": "STREAMING",
+        "statusDescription": "推流中",
+        "hlsPlaybackUrl": "https://customer-xxx.cloudflarestream.com/xxx/manifest/video.m3u8",
+        "errorMessage": "string",
+        "retryCount": 0,
+        "createdAt": "2024-01-15T10:30:00",
+        "startedAt": "2024-01-15T10:30:05",
+        "stoppedAt": "yyyy-MM-dd HH:mm:ss"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 停止推流任务
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/stream/stop
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/stream/stop
+
+描述:停止推流任务
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称      | 类型   | 必填 | 最大长度 | 描述                                            | 示例值            |
+| --------- | ------ | ---- | -------- | ----------------------------------------------- | ----------------- |
+| taskId    | String | 否   | -        | 任务 ID(与 machineId 二选一)                  | task_abc123def456 |
+| machineId | String | 否   | -        | 机器 ID(与 taskId 二选一,停止该机器所有推流) | machine_001       |
+
+#### 请求示例
+
+```
+{
+    "taskId": "task_abc123def456",
+    "machineId": "machine_001"
+}
+```
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | object  | 否   | -        | 响应数据 (ActualType: Void) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {}
+}
+```
+
+#### 错误码
+
+无
+
+### 获取任务状态
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/stream/task/{taskId}
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/stream/task/{taskId}
+
+描述:获取任务状态
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### Path 参数
+
+| 名称   | 必填 | 描述    | 示例值            |
+| ------ | ---- | ------- | ----------------- |
+| taskId | 是   | 任务 ID | task_abc123def456 |
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: StreamTaskDTO) |  |
+| └ taskId | String | 否 | - | 任务 ID | task_abc123def456 |
+| └ machineId | String | 否 | - | 机器 ID | machine_001 |
+| └ cameraId | String | 否 | - | 摄像头 ID | hikvision_ptz |
+| └ cameraChannelId | String | 否 | - | 摄像头通道 ID | hik_main |
+| └ sourceRtspUrl | String | 否 | - | 源 RTSP 地址 | rtsp://admin:password@192.168.1.101:554/stream1 |
+| └ status | String | 否 | - | 推流状态: IDLE, STARTING, STREAMING, STOPPED, ERROR | STREAMING |
+| └ statusDescription | String | 否 | - | 状态描述 | 推流中 |
+| └ hlsPlaybackUrl | String | 否 | - | HLS 播放地址 | https://customer-xxx.cloudflarestream.com/xxx/manifest/video.m3u8 |
+| └ errorMessage | String | 否 | - | 错误信息 |  |
+| └ retryCount | int | 否 | - | 重试次数 | 0 |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2024-01-15T10:30:00 |
+| └ startedAt | LocalDateTime | 否 | - | 开始推流时间 | 2024-01-15T10:30:05 |
+| └ stoppedAt | LocalDateTime | 否 | - | 停止推流时间 | yyyy-MM-dd HH:mm:ss |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "taskId": "task_abc123def456",
+        "machineId": "machine_001",
+        "cameraId": "hikvision_ptz",
+        "cameraChannelId": "hik_main",
+        "sourceRtspUrl": "rtsp://admin:password@192.168.1.101:554/stream1",
+        "status": "STREAMING",
+        "statusDescription": "推流中",
+        "hlsPlaybackUrl": "https://customer-xxx.cloudflarestream.com/xxx/manifest/video.m3u8",
+        "errorMessage": "string",
+        "retryCount": 0,
+        "createdAt": "2024-01-15T10:30:00",
+        "startedAt": "2024-01-15T10:30:05",
+        "stoppedAt": "yyyy-MM-dd HH:mm:ss"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 获取机器推流任务列表
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/stream/tasks
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/stream/tasks
+
+描述:获取机器推流任务列表
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称      | 类型   | 必填 | 最大长度 | 描述    | 示例值      |
+| --------- | ------ | ---- | -------- | ------- | ----------- |
+| machineId | string | 是   | -        | 机器 ID | machine_001 |
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | array | 否 |  | 响应数据 (ActualType: List) |  |
+| └ taskId | String | 否 | - | 任务 ID | task_abc123def456 |
+| └ machineId | String | 否 | - | 机器 ID | machine_001 |
+| └ cameraId | String | 否 | - | 摄像头 ID | hikvision_ptz |
+| └ cameraChannelId | String | 否 | - | 摄像头通道 ID | hik_main |
+| └ sourceRtspUrl | String | 否 | - | 源 RTSP 地址 | rtsp://admin:password@192.168.1.101:554/stream1 |
+| └ status | String | 否 | - | 推流状态: IDLE, STARTING, STREAMING, STOPPED, ERROR | STREAMING |
+| └ statusDescription | String | 否 | - | 状态描述 | 推流中 |
+| └ hlsPlaybackUrl | String | 否 | - | HLS 播放地址 | https://customer-xxx.cloudflarestream.com/xxx/manifest/video.m3u8 |
+| └ errorMessage | String | 否 | - | 错误信息 |  |
+| └ retryCount | int | 否 | - | 重试次数 | 0 |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2024-01-15T10:30:00 |
+| └ startedAt | LocalDateTime | 否 | - | 开始推流时间 | 2024-01-15T10:30:05 |
+| └ stoppedAt | LocalDateTime | 否 | - | 停止推流时间 | yyyy-MM-dd HH:mm:ss |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": [
+        {
+            "taskId": "task_abc123def456",
+            "machineId": "machine_001",
+            "cameraId": "hikvision_ptz",
+            "cameraChannelId": "hik_main",
+            "sourceRtspUrl": "rtsp://admin:password@192.168.1.101:554/stream1",
+            "status": "STREAMING",
+            "statusDescription": "推流中",
+            "hlsPlaybackUrl": "https://customer-xxx.cloudflarestream.com/xxx/manifest/video.m3u8",
+            "errorMessage": "string",
+            "retryCount": 0,
+            "createdAt": "2024-01-15T10:30:00",
+            "startedAt": "2024-01-15T10:30:05",
+            "stoppedAt": "yyyy-MM-dd HH:mm:ss"
+        }
+    ]
+}
+```
+
+#### 错误码
+
+无
+
+### 获取所有活动任务
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/stream/tasks/active
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/stream/tasks/active
+
+描述:获取所有活动任务
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | array | 否 |  | 响应数据 (ActualType: List) |  |
+| └ taskId | String | 否 | - | 任务 ID | task_abc123def456 |
+| └ machineId | String | 否 | - | 机器 ID | machine_001 |
+| └ cameraId | String | 否 | - | 摄像头 ID | hikvision_ptz |
+| └ cameraChannelId | String | 否 | - | 摄像头通道 ID | hik_main |
+| └ sourceRtspUrl | String | 否 | - | 源 RTSP 地址 | rtsp://admin:password@192.168.1.101:554/stream1 |
+| └ status | String | 否 | - | 推流状态: IDLE, STARTING, STREAMING, STOPPED, ERROR | STREAMING |
+| └ statusDescription | String | 否 | - | 状态描述 | 推流中 |
+| └ hlsPlaybackUrl | String | 否 | - | HLS 播放地址 | https://customer-xxx.cloudflarestream.com/xxx/manifest/video.m3u8 |
+| └ errorMessage | String | 否 | - | 错误信息 |  |
+| └ retryCount | int | 否 | - | 重试次数 | 0 |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2024-01-15T10:30:00 |
+| └ startedAt | LocalDateTime | 否 | - | 开始推流时间 | 2024-01-15T10:30:05 |
+| └ stoppedAt | LocalDateTime | 否 | - | 停止推流时间 | yyyy-MM-dd HH:mm:ss |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": [
+        {
+            "taskId": "task_abc123def456",
+            "machineId": "machine_001",
+            "cameraId": "hikvision_ptz",
+            "cameraChannelId": "hik_main",
+            "sourceRtspUrl": "rtsp://admin:password@192.168.1.101:554/stream1",
+            "status": "STREAMING",
+            "statusDescription": "推流中",
+            "hlsPlaybackUrl": "https://customer-xxx.cloudflarestream.com/xxx/manifest/video.m3u8",
+            "errorMessage": "string",
+            "retryCount": 0,
+            "createdAt": "2024-01-15T10:30:00",
+            "startedAt": "2024-01-15T10:30:05",
+            "stoppedAt": "yyyy-MM-dd HH:mm:ss"
+        }
+    ]
+}
+```
+
+#### 错误码
+
+无
+
+### 获取推流通道列表
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/stream/channels
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/stream/channels
+
+描述:获取推流通道列表
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | array | 否 |  | 响应数据 (ActualType: List) |  |
+| └ channelId | String | 否 | - | 通道 ID | cf_channel_001 |
+| └ name | String | 否 | - | 通道名称 | 主推流通道 |
+| └ mode | String | 否 | - | 推流模式: WHIP, RTMPS | WHIP |
+| └ hlsPlaybackUrl | String | 否 | - | HLS 播放地址 | https://customer-xxx.cloudflarestream.com/xxx/manifest/video.m3u8 |
+| └ recordingEnabled | boolean | 否 | - | 是否启用录制 | false |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": [
+        {
+            "channelId": "cf_channel_001",
+            "name": "主推流通道",
+            "mode": "WHIP",
+            "hlsPlaybackUrl": "https://customer-xxx.cloudflarestream.com/xxx/manifest/video.m3u8",
+            "recordingEnabled": false
+        }
+    ]
+}
+```
+
+#### 错误码
+
+无
+
+### 切换推流源
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/stream/switch
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/stream/switch
+
+描述:切换推流源
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称         | 类型   | 必填 | 最大长度 | 描述              | 示例值                                          |
+| ------------ | ------ | ---- | -------- | ----------------- | ----------------------------------------------- |
+| taskId       | String | 是   | -        | 任务 ID           | task_abc123def456                               |
+| newChannelId | String | 是   | -        | 新的摄像头通道 ID | hik_main                                        |
+| newRtspUrl   | String | 是   | -        | 新的 RTSP 地址    | rtsp://admin:password@192.168.1.101:554/stream1 |
+
+#### 请求示例
+
+```
+{
+    "taskId": "task_abc123def456",
+    "newChannelId": "hik_main",
+    "newRtspUrl": "rtsp://admin:password@192.168.1.101:554/stream1"
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: StreamTaskDTO) |  |
+| └ taskId | String | 否 | - | 任务 ID | task_abc123def456 |
+| └ machineId | String | 否 | - | 机器 ID | machine_001 |
+| └ cameraId | String | 否 | - | 摄像头 ID | hikvision_ptz |
+| └ cameraChannelId | String | 否 | - | 摄像头通道 ID | hik_main |
+| └ sourceRtspUrl | String | 否 | - | 源 RTSP 地址 | rtsp://admin:password@192.168.1.101:554/stream1 |
+| └ status | String | 否 | - | 推流状态: IDLE, STARTING, STREAMING, STOPPED, ERROR | STREAMING |
+| └ statusDescription | String | 否 | - | 状态描述 | 推流中 |
+| └ hlsPlaybackUrl | String | 否 | - | HLS 播放地址 | https://customer-xxx.cloudflarestream.com/xxx/manifest/video.m3u8 |
+| └ errorMessage | String | 否 | - | 错误信息 |  |
+| └ retryCount | int | 否 | - | 重试次数 | 0 |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2024-01-15T10:30:00 |
+| └ startedAt | LocalDateTime | 否 | - | 开始推流时间 | 2024-01-15T10:30:05 |
+| └ stoppedAt | LocalDateTime | 否 | - | 停止推流时间 | yyyy-MM-dd HH:mm:ss |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "taskId": "task_abc123def456",
+        "machineId": "machine_001",
+        "cameraId": "hikvision_ptz",
+        "cameraChannelId": "hik_main",
+        "sourceRtspUrl": "rtsp://admin:password@192.168.1.101:554/stream1",
+        "status": "STREAMING",
+        "statusDescription": "推流中",
+        "hlsPlaybackUrl": "https://customer-xxx.cloudflarestream.com/xxx/manifest/video.m3u8",
+        "errorMessage": "string",
+        "retryCount": 0,
+        "createdAt": "2024-01-15T10:30:00",
+        "startedAt": "2024-01-15T10:30:05",
+        "stoppedAt": "yyyy-MM-dd HH:mm:ss"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+## 房间控制 Controller
+
+房间播放 API 接口:
+
+- GET /room/{roomId}/play 获取播放信息(按需启动推流)
+- POST /room/{roomId}/stop 停止房间推流
+
+### 获取房间播放信息
+
+按需启动推流:
+
+- 检查 FFmpeg 是否在跑
+- 没跑则自动启动
+- 返回 WHEP URL (WebRTC 播放)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/room/{roomId}/play
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/room/{roomId}/play
+
+描述:获取房间播放信息
+
+按需启动推流:
+
+- 检查 FFmpeg 是否在跑
+- 没跑则自动启动
+- 返回 WHEP URL (WebRTC 播放)
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### Path 参数
+
+| 名称   | 必填 | 描述    | 示例值 |
+| ------ | ---- | ------- | ------ |
+| roomId | 是   | 房间 ID |        |
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称           | 类型    | 必填 | 最大长度 | 描述                                    | 示例值 |
+| -------------- | ------- | ---- | -------- | --------------------------------------- | ------ |
+| success        | Boolean | 否   | -        | 请求是否成功                            | true   |
+| errCode        | String  | 否   | -        | 错误码(失败时返回)                    |        |
+| errMessage     | String  | 否   | -        | 错误信息(失败时返回)                  |        |
+| data           | object  | 否   |          | 响应数据 (ActualType: RoomPlayResponse) |        |
+| └ roomId       | String  | 否   | -        | 房间 ID                                 |        |
+| └ taskId       | String  | 否   | -        | 推流任务 ID                             |        |
+| └ whepUrl      | String  | 否   | -        | WHEP 播放地址 (WebRTC)                  |        |
+| └ status       | String  | 否   | -        | 推流状态                                |        |
+| └ newlyStarted | boolean | 否   | -        | 是否为新启动的推流                      | true   |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "roomId": "string",
+        "taskId": "string",
+        "whepUrl": "string",
+        "status": "string",
+        "newlyStarted": true
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 停止房间推流
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/room/{roomId}/stop
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/room/{roomId}/stop
+
+描述:停止房间推流
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### Path 参数
+
+| 名称   | 必填 | 描述    | 示例值 |
+| ------ | ---- | ------- | ------ |
+| roomId | 是   | 房间 ID |        |
+
+#### 请求参数
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | object  | 否   | -        | 响应数据 (ActualType: Void) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {}
+}
+```
+
+#### 错误码
+
+无

+ 194 - 0
scripts/download-torna-openapi.sh

@@ -0,0 +1,194 @@
+#!/bin/bash
+
+# Torna OpenAPI 文档下载脚本
+# 用法: ./download-torna-openapi.sh
+
+TORNA_API_URL="https://dev-torna.pwtk.cc/api"
+TOKEN="339f378808a9416ca5365e5b991e0878"
+OUTPUT_DIR="./torna-docs"
+VERSION="1.0"
+
+mkdir -p "$OUTPUT_DIR"
+
+echo "========================================"
+echo "Torna OpenAPI 文档下载工具"
+echo "========================================"
+echo ""
+
+# 函数:发送 OpenAPI 请求 (使用 Torna SDK 的格式 - JSON Body)
+send_request() {
+    local api_name="$1"
+    local data="$2"
+    local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
+
+    # URL 编码 data 参数
+    local encoded_data=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$data'))")
+
+    # 构建 JSON 请求体
+    local json_body=$(cat << EOF
+{
+    "name": "$api_name",
+    "version": "$VERSION",
+    "data": "$encoded_data",
+    "timestamp": "$timestamp",
+    "access_token": "$TOKEN"
+}
+EOF
+)
+
+    curl -s -X POST "${TORNA_API_URL}" \
+        -H "Content-Type: application/json" \
+        -H "Accept-Language: zh-CN" \
+        -d "$json_body"
+}
+
+# 1. 获取模块信息
+echo ">>> 1. 获取模块信息 (module.get)..."
+MODULE_RESULT=$(send_request "module.get" "{}")
+echo "$MODULE_RESULT" | jq .
+echo "$MODULE_RESULT" > "$OUTPUT_DIR/module-info.json"
+echo ""
+
+# 2. 获取文档列表
+echo ">>> 2. 获取文档列表 (doc.list)..."
+DOC_LIST_RESULT=$(send_request "doc.list" "{}")
+echo "$DOC_LIST_RESULT" | jq .
+echo "$DOC_LIST_RESULT" > "$OUTPUT_DIR/doc-list.json"
+echo ""
+
+# 检查是否成功
+CODE=$(echo "$DOC_LIST_RESULT" | jq -r '.code // "1"')
+if [ "$CODE" != "0" ]; then
+    echo "请求失败,错误信息: $(echo "$DOC_LIST_RESULT" | jq -r '.msg')"
+    echo ""
+    echo "可能的原因:"
+    echo "1. Token 已过期或无效"
+    echo "2. API URL 不正确"
+    echo ""
+    exit 1
+fi
+
+# 提取文档 ID 列表
+DOC_IDS=$(echo "$DOC_LIST_RESULT" | jq -r '.data.docList[]?.id // empty' 2>/dev/null | head -50)
+
+if [ -n "$DOC_IDS" ]; then
+    echo ">>> 3. 获取文档详情..."
+
+    # 将 ID 列表转换为 JSON 数组
+    IDS_ARRAY=$(echo "$DOC_IDS" | jq -R -s 'split("\n") | map(select(length > 0) | tonumber)')
+
+    # 批量获取文档详情
+    DETAILS_RESULT=$(send_request "doc.details" "{\"docIds\":$IDS_ARRAY}")
+    echo "$DETAILS_RESULT" | jq .
+    echo "$DETAILS_RESULT" > "$OUTPUT_DIR/doc-details.json"
+    echo ""
+
+    # 转换为 OpenAPI 格式
+    echo ">>> 4. 生成 OpenAPI 规范文件..."
+
+    python3 << 'PYTHON_SCRIPT'
+import json
+import sys
+
+try:
+    with open("./torna-docs/doc-details.json", "r") as f:
+        data = json.load(f)
+
+    with open("./torna-docs/module-info.json", "r") as f:
+        module_data = json.load(f)
+
+    module_name = module_data.get("data", {}).get("name", "Torna API")
+
+    openapi = {
+        "openapi": "3.0.0",
+        "info": {
+            "title": module_name,
+            "version": "1.0.0",
+            "description": "从 Torna 导出的 API 文档"
+        },
+        "servers": [],
+        "paths": {},
+        "components": {
+            "schemas": {}
+        }
+    }
+
+    docs = data.get("data", [])
+    if not docs:
+        docs = []
+
+    for doc in docs:
+        if doc.get("isFolder") == 1:
+            continue
+
+        url = doc.get("url", "/unknown")
+        if not url:
+            continue
+
+        method = (doc.get("httpMethod") or "GET").lower()
+
+        if url not in openapi["paths"]:
+            openapi["paths"][url] = {}
+
+        operation = {
+            "summary": doc.get("name", ""),
+            "description": doc.get("description", ""),
+            "operationId": f"{method}_{url.replace('/', '_').replace('{', '').replace('}', '')}",
+            "tags": [],
+            "parameters": [],
+            "responses": {
+                "200": {
+                    "description": "成功响应"
+                }
+            }
+        }
+
+        # 添加请求参数
+        for param in (doc.get("requestParams") or []):
+            param_obj = {
+                "name": param.get("name", ""),
+                "in": "query",
+                "description": param.get("description", ""),
+                "required": param.get("required") == 1,
+                "schema": {
+                    "type": param.get("type", "string")
+                }
+            }
+            operation["parameters"].append(param_obj)
+
+        # 添加请求头
+        for header in (doc.get("headerParams") or []):
+            header_obj = {
+                "name": header.get("name", ""),
+                "in": "header",
+                "description": header.get("description", ""),
+                "required": header.get("required") == 1,
+                "schema": {
+                    "type": "string"
+                }
+            }
+            operation["parameters"].append(header_obj)
+
+        openapi["paths"][url][method] = operation
+
+    with open("./torna-docs/openapi.json", "w", encoding="utf-8") as f:
+        json.dump(openapi, f, ensure_ascii=False, indent=2)
+
+    print(f"成功生成 OpenAPI 文档,包含 {len(openapi['paths'])} 个接口路径")
+
+except Exception as e:
+    print(f"转换失败: {e}")
+    import traceback
+    traceback.print_exc()
+    sys.exit(1)
+PYTHON_SCRIPT
+
+else
+    echo "未找到文档 ID,跳过详情获取"
+fi
+
+echo ""
+echo "========================================"
+echo "下载完成!文件保存在: $OUTPUT_DIR/"
+echo "========================================"
+ls -la "$OUTPUT_DIR/"

+ 34 - 10
src/api/camera.ts

@@ -1,22 +1,26 @@
 import { get, post } from '@/utils/request'
 import type {
-  IResponse,
   IBaseResponse,
+  IListResponse,
+  IPageResponse,
   BaseResponse,
   CameraDTO,
   ChannelDTO,
   CameraInfoDTO,
   CameraAddRequest,
   CameraUpdateRequest,
+  CameraListRequest,
   SwitchChannelRequest,
-  PTZAction
+  PTZAction,
+  PTZControlRequest,
+  AdminPTZRequest
 } from '@/types'
 
 // ==================== Controller APIs (MVP) ====================
 
-// 获取摄像头列表
-export function listCameras(machineId?: string): Promise<IResponse<CameraDTO>> {
-  return get('/camera/list', machineId ? { machineId } : undefined)
+// 获取摄像头列表 (POST)
+export function listCameras(machineId?: string): Promise<IListResponse<CameraDTO>> {
+  return post('/camera/list', machineId ? { machineId } : {})
 }
 
 // 获取摄像头信息
@@ -46,11 +50,21 @@ export function ptzStop(cameraId: string): Promise<BaseResponse> {
   return post(`/camera/${cameraId}/ptz/stop`)
 }
 
+// PTZ 直接控制 (pan/tilt/zoom 方式)
+export function ptzDirectControl(cameraId: string, data: PTZControlRequest): Promise<BaseResponse> {
+  return post(`/camera/${cameraId}/ptz/control`, data)
+}
+
 // ==================== Admin APIs ====================
 
-// 获取摄像头列表 (管理后台)
-export function adminListCameras(machineId?: string): Promise<IResponse<CameraInfoDTO>> {
-  return get('/admin/cameras/list', machineId ? { machineId } : undefined)
+// 获取摄像头列表 (管理后台,分页)
+export function adminListCameras(params?: CameraListRequest): Promise<IPageResponse<CameraInfoDTO>> {
+  return post('/admin/cameras/list', params || {})
+}
+
+// 获取摄像头列表 (全部,不分页)
+export function adminListAllCameras(machineId?: string): Promise<IListResponse<CameraInfoDTO>> {
+  return get('/admin/cameras/listAll', machineId ? { machineId } : undefined)
 }
 
 // 获取摄像头详情
@@ -82,10 +96,20 @@ export function adminCheckCamera(id: number): Promise<IBaseResponse<boolean>> {
   })
 }
 
+// 获取摄像头快照
+export function adminGetSnapshot(id: number): Promise<IBaseResponse<Blob>> {
+  return get('/admin/cameras/snapshot', { id }, { responseType: 'blob' })
+}
+
+// PTZ 控制 (Admin)
+export function adminPTZControl(data: AdminPTZRequest): Promise<IBaseResponse<boolean>> {
+  return post('/admin/cameras/ptz', data)
+}
+
 // ==================== 兼容旧代码的别名 ====================
 
-// 获取设备列表 (兼容)
-export const listDevice = adminListCameras
+// 获取设备列表 (兼容 - 使用不分页接口)
+export const listDevice = adminListAllCameras
 
 // 获取设备详情 (兼容)
 export const getDevice = (deviceId: string) => getCamera(deviceId)

+ 12 - 12
src/api/machine.ts

@@ -1,23 +1,23 @@
 import { get, post } from '@/utils/request'
 import type {
-  IResponse,
   IBaseResponse,
+  IPageResponse,
+  IListResponse,
   BaseResponse,
   MachineDTO,
   MachineAddRequest,
-  MachineUpdateRequest
+  MachineUpdateRequest,
+  MachineListRequest
 } from '@/types'
 
-// 获取机器列表
-export function listMachines(params?: {
-  page?: number
-  size?: number
-  keyword?: string
-  enabled?: boolean
-  sortBy?: string
-  sortDir?: 'ASC' | 'DESC'
-}): Promise<IResponse<MachineDTO>> {
-  return get('/admin/machines/list', params)
+// 获取机器列表 (分页)
+export function listMachines(params?: MachineListRequest): Promise<IPageResponse<MachineDTO>> {
+  return post('/admin/machines/list', params || {})
+}
+
+// 获取机器列表 (全部,不分页)
+export function listAllMachines(): Promise<IListResponse<MachineDTO>> {
+  return get('/admin/machines/listAll')
 }
 
 // 获取机器详情

+ 80 - 3
src/types/index.ts

@@ -27,18 +27,55 @@ export interface IResponse<T> {
 // 兼容旧代码 - ApiResponse 别名
 export type ApiResponse<T = any> = IResponse<T>
 
-// 分页参数
+// 分页参数(旧版,兼容)
 export interface PageParams {
   pageNum: number
   pageSize: number
 }
 
-// 分页响应
+// 分页响应(旧版,兼容)
 export interface PageResult<T = any> {
   total: number
   rows: T[]
 }
 
+// 分页请求参数(新版)
+export interface PageRequest {
+  page?: number
+  size?: number
+  keyword?: string
+  enabled?: boolean | null
+  sortBy?: string
+  sortDir?: 'ASC' | 'DESC'
+}
+
+// 分页响应(新版)
+export interface PageResponse<T> {
+  list: T[]
+  page: number
+  size: number
+  total: number
+  totalPages: number
+  hasNext: boolean
+  hasPrevious: boolean
+}
+
+// 带分页的 API 响应
+export interface IPageResponse<T> {
+  success: boolean
+  errCode?: string
+  errMessage?: string
+  data: PageResponse<T>
+}
+
+// 列表 API 响应(数组形式)
+export interface IListResponse<T> {
+  success: boolean
+  errCode?: string
+  errMessage?: string
+  data: T[]
+}
+
 // 登录参数
 export interface LoginParams {
   username: string
@@ -221,7 +258,47 @@ export interface ChangePasswordRequest {
 }
 
 // PTZ 动作类型
-export type PTZAction = 'up' | 'down' | 'left' | 'right' | 'zoom_in' | 'zoom_out' | 'stop'
+export type PTZAction =
+  | 'up'
+  | 'down'
+  | 'left'
+  | 'right'
+  | 'zoom_in'
+  | 'zoom_out'
+  | 'stop'
+  | 'UP'
+  | 'DOWN'
+  | 'LEFT'
+  | 'RIGHT'
+  | 'ZOOM_IN'
+  | 'ZOOM_OUT'
+  | 'STOP'
+
+// PTZ 直接控制请求 (pan/tilt/zoom 方式)
+export interface PTZControlRequest {
+  pan?: number // 水平移动速度 -100 ~ 100
+  tilt?: number // 垂直移动速度 -100 ~ 100
+  zoom?: number // 缩放速度 -100 ~ 100
+  channel?: number // 通道号(可选)
+}
+
+// Admin PTZ 控制请求
+export interface AdminPTZRequest {
+  id: number
+  action: PTZAction
+  speed?: number // 0.0 - 1.0
+}
+
+// 摄像头列表请求参数
+export interface CameraListRequest extends PageRequest {
+  machineId?: string
+  status?: 'ONLINE' | 'OFFLINE'
+}
+
+// 机器列表请求参数
+export interface MachineListRequest extends PageRequest {
+  // 继承 PageRequest 的所有属性
+}
 
 // 兼容旧代码的类型别名
 export type CameraDevice = CameraInfoDTO

+ 8 - 4
src/views/camera/index.vue

@@ -186,7 +186,7 @@ import { ref, reactive, onMounted, computed } from 'vue'
 import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
 import { Search, Refresh, Plus, View, Edit, Delete, Connection } from '@element-plus/icons-vue'
 import { adminListCameras, adminAddCamera, adminUpdateCamera, adminDeleteCamera, adminCheckCamera } from '@/api/camera'
-import { listMachines } from '@/api/machine'
+import { listAllMachines } from '@/api/machine'
 import type { CameraInfoDTO, ChannelInfoDTO, CameraAddRequest, CameraUpdateRequest, MachineDTO } from '@/types'
 
 const loading = ref(false)
@@ -254,9 +254,9 @@ const rules: FormRules = {
 
 async function getMachines() {
   try {
-    const res = await listMachines()
+    const res = await listAllMachines()
     if (res.success) {
-      machineList.value = res.data.list
+      machineList.value = res.data
     }
   } catch (error) {
     console.error('获取机器列表失败', error)
@@ -266,7 +266,11 @@ async function getMachines() {
 async function getList() {
   loading.value = true
   try {
-    const res = await adminListCameras(queryParams.machineId || undefined)
+    const params = {
+      machineId: queryParams.machineId || undefined,
+      status: queryParams.status || undefined
+    }
+    const res = await adminListCameras(params)
     if (res.success) {
       cameraList.value = res.data.list
     }

+ 41 - 9
tests/e2e/mocks/api-handlers.ts

@@ -5,7 +5,9 @@ import {
   mockMachines,
   mockCameras,
   mockDashboardStats,
-  wrapResponse
+  wrapResponse,
+  wrapPageResponse,
+  wrapArrayResponse
 } from '../../fixtures'
 
 /**
@@ -73,15 +75,29 @@ export async function mockAdminInfoAPI(page: Page) {
 }
 
 /**
- * Mock machines list API responses
- * GET /admin/machines/list
+ * Mock machines list API responses (paginated)
+ * POST /admin/machines/list
  */
 export async function mockMachinesListAPI(page: Page) {
   await page.route('**/admin/machines/list', async (route) => {
     await route.fulfill({
       status: 200,
       contentType: 'application/json',
-      body: JSON.stringify(wrapResponse(mockMachines))
+      body: JSON.stringify(wrapPageResponse(mockMachines))
+    })
+  })
+}
+
+/**
+ * Mock machines listAll API responses (non-paginated)
+ * GET /admin/machines/listAll
+ */
+export async function mockMachinesListAllAPI(page: Page) {
+  await page.route('**/admin/machines/listAll', async (route) => {
+    await route.fulfill({
+      status: 200,
+      contentType: 'application/json',
+      body: JSON.stringify(wrapArrayResponse(mockMachines))
     })
   })
 }
@@ -104,29 +120,43 @@ export async function mockMachineDetailAPI(page: Page) {
 }
 
 /**
- * Mock cameras list API responses
- * GET /admin/cameras/list
+ * Mock cameras list API responses (paginated)
+ * POST /admin/cameras/list
  */
 export async function mockCamerasListAPI(page: Page) {
   await page.route('**/admin/cameras/list', async (route) => {
     await route.fulfill({
       status: 200,
       contentType: 'application/json',
-      body: JSON.stringify(wrapResponse(mockCameras))
+      body: JSON.stringify(wrapPageResponse(mockCameras))
+    })
+  })
+}
+
+/**
+ * Mock cameras listAll API responses (non-paginated)
+ * GET /admin/cameras/listAll
+ */
+export async function mockCamerasListAllAPI(page: Page) {
+  await page.route('**/admin/cameras/listAll', async (route) => {
+    await route.fulfill({
+      status: 200,
+      contentType: 'application/json',
+      body: JSON.stringify(wrapArrayResponse(mockCameras))
     })
   })
 }
 
 /**
  * Mock camera list API (controller)
- * GET /camera/list
+ * POST /camera/list
  */
 export async function mockCameraListAPI(page: Page) {
   await page.route('**/camera/list', async (route) => {
     await route.fulfill({
       status: 200,
       contentType: 'application/json',
-      body: JSON.stringify(wrapResponse(mockCameras))
+      body: JSON.stringify(wrapArrayResponse(mockCameras))
     })
   })
 }
@@ -153,8 +183,10 @@ export async function setupAuthenticatedMocks(page: Page) {
     mockAdminInfoAPI(page),
     mockLogoutAPI(page),
     mockMachinesListAPI(page),
+    mockMachinesListAllAPI(page),
     mockMachineDetailAPI(page),
     mockCamerasListAPI(page),
+    mockCamerasListAllAPI(page),
     mockCameraListAPI(page),
     mockDashboardStatsAPI(page)
   ])

+ 31 - 8
tests/fixtures/index.ts

@@ -190,7 +190,37 @@ export function wrapResponse<T>(data: T, success = true, errMessage?: string) {
 }
 
 /**
- * 包装分页列表响应 (IResponse<T>)
+ * 包装分页列表响应 (IPageResponse<T>) - 新版分页格式
+ */
+export function wrapPageResponse<T>(list: T[], page = 1, size = 20) {
+  const total = list.length
+  const totalPages = Math.ceil(total / size)
+  return {
+    success: true,
+    data: {
+      list,
+      page,
+      size,
+      total,
+      totalPages,
+      hasNext: page < totalPages,
+      hasPrevious: page > 1
+    }
+  }
+}
+
+/**
+ * 包装数组列表响应 (IListResponse<T>) - 不分页
+ */
+export function wrapArrayResponse<T>(list: T[]) {
+  return {
+    success: true,
+    data: list
+  }
+}
+
+/**
+ * @deprecated 使用 wrapPageResponse 代替
  */
 export function wrapListResponse<T>(list: T[], total?: number) {
   return {
@@ -213,10 +243,3 @@ export function wrapErrorResponse(errMessage: string, errCode = 'ERROR') {
     data: undefined
   }
 }
-
-/**
- * @deprecated 使用 wrapListResponse 代替
- */
-export function wrapPageResponse<T>(rows: T[], total?: number) {
-  return wrapListResponse(rows, total)
-}

+ 1 - 0
torna-docs/doc-list.json

@@ -0,0 +1 @@
+{ "code": "1", "data": null, "msg": "调用不存在的服务请求" }

+ 1 - 0
torna-docs/module-info.json

@@ -0,0 +1 @@
+{ "code": "1", "data": null, "msg": "调用不存在的服务请求" }