目录 · 第 03 章4 卷 12 章

第 03 章 · 卷一 先搞懂整个项目

核心业务流程与模块交互

一次圈选巡检从请求进来到任务归档的完整生命周期,含任务状态机。

约 12 分钟9 节
已知事实

平台已有设备接入、航线规划、任务下发、遥测回传和告警处置能力。技术栈为 Java 21、Spring Boot 3、Spring AI、Spring AI Alibaba、PostgreSQL、Redis、RocketMQ、Milvus、MinIO、Vue 3、Cesium、SSE。

目标设计

在现有平台上增加 Copilot 编排层,打通意图理解、设备查询、航线规划、任务审批、飞行执行和报告归档。本文涉及的类名、表名、接口路径及状态机均为建议的实现设计。

验证结果

暂无,本文只做方案设计。没有已上线或实测的数据可以证明该流程已经跑通。

1要解决的问题

核心问题是:怎么把用户的一句话,变成一项真实执行、过程可追踪、结果可归档的无人机巡检任务。

用户在三维地图上圈选一块区域,输入“这块地今天巡一遍消防通道”。系统不能直接把这句话交给无人机执行。它需要完成一系列业务动作:

  • 理解用户意图,提取巡检区域、时间和目标。
  • 查询可用无人机,检查设备状态与作业空域。
  • 根据区域和巡检要求规划航线,生成任务草稿。
  • 让用户确认任务内容。需要审批的任务进入审批流程。
  • 审批通过后将任务排队,下发到无人机执行。
  • 接收遥测数据、飞行状态和视觉告警,向用户反馈进度。
  • 飞行结束后汇总任务记录、巡检结果和异常处置情况,生成报告并归档。

这里有一个关键设计:AI 负责理解、组织和建议,传统业务模块负责校验、落库和执行。

大模型可以提出“使用某架无人机巡检这片区域”的建议,但不能仅凭模型输出就认定设备可用,也不能绕过审批直接下发飞行任务。

本章要解决的,是这些模块之间的调用顺序、数据交接、状态变化和失败恢复问题。

本章事实边界

类别内容
已知事实平台已有设备接入、航线规划、任务下发、遥测回传和告警处置能力。技术栈为 Java 21、Spring Boot 3、Spring AI、Spring AI Alibaba、PostgreSQL、Redis、RocketMQ、Milvus、MinIO、Vue 3、Cesium、SSE。
目标设计在现有平台上增加 Copilot 编排层,打通意图理解、设备查询、航线规划、任务审批、飞行执行和报告归档。本文涉及的类名、表名、接口路径及状态机均为建议的实现设计。
验证结果暂无,本文只做方案设计。没有已上线或实测的数据可以证明该流程已经跑通。

2在整个系统中的位置

Copilot 位于前端交互入口与传统无人机业务能力之间。它不替代原有业务模块,而是把原本需要用户逐步操作的流程组织起来。

系统按职责划分为四层。

层次核心模块主要职责
交互层Vue 3、Cesium、SSE 客户端圈选区域、输入目标、展示草稿、接收进度和告警
AI 编排层Copilot API、意图解析、流程编排、模型调用理解自然语言、组织工具调用、生成任务草稿、提供处置建议
业务能力层设备服务、空域服务、航线服务、任务服务、审批服务、报告服务校验业务条件、保存数据、控制状态、下发任务、生成报告
基础设施层PostgreSQL、Redis、RocketMQ、Milvus、MinIO持久化、缓存、事件传递、知识检索、文件存储

传统业务模块与 AI 模块的分工

操作负责模块AI 是否有最终决定权
解析“巡一遍消防通道”意图解析模块否,解析结果需要校验
查询可用设备设备服务否,设备服务返回真实状态
检查空域限制空域服务否,必须通过业务规则校验
生成航线建议AI 编排层调用航线服务否,航线服务负责校验和生成正式航线
生成任务草稿Copilot 编排层否,任务服务负责持久化
用户确认、审批任务服务、审批服务否,按业务权限和审批规则处理
下发飞行任务执行服务、设备接入模块否,必须通过执行前检查
分析告警并提出建议AI 分析模块否,处置动作由业务规则和授权决定
生成报告内容报告服务,可调用 AI 辅助整理否,报告数据必须来自真实任务记录

