diff --git a/changelogs-v2/2026-08/08_5704_核单域下线数据指纹-修改接口-管理后台.md b/changelogs-v2/2026-08/08_5704_核单域下线数据指纹-修改接口-管理后台.md new file mode 100644 index 0000000..971e108 --- /dev/null +++ b/changelogs-v2/2026-08/08_5704_核单域下线数据指纹-修改接口-管理后台.md @@ -0,0 +1,305 @@ +--- +schema: "hl-changelog/v2" +ticket: "5704" +title: "核单域下线数据指纹乐观锁:6 个端点删除指纹/版本号入参与出参字段" +consumer: "admin" +change_type: "修改接口" +author: "yaosutu(GIT)" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "PR #5709 已合并 dev-v3;核单域 6 个端点删除 expectedSourceFingerprint/version 入参与 sourceFingerprint/version 出参,错误码 584108/584110/584325 同步下线。字段删除属硬破坏契约,前端必须先停传这些字段再与后端同批发布。" +updated_at: "2026-08-08" +base: "dev-v3" +--- + +# 【⚠️ 修改接口·管理后台】核单域下线数据指纹乐观锁,6 个端点删除指纹/版本号字段(#5704) + +> **PR**: [#5709](https://git.1814.love:8443/wx/HL/pulls/5709) | **服务**: hl-order-service-v3 | **更新时间**: 2026-08-08 + +## 1. 接口背景 + +核单为单人负责场景,不存在多人并发同时修改同一订单核单数据的情况。此前引入的数据指纹(sha256)/版本号乐观锁机制(前端 GET 拿到指纹,保存/确认/提交时回传,数据被他人改动则拒存)对单人操作无实际保护价值,且强制前端每次保存前 GET 并回传 64 位十六进制指纹,增加对接负担。本次将核单域内该机制整体下线:**删除 6 个端点的指纹/版本号入参字段与对应出参字段,3 个相关错误码不再触发**。 + +> ⚠️ 本次为**字段删除类硬破坏契约**:其中 5 个端点的请求体带未知字段白名单校验,前端若继续传已删除的字段会被拒绝(详见 §11)。**前端必须先删除这些字段的传参,再与后端同批发布。** + +## 2. 变更清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 前端动作 | +|---|---|---|---|---|---| +| 1 | 保存导游费用 | PUT | `/v3/admin/order/{orderId}/settlement/guide-fees` | 删除入参 `expectedSourceFingerprint`、删除出参 `sourceFingerprint` | 停止传参/读字段 | +| 2 | 确认导游费用 | POST | `/v3/admin/order/{orderId}/settlement/guide-fees/confirm` | 删除入参 `expectedSourceFingerprint` | 停止传参 | +| 3 | 保存摄影费用 | PUT | `/v3/admin/order/{orderId}/settlement/photographer-fees` | 删除入参 `expectedSourceFingerprint`、删除出参 `sourceFingerprint` | 停止传参/读字段 | +| 4 | 确认摄影费用 | POST | `/v3/admin/order/{orderId}/settlement/photographer-fees/confirm` | 删除入参 `expectedSourceFingerprint` | 停止传参 | +| 5 | 保存车辆核单草稿 | PUT | `/v3/admin/order/{orderId}/settlement/step3/vehicles` | 删除入参 `version`、删除出参 `version` | 停止传参/读字段 | +| 6 | 完成核单 | POST | `/v3/admin/order/{orderId}/settlement/finalize` | 删除入参 `reimbursementExpectedSourceFingerprint`、`groupExpectedSourceFingerprint` | 停止传参 | + +同时下线的错误码:`584108`、`584110`、`584325`(详见 §7)。 + +## 3. 接口详情 + +- **使用场景**:核单人员在订单核单页维护导游费用、摄影费用、车辆核单草稿,并在全部分类就绪后完成核单提交。 +- **认证**:需要管理后台登录态(Bearer Token)。 +- **幂等性**:保存类接口按订单维度覆盖式保存,重复提交相同载荷结果一致;确认/提交接口重放安全(不再有指纹前置校验)。 +- **限流**:未声明接口专属限流。 +- **方法/路径**:见 §2 变更清单(共 6 个端点;对应的 GET 查询端点出参同步删除指纹/版本号字段,见 §5)。 + +## 4. 接口入参 + +### 4.1 路径参数 + +| 参数 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `orderId` | Long | 是 | 订单 ID,路径参数,6 个端点一致 | + +### 4.2 请求体字段(变更后现状) + +**PUT /settlement/guide-fees(保存导游费用)** + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `items` | Array | 是 | 导游费用明细全量集合,最多 200 条;每项含 `id`/`candidateKey`/`staffAssignmentId`/`serviceDate`/`name`/`serviceType`/`paymentMethod`/`amount`/`remark`/`voucherUrls`/`sourceResolution`/`candidateStatus` | +| `excludedCandidateKeys` | Array of String | 否 | 明确排除的候选 key;空数组表示本次不新增排除项 | +| ~~`expectedSourceFingerprint`~~ | - | - | **已删除,禁止再传**(传了会被 584128 白名单拒绝) | + +**POST /settlement/guide-fees/confirm(确认导游费用)** + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `itemIds` | Array of Long | 是 | 待确认 INCLUDED 费用明细 ID 列表(JSON 中每项为字符串),1~200 条 | +| ~~`expectedSourceFingerprint`~~ | - | - | **已删除,禁止再传** | + +**PUT /settlement/photographer-fees、POST /settlement/photographer-fees/confirm**:字段结构同导游两个端点,仅业务对象为摄影费用,删除字段同为 `expectedSourceFingerprint`。 + +**PUT /settlement/step3/vehicles(保存车辆核单草稿)** + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `items` | Array | 是 | 车辆核单全量明细 | +| ~~`version`~~ | - | - | **已删除,禁止再传**(传了会被白名单拒绝,返回 400) | + +**POST /settlement/finalize(完成核单)** + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `remark` | String | 否 | 提交备注 | +| `reimbursementConfirmation` | Object | 条件必填 | 主报账确认凭据;含 `transferDate`(转账日期)、`transferRef`(转账流水号,主报账净额非 0 时必填,trim 后最长 128 字符)、`advanceSettledFlag`(预支是否已处理,必填布尔)、`signedVoucher`(签字凭证,至少 1 个 URL 非空文件:`files[{name,url}]` + `note`) | +| ~~`reimbursementExpectedSourceFingerprint`~~ | - | - | **已删除,禁止再传** | +| ~~`groupExpectedSourceFingerprint`~~ | - | - | **已删除,禁止再传** | + +> 注:finalize 请求体未启用未知字段白名单,误传旧指纹字段会被**静默忽略**(不报 400),但前端仍应停止传参,避免依赖「传了也没事」的行为。 + +## 5. 出参字段 + +成功路径出参结构不变,仅删除指纹/版本号字段: + +| 端点 | 删除的出参字段 | 原作用 | +|---|---|---| +| GET/PUT `/settlement/guide-fees`、POST `/settlement/guide-fees/confirm` 响应 | ~~`sourceFingerprint`~~ | 导游费用来源数据 sha256 指纹 | +| GET/PUT `/settlement/photographer-fees`、POST `/settlement/photographer-fees/confirm` 响应 | ~~`sourceFingerprint`~~ | 摄影费用来源数据指纹 | +| GET/PUT `/settlement/step3/vehicles` 响应 | ~~`version`~~ | 车辆核单草稿版本号 | + +其余出参字段(如导游/摄影的 `category`/`totalAmount`/`cashPaidAmount`/`unconfirmedCount`/`pendingCandidateCount`/`settlementReady`/`blockReasonCode`/`items`/`editable`/`readOnlyReasonCode`,车辆的 `orderId`/`totalAmount`/`allConfirmed`/`settlementReady`/`blockReasonCode`/`items`/`frozen` 等)均无变化。前端不要再读取 `sourceFingerprint` / `version`,读取结果恒为 undefined。 + +## 6. 枚举 / 数据字典 + +本次不涉及枚举或字典的新增、删除、改值、改语义。`serviceType`(FULL_COURSE_GUIDE/LOCAL_GUIDE/COMMENTARY_SERVICE/TEMPORARY_SUPPLEMENT)、`paymentMethod`(COMPANY_PAID/CASH_PAID/SIGNED)、`blockReasonCode` 等既有取值不变。 + +## 7. 错误码 + +| code | 含义 | 本次变化 | +|---:|---|---| +| `584108` | 车辆核单明细已变化,请刷新 | **已删除,不再触发** | +| `584110` | 导游或摄影费用数据已变化,请刷新 | **已删除,不再触发** | +| `584325` | 核单提交指纹缺失(FINALIZE_FINGERPRINT_REQUIRED) | **已删除,不再触发** | +| `584315` | 核单来源数据已变化,请刷新后重新确认 | **仍在用**:车辆保存的来源数据漂移门禁改抛此码(承接原 584108 场景) | +| `584128` | 导游或摄影费用请求字段不合法:{具体原因} | 不变;前端误传已删除字段时由该码拒绝(见 §8.3) | + +> 前端如曾对 `584108`/`584110`/`584325` 写过特判(专属提示/自动刷新分支),这些分支不会再命中,应移除;车辆来源漂移场景改判 `584315`。 + +## 8. 示例 + +### 8.1 典型成功(保存导游费用,不再回传指纹) + +```http +PUT /v3/admin/order/2084000000000002978/settlement/guide-fees +Authorization: Bearer +Content-Type: application/json +``` + +```json +{ + "items": [ + { + "id": "9001", + "candidateKey": null, + "staffAssignmentId": "11", + "serviceDate": "2026-08-08", + "name": "导游甲", + "serviceType": "FULL_COURSE_GUIDE", + "paymentMethod": "COMPANY_PAID", + "amount": "500.00", + "remark": null, + "voucherUrls": [], + "sourceResolution": null, + "candidateStatus": "COMPLETE" + } + ], + "excludedCandidateKeys": [] +} +``` + +```json +{ + "code": 200, + "message": "success", + "data": { + "category": "GUIDE", + "totalAmount": "500.00", + "cashPaidAmount": "0.00", + "unconfirmedCount": 1, + "pendingCandidateCount": 0, + "settlementReady": false, + "blockReasonCode": "UNCONFIRMED_ITEMS", + "items": [], + "editable": true, + "readOnlyReasonCode": null + }, + "success": true +} +``` + +> 注意:响应中已无 `sourceFingerprint` 字段。 + +### 8.2 边界(完成核单,不再传双指纹) + +```http +POST /v3/admin/order/2084000000000002978/settlement/finalize +Authorization: Bearer +Content-Type: application/json +``` + +```json +{ + "remark": "核单完成", + "reimbursementConfirmation": { + "transferDate": "2026-08-08", + "transferRef": "TX20260808001", + "advanceSettledFlag": true, + "signedVoucher": { + "files": [{"name": "voucher.jpg", "url": "https://oss.example.com/voucher/1.jpg"}], + "note": null + } + } +} +``` + +```json +{"code": 200, "message": "success", "data": {"submitted": true}, "success": true} +``` + +### 8.3 业务失败(旧前端仍传已删除字段,被白名单拒绝) + +场景 A:保存导游费用仍传 `expectedSourceFingerprint`。 + +```http +PUT /v3/admin/order/2084000000000002978/settlement/guide-fees +Authorization: Bearer +Content-Type: application/json +``` + +```json +{ + "expectedSourceFingerprint": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "items": [] +} +``` + +```json +{"code": 584128, "message": "导游或摄影费用请求字段不合法:导游费用请求不支持字段: expectedSourceFingerprint", "data": null, "success": false} +``` + +场景 B:保存车辆核单草稿仍传 `version`。 + +```http +PUT /v3/admin/order/2084000000000002978/settlement/step3/vehicles +Authorization: Bearer +Content-Type: application/json +``` + +```json +{ + "version": 3, + "items": [] +} +``` + +```json +{"code": 400, "message": "请求数据格式错误:车辆核单请求不支持字段: version", "data": null, "success": false} +``` + +## 9. 业务边界 + +- ✅ 保存/确认/提交不再要求前端先 GET 取指纹,可直接操作;单人负责场景下后写覆盖先写,与既有使用方式一致。 +- ✅ 车辆保存仍保留「来源数据漂移」业务门禁:草稿加载后若派单/用车来源数据已变化,保存时返回 `584315`,提示刷新后重新确认——这不是乐观锁,是业务一致性校验。 +- ❌ 导游/摄影 4 个端点与车辆保存端点对请求体做字段白名单校验,**任何未知字段都会被拒**(含本次删除的指纹/版本号字段),不要把查询响应整个 echo 回请求体。 +- ❌ 已终态(冻结)的核单数据仍不可编辑,该约束与本次变更无关,保持不变。 + +## 10. 修改前后对比 + +### 10.1 字段级对比 + +| 端点 | 字段 | 原来 | 现在 | +|---|---|---|---| +| PUT guide-fees / photographer-fees | `expectedSourceFingerprint` | 入参,回传 GET 拿到的指纹 | **已删除** | +| POST guide-fees/confirm、photographer-fees/confirm | `expectedSourceFingerprint` | 入参 | **已删除** | +| GET/PUT guide-fees、photographer-fees 响应 | `sourceFingerprint` | 出参,64 位十六进制 | **已删除** | +| PUT step3/vehicles | `version` | 入参,草稿版本号 | **已删除** | +| GET/PUT step3/vehicles 响应 | `version` | 出参,整数版本号 | **已删除** | +| POST finalize | `reimbursementExpectedSourceFingerprint`、`groupExpectedSourceFingerprint` | 入参,双指纹 | **已删除** | + +### 10.2 行为级对比 + +| 场景 | 原来 | 现在 | +|---|---|---| +| 保存导游/摄影费用 | 必须先 GET 取 `sourceFingerprint` 回传,指纹不匹配返回 584110 | 直接保存,无指纹校验 | +| 保存车辆核单草稿 | 必须回传 `version`,不匹配返回 584108 | 直接保存;来源数据漂移改返回 584315 | +| 完成核单提交 | 必须传主报账+单团核算双指纹,缺失返回 584325 | 直接提交,无指纹校验 | +| 请求体含已删除字段 | 正常受理 | 导游/摄影返回 584128、车辆返回 400;finalize 静默忽略 | + +## 11. 影响评估 / 回滚 + +### 11.1 影响评估 + +- **是否破坏向后兼容**:**是,硬破坏**。导游/摄影 4 个端点 + 车辆保存端点带请求体字段白名单,旧前端继续传 `expectedSourceFingerprint` / `version` 会被拒绝(584128 / 400),核单保存、确认链路直接不可用。 +- **前端是否必须同步上线**:**必须同批**。前端需先删除上述字段的传参与读取,再与后端同批发布;旧前端 + 新后端 = 核单保存/确认全部报错。 +- **上线顺序边界**:前后端同批发布;若必须分先后,**先上前端(停传字段),再上后端**。 +- **前端特判清理**:移除对 `584108`/`584110`/`584325` 的特判;车辆来源漂移提示改挂 `584315`。 + +### 11.2 回滚方案 + +- 后端回滚即恢复原指纹契约;但已改造的新前端(不传指纹)在旧后端上会触发指纹校验失败——**回滚必须前后端同批回滚**。 +- 数据侧无迁移:指纹/版本号不持久化在业务表,回滚无数据修复成本。 + +## 12. 注意事项 + +- 本次只下线核单域内上述 6 个端点的指纹机制;应收总览/逐条优惠确认的指纹错误码 `584300`/`584302`(OVERVIEW_FINGERPRINT_EXPIRED / DISCOUNT_FINGERPRINT_EXPIRED)**不在本次范围,仍在用**,相关确认接口的指纹传参保持不变。 +- 保存类接口幂等语义不变:按订单维度覆盖式全量保存,重复提交相同载荷结果一致。 +- 排查用户报错时,`584128` / 400 的 message 已含具体不支持的字段名,可直接据此定位前端是否还在传旧字段。 +- 小程序端(/v3/mp/*)不涉及本次变更,无需任何改动。 + +## 13. 关联 / 联系人 + +### 13.1 链接 + +- **Issue**: [#5704](https://git.1814.love:8443/wx/HL/issues/5704) +- **代码 PR**: [#5709](https://git.1814.love:8443/wx/HL/pulls/5709) +- **文档 PR**: [#5711](https://git.1814.love:8443/wx/HL/pulls/5711) + +### 13.2 联系人 + +- **后端负责人**: @yaosutu (yst)