changelog: #8225 订单调整统一提交远程调用移出锁与事务,新增失败码 587043
changelog-filename-gate / validate (push) Failing after 2s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
这个提交包含在:
jw
2026-09-23 16:12:15 +08:00
共同撰写人 Claude Opus 5.5
父节点 40165d4e47
当前提交 226deac4a2
@@ -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<AdjustmentSubmitRespVO>`
| 字段 | 类型 | 说明 |
|------|------|------|
| 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