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 |
请求示例
响应示例(占锁,抢锁失败)
空数据 / 降级响应
抢锁失败不是降级:请求没有进入业务逻辑(不写库、不扣库存)。释放锁后重试即可正常进入业务处理。
错误响应
| 场景 |
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 后调用):
- 跨实例:4 次占锁调用分别落在 order-v3
:8086(2 次)与 :8186(2 次),两实例结论一致;Unexpected error 计数均为 0。
- 兼容性结论:抢锁失败分支已稳定返回 100503,HTTP 200 与响应结构保持不变。
十、相关文档
关联 / 联系人
链接
联系人