# 铁路无人机智能巡检平台——大疆上云 API 对接设计

> 文档状态：设计基线  
> 编写日期：2026-07-23  
> 适用项目：AItrackwalker 铁路无人机智能巡检平台  
> 参考实现：DJI Cloud API Demo 1.10.0（前端与后端）  
> 协议依据：大疆上云 API 当前官方文档，官方文档与 Demo 不一致时以官方文档和目标设备实测为准  
> 架构决策：首期即独立部署`uav-access-service`，DJI和后续纵横均作为厂商适配器接入

## 1. 文档目的

本文给出当前铁路无人机智能巡检平台对接大疆上云 API 的完整设计，覆盖：

- DJI Pilot 2 和大疆机场两种上云模式。
- 面向DJI、纵横等多厂商的独立接入服务边界。
- 设备拓扑、在线状态、遥测、航线、飞行任务、媒体、地图元素、直播和 HMS。
- 当前平台领域模型与大疆协议模型之间的转换边界。
- MQTT、HTTPS、WebSocket、对象存储的部署与安全要求。
- 数据库、接口、状态机、幂等、重试、监控、测试和分阶段实施方案。

本文不是对 DJI Demo 的直接移植方案。两个 Demo 用于理解协议交互和功能组织。生产实现从第一阶段就建设独立的`uav-access-service`，继续复用当前项目的 Spring Boot 3、PostgreSQL/PostGIS、Redis、Kafka、MinIO 和 Vue 3 技术栈；DJI只是首个厂商适配器，后续纵横等厂商通过相同接入契约扩展。

## 2. 设计依据与版本边界

### 2.1 官方资料

