# 订单服务v3 接口契约审计修复（W1 钱/状态热区：金额=String + 被保人脱敏 + 退款筛选 + 投保幂等）— 修改接口 — 管理后台

> 变更类型：⚠️ 序列化口径变更（金额字段改 String）+ 安全收紧（PII 脱敏）+ 行为收紧（投保幂等）+ 数据补全（退款筛选/派生字段），无破坏性删除
> 端类型：管理后台（保险 / 订单 / 支付 / 退款 / 结算 / 调价 / 折扣 / 班期）
> 日期：2026-06-18
> 服务：hl-order-service-v3
> PR：https://git.1814.love:8443/wx/HL/pulls/3965

---

## ⚠️ 关键说明

经 `api-contract-audit` workflow 对 order-v3 钱/状态关键热区 14 个 Controller 做语义审计（实现 ⨯ 声明契约 ⨯ 设计文档 ⨯ 7 维度业务规则）+ 对抗式复核，97 发现 → 50 确认（P0=0/P1=17/P2=19/P3=14），本批修复其中 32 条清晰契约 bug。已合并 dev-v3、部署测试服双实例（totalCodes=626）、本地全量 3566 单测零净增回归、测试服 9443 + 真 admin token 行为实测通过。

**前端最需关注：第 1 条金额字段序列化口径变更（解析方式要改）。**

---

## 1. 🔴 金额字段统一改为 String（解析口径变更，前端必看）

平台铁律「JSON 金额必为 String」此前在 order-v3 多处 RespVO 漏标 `@JsonSerialize(ToStringSerializer)`，金额被序列化成 JSON number。本次全部补齐，**这些字段从「数字」变为「带引号字符串」**：

| 端点 | VO / 字段 |
|---|---|
| `GET /v3/admin/order/{id}/finance` | FinanceVO 全部金额 + 内部 payments/discounts/surcharges.amount |
| `GET /v3/admin/order` 列表 / `GET /v3/admin/order/list` | 列表项 totalAmount/paidAmount/balanceAmount/depositAmount/singleRoomSurcharge |
| `GET /v3/admin/order/{id}/refund`（退款 Tab） | totalRefundAmount/refundAmount/amount |
| `POST /v3/admin/order/{id}/cancel/pre-trip`、`/terminate`、`GET /cancel-preview` | refundAmount/baselineRefund/adjustAmount/finalRefund/paidAmount/deductAmount |
| `GET /v3/admin/payment/list`、`/{transactionId}`、`/order/{orderId}` | PaymentTransactionVO.totalAmount / PaymentListItemRespVO.amount |
| `GET /v3/admin/order/{orderId}/payment/list` | amount |
| `GET /v3/admin/refund/{refundId}` 等退款记录 | RefundRecordVO.refundAmount/totalAmount |
| 折扣 5 端点 / 结算各保存与汇总 / 调价 / 保险方案 preview-apply / 保险方案保费 | 各金额字段 |

注：比率字段（profitRate 等）仍为数字，不受影响。

示例（支付列表）：

```json
{ "code": 200, "data": { "records": [ { "totalAmount": "1500.00" } ] } }   // 此前是 1500.00（数字）
```

前端请用字符串解析金额（`new Decimal(str)` / `parseFloat(str)`），勿假设为 number。

---

## 2. 被保人证件号 / 手机号脱敏（安全收紧）

此前以下端点返回的 `insuredPersons[].idNo / idCardNo / phone` 为**明文**（VO 声明「脱敏」但实现直塞解密值）。本次在装配处统一脱敏（前 3 后 4，与司机险链口径一致）：

| 端点 |
|---|
| `GET /v3/admin/insurance/orders/{id}`（详情） |
| `GET /v3/admin/insurance/coverage/{orderId}` |
| `GET /v3/internal/insurance/coverage/{orderId}` |
| `POST /v3/admin/insurance/purchase` / `selective-purchase` / `orders/{id}/sync-status`（返回详情） |

```json
{ "insuredPersons": [ { "idNo": "152***********5216", "phone": "138****8000" } ] }
```

如管理端确需明文，需另开专用解密端点（产品决策，见 PR）。

---

## 3. 退款申请分页：筛选维度补齐 + 派生字段填充

`GET /v3/admin/refund/application/page` 此前声称支持多维筛选但实现全部忽略（仅分页 + createTime 倒序）。本次补齐：

- **筛选**（均可选）：`orderId` / `orderNo`（模糊）/ `applicantName`（模糊）/ `applicantId` / `status`（多选）/ `auditStatus` / `createTimeFrom`-`createTimeTo` / `refundedAtFrom`-`refundedAtTo`
- **排序**：`sortField`（白名单 createTime/refundedAt/actualAmount）+ `sortOrder`（ASC/DESC，默认 createTime DESC）
- **派生字段填充**：`reviewerNameLast`（最后审核人）、`refundRecordCount`（退款执行记录数）此前恒 null，现批量回填
- **移除字段**：`appealRoundCount`（refund_appeal 表已于 V20260527_001 DROP，该字段永不可填，删除避免误导）

---

## 4. 投保幂等防护（行为收紧）

`POST /v3/admin/insurance/purchase`（手动投保）/ `selective-purchase`（选择性投保）此前无幂等/锁，重复提交会重复出单扣保费。本次加 `@Idempotent + @Lock4j`（按 orderId 串行 + 业务键去重），**短时间内完全相同的重复请求会被拦截**，前端无须额外处理但可知悉。

---

## 5. 其它（前端基本无感 / 字典对齐）

- **支付交易状态字典**：`FAILED` → `REFUNDED`（对齐枚举/DB；此前 notes/VO/DTO 写的 FAILED 实际无法存储）。状态取值：`PENDING/SUCCESS/CLOSED/REFUNDED`。
- **新增错误码 `589516`**：团期共享成本录入的成本类型非法（此前抛裸异常）。
- **保单下载 notes 修正**：`GET /v3/internal/insurance/policy/{id}/download` 返回的是 **OSS 下载 URL 字符串**（非 Base64），未生成返 540310 / 手工单返 540226。
- **保险方案删除**：删除前引用校验只统计在途/生效保单（INSURED/INSURING），历史退保/失败单不再永久拦死删除。
- **保险方案 toggle-status**：改为原子操作（消除并发读改写竞态）。
- **被保人 gender**：新投保单详情正确返回性别（1=男/2=女/0=未知）；修复前的历史单仍可能为 null（不回填历史）。
- 删除若干孤儿字段（保险方案 autoInsure/segmentType/destinationCode 等声明但从未赋值）。

---

## 备注

- P3 + 低置信发现暂未处理；以下**产品/行为决策项**未改行为，待确认：删退款政策语义（禁删 vs 软删降级）、结算 step6 金额公式与状态语义、后台建单幂等键定义、班期结算 D1/D2 返回结构、preview-apply 覆盖参数、PARTIAL 部分退款语义、transition 的 Map payload。
- **结算 step6 逻辑补全**（confirmCheckHash 校验 / OrderSettledEvent MQ / Step 前置校验 / Step3 响应字段 / insurance_premium 实值）建议独立工单（P1，重型）。
