文件
hl-api-changelog/changelogs-v2/2026-10/02_8665_配房删改并发冲突返808932-修改接口-管理后台.md
T
API Changelog Bot和Claude Opus 5.5 9fe70242a6
changelog-filename-gate / validate (push) Failing after 2s
changelog(#8665): 配房删除/修改/单日确认并发冲突新增 808932
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 09:42:38 +08:00

18 KiB
原始文件 Blame 文件历史

schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
schema ticket title consumer author change_type backend_status gateway_status frontend_status frontend_owner frontend_ref target_release verified_at status_note updated_at base
hl-changelog/v2 8665 配房删改与单日确认并发收口:三个写口新增 808932 并发冲突码 admin wx(GIT) 修改接口 deployed not_required not_required 2026-10-02 dev-v3

配房删改与单日确认并发收口:新增 808932 并发状态码

存放目录: 二期 → changelogs-v2/2026-10/

服务: hl-order-service-v3 Issue: #8665 日期: 2026-10-02 影响范围: 管理后台房务配房操作:删除、修改、单日确认三个写口,并发交错时新增返回错误码 808932


⚠️ 关键变化

  • 新增错误码 808932(房务状态已被并发修改,请刷新后重试):删除配房、修改配房、单日确认三个写口在并发交错时返回,不再产生孤儿应付台账行。
  • 请求/响应结构无变化:三个接口的方法、路径、入参字段均未变,仅错误码表新增一条。
  • 808932 与其他业务错误码走同一套契约:HTTP 200 + code + message,按 code 区分即可,无需为该码新增专门的前端处理分支;hl-ui v2.1 现有的通用业务错误拦截器(src/utils/request.js 的业务错误分支 + errorBus.js)会把后端返回的 message 原样呈现,不要求前端硬编码该文案。

一、背景

配房行的删除与修改两个写口(DELETE /assignments/{id} 与 PUT /assignments/{id})原无互斥锁,与单日确认(confirmDayPersist)并发交错时,可能产生「配房行已软删,但应付台账仍留下可付款行」的孤儿记录。工单 #8665 为三个写口补充行级锁定读与版本控制,在并发修改被检测时返回 808932,整体回滚包括该行的任何台账产出。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 删除配房 DELETE /v3/admin/order/assignments/{id} 修改 新增错误码 808932(并发冲突)
2 修改配房 PUT /v3/admin/order/assignments/{id} 修改 新增错误码 808932(并发冲突)
3 单日确认 POST /v3/admin/order/hotel-requirements/{requirementId}/assignments/days/{dayNumber}/confirm 修改 新增错误码 808932(并发冲突)

三、接口详情

本节示例 JSON 的字段名、类型、错误码与文案逐一取自源码;ID、日期等取值为说明用的构造值。所有接口统一返回 Result 信封(code / message / data / traceId / success),示例省略 traceId;业务失败与入参校验失败均为 HTTP 200,靠 code 区分。

1. 删除配房 DELETE /v3/admin/order/assignments/{id}

VO: 无请求体 → Void

使用场景

房务管理员在管理后台删除已配置的某条配房行,通常在需要重新调整房型或取消某晚房务时使用。

入参字段表

字段 位置 类型 必填 约束 说明
id Path Long ✅ - 配房行 ID

出参字段表

字段 类型 说明
(无数据体) - 删除成功时 Result.data 为 null

请求示例

DELETE /v3/admin/order/assignments/2105709698592440321

响应示例

{
  "code": 200,
  "message": "成功",
  "data": null,
  "success": true
}

空数据 / 降级响应

  • 若配房行不存在(已被删除或 ID 无效):返回业务错误码 808120「配房不存在」。

错误响应

{
  "code": 808932,
  "message": "房务状态已被并发修改,请刷新后重试",
  "data": null,
  "success": false
}

业务边界

  • 触发 808932 的场景:该配房行在读取后被并发确认(单日确认的保留行流程)或被并发修改(改配房),版本对不上或已被软删,带版本谓词的软删(softDeleteWithVersion)影响 0 行。
  • 建议的前端处理:收到 808932 时提示用户刷新该订单的配房列表后重试;无需为该码单独编码重试逻辑,交由通用错误提示呈现即可。
  • 串行无 808932:若删除在单日确认完全提交之后才发起,行已稳定,返回 200;若确认在删除完全提交之后才查询候选,会命中更早的 808118(该天无可确认的询房中候选),不会到达锁定复读分支。

2. 修改配房 PUT /v3/admin/order/assignments/{id}

VO: AssignmentUpdateReqVO → Void

使用场景

房务管理员修改已配置配房行的协议价、结算价、支付方式、备注、早餐、酒店主数据快照同步开关,不支持修改酒店、房型、间数(需要换酒店/换房型走 §2.3b 调整配房端点,或删除后用 §2.2 重新创建)。EXCEPTION 异常态下的配房仍允许走本接口改价,用于异常桶的人工处置(如与酒店协商退款后的补偿调整)。

入参字段表

字段 位置 类型 必填 约束 说明
id Path Long ✅ - 配房行 ID
protoPrice Body BigDecimal ❌ ≥0.00 协议价
settlementPrice Body BigDecimal ❌ ≥0.00 结算价
settleType Body String ❌ 正则 cash|sign|company 结算方式
syncProtocolPrice Body Boolean ❌ - 是否同步协议价到酒店主数据快照
syncSettlementPrice Body Boolean ❌ - 是否同步结算价到酒店主数据快照
syncSettleType Body Boolean ❌ - 是否同步结算方式到酒店主数据快照
remark Body String ❌ 无长度校验注解 备注
syncHotelSnapshot Body Boolean ❌ - 是否整体同步酒店主数据快照
breakfast Body String ❌ 正则(HouseBreakfast.VALUE_REGEX,即 INCLUDED/EXCLUDED/PENDING) 早餐

出参字段表

字段 类型 说明
(无数据体) - 修改成功时 Result.data 为 null

请求示例

{
  "settlementPrice": "320.50",
  "remark": "与酒店协商调整"
}

响应示例

{
  "code": 200,
  "message": "成功",
  "data": null,
  "success": true
}

空数据 / 降级响应

  • 若配房行不存在:返回 808120「配房不存在」。

错误响应

{
  "code": 808932,
  "message": "房务状态已被并发修改,请刷新后重试",
  "data": null,
  "success": false
}

业务边界

  • 触发 808932 的场景:该配房行在读取后被并发删除或被并发修改(版本漂移),带 @Version 校验的 updateById 影响 0 行。
  • EXCEPTION 态改价:本接口不经过「需求级写锁 + EXCEPTION 写闸」(assertWritableForAssignment)那条校验链,因此配房所属需求处于 EXCEPTION(异常态)时仍可调用本接口改价,用于异常桶的人工处置;并发删除/修改仍会返 808932。
  • 建议的前端处理:收到 808932 时提示用户刷新该配房行详情后重试,无需额外编码特殊逻辑。

3. 单日确认 POST /v3/admin/order/hotel-requirements/{requirementId}/assignments/days/{dayNumber}/confirm

VO: AssignmentDayConfirmReqVO → Void

使用场景

房务确认某个住宿需求的某一晚配房方案,触发应付台账推送和库存更新。

入参字段表

字段 位置 类型 必填 约束 说明
requirementId Path Long ✅ - 住宿需求 ID
dayNumber Path Integer ✅ 超出该需求行程晚数上界返 808102,无下界校验注解 第几晚(从 1 开始计数)
keepAssignmentIds Body List ❌ 非空时每个 id 须属于该晚询房中候选集,否则返 808123 保留的配房行 ID 清单(待翻 CONFIRMED);整个请求体、本字段均可省略,或传空数组——两者语义相同,均表示保留该晚全部询房中候选(全部翻 CONFIRMED),不传则不会软删任何候选行;显式传非空列表时,列表外的该晚候选行才会被软删

出参字段表

字段 类型 说明
(无数据体) - 确认成功时 Result.data 为 null

请求示例

{
  "keepAssignmentIds": [2105709698592440321]
}

响应示例

{
  "code": 200,
  "message": "成功",
  "data": null,
  "success": true
}

空数据 / 降级响应

  • 若该晚无询房中的候选(已被删除或已确认):返回 808118「该天无可确认的询房中候选, 请先配房再确认」。
  • 若该需求所属订单已取消或处于异常处置中(house_status=EXCEPTION):返回 808119「订单已取消或异常处置中, 该需求不可新增或确认配房, 请在异常桶处理」。
  • 若 keepAssignmentIds 非空且其中存在不属于该晚询房中候选的 id:返回 808123「保留的配房行不属于该天询房中候选(可能配房已更新), 请刷新后重试」。

错误响应

{
  "code": 808932,
  "message": "房务状态已被并发修改,请刷新后重试",
  "data": null,
  "success": false
}

业务边界

  • 触发 808932 的场景:确认流程中保留行(keepAssignmentIds 对应的候选行)在候选列表读取之后被并发删除或修改,锁定复读时发现该行已不存在、已非询房中状态、版本不一致,或翻 CONFIRMED 的 updateById 影响 0 行。
  • 整体回滚:落选候选先软删、保留候选再逐行锁定复读,任一保留行复读发现不一致(抛 808932)时整个事务回滚——包括同一事务内已执行的落选行软删——该确认涉及的应付台账均不推送,确保终态一致,不留半截状态。
  • 建议的前端处理:收到 808932 时提示用户刷新该晚配房列表后重试,无需额外编码特殊逻辑。
  • 串行与并发的区别:若删除完全提交在确认发起之前,确认查询该晚候选时已无询房中的行,会命中更早的 808118(候选预检),不会到达锁定复读分支(808932 仅针对确认读取候选之后、锁定复读之前这段并发窗口);两条路径的共同效果一致:均不推送应付台账。

四、契约约束与正确调用方式

  • 三个接口的请求方式、路径、参数均无变化,仅错误码表补充。
  • 808932 与其他业务错误码(如 808118、808119、808120、808123)一样走 HTTP 200 + 业务码的行为,前端按 code 区分即可;hl-ui v2.1 的通用业务错误拦截器会将后端 message 原样呈现,不要求为 808932 单独编码处理分支。
  • 单日确认(接口 3)请求体可以整体省略:省略或 keepAssignmentIds 传空数组语义相同,均表示保留该晚全部询房中候选;若需要落选部分候选行,必须显式传入要保留的 id 列表。

五、数据库行为

  • 无表结构变更、无 Flyway 迁移(本次合并仅涉及 Service/测试代码与 API 文档,对比 #8665 合并提交的文件清单确认无 db/migration 变更)。
  • 配房表 house_hotel_assignment 的 version 列与实体上的 @Version 注解系已有能力(早于本次改动),本次复用它为删除、修改、单日确认三个写口补齐「锁定读(SELECT...FOR UPDATE)+ 带版本更新/软删」的组合:先用锁定读拿到最新行与其 version,再执行带版本谓词的写操作(updateById 走 MyBatis-Plus 全局注册的乐观锁拦截器自动拼 version 条件;软删复用既有的 softDeleteWithVersion(id, version)),任一写操作影响 0 行即判定为并发冲突并抛 808932,整体事务回滚。

六、边界行为

  • 离线与网络异常:808932 不涉及网络/超时,纯业务并发码,离线时前端仍会收到错误响应。
  • 灰度与开关:本次改动无灰度开关,所有测试环境已包含该并发收口逻辑。
  • 下游链路:应付台账推送、库存扣减、其他异步链路仅当确认成功(code=200)才执行,808932 回滚后不产生任何账务记录。

六.5 枚举

不涉及新增枚举值;配房状态(INQUIRING/CONFIRMED/EXCEPTION 等)无变化。


六.6、修改前后对比

并发行为对比

项 改前 改后
删除配房并发于确认 可能双 200,留下孤儿应付台账行 后提交方返 808932,回滚,无台账产出
修改配房并发于确认 可能按过期快照推台账 后提交方返 808932,回滚,无台账产出
单日确认保留行被删 静默跳过已删行,继续推台账 锁定复读检测到行已删,返 808932,回滚,无台账产出
返回的错误码 无 808932 新增 808932(仅在并发窗口内返回)

六.7、影响评估

  • 是否破坏向后兼容:否。新增错误码不影响其他业务流程,正常序列的删除、修改、确认仍返回 200。
  • 前端是否必须同步上线:否。hl-ui v2.1 现有的通用业务错误拦截器会把后端返回的 message 原样呈现,808932 走的是这条通用路径,不需要新增前端代码。
  • 前端 workaround 清理点:无。本次改动前 808932 这个码不存在,故前端不会有针对它的既有 workaround 需要清理。

影响范围说明:

  • 前端 hl-ui:请求/响应字段无改动;808932 走通用业务错误展示路径,后端返回的 message 即为用户看到的提示文案,无需前端硬编码该文案。
  • 管理后台网关:路由无变更,仅错误响应码增加,不影响路由规则和鉴权。
  • 其他服务:本次改动是 order-v3 内部的行级锁定读 + 乐观锁版本校验,不新增分布式锁、不改 Feign 契约;finance、resource 等消费方零感知。
  • 数据一致性:通过锁定读 + 版本控制,堵住了孤儿应付台账的产生根源,后续应付台账取消、对账等链路无需额外补丁。

七、不影响范围

  • EXCEPTION 异常态下调用修改配房(接口 2)改价仍被允许——该接口本就不经过 assertWritableForAssignment 这条 EXCEPTION 写闸——未受本次新增版本控制的影响;本次改动只新增 808932 这一个并发冲突码,不改变任何既有的状态校验逻辑。
  • 其他写口(如转房 changeId、取消级联删除等)未涉及本次改动,仍按既有逻辑。
  • 只读接口(查询配房列表、查询单日候选等)逻辑无变化。

八、测试环境已验证

部署状态:

  • hl-order-service-v3 @cd82a19ab(origin/dev-v3 的祖先,#8665 的合并提交 f8379ddfc3 已包含)
  • hl-gateway @71def6dc5(网关落后 dev-v3 156 个提交,本次无路由变化,不影响)
  • 部署时刻:2026-10-01 22:50:33

测试链路 A:确认第 1 晚 → 删除该行

  • 操作序列:POST /confirm 返回 200 → DELETE /assignments/{id} 返回 200
  • 终态:配房行 deleted_at 非空;应付台账该行已软删(无活跃 NORMAL 记录);日历库存回复
  • 结论:PASS(两步均 200,无 808932)

测试链路 B:删除该行 → 确认第 2 晚

  • 操作序列:DELETE /assignments/{id} 返回 200 → POST .../days/2/confirm 返回 808118(严格串行,无并发窗口)
  • 终态:应付台账对该行零产出(fin_payable_line WHERE source_ref_id=<该行id> 为 0 行)
  • 结论:PASS——判据是「第二步不再为该行产生台账行」,不要求第二步返回 200;完全串行操作下,confirmDay 在进入锁定复读之前先按该晚候选集过滤,发现该晚询房中候选集已为空,命中更早的 808118(该天无可确认的询房中候选),而不是 #8665 新增的 808932(锁定复读检查需要先有候选才会走到)。

并发窗口说明:

  • 808932 是为「删除/修改读取之后、带版本的写操作之前」这段时间窗口的并发交错设计的,靠锁定读 + 乐观锁版本校验堵住。
  • 完全串行操作(一方提交完成后另一方才查询)会先命中 808118(无候选)或 808120(配房不存在),不会到达锁定复读检查点;这两条路径的共同效果都是零台账产出。
  • 测试环境部署状态:hl-order-service-v3 对 origin/dev-v3 零落后,#8665 的合并提交已在部署字节内,以上两条测试链路均为该部署字节下的实测结果。

十、相关文档

  • API 规范:docs/order-v3/api/API-SPEC-HOUSE-V1.1.html(v1.1.20,2026-10-01)
    • §2.2b 单日确认 / §2.3 修改配房 / §2.4 删除配房错误码表新增 808932
    • §11.9 内部接口 + 跨服务(808900-808999)全表补 808932「房务状态已被并发修改,请刷新后重试」
  • 工单正文:Gitea #8665(设计、验收、口径定案)
  • 单测覆盖:
    • HouseAssignmentServiceTest#confirmDayPersist_keepUpdateAffectsZeroRows_throws808932AndNoPayablePush:confirmDayPersist 中某 keep 行 updateById 影响 0 行时抛 808932,整体回滚,零推台账,落选不回补库存
    • HouseAssignmentServiceTest#delete_softDeleteAffectsZeroRows_throws808932NoPayableNoRestore:delete() 在 softDeleteWithVersion 返 0 时抛 808932,不碰台账、不回补库存
    • HouseAssignmentServiceTest#updateTx_updateAffectsZeroRows_throws808932NoPayableRewrite:updateTx() 乐观锁写库影响 0 行时抛 808932,不判台账、不作废不重推
    • HouseAssignmentDeleteConfirmDayOrderingIT:固定时序交错 IT,覆盖「确认快照之后删配房提交→确认返 808932」与「删配房持行锁期间确认进入事务→确认被挡住随后 808932」两条交错,均断言无孤儿台账

关联 / 联系人

联系人

  • 后端负责人: @wx