这种分工可以避免一个常见问题:模型说任务已经创建,并不代表数据库里真的存在任务;模型说无人机可以起飞,也不代表设备和空域检查已经通过。

任何会改变真实业务状态的操作,都必须经过对应的业务服务。

3总体设计

系统采用“Copilot 编排 + 业务服务执行 + 事件驱动反馈”的设计。

Copilot 通过明确的接口调用设备、空域、航线和任务服务。涉及飞行执行的操作必须经过确认、审批和执行前校验。任务执行后的状态与遥测数据通过事件传递给任务状态管理模块,再由 SSE 推送到前端。

  1. Vue 3 + Cesium圈选区域、输入巡检目标、展示任务和进度
  2. CopilotController接收请求,创建会话或巡检流程
  3. IntentParser提取区域、巡检目标、时间和约束
  4. CopilotOrchestrator编排工具调用,维护流程上下文
  5. DeviceService查询设备、检查在线状态和任务占用
  6. AirspaceService检查空域限制和作业权限
  7. RouteService规划航线、校验航线参数
  8. MissionService创建任务草稿、持久化任务、管理状态
  9. ApprovalService判断审批要求、提交审批、记录审批结果
  10. ExecutionService执行前检查、排队、下发任务
  11. DeviceAdapter对接无人机设备和既有设备接入平台
  12. TelemetryConsumer消费遥测、飞行状态和视觉告警事件
  13. MissionStateService校验状态迁移、更新任务状态
  14. ReportService汇总执行记录、告警和处置结果
  15. ObjectStorageService将报告文件保存到 MinIO
  16. PostgreSQL / Redis / RocketMQ分别承担持久化、缓存和事件传递
  17. SseController向前端推送任务状态、进度和告警

这里有三个需要注意的设计点。

第一,CopilotOrchestrator 负责组织流程,不直接操作无人机。它调用的是业务服务接口。

第二,任务状态以 PostgreSQL 中的任务记录为准。Redis 可以缓存任务进度,但不能成为唯一的状态来源。RocketMQ 负责传递执行事件,不能代替业务数据库的事务。

第三,SSE 只负责服务端向前端推送信息。用户确认、取消任务等操作仍然通过 HTTP API 发起,不通过 SSE 反向提交。

4完整执行链路

本节以“圈选区域,今天巡一遍消防通道”为例,说明一次巡检从请求进入到报告归档的完整生命周期。

4.1 接收用户请求

前端通过 Cesium 获取圈选区域的 GeoJSON,连同用户输入的自然语言一起提交。

接口:

POST /api/copilot/missions/drafts

请求中包含区域、用户目标、任务日期和幂等键。服务端验证用户身份和区域数据格式后,创建一次 Copilot 流程记录。

此时只代表系统收到了请求,不代表任务已经可以执行。

4.2 解析意图并检查业务条件

CopilotOrchestrator 调用 IntentParser,将自然语言转换成结构化的巡检意图。

例如:

  • 作业目标:消防通道巡检。
  • 作业区域:前端圈选的多边形。
  • 作业日期:当天。
  • 任务要求:覆盖指定区域,并采集巡检影像。

用户没有提供的信息不能由模型随意补成确定事实。缺少必要参数时,系统应要求用户补充,或者明确标记采用了什么默认值。

意图解析完成后,编排层依次调用:

  1. DeviceService:查询候选设备,检查在线状态、电量、载荷能力和任务占用情况。
  2. AirspaceService:检查区域对应的空域限制、作业权限和时间约束。
  3. RouteService:根据区域、设备能力和巡检要求生成航线方案,并执行航线校验。

设备在线不代表设备一定适合当前任务。航线生成成功也不代表任务一定可以执行。每个模块都需要返回明确的业务结果。

4.3 创建任务草稿

编排层收集设备查询、空域检查和航线规划的结果,形成任务草稿。

MissionService 校验必填字段、设备能力、航线版本和业务约束,再将草稿写入 PostgreSQL。

建议使用以下数据表:

  • copilot_sessions:记录 Copilot 流程及其当前阶段。
  • mission_drafts:保存待确认的任务方案。
  • missions:保存正式任务及其执行状态。
  • mission_events:记录任务状态变化和关键业务事件。
  • approval_records:记录审批人、审批结果和审批意见。
  • mission_reports:记录报告元数据和归档状态。

