# 前端接入文档：新增平台 HTTP 接口

## 1. 背景

本次后端新增了一组 HTTP 接口，供前端在现有 WebSocket 交互之外，补充以下能力：

- 同步“当前关心的对象及其类型”
- 运行时修改部分碰撞检测相关参数

## 2. 通用约定

- 所有接口均为 `POST`
- 所有请求和响应均为 `application/json`
- 当前接口无鉴权、无 token、无签名校验
- 当前后端未补 CORS
  - 如果前端是浏览器并且跨域访问，后续可能还需要后端补 CORS
- 配置修改仅在内存中生效
  - 服务重启后恢复默认配置
- `VehicleRegistry` 是“增量更新”
  - 本次未传入的历史对象不会被清空

建议前端统一配置接口基础地址，例如：

```ts
const API_BASE = 'http://<后端IP>:8080';
```

## 3. 接口一：对象注册同步

### 3.1 接口地址

```http
POST /api/VehicleRegistry
```

### 3.2 用途

前端将“当前关心的对象及其类型”增量同步给后端。

该接口会直接影响：

- 哪些对象参与碰撞检测
- 哪些车辆可能被系统下发控制命令

### 3.3 请求头

```http
Content-Type: application/json
```

### 3.4 请求体

```json
[
  { "vehicleID": "QN001", "vehicleType": "WUREN" },
  { "vehicleID": "TQ001", "vehicleType": "TEQIN" },
  { "vehicleID": "AC001", "vehicleType": "HANGKONG" }
]
```

### 3.5 字段说明

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `vehicleID` | `string` | 是 | 对象唯一标识，不能为空 |
| `vehicleType` | `string` | 是 | 对象类型，只允许固定枚举值 |

### 3.6 `vehicleType` 可选值

- `WUREN`
- `TEQIN`
- `HANGKONG`
- `PUTONG`
- `JIUYUAN`

### 3.7 业务语义

#### `WUREN`

- 加入“可控车辆集合”
- 加入“受管车辆集合”

#### `TEQIN`

- 加入“受管车辆集合”
- 不属于“可控车辆集合”

#### `HANGKONG`

- 加入“已选中航空器集合”
- 用于参与航空器相关碰撞/安全区计算

#### `PUTONG`

- 仅记录类型
- 不进入可控/受管/已选航空器集合

#### `JIUYUAN`

- 仅记录类型
- 不进入可控/受管/已选航空器集合

### 3.8 成功响应

```json
{
  "status": "success",
  "updatedAt": 1742280000000,
  "updated": 3,
  "controllableCount": 1,
  "typesCount": {
    "WUREN": 1,
    "TEQIN": 1,
    "HANGKONG": 1
  },
  "controllableVehicleIDs": ["QN001"]
}
```

### 3.9 响应字段说明

| 字段 | 类型 | 说明 |
|---|---|---|
| `status` | `string` | 固定为 `success` |
| `updatedAt` | `number` | 毫秒时间戳 |
| `updated` | `number` | 本次成功处理的条目数 |
| `controllableCount` | `number` | 当前系统内全部 `WUREN` 总数 |
| `typesCount` | `object` | 本次请求内各类型数量统计 |
| `controllableVehicleIDs` | `string[]` | 当前系统内全部可控车辆 ID 列表，即所有 `WUREN` |

### 3.10 错误响应示例

#### 请求体不是数组

```json
{
  "status": "error",
  "message": "Body must be an array"
}
```

#### 参数非法

```json
{
  "status": "error",
  "message": "Invalid request",
  "errors": [
    "item must be an object",
    "missing vehicleID",
    "missing vehicleType",
    "vehicleID must be string",
    "vehicleType must be string",
    "vehicleID must be non-empty",
    "invalid vehicleType=xxx"
  ]
}
```

#### 非法 JSON

```json
{
  "status": "error",
  "message": "Invalid JSON",
  "detail": "..."
}
```

