文件
hl-api-changelog/changelogs-v2/2026-09/17_7491_Lock4j抢锁失败统一返100503-修改接口-管理后台.md
T

11 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 7491 所有 @Lock4j 抢锁失败统一返回 100503(不再返回 500) multiple wx(GIT) 修改接口 deployed verified not_required 2026-09-17 抢锁失败分支的 code 由 500 改为 100503,HTTP 仍为 200;前端应把「可重试」提示挂到 100503 上。前端 2026-09-17 复核:grep 实证无 100503/500 抢锁硬编码,业务错误统一由拦截器透 body.message 弹出(request.js:465),新码文案「资源被占用,请稍后重试」原样可达用户,同 #7529 先例不建错误码字典,判 not_required(无业务改动)。 2026-09-17 dev-v3

common:所有 @Lock4j 抢锁失败统一返回 100503

服务: 全部使用 hl-starter-protection 的 @Lock4j 后端服务(order-v3、fleet、mp、product-v2、resource、user、finance 等) PR: #7885(目标 dev-v3) Issue: #7491 日期: 2026-09-17 影响范围: 所有带 @Lock4j 的写接口在被别的请求占锁时的失败响应(code / message / 是否带 traceId)


⚠️ 关键变化

所有带 @Lock4j 的端点在抢锁失败(等满 acquire-timeout 仍未拿到锁)时:

  • code:500(系统繁忙请稍后重试或联系客服) → 100503(资源被占用,请稍后重试)
  • message:系统繁忙请稍后重试或联系客服 → 资源被占用,请稍后重试
  • HTTP 状态:仍为 200(业务失败仍走 HTTP 200 包络,未变为 4xx/5xx)
  • traceId:该分支不再返回 traceId 字段(此前 500 分支会带 traceId)

前端必须知道的一句话:以前抢锁失败看起来像系统故障(500 + traceId),现在是一个明确的业务提示(100503 + 中文文案);请把「稍后重试」类提示挂到 100503 上,不要再按 500 走系统异常兜底。


一、背景