草稿创建成功后,接口同步返回 draftId、任务摘要、候选设备、航线摘要和当前状态。前端展示确认页面。

如果设备查询或航线规划失败,系统不应创建可执行任务。可以保留失败的流程记录,方便用户修改条件后重试。

4.4 用户确认与审批

用户确认草稿时,前端调用:

POST /api/missions/{missionId}/confirm

服务端重新检查草稿版本、用户权限、设备状态及关键业务约束,避免用户确认之后,设备或作业条件已经发生变化。

确认通过后,任务进入 PENDING_APPROVAL 或 QUEUED:

  • 需要审批:进入 PENDING_APPROVAL,由 ApprovalService 发起审批。
  • 不需要审批:通过执行前检查后进入 QUEUED。

审批通过后,审批服务通知任务服务推进状态。审批拒绝则任务退回可修改状态,或者终止当前任务,具体取决于审批策略。

审批通过不等于立即起飞。真正下发前,还需要重新检查设备、空域、航线和任务有效性。

4.5 排队与飞行执行

ExecutionService 接收已经确认并满足审批条件的任务,执行最后一道检查。

检查通过后,任务进入 QUEUED,执行服务生成下发指令,并通过既有设备接入能力发送给无人机。

下发请求需要携带唯一的执行指令 ID,防止消息重试造成重复执行。设备接入模块返回受理结果后,任务服务记录下发信息。只有收到可信的执行状态反馈,才能将任务推进到 EXECUTING。

任务执行期间:

  • 遥测数据更新位置、电量、速度等运行信息。
  • 飞行状态事件更新任务进度。
  • 视觉告警事件触发告警分析。
  • AI 可以根据告警上下文给出处置建议,但不能绕过安全规则直接执行危险动作。

例如,发现消防通道被占用时,系统可以生成“标记异常位置并通知值班人员”的建议,再交给告警处置模块按权限执行。

4.6 状态反馈与报告归档

TelemetryConsumer 消费设备状态、遥测和告警事件,调用 MissionStateService 更新任务状态,并将重要变化写入 mission_events。

SseController 根据任务事件向前端推送:

  • 任务状态变化。
  • 当前执行进度和最新位置。
  • 告警信息及处置建议。
  • 执行失败原因。
  • 报告生成和归档结果。

任务完成后,ReportService 根据任务记录、航线执行记录、遥测摘要、影像索引和告警处置结果生成报告。报告文件存入 MinIO,文件地址、摘要、版本和归档状态保存到 PostgreSQL。

报告生成失败时,任务仍然可以保持 COMPLETED,报告单独标记为待重试或生成失败,避免把已经完成的飞行任务错误地回滚成失败任务。

4.7 端到端时序

  1. 用户圈选区域并输入目标前端生成 GeoJSON 和自然语言请求
  2. CopilotController校验身份、参数和幂等键
  3. IntentParser将自然语言转换为结构化意图
  4. CopilotOrchestrator组织设备、空域和航线查询
  5. DeviceService查询候选设备并检查能力
  6. AirspaceService检查空域和作业权限
  7. RouteService生成并校验航线方案
  8. MissionService保存任务草稿并返回 draftId
  9. 前端确认用户检查任务摘要并提交确认
  10. MissionService复核版本和业务条件
  11. ApprovalService按策略审批或直接进入执行准备
  12. ExecutionService执行前检查并将任务排队
  13. DeviceAdapter下发任务并接收设备执行反馈
  14. TelemetryConsumer消费遥测、状态和告警事件
  15. MissionStateService校验状态迁移并记录事件
  16. SseController推送状态、进度和告警
  17. ReportService汇总真实执行记录并生成报告
  18. MinIO + PostgreSQL保存报告文件和归档元数据
  19. 前端展示完成状态和报告入口

4.8 关键 API 的输入与输出

以下接口路径均为目标设计,不代表现有平台已经实现。