### 3.11 前端调用示例

```ts
await fetch(`${API_BASE}/api/VehicleRegistry`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify([
    { vehicleID: 'QN001', vehicleType: 'WUREN' },
    { vehicleID: 'TQ001', vehicleType: 'TEQIN' },
    { vehicleID: 'AC001', vehicleType: 'HANGKONG' }
  ])
});
```

### 3.12 接入建议

- 页面首次拿到关注对象列表后调用一次
- 当前关注对象发生变化时再次调用
- 请求体必须是数组，即使只有一条数据也必须传数组
- 后端是“增量更新”，不要假设未传对象会被清空

## 4. 接口二：跑道区域航空器预警区半径

### 4.1 接口地址

```http
POST /config/runway/warning_zone_radius/aircraft
```

### 4.2 用途

前端运行时修改跑道区域的“航空器预警区半径”。

### 4.3 请求头

```http
Content-Type: application/json
```

### 4.4 请求体

```json
{ "value": 300 }
```

### 4.5 字段说明

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `value` | `number` | 是 | 必须是大于 0 的有限数值 |

### 4.6 成功响应

```json
{
  "status": "success",
  "area": "runway",
  "field": "warning_zone_radius.aircraft",
  "old": 200.0,
  "new": 300.0
}
```

### 4.7 错误响应示例

#### 缺少字段

```json
{
  "status": "error",
  "message": "Missing field: value"
}
```

#### 值非法

```json
{
  "status": "error",
  "message": "Invalid value: must be a finite number greater than 0"
}
```

#### 非法 JSON

```json
{
  "status": "error",
  "message": "Invalid JSON",
  "detail": "..."
}
```

#### 字段类型错误

```json
{
  "status": "error",
  "message": "Invalid field type",
  "detail": "..."
}
```

### 4.8 前端调用示例

```ts
await fetch(`${API_BASE}/config/runway/warning_zone_radius/aircraft`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ value: 300 })
});
```

## 5. 接口三：跑道区域航空器告警区半径

### 5.1 接口地址

```http
POST /config/runway/alert_zone_radius/aircraft
```

### 5.2 用途

前端运行时修改跑道区域的“航空器告警区半径”。

### 5.3 请求体

```json
{ "value": 200 }
```

### 5.4 成功响应

```json
{
  "status": "success",
  "area": "runway",
  "field": "alert_zone_radius.aircraft",
  "old": 150.0,
  "new": 200.0
}
```

### 5.5 前端调用示例

```ts
await fetch(`${API_BASE}/config/runway/alert_zone_radius/aircraft`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ value: 200 })
});
```

### 5.6 说明

该接口的请求规则、错误规则与“预警区半径”接口一致，唯一差异是：

- 路径不同
- 成功响应中的 `field` 固定为 `alert_zone_radius.aircraft`

## 6. 接口四：冲突解除距离阈值

### 6.1 接口地址

```http
POST /config/collision/diverging_release_distance
```

### 6.2 用途

前端运行时修改“冲突解除距离阈值”。

### 6.3 请求体

```json
{ "value": 50 }
```

### 6.4 成功响应

```json
{
  "status": "success",
  "area": "collision",
  "field": "collision.diverging_release_distance",
  "old": 40.0,
  "new": 50.0
}
```

### 6.5 前端调用示例

```ts
await fetch(`${API_BASE}/config/collision/diverging_release_distance`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ value: 50 })
});
```

### 6.6 说明

该接口的请求规则、错误规则与前两个配置接口一致，唯一差异是：

- 路径不同
- 成功响应中的：
  - `area` 固定为 `collision`
  - `field` 固定为 `collision.diverging_release_distance`

## 7. 推荐前端封装

### 7.1 通用 POST JSON 请求

```ts
async function postJson<T>(url: string, body: unknown): Promise<T> {
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(body)
  });

  const data = await response.json();

  if (!response.ok) {
    throw data;
  }

  return data as T;
}
```

