From 226deac4a2487e1680d0ee278b90c4b2bb930e52 Mon Sep 17 00:00:00 2001 From: jw Date: Wed, 23 Sep 2026 16:10:30 +0800 Subject: [PATCH] =?UTF-8?q?changelog:=20#8225=20=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=BB=9F=E4=B8=80=E6=8F=90=E4=BA=A4=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E8=B0=83=E7=94=A8=E7=A7=BB=E5=87=BA=E9=94=81=E4=B8=8E?= =?UTF-8?q?=E4=BA=8B=E5=8A=A1=EF=BC=8C=E6=96=B0=E5=A2=9E=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E7=A0=81=20587043?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) --- ...出锁与事务并新增并发改动码587043-修改接口-管理后台.md | 239 ++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 changelogs-v2/2026-09/23_8225_订单调整提交远程调用移出锁与事务并新增并发改动码587043-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/23_8225_订单调整提交远程调用移出锁与事务并新增并发改动码587043-修改接口-管理后台.md b/changelogs-v2/2026-09/23_8225_订单调整提交远程调用移出锁与事务并新增并发改动码587043-修改接口-管理后台.md new file mode 100644 index 00000000..c01c9f9a --- /dev/null +++ b/changelogs-v2/2026-09/23_8225_订单调整提交远程调用移出锁与事务并新增并发改动码587043-修改接口-管理后台.md @@ -0,0 +1,239 @@ +--- +schema: "hl-changelog/v2" +ticket: "8225" +title: "订单调整统一提交:远程调用移出锁与事务,新增失败码 587043「提交期间订单已被其他操作修改,请刷新后重试」" +consumer: "admin" +author: "jw(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "not_required" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "PR #8255 已合并 dev-v3(c2b261cd2),2026-09-23 15:40 滚动部署 TEST 双实例;其后另一会话部署的 f6d21648b 包含本单。真实网关实测:非法座位数提交返回 582024 且零写入,日志确认拒绝发生在拿锁之前;改出行人姓名正向往返 200 并落调整记录,已改回原值;无 token / 伪造 token 均 401。入参、出参结构不变,唯一的契约变化是新增失败码 587043;前端按通用失败提示展示后端 message 即可,无需改动。" +updated_at: "2026-09-23" +base: "dev-v3" +--- + +# 订单调整: 统一提交的远程调用移出锁与事务 + 新增失败码 587043 + +> **存放目录**: 二期(v3) → `changelogs-v2/2026-09/` +> +> **服务**: hl-order-service-v3 +> **PR**: #8255 +> **Issue**: #8225 +> **日期**: 2026-09-23 +> **影响范围**: 管理后台订单详情「调整订单」弹窗的统一提交 + +--- + +## ⚠️ 关键变化(非必须,本版与上版行为不同 / 纠错 / 撤销时必写) + +- 本次变化:统一提交接口多了一个失败码 `587043「提交期间订单已被其他操作修改,请刷新后重试」`。 +- 前端以前以为的:同一订单两次调整并发提交时,后提交的那次会排队等前一次完成,然后照常成功。 +- 实际新行为:算价、协议价、车型校验这些远程调用现在在**拿锁之前**完成。如果在这段时间里另一笔调整改了本单的出发日、人数或产品,本次提交会被拒绝并返回 587043,整笔不写库;用户刷新后重新提交即可。请求和响应的结构都没有变。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 调整订单统一提交 | POST | `/v3/admin/order/:id/adjustment/submit` | 新增失败码 | 新增 587043;入参、出参不变 | + +--- + +## 三、接口详情 + +### 1. 调整订单统一提交 `POST /v3/admin/order/:id/adjustment/submit` + +**VO**: `AdjustmentSubmitReqVO → AdjustmentSubmitRespVO` + +#### 使用场景 + +管理后台订单详情页「调整订单」弹窗,前端在内存里收集出行人、改期、行程、用房需求、行程用车、接送机用车等改动后一次性提交。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| Authorization | Header | String | ✅ | - | 管理端登录令牌 | +| id | Path | Long | ✅ | 订单须存在(581007) | 订单 ID | +| updates | Body | Object | ✅ | 至少一个子领域有实际改动(587033) | 各子领域修改内容,未改的子领域传 null | +| updates.people | Body | Object | ❌ | - | 订单级紧急联系人 + 出行人增删改 | +| updates.travelers | Body | Object | ❌ | - | 出行人 add / update / remove | +| updates.schedule | Body | Object | ❌ | departDate 为 `yyyy-MM-dd` | 改期 | +| updates.itinerary | Body | Object | ❌ | - | 行程天与节点的完整新版本 | +| updates.hotelRequirement | Body | Object | ❌ | - | 用房需求完整新版本 | +| updates.vehicleRequirement | Body | Object | ❌ | fleet 车型须为车型大类、座位数须在车型库选项内 | 行程用车需求完整新版本 | +| updates.transferRequirement | Body | Object | ❌ | 同上 | 接送机用车需求完整新版本 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| success | Boolean | 提交成功恒为 true | + +#### 请求示例 + +```json +{ + "updates": { + "travelers": { + "update": [{ "id": "2098374040010772481", "name": "杨知川" }] + } + } +} +``` + +#### 响应示例 + +```json +{ "code": 200, "message": "成功", "data": { "success": true }, "traceId": null, "success": true } +``` + +#### 空数据 / 降级响应 + +写接口没有空数据场景。所有提交都没有实际改动时返回 587033,零写入: + +```json +{ "code": 587033, "message": "未检测到有效变更,无需提交", "data": null, "traceId": null, "success": false } +``` + +#### 错误响应 + +本次新增: + +```json +{ "code": 587043, "message": "提交期间订单已被其他操作修改,请刷新后重试", "data": null, "traceId": null, "success": false } +``` + +既有错误码照旧,例如车型座位数不合法: + +```json +{ "code": 582024, "message": "座位数不在该车型大类可选座位数中,请检查车型库", "data": null, "traceId": null, "success": false } +``` + +#### 业务边界 + +- 鉴权:未登录 → 业务码 `401`(「缺少有效的 Authorization 头」),token 签名不对 → `401`(「Token 无效」)。 +- 587043 只在「本次提交读到的订单」与「拿锁后的订单」在出发日、人数、产品上不一致时出现,也就是有另一笔调整恰好在同一时刻提交;单人操作不会遇到。 +- 587043 与其它失败码一样整笔零写入,重新提交会按最新的订单状态重新计算差价。 + +--- + +## 四、契约约束与正确调用方式(接口类必写) + +> 本节只写**后端接受/拒绝 payload 的规则**,不写 UI 渲染建议。 + +### ✅ 正确 / ❌ 错误 payload 对照 + +| 场景 | payload | 结果 | +|------|---------|------| +| ✅ 只改出行人姓名 | `updates.travelers.update=[{id,name}]` | 200 | +| ❌ 没有任何实际改动 | `updates: {}` | 587033 | +| ❌ 行程用车座位数不在车型库选项内 | `fleet=[{vehicleType:"大巴", seats:999, count:1}]` | 582024,零写入 | +| ❌ 提交期间另一笔调整改了出发日 / 人数 / 产品 | 任意 | 587043,零写入(**本次新增**) | + +- 请求与响应结构没有任何变化;587043 的 `message` 已写明处理方式,前端按通用失败提示展示后端文案即可。 + +--- + +## 五、数据库行为(涉及写操作时必写) + +- 无 Flyway migration、无 DDL、无表结构变更。 +- 写库集合与改前一致(出行人、行程、用房/用车需求版本、优惠加费、调整记录、状态日志),仍在同一个事务内原子完成。 +- 变化在事务边界:算价、协议价、车型校验等远程调用改为在拿锁、开事务之前完成,订单行与用车确认 fence 行的锁持有时间不再包含这些远程往返。 +- 任何失败(含新增的 587043)都在写库之前抛出,整笔零写入。TEST 实测:非法座位数提交后,订单主单、fence 行、用车需求、调整记录前后逐字段一致。 + +--- + +## 六、边界行为 + +- 未登录 → 业务码 `401`。 +- 越权规则不变:非管理员只能调整自己负责的订单。 +- 拒绝原因的先后顺序不变:终态、已过天锁定、出行人/改期窗口、改期日期格式等守卫,仍然先于算价与车型校验。 +- 团期子订单改期仍只能改回所属团期出发日(587039),不走个人价格日历报价。 + +--- + +## 六.6、修改前后对比(修改/删除类接口必写,新增跳过) + +### 字段级对比 + +| 字段 | 改前 | 改后 | +|------|------|------| +| 请求体 | `AdjustmentSubmitReqVO` | 不变 | +| 响应体 | `{ success }` | 不变 | +| 失败码 | 无 587043 | 新增 587043 | + +### 行为级对比 + +| 行为 | 改前 | 改后 | +|------|------|------| +| 远程调用(算价、协议价、车型字典/座位、字典标签)发生时机 | 拿锁、开事务之后 | 拿锁、开事务之前 | +| 两笔调整并发提交,前一笔改了出发日/人数 | 后一笔等锁后按新状态照常执行 | 后一笔返回 587043,刷新重提 | +| 车队 / 产品 / 资源服务响应慢 | 订单行被锁住直到远程返回 | 不影响锁持有时间 | + +--- + +## 六.7、影响评估(修改/删除类必写) + +- **是否破坏向后兼容**:请求、响应结构不变;只新增一个失败码,只在并发调整同一订单时出现。 +- **前端是否必须同步上线**:否。前端的通用失败提示会展示后端 `message`(「提交期间订单已被其他操作修改,请刷新后重试」),无需改动。 +- **前端 workaround 清理点**:无。 + +--- + +## 七、不影响范围(显式声明, 帮前端/QA 缩小排查面) + +- **仅影响**:`POST /v3/admin/order/:id/adjustment/submit` 的内部执行顺序,以及新增失败码 587043。 +- **零影响**: + - `GET /v3/admin/order/:id/adjustment/snapshot`(调整弹窗初始化数据)与 `GET /v3/admin/order/:id/adjustment-record`(调整记录)。 + - 行程查询 `getItinerary` 返回的服务标准字段、大交通批次列表的旅客类型中文名(管理端读接口行为不变)。 + - 既有失败码与文案。 + +--- + +## 八、测试环境已验证 + +部署:PR #8255 合并 `dev-v3`(`c2b261cd2`),2026-09-23 15:40 滚动部署 TEST 双实例;其后另一会话部署的 `f6d21648b` 以本单为祖先,且其间没有提交改动本单涉及的文件。 + +真实网关(`https://api.test.1814.love`,管理端 token,订单 `2098372757820428289`): + +``` +POST /v3/admin/order/:id/adjustment/submit 无 token → 401 缺少有效的 Authorization 头 ✓ +POST /v3/admin/order/:id/adjustment/submit 伪造 token → 401 Token 无效 ✓ +POST ... updates={} → 587033 未检测到有效变更 ✓ +POST ... vehicleRequirement.fleet=[大巴 × 999 座] → 582024,订单/fence/用车需求/调整记录前后一致 ✓ + 同时刻 primary 实例日志:先是不加锁的 order_main 查询,全程 0 条 fence SQL、0 条 FOR UPDATE ✓ +POST ... travelers.update 改姓名 → 200,姓名已改、调整记录 0→1 ✓ +POST ... travelers.update 改回原名 → 200,姓名还原 ✓ +``` + +587043 需要两笔调整真实竞态才能触发,TEST 上未造出;由单元测试覆盖(两段之间出发日变化 → 587043 且零写入;出发日未变 → 放行的对照)。 + +单元测试:rebase 后定向 239 类 / 3290 例 0 失败,含 `AdjustmentServiceSubmitTest` 86、`TransactionalRemoteCallArchTest` 2、`SettlementLockContractTest` 5。 + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#8225](https://git.1814.love/wx/HL/issues/8225) +- 关联 PR: [wx/HL#8255](https://git.1814.love/wx/HL/pulls/8255) +- 上游来源:#8202(CR 带出本单) + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#8225](https://git.1814.love/wx/HL/issues/8225) +- **PR**: [#8255](https://git.1814.love/wx/HL/pulls/8255) +- **Merge commit**: [c2b261cd2](https://git.1814.love/wx/HL/commit/c2b261cd2) + +### 联系人 + +- **后端负责人**: @jw +- **前端负责人**: @mmg