接口输入同步返回
POST /api/copilot/missions/drafts区域 GeoJSON、用户目标、任务日期、幂等键sessionId、draftId、草稿摘要、当前状态
GET /api/devices/available区域、时间、设备能力要求候选设备及不可用原因
POST /api/routes/plan区域、设备 ID、巡检要求航线 ID、航点、航线校验结果
POST /api/missions/{id}/confirm草稿版本、确认信息新状态、是否需要审批
POST /api/missions/{id}/approve审批结果、意见审批记录及任务状态
POST /api/missions/{id}/execute任务版本、执行幂等键受理结果、任务 ID、当前状态
GET /api/missions/{id}任务 ID当前状态、进度、设备和航线摘要
GET /api/missions/{id}/report任务 ID报告状态、报告元数据及文件访问入口

设备查询、航线规划和任务确认等 HTTP 接口返回的是当前操作的处理结果,不应要求客户端一直等待整次飞行结束。

SSE 使用 GET /api/missions/{id}/events 建立连接。服务端通过事件 ID 和事件类型推送状态变化、进度、告警和报告状态。SSE 不代替 HTTP 接口返回完整任务对象,也不保证客户端断线期间收到每一条事件。客户端重连后应通过任务查询接口恢复当前状态。

4.9 任务状态机

建议将任务状态定义为:

状态含义
DRAFT草稿已生成,等待用户检查
PENDING_CONFIRMATION草稿通过初步校验,等待用户确认
PENDING_APPROVAL已确认,等待审批
QUEUED满足执行条件,等待下发或调度
EXECUTING设备已开始执行任务
PAUSED任务执行被暂停
COMPLETED飞行任务正常完成
FAILED任务执行失败或发生不可恢复错误
CANCELLED任务已取消

允许的主要状态迁移如下:

text
DRAFT -> PENDING_CONFIRMATION
PENDING_CONFIRMATION -> DRAFT
PENDING_CONFIRMATION -> PENDING_APPROVAL
PENDING_CONFIRMATION -> QUEUED
PENDING_CONFIRMATION -> CANCELLED
PENDING_APPROVAL -> QUEUED
PENDING_APPROVAL -> DRAFT
PENDING_APPROVAL -> CANCELLED
QUEUED -> EXECUTING
QUEUED -> CANCELLED
QUEUED -> FAILED
EXECUTING -> PAUSED
EXECUTING -> COMPLETED
EXECUTING -> FAILED
EXECUTING -> CANCELLED
PAUSED -> EXECUTING
PAUSED -> CANCELLED
PAUSED -> FAILED

状态机还有几条约束:

  • DRAFT 和 PENDING_CONFIRMATION 可以修改任务参数,但修改后必须重新校验。
  • PENDING_APPROVAL 的关键字段发生变化时,应撤销原审批结果并重新审批。
  • QUEUED 可以取消,但要同时撤销尚未下发的执行指令。
  • EXECUTING 不能直接改成 COMPLETED,必须有可信的执行完成依据。
  • PAUSED 恢复执行前,需要重新检查设备状态、航线进度和安全条件。
  • COMPLETED、FAILED、CANCELLED 都是终态,不允许直接跳回 QUEUED。

失败后需要重新执行时,应保留原任务和事件记录,再创建新的执行尝试或新任务。不能为了方便重试而把失败历史覆盖掉。

5技术选型与原理

本章的技术重点不是把所有组件都用上,而是让业务流程可控、状态可信、失败可恢复。

5.1 模块交互方式怎么选

方案优点问题本项目选择
所有模块直接同步调用实现简单,调用结果直观飞行过程长,容易让请求链路过长;故障可能级联用于短时查询和业务校验
全部使用消息队列模块解耦,适合异步处理用户难以立即获得明确结果,状态追踪复杂不用于所有接口
同步调用 + 异步事件请求阶段及时返回,执行阶段可持续反馈需要处理事件重复、顺序和最终一致性采用此方案

设备查询、空域检查、航线规划属于请求阶段的业务操作,可以通过服务接口同步调用。任务下发后的遥测、告警和状态变化适合通过 RocketMQ 传递。

5.2 核心组件的职责

技术在本流程中的用途设计要点
Spring AI / Spring AI Alibaba接入模型、组织工具调用和生成结构化结果限制模型可调用的工具,校验模型输出
PostgreSQL保存任务、审批、事件和报告元数据事务内完成关键状态更新
Redis缓存短期流程上下文、进度等热点信息不作为任务状态的唯一来源
RocketMQ传递任务执行事件、遥测摘要和告警事件消费端必须幂等
Milvus检索巡检规范、设备手册和处置知识检索结果只辅助判断,不代替业务规则
MinIO保存巡检影像、报告文件等对象数据库记录对象标识和归档状态
SSE向前端推送任务状态与进度断线后通过任务查询恢复
Vue 3 + Cesium展示地图、航线、设备位置和任务状态以服务端返回的数据为准

