# 订单调整记录——新增「调整记录」列表接口，按次展示每次 submit 改了什么

> 端类型：管理后台
> 变更类型：新增接口
> 涉及接口：
> - `GET /v3/admin/order/{id}/adjustment-record`（查询订单调整记录列表）

---

## ① 接口背景

订单调整（submit）原来只在时间线追加一条「订单调整」无明细说明。前端调整弹窗无法向操作人展示「本次调整改了哪些项」。

本期新建独立「调整记录」：每次 submit 成功后在 `order_adjustment_record` 表落一条记录（含每项变更的类型、文案、前值、后值、金额差），后台订单详情「调整记录」弹窗调用本接口逐条渲染。

---

## ② 变更清单

| 编号 | 类型 | 说明 |
|---|---|---|
| 1 | ✨ 新增接口 | `GET /v3/admin/order/{id}/adjustment-record`，返回本订单全部调整记录（按时间倒序） |
| 2 | ✨ 新增 VO | `AdjustmentRecordVO`（记录主体）/ `ChangeItemVO`（单项变更）/ `StatusNoteVO`（状态摘要） |
| 3 | ✨ 新增枚举 | `AdjustChangeType`，定义 8 个变更类型（见§⑥） |

---

## ③ 接口详情

| 项目 | 说明 |
|---|---|
| 方法 | GET |
| 路径 | `/v3/admin/order/{id}/adjustment-record` |
| 接口名 | 查询调整记录列表 |
| 认证 | 需要 JWT（管理后台 Token） |
| 幂等性 | 只读，天然幂等 |
| 限流 | 无特殊限流 |
| 分页 | 不分页（全量返回；单订单调整次数预期 < 50 条） |

---

## ④ 接口入参

### 4.1 路径参数

| 参数 | 类型 | 必填 | 说明 |
|---|---|:---:|---|
| `id` | Long（String） | 是 | 订单 ID（雪花 ID，JS 请当字符串处理） |

### 4.2 请求体

无请求体。

---

## ⑤ 出参字段

### 顶层结构

```
Result<List<AdjustmentRecordVO>>
```

data 为 `AdjustmentRecordVO` 数组，按 `occurredAt` 降序（最新调整在前）。无调整记录时 `data: []`。

### AdjustmentRecordVO（调整记录主体）

| 字段 | 类型 | 可空 | 说明 |
|---|---|:---:|---|
| `id` | String | 否 | 调整记录 ID（雪花 ID 序列化为字符串，防 JS 精度丢失） |
| `occurredAt` | String | 否 | 调整发生时间，格式 `yyyy-MM-dd HH:mm:ss` |
| `operatorName` | String | 否 | 操作人姓名（定制师真名） |
| `changeCount` | Integer | 否 | 本次变更项总数，渲「N 项变更」 |
| `items` | List&lt;ChangeItemVO&gt; | 否 | 变更明细列表（至少 1 项） |
| `statusNote` | StatusNoteVO | 是 | 本次调整引发的状态摘要；无状态变化时为 `null` |
| `balanceBefore` | Number | 否 | 调整前待付尾款（BigDecimal，无精度丢失风险，保留两位小数） |
| `balanceAfter` | Number | 否 | 调整后待付尾款（BigDecimal，保留两位小数） |

> `balanceBefore` / `balanceAfter` 是权威尾款，不要用 `Σ(items[].amountDelta)` 推算（两者不同轴）。

### ChangeItemVO（单项变更明细）

| 字段 | 类型 | 可空 | 说明 |
|---|---|:---:|---|
| `type` | String | 否 | 变更类型枚举值（见§⑥ AdjustChangeType） |
| `label` | String | 否 | 前端直显文案，后端已拼好（含资源名/翻译），**前端零拼接** |
| `before` | String | 是 | 变更前值（新增类为 null） |
| `after` | String | 是 | 变更后值（删除类为 null） |
| `amountDelta` | Number | 是 | 本项声明价差（正=加费/负=减费；仅行程节点类有值，其余 null） |

> `before` / `after` 都非空时渲「before → after」；`amountDelta` 非 null 时渲金额差（>0 渲 `+¥X`，<0 渲 `-¥X`，=0 渲 `+¥0`）。

### StatusNoteVO（状态变化摘要，可空）

| 字段 | 类型 | 可空 | 说明 |
|---|---|:---:|---|
| `title` | String | 否 | 摘要标题，如「状态回滚」 |
| `content` | String | 否 | 摘要内容，如「房型与车辆已退回准备中，等待房控/车控复核」 |

---

## ⑥ 枚举 / 数据字典

### AdjustChangeType（items[].type 取值）

| 枚举值 | 含义 |
|---|---|
| `HEADCOUNT` | 出行人数量变化（成人/儿童/小童/婴儿各档分别一条） |
| `DEPART_DATE` | 出发日期调整 |
| `TRIP_DAYS` | 行程天数变化（汇总一行，不逐天刷屏） |
| `EDIT_NODE` | 编辑已有行程节点（单价/数量改价） |
| `ADD_NODE` | 新增行程节点（增项） |
| `REMOVE_NODE` | 删除行程节点（删项） |
| `HOTEL_REQ` | 酒店需求已调整（粒度：本期只记「已调整」一句） |
| `VEHICLE_REQ` | 车辆需求已调整（粒度：本期只记「已调整」一句） |