hl-starter-protection 的 LockFailureExceptionHandler(@ExceptionHandler(LockException.class))从来没有被命中过:Spring MVC 先按 advice 顺序挑选 ControllerAdviceBean,再在该 bean 内部匹配异常类型,不会跨 advice 比较哪个异常类型更具体。hl-common-log 的 GlobalExceptionHandler(@ExceptionHandler(Exception.class))注册在前,于是全部 @Lock4j 抢锁失败都以 code=500 的系统异常兜底返回,错误码表里的 100503 从未对外生效(由 #7324 的 AC-59 实测发现并转结本单)。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 团期按日订房(H4,示例端点) POST /v3/admin/house/group-batches/{groupBatchId}/room-plans 修改响应 占锁时由 500 改为 100503
2 其余全部 @Lock4j 端点 任意 各服务 @Lock4j 写口 修改响应 同一共享 starter 行为,抢锁失败一律 100503

三、接口详情

1. 团期按日订房 POST /v3/admin/house/group-batches/{groupBatchId}/room-plans

VO: GroupBatchRoomPlanSaveReqVO → Result<List<GroupBatchRoomPlanRespVO>>

使用场景

房务按日提交订房计划。该端点带团期级 @Lock4j(锁键 lock4j:house:gb:room#{groupBatchId},acquire-timeout 默认 3s)。当同一团期已有请求持锁且超过 3s 未释放时,后到请求返回 100503。

入参

字段 位置 类型 必填 约束 说明
groupBatchId Path Long ✅ 正整数 团期主订单 ID
items Body List ✅ 1-200 行 订房行(stayDate / hotelId / roomTypeId / roomCount)

出参 Result<List<GroupBatchRoomPlanRespVO>>

字段 类型 说明
code Integer 抢锁失败时为 100503
message String 抢锁失败时为 资源被占用,请稍后重试
data null 抢锁失败时为 null
traceId — 抢锁失败分支不再返回该字段
success Boolean false

请求示例

POST /v3/admin/house/group-batches/999999100000021/room-plans
Authorization: Bearer ***
Content-Type: application/json

{ "items": [ { "stayDate": "2027-07-01", "hotelId": 1, "roomTypeId": 1, "roomCount": 1 } ] }

响应示例(占锁,抢锁失败)

{ "code": 100503, "message": "资源被占用,请稍后重试", "data": null, "traceId": null, "success": false }

空数据 / 降级响应

抢锁失败不是降级:请求没有进入业务逻辑(不写库、不扣库存)。释放锁后重试即可正常进入业务处理。

错误响应

{ "code": 100503, "message": "资源被占用,请稍后重试", "data": null, "traceId": null, "success": false }
场景 code message
抢锁失败(本单变化点) 100503 资源被占用,请稍后重试
3 秒内重复提交(幂等拦截,行为不变) 100502 订房计划提交处理中,请勿重复提交
锁已释放后业务校验失败(行为不变) 业务码(如 808691 / 589500 等) 对应业务文案

业务边界

  • 只在「等满 acquire-timeout(默认 3s)仍抢不到锁」时返回 100503;正常拿到锁的请求行为完全不变。
  • 抢锁失败不进入业务逻辑:不写库、不扣库存、不产生状态流转。
  • 幂等窗口(3s)内的重复提交仍返回 100502,与抢锁失败明确区分。
  • 锁释放后重试同一请求可立即进入业务处理(实测返回业务码而非 500/100503)。

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

✅ 正确 / ❌ 错误做法对照

场景 调用方行为
✅ 收到 100503 按「资源被占用」给用户可重试提示;如需重试请间隔后再发,并保证重试请求体/幂等键语义正确
✅ 判断成功 只看 success / code,不要依赖 HTTP 状态码区分业务失败
❌ 把 100503 当系统故障 不要再按 500 走「系统繁忙/联系客服」兜底文案
❌ 依赖 traceId 抢锁失败分支不再返回 traceId;需要排查请让后端按时间窗查日志

前端需要做的动作

  • 在错误码映射中把 100503 映射为「资源被占用,请稍后重试」的可重试提示。
  • 若此前对 500 做了「系统异常」特殊处理,抢锁失败场景将不再走该分支。

五、数据库行为

  • 抢锁失败分支:零数据库写入——请求在锁层即被拒绝,不产生任何行变更、状态流转或日志写入。
  • 正常抢到锁的请求:数据库行为与本次修改前完全一致,无字段、无表结构变化。

六、边界行为

  • 鉴权、路由、请求字段、必填性、响应结构均未变。
  • HTTP 状态仍为 200(业务失败约定不变;鉴权 401/403 与未知系统异常 500 不受影响)。
  • 用户自定义 LockFailureStrategy 时默认策略自动退让,行为由自定义实现决定。
  • 只在「等满 acquire-timeout 仍抢不到锁」时返回 100503;正常拿到锁的请求行为完全不变。

六.6、修改前后对比

字段级对比

字段 改前 改后
code 500 100503
message 系统繁忙请稍后重试或联系客服 资源被占用,请稍后重试
traceId 返回(非空) 该分支不返回
data null null(不变)
HTTP 状态 200 200(不变)

行为级对比

行为 改前 改后
抢锁失败 通用异常兜底,日志 ERROR Unexpected error 业务异常,日志 WARN Business error [common.100503]
是否进入业务逻辑 否 否(不变)
锁释放后重试 正常进入业务 正常进入业务(不变)

六.7、影响评估

  • 是否破坏向后兼容:code/message 属行为纠正,前端若硬编码 500 文案需同步调整为 100503;响应结构与 HTTP 状态未变。
  • 前端是否必须同步上线:建议同步(把可重试提示挂到 100503);未同步时表现为文案不精确,不会导致功能性失败。
  • 后端覆盖范围:hl-starter-protection 是共享 starter,所有依赖它的服务(order-v3、fleet、mp、product-v2、resource、user、finance 等)行为同时变化,需一起滚动部署。

七、不影响范围

  • 仅影响:@Lock4j 抢锁失败分支的 code / message 与该分支的 traceId 字段。
  • 零影响:
    • 幂等拦截(100502)、鉴权(401/403)、参数校验(HTTP 200 + 业务码)等既有分支
    • 正常抢到锁的请求:入参、出参、业务规则、库存扣减行为
    • 数据库结构与存量数据
    • 非 @Lock4j 异常(业务码、未知系统异常 500)

八、测试环境已验证

  • 部署:order-v3 / fleet / mp / product-v2 / resource / user 六个进程滚动到 fix/7491-lock-failure-handler @65715c2f1,deploy-status.sh 逐行 STATE=ok、BEHIND 0/N;合并提交 716cd17656c05850ff5fc476c91e48032c63a187。
  • 网关实测(占锁 60s 后调用):
POST /v3/admin/house/group-batches/999999100000021/room-plans
(锁键 lock4j:house:gb:room#999999100000021 已被占)
→ HTTP 200
→ {"code":100503,"message":"资源被占用,请稍后重试","data":null,"traceId":null,"success":false} ✓

DEL 锁键 → EXISTS 0 → 同一请求再次调用
→ HTTP 200 {"code":589500,"message":"团期不存在"} ✓
(业务码,非 500/100503,证明锁释放后立即穿透业务逻辑)
  • 跨实例:4 次占锁调用分别落在 order-v3 :8086(2 次)与 :8186(2 次),两实例结论一致;Unexpected error 计数均为 0。
  • 兼容性结论:抢锁失败分支已稳定返回 100503,HTTP 200 与响应结构保持不变。

十、相关文档

关联 / 联系人

链接

联系人

  • 后端负责人: @wx