Spring AI 的工具调用不应被理解为模型拥有了直接操作业务数据库或设备的权限。模型可以请求调用一个受控工具,工具再调用具体业务服务。权限校验、参数校验和业务状态校验仍由服务端执行。

5.3 为什么需要事件与数据库配合

假设设备接入模块已经接收了任务下发指令,但任务服务在记录结果前发生故障。如果系统只依赖一次 HTTP 调用,就可能出现设备已经执行、数据库却仍显示排队的情况。

目标设计中,关键状态变化应写入 PostgreSQL,并通过可靠的事件发布机制通知其他模块。可以使用事务消息,或者使用事务发件箱表,例如 outbox_events,在同一数据库事务中保存业务变更和待发布事件,再由独立发布器发送到 RocketMQ。

事件消费端根据 eventId 去重,并检查任务当前状态。重复收到“任务已完成”事件时,不应重复生成任务完成记录或重复触发后续动作。

6关键数据结构与代码

以下代码展示核心接口、请求 DTO 和状态机的目标实现。包名、类名和字段可以按现有工程调整。

6.1 请求 DTO

java
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

import java.time.LocalDate;
import java.util.List;
import java.util.Map;

public record CreateMissionDraftRequest(
    @NotBlank String requestId,
    @NotBlank @Size(max = 2000) String userGoal,
    @NotNull LocalDate scheduledDate,
    @NotNull Map<String, Object> areaGeoJson,
    List<String> requiredCapabilities
) {}

public record ConfirmMissionRequest(
    @NotBlank String draftVersion,
    @NotNull Boolean confirmed
) {}

public record ExecuteMissionRequest(
    @NotBlank String missionVersion,
    @NotBlank String idempotencyKey
) {}

public record MissionDraftResponse(
    String sessionId,
    String draftId,
    String status,
    String summary,
    String routeId,
    String deviceId
) {}

areaGeoJson 代表前端圈选的区域数据。生产实现应使用明确的 GeoJSON 类型,并检查坐标系、几何类型、坐标范围和多边形有效性。这里使用 Map 只是为了突出请求结构。

requestId 用于识别一次创建请求,避免客户端重试时生成重复草稿。执行阶段的 idempotencyKey 用于避免重复下发。

6.2 状态枚举

java
public enum MissionStatus {
    DRAFT,
    PENDING_CONFIRMATION,
    PENDING_APPROVAL,
    QUEUED,
    EXECUTING,
    PAUSED,
    COMPLETED,
    FAILED,
    CANCELLED
}

状态迁移不能由 Controller 随意修改。应集中放在 MissionStateService 中。

java
import java.util.Map;
import java.util.Set;

public class MissionStateMachine {

    private static final Map<MissionStatus, Set<MissionStatus>>
        ALLOWED = Map.of(
            MissionStatus.DRAFT,
            Set.of(MissionStatus.PENDING_CONFIRMATION,
                   MissionStatus.CANCELLED),

            MissionStatus.PENDING_CONFIRMATION,
            Set.of(MissionStatus.DRAFT,
                   MissionStatus.PENDING_APPROVAL,
                   MissionStatus.QUEUED,
                   MissionStatus.CANCELLED),

            MissionStatus.PENDING_APPROVAL,
            Set.of(MissionStatus.DRAFT,
                   MissionStatus.QUEUED,
                   MissionStatus.CANCELLED),

            MissionStatus.QUEUED,
            Set.of(MissionStatus.EXECUTING,
                   MissionStatus.FAILED,
                   MissionStatus.CANCELLED),

            MissionStatus.EXECUTING,
            Set.of(MissionStatus.PAUSED,
                   MissionStatus.COMPLETED,
                   MissionStatus.FAILED,
                   MissionStatus.CANCELLED),

            MissionStatus.PAUSED,
            Set.of(MissionStatus.EXECUTING,
                   MissionStatus.FAILED,
                   MissionStatus.CANCELLED),

            MissionStatus.COMPLETED, Set.of(),
            MissionStatus.FAILED, Set.of(),
            MissionStatus.CANCELLED, Set.of()
        );