- [大疆上云 API 文档阅读指引](https://developer.dji.com/doc/cloud-api-tutorial/cn/tutorial-map.html)
- [产品架构](https://developer.dji.com/doc/cloud-api-tutorial/cn/overview/product-architecture.html)
- [产品支持](https://developer.dji.com/doc/cloud-api-tutorial/cn/overview/product-support.html)
- [Pilot 上云](https://developer.dji.com/doc/cloud-api-tutorial/cn/feature-set/pilot-feature-set/pilot-access-to-cloud.html)
- [MQTT Topic 定义](https://developer.dji.com/doc/cloud-api-tutorial/cn/api-reference/pilot-to-cloud/mqtt/topic-definition.html)
- [机场设备管理](https://developer.dji.com/doc/cloud-api-tutorial/cn/feature-set/dock-feature-set/dock-device-management.html)
- [机场航线管理](https://developer.dji.com/doc/cloud-api-tutorial/cn/feature-set/dock-feature-set/dock-wayline-management.html)
- [机场媒体管理](https://developer.dji.com/doc/cloud-api-tutorial/cn/feature-set/dock-feature-set/dock-media-management.html)
- [地图元素](https://developer.dji.com/doc/cloud-api-tutorial/cn/feature-set/pilot-feature-set/pilot-map-elements.html)
- [直播功能](https://developer.dji.com/doc/cloud-api-tutorial/cn/feature-set/dock-feature-set/dock-livestream.html)
- [HMS 功能](https://developer.dji.com/doc/cloud-api-tutorial/cn/feature-set/dock-feature-set/hms.html)
- [DJI WPML 总体介绍](https://developer.dji.com/doc/cloud-api-tutorial/cn/api-reference/dji-wpml/overview.html)
- [对象存储临时凭证](https://developer.dji.com/doc/cloud-api-tutorial/cn/api-reference/pilot-to-cloud/https/media-management/obtain-temporary-credential.html)

截至本文编写时，官方发布记录已高于 1.10.0，产品支持页也已覆盖大疆机场 3、Matrice 4D/4TD 等较新设备。因此：

1. Demo 1.10.0 只能作为交互和代码结构参考。
2. 不复制 Demo 中的机型枚举、物模型版本和错误码作为长期常量。
3. 每个生产连接必须冻结“Cloud API版本、设备型号、固件、Pilot 2版本、WPML版本”兼容矩阵。
4. 官方已经停止维护 Cloud API Demo 示例项目，但不是停止 Cloud API；生产代码不得依赖 Demo 后续更新。

### 2.2 本地 Demo 参考点

前端 Demo：

- [Pilot JSBridge封装](../Cloud-API-Demo-Web-1.10.0/src/api/pilot-bridge.ts)
- [Pilot登录与License校验](../Cloud-API-Demo-Web-1.10.0/src/pages/page-pilot/pilot-index.vue)
- [Pilot模块加载与设备绑定](../Cloud-API-Demo-Web-1.10.0/src/pages/page-pilot/pilot-home.vue)
- [航线接口](../Cloud-API-Demo-Web-1.10.0/src/api/wayline.ts)
- [媒体接口](../Cloud-API-Demo-Web-1.10.0/src/api/media.ts)
- [地图元素接口](../Cloud-API-Demo-Web-1.10.0/src/api/layer.ts)
- [WebSocket客户端](../Cloud-API-Demo-Web-1.10.0/src/websocket/index.ts)

后端 Demo：

- [MQTT Topic常量](../DJI-Cloud-API-Demo-1.10.0/cloud-sdk/src/main/java/com/dji/sdk/mqtt/TopicConst.java)
- [MQTT消息路由](../DJI-Cloud-API-Demo-1.10.0/cloud-sdk/src/main/java/com/dji/sdk/mqtt/InboundMessageRouter.java)
- [MQTT事件路由](../DJI-Cloud-API-Demo-1.10.0/cloud-sdk/src/main/java/com/dji/sdk/mqtt/events/EventsRouter.java)
- [设备拓扑与遥测处理](../DJI-Cloud-API-Demo-1.10.0/sample/src/main/java/com/dji/sample/manage/service/impl/SDKDeviceService.java)
- [航线任务准备与执行](../DJI-Cloud-API-Demo-1.10.0/sample/src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java)
- [航线进度与资源请求](../DJI-Cloud-API-Demo-1.10.0/sample/src/main/java/com/dji/sample/wayline/service/impl/SDKWaylineService.java)
- [存储凭证服务](../DJI-Cloud-API-Demo-1.10.0/sample/src/main/java/com/dji/sample/storage/service/impl/StorageServiceImpl.java)
- [媒体上传事件处理](../DJI-Cloud-API-Demo-1.10.0/sample/src/main/java/com/dji/sample/media/service/impl/MediaServiceImpl.java)
- [地图元素同步](../DJI-Cloud-API-Demo-1.10.0/sample/src/main/java/com/dji/sample/map/service/impl/WorkspaceElementServiceImpl.java)

## 3. 当前项目基础与差距

### 3.1 已有基础

当前项目已经具备：

- Spring Boot 3.3、Java 17业务平台。
- Vue 3 + TypeScript管理端。
- PostgreSQL 16 + PostGIS 3.4、Redis、Kafka、MinIO。
- 厂商适配器接口和本地模拟器。
- 厂商连接、无人机、机场、载荷、飞行任务、命令、厂商事件、遥测和航迹数据模型。
- 航线、航线版本、航点、载荷动作和发布流程。
- 巡检资源、预处理、AI分析、规则判定、告警、工单和归档闭环。
- 无人机连接、设备、任务和遥测管理页面。

关键现状文件：

- `platform/backend/src/main/java/com/ai/trackwalker/capability/uav/UavVendorAdapter.java`
- `platform/backend/src/main/java/com/ai/trackwalker/capability/uav/SimulatedUavVendorAdapter.java`
- `platform/backend/src/main/java/com/ai/trackwalker/capability/service/CapabilityCompletionService.java`
- `platform/backend/src/main/resources/db/migration/V13__capability_completion_foundation.sql`
- `frontend/src/views/uav-operations/UavOperationsView.vue`
- `infra/docker-compose.yml`

### 3.2 主要差距

| 范围 | 当前状态 | 目标状态 |
| --- | --- | --- |
| MQTT | 只有Kafka，没有面向设备的MQTT Broker | 增加支持TLS、ACL和持久会话的MQTT Broker |
| 服务边界 | 厂商模拟器和适配接口位于业务后端 | 独立部署`uav-access-service`，业务平台不直接依赖厂商SDK或协议 |
| 适配器 | 同步方法 + `Map<String,Object>` | DJI类型化协议适配器、异步命令和事件规范化 |
| 命令 | 数据库事务内调用适配器并立即改变任务状态 | Outbox发送、`tid/bid`关联、回复与进度事件驱动状态 |
| 设备 | 模拟器主动同步设备 | `status`拓扑推送为主，主动同步仅做管理校正 |
| 航线 | 平台统一航点JSON/PostGIS | 生成并冻结DJI WPML/KMZ厂商制品 |
| 媒体 | 平台自有分片上传 | 增加DJI STS凭证、上传回调、文件组和任务关联 |
| Pilot 2 | 没有内嵌H5 | 独立Pilot登录、JSBridge初始化、设备绑定和设置页面 |
| WebSocket | 业务侧主要使用SSE | 增加DJI地图元素所需WebSocket通道 |
| 直播 | 没有生产流媒体服务 | 推流地址签发、流媒体接入和短时播放地址 |
| HMS | 业务隐患告警，不区分设备告警 | 新建设备HMS告警域，不与巡检隐患混表 |

### 3.3 对现有状态说明的修正

当前代码将 DJI 连接统一标记为“缺少 AppKey、AppSecret、License、接口包”。官方当前接入参数是 APP ID、APP Key、APP License；大部分协议资料已经公开。DJI接入真正的外部前置条件是：

- Cloud API应用及License。
- 受支持的设备和固件。
- 可联调的Pilot 2或机场。
- 设备可访问的HTTPS、MQTT、WebSocket和对象存储网络。
- 安全飞行场地、操作人员和审批。

## 4. 目标与非目标

### 4.1 首期目标

首期形成以下真实闭环：

1. Pilot 2或机场登录平台。
2. 网关、飞机和载荷拓扑上线。
3. 实时状态和遥测进入平台GIS。
4. 平台航线转换为WPML/KMZ并存入MinIO。
5. 机场接收任务准备、执行、暂停、恢复和取消命令。
6. 任务进度驱动平台任务状态。
7. 照片、视频、PPK/RTCM等文件上传MinIO。
8. 媒体自动登记为巡检资源并进入AI分析闭环。

### 4.2 首期非目标

- 不在首期开放DRC实时杆量控制。
- 不在首期实现固件升级、远程调试和远程日志全能力。
- 不把直播作为任务完成的前置条件。
- 不直接把Demo数据库和用户体系迁入当前项目。
- 不允许大疆协议字段扩散到巡检任务、告警和工单核心领域。

## 5. 接入模式

### 5.1 DJI Pilot 2模式

适用于人工飞行、应急巡检和移动作业：

- Pilot 2内嵌本项目H5登录页。
- H5通过JSBridge验证License、设置Token和工作空间。
- Pilot 2加载设备上云、API、WebSocket、地图、态势、媒体、航线和直播模块。
- 航线以工作空间航线库方式同步，由飞手在Pilot端选择和执行。
- 媒体可以自动或手动上传。

Pilot 2模式下，“航线发布到Pilot航线库”和“飞机已经开始飞行”是两个不同状态，平台不得把航线同步成功解释为任务已执行。

### 5.2 大疆机场模式

适用于周期计划、无人值守和固定设施巡检：

- 机场作为MQTT网关，飞机和载荷作为子设备上报。
- 平台使用`flighttask_prepare`准备任务，使用`flighttask_execute`执行任务。
- 暂停、恢复、取消和进度均通过MQTT交互。
- 机场通过对象存储临时凭证回传媒体。
- 任务执行状态以`flighttask_progress`等事件为准。

### 5.3 统一执行模式

`flight_missions`增加：

- `execution_mode`：厂商无关枚举`MANUAL_GCS`、`AUTONOMOUS_DOCK`；DJI适配器分别映射Pilot 2与大疆机场。
- `gateway_device_id`：遥控器或机场。
- `vendor_flight_id`：厂商侧任务ID，DJI对应`flight_id`。
- `vendor_file_id`：对应航线制品。

页面根据模式展示不同操作：

| 操作 | Pilot 2 | 机场 |
| --- | --- | --- |
| 航线同步 | 支持 | 支持 |
| 云端自动执行 | 默认不提供 | 支持 |
| 暂停/恢复 | 按机型和授权能力 | 支持时启用 |
| 返航 | 按机型和控制权 | 按机场协议与安全策略 |
| 媒体回传 | 手动/自动 | 自动 |
| DRC | 后续阶段，遥控器确认 | 后续阶段，独立授权 |

## 6. 总体架构

```mermaid
flowchart LR
    subgraph DJI["大疆现场端"]
        P["DJI Pilot 2"]
        D["DJI Dock"]
        DA["飞行器与载荷"]
        DA --> P
        DA --> D
    end

    subgraph ZH["纵横现场端（后续）"]
        ZD["纵横设备或厂商平台"]
    end

    subgraph Access["独立 uav-access-service"]
        H5["Pilot H5"]
        API["统一接入应用层"]
        CORE["厂商无关接入内核"]
        DJI_ADAPTER["DJI Adapter"]
        ZH_ADAPTER["Zongheng Adapter"]
        STS["对象存储凭证服务"]
        ARTIFACT["厂商航线制品转换器"]
        LIVE["流媒体网关"]
    end

    subgraph Platform["现有业务平台"]
        UAV["统一无人机领域"]
        TASK["巡检任务与航线"]
        GIS["GIS与遥测"]
        MEDIA["巡检资源"]
        AI["预处理与AI分析"]
        ALARM["告警与工单"]
    end

    subgraph Data["数据基础设施"]
        MQ["MQTT Broker"]
        APG["uav_access 数据库Schema"]
        PPG["业务平台数据库Schema"]
        REDIS["Redis"]
        KAFKA["Kafka"]
        MINIO["MinIO"]
    end

    P --> H5
    P --> MQ
    D --> MQ
    P --> API
    D --> API
    P --> LIVE
    D --> LIVE
    H5 --> API
    MQ --> DJI_ADAPTER
    API --> DJI_ADAPTER
    ZD --> ZH_ADAPTER
    DJI_ADAPTER --> CORE
    ZH_ADAPTER --> CORE
    CORE --> STS
    CORE --> ARTIFACT
    CORE <--> KAFKA
    KAFKA <--> UAV
    CORE --> APG
    CORE --> REDIS
    UAV --> PPG
    STS --> MINIO
    ARTIFACT --> MINIO
    UAV --> GIS
    TASK --> UAV
    MINIO --> MEDIA
    MEDIA --> AI
    AI --> ALARM
```

### 6.1 逻辑与物理拆分

首期直接创建可独立构建、部署和扩缩容的服务，不再把DJI实现放入现有业务后端：

```text
uav-access-service/
└── src/main/java/com/ai/trackwalker/uavaccess
    ├── application
    │   ├── command
    │   ├── event
    │   └── query
    ├── domain
    │   ├── model
    │   ├── service
    │   └── port
    ├── adapter
    │   ├── inbound
    │   │   ├── kafka
    │   │   └── http
    │   ├── outbound
    │   │   ├── kafka
    │   │   ├── persistence
    │   │   └── storage
    │   └── vendor
    │       ├── dji
    │       │   ├── mqtt
    │       │   ├── pilot
    │       │   ├── device
    │       │   ├── wayline
    │       │   ├── media
    │       │   ├── map
    │       │   ├── livestream
    │       │   └── hms
    │       └── zongheng
    │           └── placeholder
    ├── config
    └── observability
```

服务边界规则：

1. `uav-access-service`不依赖业务后端实现类、Repository或数据库表映射。
2. 业务命令通过版本化Kafka消息进入接入服务；低频配置和查询使用内部REST接口。
3. 设备、遥测、任务进度、媒体和HMS统一为厂商无关事件后发布到Kafka。
4. 接入服务和业务平台禁止共同写同一张表；首期可以共用PostgreSQL实例，但必须使用独立Schema和数据库账号。
5. 厂商SDK、Topic、错误码、签名算法和原始DTO只能存在于`adapter.vendor.<vendor>`。
6. 应用层按`vendor_code`从注册表选择适配器，禁止在业务服务中散落`if (DJI)`或`switch (vendor)`。
7. 服务必须拥有独立镜像、配置、迁移脚本、健康检查、指标、告警和发布流水线。

### 6.2 厂商适配器契约

厂商扩展采用编译期模块和显式端口，不采用运行时加载不受控JAR的热插拔方案。核心端口按能力拆分：

| 端口 | 职责 |
| --- | --- |
| `VendorConnectionDriver` | 校验连接、建立会话、订阅与重连 |
| `VendorEventTranslator` | 把原始设备消息转换为统一拓扑、遥测、进度、媒体和告警事件 |
| `VendorCommandDriver` | 把统一命令转换为厂商协议并返回受理结果 |
| `VendorRouteCompiler` | 把统一航线编译为厂商制品，DJI实现为WPML/KMZ |
| `VendorMediaDriver` | 临时凭证、上传回调和媒体元数据规范化 |
| `VendorCapabilityProvider` | 返回设备、固件与协议版本对应的能力矩阵 |

统一命令和事件对象只允许出现平台语义，例如`MISSION_DISPATCH`、`MISSION_PROGRESS`和`DEVICE_TOPOLOGY_CHANGED`；`flighttask_prepare`等DJI方法名只能出现在DJI适配器及协议审计记录中。

纵横接入时新增`adapter.vendor.zongheng`并实现其实际支持的端口。其协议类型、鉴权和航线格式在取得官方资料和测试设备后冻结，不为追求表面统一而假设与DJI协议等价。缺失能力由`VendorCapabilityProvider`显式返回`UNSUPPORTED`，前端依据能力矩阵禁用操作。

### 6.3 跨服务契约

业务平台与接入服务之间使用稳定的事件信封：

```json
{
  "event_id": "UUID",
  "event_type": "uav.access.command.requested.v1",
  "schema_version": 1,
  "occurred_at": "2026-07-23T10:00:00Z",
  "trace_id": "UUID",
  "workspace_id": "UUID",
  "vendor_code": "DJI",
  "aggregate_id": "UUID",
  "payload": {}
}
```

首批契约：

| 方向 | 事件 |
| --- | --- |
| 业务平台→接入服务 | `uav.access.connection.changed.v1` |
| 业务平台→接入服务 | `uav.access.command.requested.v1` |
| 业务平台→接入服务 | `uav.access.route-artifact.requested.v1` |
| 接入服务→业务平台 | `uav.access.device-topology.changed.v1` |
| 接入服务→业务平台 | `uav.access.telemetry.received.v1` |
| 接入服务→业务平台 | `uav.access.command-status.changed.v1` |
| 接入服务→业务平台 | `uav.access.mission-progressed.v1` |
| 接入服务→业务平台 | `uav.access.media-ingested.v1` |
| 接入服务→业务平台 | `uav.access.hms-reported.v1` |

消费者必须按`event_id`幂等；事件字段只新增不删除，破坏性变更发布新版本。Kafka不可用时由双方各自的Outbox保留待发送消息，不用同步HTTP回调跨越业务事务。

## 7. 关键模块设计

### 7.1 DJI连接配置

每个DJI连接配置包括：

- 平台连接ID、环境、启停状态。
- APP ID、APP Key、APP License的密钥引用。
- 工作空间UUID、平台名称、工作空间名称和说明。
- MQTT地址、TLS、认证方式、客户端ID策略和ACL策略。
- HTTPS外部地址、WebSocket外部地址。
- MinIO外部Endpoint、桶、Region和对象前缀。
- NTP地址。
- 支持产品、最低固件、物模型版本和能力开关。
- DRC独立地址与开关；默认关闭。

`workspace_id`必须使用UUID。当前模拟器的`local-workspace`只能用于本地模拟，不能传给Pilot 2。

### 7.2 MQTT Broker

Kafka不能替代DJI所需MQTT Broker。新增支持以下能力的Broker：

- MQTT 5或与目标固件验证兼容的协议版本。
- TLS、用户名/密码或证书认证。
- Topic级ACL。
- 持久会话、离线消息和连接踢除审计。
- 指标、连接日志和限流。
- WebSocket/WSS接入能力，供需要的终端或DRC链路使用。

Broker只负责设备通信；`uav-access-service`中的DJI适配器消费MQTT，再由厂商无关内核把统一事件写入Kafka。

### 7.3 MQTT Topic路由

| Topic | 方向 | 处理 |
| --- | --- | --- |
| `sys/product/{gateway_sn}/status` | 设备→平台 | 网关/子设备拓扑、上线、下线 |
| `sys/product/{gateway_sn}/status_reply` | 平台→设备 | 拓扑确认 |
| `thing/product/{device_sn}/osd` | 设备→平台 | 定频遥测和位置 |
| `thing/product/{device_sn}/state` | 设备→平台 | 状态变化和能力变化 |
| `thing/product/{gateway_sn}/services` | 平台→设备 | 任务、直播、控制等命令 |
| `thing/product/{gateway_sn}/services_reply` | 设备→平台 | 命令回复 |
| `thing/product/{gateway_sn}/events` | 设备→平台 | 任务进度、HMS、媒体结果等 |
| `thing/product/{gateway_sn}/events_reply` | 平台→设备 | 事件确认 |
| `thing/product/{gateway_sn}/requests` | 设备→平台 | 航线资源、存储凭证等请求 |
| `thing/product/{gateway_sn}/requests_reply` | 平台→设备 | 请求回复 |
| `thing/product/{gateway_sn}/property/set` | 平台→设备 | 可写设备属性 |
| `thing/product/{gateway_sn}/property/set_reply` | 设备→平台 | 属性设置回复 |
| `thing/product/{gateway_sn}/drc/up` | 设备→平台 | DRC上行 |
| `thing/product/{gateway_sn}/drc/down` | 平台→设备 | DRC下行 |

处理规则：

1. 所有入站消息先写`access_event_inbox`，再进行业务处理。
2. 使用`connection_id + topic + tid + bid + method`作为协议幂等基础。
3. `events`和`requests`必须按协议及时回复，业务后处理失败不能导致无限重复回复。
4. 未识别方法保存原始报文并产生兼容性告警。
5. `osd`允许批量发布标准遥测事件；设备最新快照写接入服务Redis。
6. 业务平台先写业务命令和平台Outbox；接入服务消费后再写`access_commands`和接入服务Outbox。

### 7.4 命令发送与回复关联

Demo使用内存`Chan`阻塞线程等待`services_reply`，其源码已经标注“不推荐”。生产实现不得使用该方式。

目标流程：

1. 业务平台事务创建`flight_missions`、平台`uav_commands`和平台Outbox事件。
2. Kafka发布`uav.access.command.requested.v1`，内容携带`platform_command_id`、厂商、设备和统一命令。
3. 接入服务幂等消费，在自身事务中创建`access_commands`和接入服务Outbox。
4. DJI Outbox工作线程生成`tid`和`bid`并发布MQTT。
5. 接入命令状态变为`SENT`，记录发送次数和超时时间。
6. `services_reply`先进入接入服务Inbox，根据`tid/bid/method/gateway_sn`关联接入命令。
7. 接入服务发布`uav.access.command-status.changed.v1`，业务平台据此更新平台命令。
8. 最终飞行状态仍由`uav.access.mission-progressed.v1`驱动。
9. 超时命令按方法幂等性决定是否重试；不安全命令禁止自动重放。

命令状态：

```text
CREATED -> PENDING -> SENT -> ACKNOWLEDGED -> SUCCEEDED
                         \-> RETRY_WAIT -> SENT
                         \-> FAILED
                         \-> TIMED_OUT
```

### 7.5 设备拓扑与遥测

拓扑消息同时包含网关和子设备。平台模型映射：

| DJI概念 | 平台对象 |
| --- | --- |
| Pilot 2遥控器 | `uav_devices.device_type=REMOTE_CONTROLLER` |
| 大疆机场 | `uav_docks`，同时作为网关 |
| 飞行器 | `uav_devices.device_type=AIRCRAFT` |
| 相机/负载 | `uav_payloads` |
| 网关与飞机关系 | `parent_device_id`或平台拓扑关系表 |

接入服务Redis保存：

- 当前在线状态。
- 最新OSD。
- 当前载荷、控制权和直播能力。
- 当前执行中的任务和最近心跳。

接入服务把标准化事件写入Kafka，业务平台消费后在PostgreSQL/PostGIS保存：

- 设备台账和拓扑历史。
- 关键状态变化。
- 按时间分区的遥测点。
- 任务完成后的简化航迹和完整航迹。

Canonical坐标统一使用WGS84/EPSG:4326。高德地图所需GCJ-02转换只发生在展示边界，不回写业务数据。

### 7.6 Pilot 2 H5与JSBridge

新增独立前端入口：

```text
/pilot/login
/pilot/home
/pilot/device-binding
/pilot/media
/pilot/live
/pilot/tasks
```

推荐初始化顺序：

1. `platformVerifyLicense(appId, appKey, license)`。
2. 登录平台并获得短期Token。
3. 加载`api`模块并设置Token。
4. 设置平台信息和工作空间UUID。
5. 加载`thing`模块，连接MQTT。
6. MQTT连接成功后加载`liveshare`、`ws`、`map`、`tsa`、`media`和`mission`。
7. 获取遥控器和飞机SN，执行设备绑定或显示已有绑定。
8. 注册退出、返回、连接状态和Token刷新处理。

Pilot H5只保留现场需要的轻量功能，不复制桌面管理端。建议增加铁路业务入口：

- 今日巡检任务。
- 飞行前安全检查。
- 设备绑定与在线状态。
- 航线同步状态。
- 媒体上传策略和进度。
- 异常上报和现场工单入口。

### 7.7 WPML/KMZ航线转换

平台航线继续以统一模型为主：

- `route_versions.geom`
- `route_versions.waypoints`
- `route_versions.flight_parameters`
- `route_versions.payload_actions`
- `route_waypoints`

新增`DjiWpmlCompiler`把发布版本编译为KMZ：

```text
wpmz/
├── template.kml
├── waylines.wpml
└── res/
```

每个制品记录：

- 平台航线版本。
- 目标产品和载荷枚举。
- WPML命名空间/版本。
- 编译器版本。
- MinIO对象键。
- SHA-256和DJI fingerprint。
- `template.kml`、`waylines.wpml`结构校验结果。
- 高度基准、返航高度和失联策略快照。
- 创建时间和发布人。

发布前校验：

- 坐标范围和WGS84。
- 航点序号连续。
- 高度模式与执行高度。
- 速度、转弯方式和云台角度。
- 载荷型号与动作兼容性。
- 返航高度、限高、禁飞区和净空。
- 文件命名、目录结构、XML命名空间和摘要。

Demo支持上传既有KMZ；当前平台还应支持从内部航点自动生成KMZ。两种来源都进入同一厂商制品表。

### 7.8 机场任务

统一命令映射：

| 平台命令 | DJI方法 | 说明 |
| --- | --- | --- |
| `MISSION_DISPATCH` | `flighttask_prepare` | 下发任务参数、KMZ URL和fingerprint |
| `MISSION_START` | `flighttask_execute` | 执行已准备任务 |
| `MISSION_CANCEL` | `flighttask_undo` | 取消尚未完成任务 |
| `MISSION_PAUSE` | `flighttask_pause` | 暂停当前航线 |
| `MISSION_RESUME` | `flighttask_recovery` | 恢复航线 |
| `MEDIA_PRIORITIZE` | 对应媒体优先上传服务 | 提升指定任务媒体优先级 |

注意：

- 官方已废弃旧的“创建航线任务”，新实现使用prepare + execute。
- `RETURN_HOME`不能简单等同于`flighttask_undo`，必须根据目标产品协议和当前状态单独映射。
- MQTT服务回复只表示设备接受或拒绝命令，不表示任务最终成功。

### 7.9 媒体回传

媒体链路支持Pilot 2 HTTPS和机场MQTT两种入口：

1. 设备请求`storage_config_get`或Pilot调用STS接口。
2. 平台生成短期MinIO凭证，限制桶、前缀、动作和有效期。
3. 设备直传MinIO。
4. Pilot调用媒体HTTP回调，机场上报`file_upload_callback`事件。
5. 平台验证对象存在、大小、MIME和摘要。
6. 接入服务先写`dji_media_ingestions`，再发布`uav.access.media-ingested.v1`。
7. 业务平台幂等消费事件并创建`inspection_resources`，资源就绪后触发现有预处理和AI分析。

必须保留的元数据：

- `object_key`、文件名、路径、扩展名和MIME。
- `flight_id`、`file_group_id`。
- 网关SN、飞机SN、载荷产品Key。
- 拍摄时间、经纬度、绝对高度、相对高度、云台偏航。
- 原图/预览图标识。
- PPK/RTCM文件类型。
- 上传结果、重试次数、摘要和质量状态。

对象存储安全要求：

- 不向设备返回MinIO root凭据。
- 凭证前缀按`workspace/mission/device/date`隔离。
- Endpoint必须是设备可达的外部地址，不能返回容器内部地址。
- 下载和预览使用短时签名URL或平台代理。

### 7.10 地图元素与态势

新增：

- `dji_map_element_groups`
- `dji_map_elements`
- PostGIS geometry列和原始属性JSONB

同步规则：

- Pilot或Web创建、修改、删除元素后先保存数据库。
- 数据成功提交后，通过WebSocket向同一工作空间广播。
- WebSocket只发送变更通知；全量刷新仍通过HTTPS查询。
- 元素必须保留创建端、创建人、版本、更新时间和分发状态。
- DJI地图元素与铁路巡检对象分域存储；需要转为检查对象时使用显式“转为任务标记”操作。

### 7.11 直播

首期直播是可选增强，不阻塞任务和媒体闭环。

设计包括：

- `live_stream_sessions`保存设备、载荷、镜头、协议、质量、状态和有效期。
- 平台生成推流参数并通过MQTT或Pilot JSBridge启动直播。
- 流媒体服务接收RTMP、GB28181或WebRTC/WHIP。
- Web端只获取短时播放地址，不直接暴露推流密钥。
- 直播断开不改变飞行任务最终状态。
- 视频AI分析与离线媒体分析分开设计，避免实时流阻塞现有推理服务。

### 7.12 HMS设备告警

HMS属于设备健康告警，不能直接写入现有巡检隐患`alarms`。

新增`uav_device_hms`：

- 设备、网关、错误码、等级、是否在飞行中。
- 原始参数、文案Key、本地化文案。
- 首次出现、最后出现、恢复时间和确认状态。
- 原始事件ID和协议版本。

关键HMS可投递到系统运维通知；只有经过明确规则转换后才能创建业务工单。

### 7.13 DRC与远程控制

DRC延后实施，并采用独立安全边界：

- 独立MQTT连接配置、短期JWT、专用ACL。
- 默认配置关闭。
- 遥控器或机场明确授权后才能进入控制态。
- 双人确认、操作原因、控制时限和全量审计。
- 指令频率、序号、丢包和延迟独立监控。
- 控制链路断开立即进入预设安全策略。

## 8. 核心交互时序

### 8.1 Pilot 2登录与模块加载

```mermaid
sequenceDiagram
    participant P as "DJI Pilot 2"
    participant H as "Pilot H5"
    participant B as "平台后端"
    participant M as "MQTT Broker"
    participant W as "WebSocket"

    P->>H: 打开第三方平台URL
    H->>P: JSBridge验证License
    H->>B: 用户登录
    B-->>H: Token、workspace、平台配置
    H->>P: 加载API模块并设置Token
    H->>P: 设置workspace和平台信息
    H->>P: 加载Thing模块
    P->>M: MQTT认证连接
    M-->>P: 连接成功
    H->>P: 加载WS/Map/TSA/Media/Mission/Live
    P->>W: WebSocket连接
    P->>M: 上报设备拓扑
```

### 8.2 机场上线与遥测

```mermaid
sequenceDiagram
    participant D as "DJI Dock"
    participant M as "MQTT Broker"
    participant A as "uav-access-service"
    participant K as "Kafka"
    participant U as "业务平台"
    participant R as "Redis"
    participant P as "PostGIS"
    participant F as "前端GIS"

    D->>M: sys/product/{sn}/status
    M->>A: 拓扑消息
    A->>A: Inbox去重与物模型识别
    A->>K: device-topology.changed.v1
    K->>U: 标准拓扑事件
    U->>P: 保存机场、飞机和载荷
    A->>R: 保存在线快照
    A-->>M: status_reply
    D->>M: osd/state
    M->>A: 遥测与状态
    A->>R: 更新最新状态
    A->>K: telemetry.received.v1
    K->>U: 标准遥测事件
    U->>P: 批量保存历史遥测
    U-->>F: SSE/WebSocket实时状态
```

### 8.3 机场任务下发

```mermaid
sequenceDiagram
    participant U as "任务管理员"
    participant T as "任务与航线服务"
    participant PO as "平台Outbox"
    participant K as "Kafka"
    participant A as "uav-access-service"
    participant AO as "接入Outbox"
    participant S as "MinIO"
    participant D as "DJI Dock"

    U->>T: 下发已审批任务
    T->>T: 校验设备、天气、航线和权限
    T->>S: 获取KMZ签名URL
    T->>PO: 写MISSION_DISPATCH事件
    PO->>K: command.requested.v1
    K->>A: 幂等消费统一命令
    A->>AO: 写DJI协议命令
    AO->>D: flighttask_prepare
    D-->>A: services_reply
    A->>K: command-status.changed.v1
    K->>T: 命令已确认
    T->>PO: 写MISSION_START事件
    PO->>K: command.requested.v1
    K->>A: 执行统一命令
    A->>AO: 写DJI协议命令
    AO->>D: flighttask_execute
    D-->>A: services_reply
    D-->>A: flighttask_progress事件
    A->>K: mission-progressed.v1
    K->>T: 更新飞行任务状态和进度
```

### 8.4 媒体回传与AI分析

```mermaid
sequenceDiagram
    participant D as "Pilot 2或Dock"
    participant A as "uav-access-service"
    participant S as "MinIO"
    participant K as "Kafka"
    participant R as "资源中心"
    participant AI as "AI分析"

    D->>A: 请求临时凭证
    A-->>D: 限定前缀的短期凭证
    D->>S: 上传媒体/PPK/RTCM
    D->>A: 上传结果回调或事件
    A->>S: 校验对象和摘要
    A->>K: media-ingested.v1
    K->>R: 幂等创建inspection_resource
    R->>K: inspection.resource.ready
    K->>AI: 预处理和推理
    AI-->>R: AI结果和证据
```

## 9. 领域模型与数据库设计

### 9.1 数据所有权

独立服务不等于立即增加一套独立数据库集群，但必须从第一天建立数据所有权：

| 所有者 | 权威数据 |
| --- | --- |
| 业务平台 | 统一设备台账、航线、飞行任务、业务命令、历史遥测、航迹、巡检资源、告警和工单 |
| `uav-access-service` | 厂商连接、协议设备绑定、协议命令投递、原始Inbox、接入Outbox、厂商航线制品、媒体回调暂存和协议审计 |
| Redis | 接入会话、最新OSD、连接租约和短期去重缓存，不作为最终权威 |
| MinIO | 航线制品、媒体、日志和厂商资源文件 |

首期可在现有PostgreSQL实例中增加`uav_access` Schema，业务平台继续使用原Schema。两个服务使用不同数据库账号和授权，Flyway迁移分别管理。任何表只有一个写入者，跨边界读取通过事件形成本地投影或使用内部查询API。

### 9.2 现有表处置

| 现有表 | 处置 |
| --- | --- |
| `uav_vendor_connections` | 迁移到`uav_access` Schema并由接入服务独占写入；业务平台只保存连接ID投影 |
| `uav_devices`、`uav_docks`、`uav_payloads` | 保留在业务平台，消费标准拓扑事件更新 |
| `flight_missions` | 保留在业务平台，任务状态由标准命令和进度事件驱动 |
| `uav_commands` | 保留为业务命令；新增`access_commands`保存厂商协议投递，不混用两种状态 |
| `vendor_event_inbox` | 迁移到接入服务并更名或映射为`access_event_inbox` |
| `telemetry_points`、`flight_tracks` | 保留在业务平台，由标准遥测事件批量写入 |
| `routes`、`route_versions`、`route_waypoints` | 保留在业务平台，通过航线制品请求发送统一快照 |
| `inspection_resources` | 保留在业务平台，消费媒体接入事件后创建 |

迁移期间允许兼容视图读取旧表，但禁止两个服务双写。原`CapabilityCompletionService`中的同步厂商调用应改为写平台Outbox，现有`UavVendorAdapter`退化为业务端口或移除，不能继续承载厂商SDK实现。

### 9.3 字段设计

`uav_devices`：

- `vendor_code`
- `vendor_device_id`
- `gateway_vendor_device_id`
- `parent_device_id`
- `device_model_key`
- `firmware_version`
- `bound_status`
- `device_callsign`
- `capability_snapshot`

`flight_missions`：

- `execution_mode`
- `gateway_device_id`
- `vendor_file_id`
- `vendor_flight_id`
- `task_type`
- `rth_altitude_m`
- `out_of_control_action`
- `media_expected_count`
- `media_uploaded_count`

`uav_commands`：

- `access_command_id`
- `vendor_code`
- `command_type`
- `requested_at`
- `accepted_at`
- `completed_at`
- `failure_code`

`access_commands`：

- `platform_command_id`
- `vendor_code`
- `connection_id`
- `vendor_device_id`
- `command_type`
- `vendor_method`
- `protocol_correlation_id`
- `protocol_context`
- `sent_at`
- `acknowledged_at`
- `reply_code`
- `timeout_at`
- `retry_policy`

`access_event_inbox`：

- `vendor_code`
- `connection_id`
- `transport`
- `source_address`
- `message_type`
- `protocol_message_id`
- `protocol_correlation_id`
- `protocol_headers`
- `raw_payload`
- `reply_required`
- `payload_sha256`

### 9.4 接入服务表

| 表 | 所有者 | 主要用途 |
| --- | --- | --- |
| `access_vendor_connections` | 接入服务 | 各厂商连接、工作空间和密钥引用 |
| `access_device_bindings` | 接入服务 | 厂商设备标识与平台设备ID映射 |
| `access_commands` | 接入服务 | 统一命令到厂商协议投递、回复和重试 |
| `dji_command_context` | 接入服务 | DJI命令的`gateway_sn`、`tid`、`bid`和`method`关联索引 |
| `access_event_inbox` | 接入服务 | 原始厂商消息幂等、回复和审计 |
| `access_outbox_events` | 接入服务 | 接入服务命令发送和标准事件可靠发布 |
| `route_vendor_artifacts` | 接入服务 | 各厂商航线制品、摘要、版本和校验 |
| `dji_media_ingestions` | 接入服务 | DJI媒体回调和标准媒体事件之间的暂存映射 |
| `dji_map_element_groups` | 接入服务 | Pilot兼容地图元素组 |
| `dji_map_elements` | 接入服务 | Pilot地图点线面协议数据 |
| `access_live_stream_sessions` | 接入服务 | 厂商推流与播放会话 |
| `access_device_topology_history` | 接入服务 | 厂商原始网关、飞机和载荷关系变化 |
| `uav_device_hms` | 业务平台 | 标准设备健康告警投影 |

所有接入服务表必须包含`vendor_code`和`workspace_id`；厂商专属表使用明确前缀，不能把DJI字段扩散到通用表。

### 9.5 数据保留

- 原始厂商报文：在线热存30天，压缩归档不少于180天，最终按项目制度确定。
- 遥测明细：按月或任务分区；任务航迹长期保留。
- 命令、任务、HMS和媒体回调：按审计要求长期保留。
- License、Token、MQTT密码和对象存储凭证不得写入业务日志或原始事件表。

## 10. 接口设计

### 10.1 DJI兼容外部接口

Pilot 2使用的接口保持DJI要求的路径与响应信封，不能直接套用平台通用接口后改变字段：

```text
POST /manage/api/v1/login
POST /manage/api/v1/token/refresh
GET  /manage/api/v1/workspaces/current
GET  /manage/api/v1/users/current
POST /manage/api/v1/devices/{device_sn}/binding

POST /storage/api/v1/workspaces/{workspace_id}/sts

GET  /wayline/api/v1/workspaces/{workspace_id}/waylines
GET  /wayline/api/v1/workspaces/{workspace_id}/waylines/{wayline_id}/url
POST /wayline/api/v1/workspaces/{workspace_id}/waylines/file/upload

POST /media/api/v1/workspaces/{workspace_id}/fast-upload
POST /media/api/v1/workspaces/{workspace_id}/upload-callback
POST /media/api/v1/workspaces/{workspace_id}/group-upload-callback

GET    /map/api/v1/workspaces/{workspace_id}/element-groups
POST   /map/api/v1/workspaces/{workspace_id}/element-groups/{group_id}/elements
PUT    /map/api/v1/workspaces/{workspace_id}/elements/{element_id}
DELETE /map/api/v1/workspaces/{workspace_id}/elements/{element_id}
```

所有接口：

- 校验`X-Auth-Token`。
- 校验用户、工作空间和设备绑定。
- 使用DJI要求的`code/message/data`结构。
- 写接口支持平台请求ID和审计。

### 10.2 平台统一接口

现有接口继续由业务平台对前端提供，平台通过Outbox/Kafka驱动接入服务，避免浏览器直接访问内部服务：

```text
GET  /api/v1/uav/connections
POST /api/v1/uav/connections
POST /api/v1/uav/connections/{id}/check
GET  /api/v1/uav/devices
GET  /api/v1/uav/missions
POST /api/v1/uav/missions
POST /api/v1/uav/missions/{id}/commands
GET  /api/v1/uav/missions/{id}/telemetry
```

补充：

```text
GET  /api/v1/uav/devices/{id}/capabilities
GET  /api/v1/uav/devices/{id}/hms
GET  /api/v1/uav/missions/{id}/timeline
GET  /api/v1/uav/missions/{id}/media-status
GET  /api/v1/route-versions/{id}/vendor-artifacts
POST /api/v1/route-versions/{id}/vendor-artifacts?vendor=DJI
GET  /api/v1/route-versions/{id}/vendor-preview?vendor=DJI
POST /api/v1/uav/live-streams
DELETE /api/v1/uav/live-streams/{id}
```

除Pilot 2规定的兼容接口外，厂商选择统一使用`vendor`参数或连接配置，不能继续新增`/dji/*`、`/zongheng/*`业务接口。

### 10.3 接入服务内部接口

低频管理和故障诊断可使用内部REST：

```text
PUT  /internal/v1/connections/{connection_id}
POST /internal/v1/connections/{connection_id}/check
GET  /internal/v1/connections/{connection_id}/health
GET  /internal/v1/devices/{platform_device_id}/capabilities
GET  /internal/v1/commands/{access_command_id}
GET  /internal/v1/route-artifacts/{artifact_id}
```

内部接口只接受服务身份，禁止直接暴露公网。飞行命令必须通过`uav.access.command.requested.v1`进入持久化异步链路；不得为了降低延迟另开同步下发接口绕过Outbox、审计和幂等。

## 11. 状态机

### 11.1 飞行任务

```text
DRAFT
  -> READY
  -> DISPATCH_PENDING
  -> PREPARING
  -> PREPARED
  -> EXECUTE_PENDING
  -> FLYING
  -> PAUSED
  -> RETURNING
  -> UPLOADING
  -> COMPLETED

任一允许节点 -> FAILED
准备或执行前 -> CANCELLED
```

约束：

- 服务回复不能直接把任务置为`COMPLETED`。
- `flighttask_progress`是运行状态的主要权威来源。
- 媒体上传失败不应把飞行结果改为飞行失败；任务进入`MEDIA_INCOMPLETE`或资源异常状态。
- Pilot手工模式增加`AVAILABLE_IN_PILOT`和`PILOT_CONFIRMED`等同步状态。

### 11.2 设备

```text
UNKNOWN -> ONLINE -> DEGRADED -> OFFLINE
                 \-> MAINTENANCE
```

在线状态由拓扑和心跳共同判定，不能只依靠最后一条OSD。

## 12. 安全设计

### 12.1 身份与链路

- 外部HTTP、WebSocket、MQTT和MinIO统一使用TLS。
- MQTT禁止匿名连接，按设备或工作空间签发账号。
- MQTT ACL限制设备只能访问自身SN相关Topic。
- Pilot HTTP Token使用短有效期并支持刷新、吊销和退出清理。
- 服务间使用内部令牌或mTLS。

### 12.2 密钥

- APP ID、APP Key、APP License和MQTT密码使用环境密钥或Secret Manager引用。
- 数据库只保存`secret_ref`，不保存明文。
- 日志过滤Token、License、MQTT密码、STS和签名URL查询参数。
- MinIO凭证按最小权限和对象前缀签发。

### 12.3 飞行安全

下发前强制校验：

- 用户权限和任务审批。
- 设备在线、控制权、固件和能力。
- 航线已发布且厂商制品校验通过。
- 电量、存储、RTK、机场环境和舱盖状态。
- 天气、空域、铁路天窗和现场安全条件；外部接口未接入时必须人工确认。
- 返航高度、失联策略、限高和禁飞区。

远程控制、返航、强制降落等高风险命令采用二次确认、原因必填和审计。

## 13. 部署设计

### 13.1 新增运行组件

| 组件 | 用途 |
| --- | --- |
| MQTT Broker | DJI设备接入 |
| `uav-access-service` | 多厂商连接、协议转换、命令投递、事件规范化与接入审计 |
| `uav_access`数据库Schema | 接入服务私有状态、Inbox和Outbox |
| Pilot H5 | Pilot 2登录和JSBridge |
| WebSocket端点 | 地图、态势和设备实时推送 |
| 流媒体服务 | 直播推流和播放，第二阶段启用 |

### 13.2 网络入口

生产建议统一域名：

```text
https://uav.example.com/pilot/*
https://uav.example.com/manage/api/*
https://uav.example.com/map/api/*
https://uav.example.com/media/api/*
https://uav.example.com/wayline/api/*
https://uav.example.com/storage/api/*
wss://uav.example.com/ws
mqtts://mqtt.uav.example.com
https://objects.uav.example.com
```

设备必须能访问：

- Pilot H5与HTTPS API。
- MQTT Broker。
- WebSocket。
- MinIO外部Endpoint。
- NTP。
- 选定的流媒体入口。

无WAN环境必须部署设备可访问的NTP并实现配置更新，否则航线任务可能无法正常工作。

### 13.3 配置示例

```yaml
uav-access:
  instance-id: ${UAV_ACCESS_INSTANCE_ID:uav-access-01}
  database-schema: uav_access
  kafka:
    command-topic: uav.access.command.requested.v1
    event-topic: uav.access.events.v1
  vendors:
    dji:
      enabled: false
      workspace-id: ${DJI_WORKSPACE_ID:}
      platform-name: ${DJI_PLATFORM_NAME:铁路无人机智能巡检平台}
      app-id-ref: ${DJI_APP_ID_REF:}
      app-key-ref: ${DJI_APP_KEY_REF:}
      app-license-ref: ${DJI_APP_LICENSE_REF:}
      mqtt:
        uri: ${DJI_MQTT_URI:mqtts://mqtt.example.com:8883}
        client-id: ${DJI_MQTT_CLIENT_ID:rail-dji-access-01}
        username-ref: ${DJI_MQTT_USERNAME_REF:}
        password-ref: ${DJI_MQTT_PASSWORD_REF:}
      websocket-url: ${DJI_WS_URL:wss://uav.example.com/ws}
      storage:
        endpoint: ${DJI_STORAGE_ENDPOINT:https://objects.example.com}
        bucket: ${DJI_STORAGE_BUCKET:rail-dji}
        credential-ttl-seconds: ${DJI_STORAGE_CREDENTIAL_TTL:3600}
      ntp-host: ${DJI_NTP_HOST:}
      drc-enabled: ${DJI_DRC_ENABLED:false}
    zongheng:
      enabled: false
      # 鉴权、协议和设备配置在取得纵横官方资料后补充
```

### 13.4 扩缩容与故障隔离

- Kafka命令按`connection_id`分区，保证同一设备连接上的命令顺序。
- MQTT连接使用稳定clientId和数据库租约，同一连接同一时刻只允许一个服务实例持有，避免重复订阅和双重下发。
- 不同厂商使用独立线程池、限流器和熔断器；纵横侧故障不能耗尽DJI消息处理资源。
- OSD、媒体和命令回复使用不同消费队列与背压策略，高频遥测不能阻塞飞行命令。
- 可按`vendor_code`或连接分组独立扩容实例，但所有实例运行相同服务镜像和适配器注册机制。
- 滚动升级前执行事件契约兼容测试；协议DTO升级不得要求业务平台同步发布。

## 14. 可靠性与可观测性

### 14.1 可靠性

- 固定且唯一的MQTT clientId；避免Demo使用随机clientId导致会话不可恢复。
- Broker持久会话或受控共享订阅。
- 业务平台与接入服务各自使用Inbox/Outbox，保证跨服务事件和厂商消息的最终发送。
- 标准事件进入死信队列后必须支持查看、修复和按`event_id`安全重放。
- 命令重试按方法分类，飞行高风险命令默认不自动重放。
- OSD采用背压、批量写和降采样，不能阻塞事件与命令回复。
- Redis丢失后可从数据库和设备重新构建在线状态。
- MinIO不可用时不宣布资源就绪，保留上传状态并告警。

### 14.2 指标

至少采集：

- 按`vendor_code`、`connection_id`统计MQTT或其他厂商协议的连接数、重连数、消息速率和积压。
- Kafka命令与事件消费延迟、Outbox积压、Inbox重复率和死信数量。
- Topic分类消息数、解析失败数和未知方法数。
- 回复延迟、命令超时、重试和错误码分布。
- 设备在线率、遥测延迟和最后心跳。
- 任务准备、执行、失败、暂停和完成数量。
- 媒体上传成功率、积压文件数、字节量和耗时。
- WebSocket连接数和发送失败数。
- STS签发数、失败数和异常访问。

日志统一包含：

- `request_id`
- `trace_id`
- `event_id`
- `vendor_code`
- `connection_id`
- `workspace_id`
- `gateway_sn`
- `device_sn`
- `mission_id`
- `tid`
- `bid`
- `method`

## 15. 测试与验收

### 15.1 自动化测试

| 层级 | 内容 |
| --- | --- |
| 单元测试 | Topic解析、DTO转换、状态映射、WPML生成、错误码 |
| 厂商适配器契约测试 | 每个适配器执行统一连接、命令、事件、航线和能力测试套件 |
| 事件契约测试 | 业务平台与接入服务消息Schema的前后兼容和重复消费 |
| DJI协议契约测试 | 官方报文样例、Demo报文、未知字段向前兼容 |
| 组件测试 | MQTT Broker、PostgreSQL、Redis、MinIO、WebSocket |
| 集成测试 | 拓扑、遥测、任务、媒体、HMS完整流程 |
| 隔离测试 | 单一厂商故障、慢消费或消息风暴不影响其他厂商 |
| 故障测试 | 重复、乱序、延迟、断线、服务独立重启、超时、Kafka或MinIO不可用 |
| 安全测试 | ACL、Token过期、越权工作空间、STS前缀逃逸 |
| 前端测试 | Pilot环境检测、模块加载、退出清理、连接恢复 |

### 15.2 真实设备验收

首期验收场景：

1. Pilot 2登录、License验证、工作空间设置和设备绑定。
2. 机场、飞机、载荷上线并在GIS显示。
3. 遥测持续进入平台，断线后状态正确恢复。
4. 平台生成的KMZ可被目标机型解析。
5. 即时任务完成准备、执行、暂停、恢复和取消。
6. 重复进度事件不会重复推进状态。
7. 服务重启后命令回复仍能正确关联。
8. 图片和视频上传MinIO并关联正确任务、设备和航点。
9. 媒体触发AI分析、告警和工单链路。
10. HMS设备告警和巡检隐患告警互不污染。
11. 无外网环境下使用内部NTP完成任务。
12. 业务平台与`uav-access-service`可独立滚动升级和重启，未确认命令不会丢失或重复下发。

生产准入还需完成：

- 目标固件兼容矩阵。
- 弱网和大文件压力测试。
- 任务并发和媒体容量测试。
- 现场飞行安全评审。
- 密钥、证书、ACL和审计检查。

## 16. 分阶段实施计划

### M0：材料与版本冻结，3～5个工作日

- 确认Pilot 2、Dock 2或Dock 3接入模式。
- 收集设备、载荷、固件、Pilot版本和License。
- 确定网络、域名、证书、NTP和存储Endpoint。
- 建立兼容矩阵和测试设备白名单。
- 冻结`uav-access-service`边界、Kafka事件Schema和数据库所有权。

### M1：接入底座，1～2周

- 创建独立`uav-access-service`、`uav_access` Schema、镜像和发布流水线。
- 实现厂商端口、适配器注册表、统一事件信封和契约测试套件。
- 把业务后端同步适配器调用改为平台Outbox/Kafka命令。
- 部署MQTT Broker。
- 实现连接配置、TLS、ACL和健康检查。
- 实现DJI适配器、Topic路由、接入Inbox/Outbox和`tid/bid`关联。
- 完成拓扑、上下线、OSD/state和前端实时显示。

### M2：航线和机场任务，2～3周

- WPML/KMZ编译、校验、预览和制品管理。
- 实现prepare、execute、pause、recovery、undo。
- 实现任务进度、错误码和任务时间线。
- 完成真实机场航线闭环。

### M3：媒体与AI闭环，1～2周

- 实现MinIO临时凭证。
- 实现Pilot HTTP和机场MQTT媒体回调。
- 媒体标准化、质量检查和AI任务触发。
- 验证照片、视频、PPK/RTCM。

### M4：Pilot H5、地图与HMS，2～3周

- Pilot轻量页面和JSBridge。
- 地图元素HTTPS/WebSocket同步。
- HMS设备告警和运维页面。

### M5：直播和高级能力，按需实施

- 流媒体服务与直播会话。
- 固件、日志、远程调试。
- DRC独立安全评审后实施。

### M6：纵横适配器，取得资料和设备后评估

- 冻结纵横产品、协议、鉴权、航线和媒体能力矩阵。
- 实现`adapter.vendor.zongheng`所需厂商端口，不修改业务领域模型。
- 复用统一命令、事件、Inbox/Outbox、可观测性和安全基线。
- 执行适配器契约、故障隔离和真实设备闭环验收。

## 17. Demo能力取舍

| Demo实现 | 处理意见 |
| --- | --- |
| `cloud-sdk` Topic与DTO组织 | 借鉴，按当前官方协议补齐版本 |
| Spring Integration消息路由 | 可借鉴概念，使用当前Spring Boot兼容实现 |
| `Chan`内存阻塞等待回复 | 禁止用于生产，替换为持久化异步关联 |
| MySQL + MyBatis Plus | 不采用，继续PostgreSQL/PostGIS + JDBC |
| Redis保存在线设备和运行任务 | 借鉴，但数据库和事件日志保持权威 |
| MinIO/AWS/阿里云存储上下文 | 借鉴接口，当前项目优先MinIO |
| Pilot JSBridge封装 | 可抽取重写，加入类型、环境检测和安全处理 |
| Vue 3 Pilot页面结构 | 借鉴，改为当前Element Plus设计体系 |
| Demo默认账号、JWT密钥和MQTT密码 | 禁止复用 |
| Demo机型枚举和固件能力 | 不直接复用，以当前官方文档和实测为准 |
| Demo地图元素表 | 使用PostGIS重新设计 |
| Demo直播示例 | 用于联调，不直接作为生产播放安全方案 |

## 18. 待确认决策

进入实现前必须确认：

1. 首批设备是Pilot 2、Dock 2还是Dock 3。
2. 飞机、载荷和固件的具体型号与版本。
3. 是否优先完成机场无人值守，还是Pilot人工巡检。
4. 是否存在完全无外网的专网部署。
5. MinIO由设备直连还是通过边缘存储代理。
6. 首期是否需要地图元素、直播和HMS。
7. 是否需要接入天气、空域和铁路天窗系统。
8. 遥测和媒体的规模、保留期限、RTO和RPO。
9. 远程控制是否在项目范围；若在，需单独安全设计和验收。

## 19. 最终结论

当前项目不需要引入DJI Demo作为新的业务平台，也不应先把DJI代码嵌入业务后端再等待未来拆分。正确做法是从首期就建设独立`uav-access-service`，将DJI作为第一个厂商适配器，并为后续纵横接入保留相同的端口、事件和能力模型：

- 厂商协议、SDK、鉴权和错误码封闭在对应适配器中。
- DJI适配器通过MQTT、HTTPS和WebSocket完成设备与Pilot通信，通过WPML/KMZ交换航线。
- 纵横适配器复用厂商无关内核，不要求纵横协议模拟DJI。
- MinIO临时凭证和回调负责媒体回传。
- 双侧Inbox/Outbox、协议关联ID和统一状态机保证可靠性。
- Kafka承担业务平台与接入服务之间的版本化命令和事件。
- 两个服务各自拥有数据库Schema，禁止共享写表和跨库事务。
- PostGIS、巡检资源、AI分析、告警和工单保持现有平台的长期稳定内核。

按照M0至M3实施后，可以形成“独立多厂商接入底座—DJI真实设备上线—航线下发—飞行进度—媒体回传—AI分析—隐患闭环”的首个生产级版本；地图、直播、HMS和远程控制在此基础上扩展，纵横接入只新增适配器和厂商配置，不重构业务平台。