### 7.2 类型定义

```ts
type VehicleType = 'WUREN' | 'TEQIN' | 'HANGKONG' | 'PUTONG' | 'JIUYUAN';

interface VehicleRegistryItem {
  vehicleID: string;
  vehicleType: VehicleType;
}

interface VehicleRegistryResponse {
  status: 'success';
  updatedAt: number;
  updated: number;
  controllableCount: number;
  typesCount: Record<string, number>;
  controllableVehicleIDs: string[];
}

interface ConfigUpdateResponse {
  status: 'success';
  area: string;
  field: string;
  old: number;
  new: number;
}

interface ErrorResponse {
  status: 'error';
  message: string;
  detail?: string;
  errors?: string[];
}
```

### 7.3 接口封装示例

```ts
export async function syncVehicleRegistry(items: VehicleRegistryItem[]) {
  return postJson<VehicleRegistryResponse>(`${API_BASE}/api/VehicleRegistry`, items);
}

export async function updateRunwayWarningRadius(value: number) {
  return postJson<ConfigUpdateResponse>(
    `${API_BASE}/config/runway/warning_zone_radius/aircraft`,
    { value }
  );
}

export async function updateRunwayAlertRadius(value: number) {
  return postJson<ConfigUpdateResponse>(
    `${API_BASE}/config/runway/alert_zone_radius/aircraft`,
    { value }
  );
}

export async function updateDivergingReleaseDistance(value: number) {
  return postJson<ConfigUpdateResponse>(
    `${API_BASE}/config/collision/diverging_release_distance`,
    { value }
  );
}
```

## 8. 前端联调注意事项

### 8.1 `VehicleRegistry`

- 请求体必须是数组
- `vehicleID` 不能为空字符串
- `vehicleType` 必须严格使用大写枚举值
- 该接口是“增量更新”，未传对象不会被后端自动删除

### 8.2 配置接口

- `value` 必须是 `number`
- 不要传字符串，例如 `"300"` 是错误的
- 必须大于 `0`

### 8.3 跨域问题

- 当前后端未配置 CORS
- 如果前端页面和后端接口不是同域同端口，浏览器可能拦截
- 如出现跨域报错，需要后端后续补 CORS 配置

### 8.4 生效方式

- 这 3 个配置接口修改后立即在当前服务进程内生效
- 服务重启后恢复默认值，不会持久化

## 9. 联调建议流程

### 9.1 第一步：先调对象注册接口

确认前端能成功将当前关注对象同步给后端。

### 9.2 第二步：再调配置接口

确认预警半径、告警半径、冲突解除距离可运行时修改。

### 9.3 第三步：观察业务联动

重点观察：

- 碰撞检测结果是否按新对象范围生效
- 跑道区域预警/告警范围是否按新阈值变化
- 冲突解除逻辑后续接入时是否读取当前运行时配置

## 10. 最小联调示例

```ts
async function initPlatformObjects() {
  await syncVehicleRegistry([
    { vehicleID: 'QN001', vehicleType: 'WUREN' },
    { vehicleID: 'TQ001', vehicleType: 'TEQIN' },
    { vehicleID: 'AC001', vehicleType: 'HANGKONG' }
  ]);
}

async function updateRuntimeConfig() {
  await updateRunwayWarningRadius(300);
  await updateRunwayAlertRadius(200);
  await updateDivergingReleaseDistance(50);
}
```

## 11. 接口清单汇总

| 接口 | 方法 | 用途 |
|---|---|---|
| `/api/VehicleRegistry` | `POST` | 同步当前关心对象及类型 |
| `/config/runway/warning_zone_radius/aircraft` | `POST` | 修改跑道区域航空器预警区半径 |
| `/config/runway/alert_zone_radius/aircraft` | `POST` | 修改跑道区域航空器告警区半径 |
| `/config/collision/diverging_release_distance` | `POST` | 修改冲突解除距离阈值 |