    public static void validate(
        MissionStatus current,
        MissionStatus next
    ) {
        if (!ALLOWED.getOrDefault(current, Set.of()).contains(next)) {
            throw new IllegalStateException(
                "非法任务状态迁移: " + current + " -> " + next
            );
        }
    }
}

该状态机只判断迁移是否合法,不负责业务校验。实际执行时,MissionStateService 还要校验任务版本、操作者权限、审批结果和设备反馈。

数据库更新应采用带当前状态或版本号的条件更新,防止并发请求同时修改同一任务。

例如:

text
UPDATE missions
SET status = :next_status,
    version = version + 1,
    updated_at = CURRENT_TIMESTAMP
WHERE id = :mission_id
  AND status = :current_status
  AND version = :expected_version;

如果受影响行数为零,说明任务可能已被其他请求更新。服务端应重新读取状态并判断是否需要重试,不能直接覆盖。

6.3 核心业务接口

java
public interface DeviceService {
    DeviceCandidates findAvailableDevices(
        DeviceQuery query
    );
}

public interface AirspaceService {
    AirspaceCheckResult check(
        AirspaceCheckRequest request
    );
}

public interface RouteService {
    RoutePlanResult plan(
        RoutePlanRequest request
    );
}

public interface MissionService {
    MissionDraftResponse createDraft(
        CreateMissionDraftRequest request
    );

    MissionView confirm(
        String missionId,
        ConfirmMissionRequest request
    );
}

public interface ApprovalService {
    ApprovalResult submit(String missionId);

    ApprovalResult approve(
        String missionId,
        ApprovalCommand command
    );
}

public interface ExecutionService {
    ExecutionAccepted execute(
        String missionId,
        ExecuteMissionRequest request
    );

    void cancel(String missionId);
}

public interface ReportService {
    ReportResult generate(String missionId);
}

这些接口表达模块边界。DeviceCandidates、RoutePlanResult 等返回对象应包含明确的业务结果和失败原因,不能只返回一段自然语言。

CopilotOrchestrator 负责串起这些接口,但不直接更新 missions 表,也不直接调用无人机 SDK。

6.4 一次失败如何恢复

以航线规划完成后、任务草稿落库失败为例。

  1. RouteService 已返回航线方案,但 MissionService 保存草稿时数据库事务失败。
  2. 系统不应向前端返回“草稿创建成功”,也不能把尚未持久化的任务当成可执行任务。
  3. 如果航线已经作为独立业务资源保存,应按资源生命周期决定是否保留或清理。不能直接假定航线和任务处于同一事务。
  4. 服务端返回明确的错误码,例如 DRAFT_PERSIST_FAILED,并记录流程失败原因。
  5. 用户重试时携带原 requestId。服务端先检查是否已经存在对应草稿,避免重复创建。
  6. 如果确认没有成功落库,则重新执行创建流程;如果已经落库,则返回现有结果。

对于飞行执行阶段的失败,处理方式不同。系统不能通过删除任务记录或将状态直接改回 QUEUED 来实现回滚。应保留失败状态和执行记录,完成设备安全检查后,创建新的执行尝试或新任务。

7异常、安全与边界条件

7.1 AI 输出不可信

模型可能解析错区域、漏掉必要条件,或者返回不存在的设备 ID。

处理方式:

  • 使用结构化输出约束意图字段。
  • 对设备 ID、航线 ID 和任务参数执行服务端校验。
  • 对无法确定的条件要求用户确认。
  • 模型输出不得直接写入正式任务状态。
  • AI 服务超时或不可用时,保留请求记录并返回可识别错误,不得伪造规划成功结果。

7.2 设备和空域条件发生变化

草稿生成时设备可用,不代表用户确认时仍然可用。

因此,草稿创建阶段和执行前都要检查关键约束。设备已被占用、空域限制变化或航线失效时,应阻止任务进入执行状态,并要求重新规划或人工处理。

7.3 重复消息和并发操作

RocketMQ 可能重复投递消息,HTTP 客户端也可能因超时重复提交。

建议采用以下机制:

  • 创建请求使用 requestId 幂等。
  • 执行指令使用唯一的 idempotencyKey。
  • 事件表使用唯一 eventId。
  • 任务更新使用版本号或乐观锁。
  • 状态迁移与事件记录放在同一数据库事务中。
  • 下发端对执行指令进行去重,避免重复起飞。