---

## ⑦ 错误码

| code | message | 含义 | 前端处理建议 |
|---|---|---|---|
| 586001 | 订单不存在 | 订单 ID 无效或已删除 | toast 错误 |
| 200 + data:[] | （正常） | 本订单暂无调整记录 | 渲空态「暂无调整记录」 |

---

## ⑧ 示例

### 8.1 典型成功——7 项变更（含状态回滚）

**请求**

```http
GET /v3/admin/order/7910000000001/adjustment-record
Authorization: Bearer <token>
```

**响应**

```json
{
  "code": 200,
  "message": "success",
  "data": [
    {
      "id": "7920000000001",
      "occurredAt": "2026-06-28 14:35:18",
      "operatorName": "李雯",
      "changeCount": 7,
      "items": [
        { "type": "ADD_NODE", "label": "增项「敖鲁古雅驯鹿园」", "before": null, "after": null, "amountDelta": 200.00 },
        { "type": "ADD_NODE", "label": "增项「莫尔道嘎国家森林公园」", "before": null, "after": null, "amountDelta": 150.00 },
        { "type": "ADD_NODE", "label": "增项「室韦俄罗斯族民族乡」", "before": null, "after": null, "amountDelta": 0.00 },
        { "type": "ADD_NODE", "label": "增项「额尔古纳湿地公园」", "before": null, "after": null, "amountDelta": 120.00 },
        { "type": "ADD_NODE", "label": "增项「黑山头古城遗址」", "before": null, "after": null, "amountDelta": 0.00 },
        { "type": "TRIP_DAYS", "label": "行程天数调整", "before": "7天6晚", "after": "8天7晚", "amountDelta": null },
        { "type": "HEADCOUNT", "label": "成人人数调整", "before": "2", "after": "4", "amountDelta": null }
      ],
      "statusNote": {
        "title": "状态回滚",
        "content": "请核对补充新增旅客信息·房型与车辆已退回准备中，等待房控/车控复核"
      },
      "balanceBefore": 0.00,
      "balanceAfter": 0.00
    }
  ]
}
```

### 8.2 边界情况——无调整记录

**请求**

```http
GET /v3/admin/order/7910000000002/adjustment-record
Authorization: Bearer <token>
```

**响应**

```json
{
  "code": 200,
  "message": "success",
  "data": []
}
```

> 前端渲空态「暂无调整记录」。

### 8.3 业务失败——订单不存在

**请求**

```http
GET /v3/admin/order/9999999999999/adjustment-record
Authorization: Bearer <token>
```

**响应**

```json
{
  "code": 586001,
  "message": "订单不存在",
  "data": null
}
```

---

## ⑨ 业务边界

**适用：**
- 任意状态的订单均可调用（含已取消、已完成），用于查阅历史。
- 每次调整弹窗 submit 成功后落一条记录；无 submit 历史则返空数组。

**不适用：**
- 本接口不展示行程点编辑历史（行程节点单点增删改不走 adjustment-record，走时间线）。
- 不展示状态变更日志（时间线接口覆盖）。

**特殊边界：**
- `statusNote` 只在本次调整引发订单子流程状态回滚（如改期/增减天导致配房配车退回准备中）时有值，否则为 `null`，前端需做空值保护。
- `amountDelta` 是每项操作的「声明价差」，与后端算价引擎算出的实际尾款差（`balanceAfter - balanceBefore`）可能不等，**尾款以 `balanceBefore` / `balanceAfter` 为准**。

---

## ⑩ 修改前后对比

本次为新增接口，无修改前后对比。

---

## ⑪ 影响评估 / 回滚

本次为新增接口，不影响任何现有接口；回滚只需停止调用本端点。

---

## ⑫ 注意事项

1. `id` 字段是雪花 ID 序列化为字符串，JS 请勿转 Number（精度丢失）。
2. `occurredAt` 格式为 `yyyy-MM-dd HH:mm:ss`（后端 Jackson 序列化），非 ISO 8601。
3. `items[].label` 已由后端拼好（含资源中文名），前端直接渲染，**不要尝试自己拼接**。
4. `before` / `after` 双非空才渲箭头；`statusNote` 为 null 时整块不渲（无需占位符）。
5. 本接口不分页，全量返回；若某订单调整次数异常多（> 50），前端可加折叠「展开更多」做 UX 保护（后端不限制）。

---

## ⑬ 关联 / 联系人

- Issue：[#4540](https://git.1814.love:8443/wx/HL/issues/4540)
- PR：[#4556](https://git.1814.love:8443/wx/HL/pulls/4556)
- Commit：[34e921b9e](https://git.1814.love:8443/wx/HL/commit/34e921b9ef3b77f6707fc5b720187e876ec5581f)
- 后端负责人：腰苏图（yst）