7.4 暂停、取消和失联

暂停或取消不是简单修改数据库字段。

任务执行中取消时,需要执行服务向设备发送取消指令,并等待设备反馈。若设备失联,系统应记录取消请求处于处理中或待确认状态,不能仅因为接口调用成功就认定无人机已经停止执行。

对于涉及飞行安全的异常,应按既有飞控和应急规则处理。AI 可以提供解释和建议,但不能替代飞控安全机制。

7.5 报告生成失败

报告生成和飞行执行是两个独立阶段。报告失败时,应保留已完成任务的真实状态,并通过报告状态进行单独重试。

建议 mission_reports.status 至少支持 PENDING、GENERATING、ARCHIVED、FAILED。报告元数据保存到 PostgreSQL,文件存入 MinIO。报告归档完成前,前端应显示“报告生成中”或“归档失败”,不能展示虚假的下载入口。

8测试与验证

本章尚无实测结果。下面是目标设计对应的验证计划,执行后才能形成真实结论。

测试场景验证内容预期结果
正常巡检从请求到报告的完整链路状态按规定迁移,报告可查询
意图缺失未提供必要区域或目标请求被拒绝或要求补充,不创建可执行任务
无可用设备所有候选设备均不满足条件返回明确失败原因,不进入排队
空域校验失败区域不允许执行作业阻止任务确认或执行
用户重复提交重复发送相同 requestId不产生重复草稿
执行指令重复同一幂等键重复下发不产生重复执行动作
审批拒绝审批人拒绝任务不进入排队或执行
状态并发更新多个请求同时修改任务仅合法状态迁移成功
遥测事件重复重复消费同一事件不重复推进状态或重复归档
SSE 断线重连客户端断开后重新连接通过查询接口恢复当前任务状态
报告生成失败MinIO 写入或报告生成异常任务保持真实完成状态,报告可以重试
执行期间设备失联设备未返回可信状态不伪造完成结果,进入异常处置流程

验收时应重点检查三件事:数据库状态是否与真实执行结果一致;重复请求和重复消息是否会产生重复动作;失败之后是否能从明确的业务状态恢复,而不是依靠人工直接修改数据库。

9面试表达与追问

问题一:用户说一句话,系统是怎么把它变成无人机任务的?

回答:

我把整个过程分成请求理解、业务校验、任务创建、执行控制和结果归档五个阶段。

用户在三维地图上圈选区域并输入目标,Copilot 先把自然语言转换成结构化意图,再调用设备服务、空域服务和航线服务获取真实业务结果。条件满足后,由任务服务保存草稿,用户确认并完成必要审批,再由执行服务下发任务。

飞行过程中,系统通过消息事件接收遥测和告警,更新任务状态,并通过 SSE 推送给前端。任务结束后,报告服务汇总真实执行记录并完成归档。

这里最重要的原则是,AI 负责组织流程和提供建议,业务服务负责校验、落库和执行。

问题二:为什么不让大模型直接调用无人机接口?

回答:

因为模型输出不等于业务事实,也不等于执行授权。

模型可能选错设备、遗漏空域限制,或者在网络重试时重复发起动作。如果让模型直接调用无人机接口,就很难保证权限、幂等和状态一致性。

所以我会把工具调用封装成受控的业务接口。设备服务确认设备状态,空域服务执行规则校验,任务服务管理状态,执行服务负责下发。模型只能在授权范围内发起请求,最终能不能执行由业务模块决定。

问题三:无人机已经执行成功,但系统没有更新任务状态,怎么办?

回答:

这属于设备侧执行结果和平台业务状态不一致的问题。

我会给每次执行分配唯一指令 ID,在平台记录下发结果,并通过设备反馈事件推进任务状态。事件消费端根据事件 ID 去重,更新任务状态时使用事务和版本校验。

如果设备已经执行,但平台在处理反馈时发生故障,就需要通过可靠事件重试、设备状态查询或执行记录对账来恢复。不能因为消息超时就直接重复下发起飞指令,也不能把数据库里的任务状态直接改成完成。

真正的目标是让执行指令可追踪、状态变化可审计、异常能够恢复,并且始终以可信的设备反馈和业务记录为依据。