From fee08eba332e9508f7507a46c214ae678d5b0ff6 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Wed, 16 Sep 2026 09:33:06 +0800 Subject: [PATCH] =?UTF-8?q?docs(order-v3):=20#7621=20=E8=BD=AC=E5=8D=95?= =?UTF-8?q?=E7=AB=AF=E7=82=B9=E5=9B=A2=E6=9C=9F=E9=9C=80=E6=B1=82=E4=BD=9C?= =?UTF-8?q?=E7=94=A8=E5=9F=9F=E9=99=90=E5=88=B6=E4=B8=8E=20808001=20?= =?UTF-8?q?=E6=96=87=E6=A1=88=E8=AF=AF=E5=AF=BC=E8=AF=B4=E6=98=8E=EF=BC=88?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E9=9B=B6=E5=8F=98=E5=8C=96=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 三种成因分开写:普通房务对任何团期需求恒 808650;超管对已整团认领的团恒 808650; 超管对尚未整团认领、且从未被单独抢到的团期需求恒 808001,而该文案说的是 「已被其他房务抢到」,与实情不符。历史遗留数据(并团前已被抢到)是唯一能转成功的例外。 正确路径写明整团认领 POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/claim, 并标注该路径参数是团期 ID 而非订单 ID(后端 @ApiParam 的「团期主订单 ID」标签会误导, 以 2026-09-16 测试服实测为准)。 第八节如实写明本单未部署、未做测试服端到端验证,只有仓内单测。 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Mg1eNKoacprUHNvuEKxjqq --- ...求作用域限制与808001误导文案说明-修复-管理后台.md | 289 ++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 changelogs-v2/2026-09/16_7621_转单接口团期需求作用域限制与808001误导文案说明-修复-管理后台.md diff --git a/changelogs-v2/2026-09/16_7621_转单接口团期需求作用域限制与808001误导文案说明-修复-管理后台.md b/changelogs-v2/2026-09/16_7621_转单接口团期需求作用域限制与808001误导文案说明-修复-管理后台.md new file mode 100644 index 00000000..40c8f049 --- /dev/null +++ b/changelogs-v2/2026-09/16_7621_转单接口团期需求作用域限制与808001误导文案说明-修复-管理后台.md @@ -0,0 +1,289 @@ +--- +schema: "hl-changelog/v2" +ticket: "7621" +title: "转单/超管强制指派端点:明确团期需求的作用域限制与 808001 文案在该场景下的误导(接口零变化)" +consumer: "admin" +author: "wx(GIT)" +change_type: "修复" +backend_status: "not_required" +gateway_status: "not_required" +frontend_status: "not_required" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "not_required:本单不产生任何可部署的运行时变更——PR #7623 只补 javadoc/@ApiOperation 说明,PR #7782 只补单测,请求/响应结构、错误码集合、路由均一字未改。核心场景(超管对尚未被整团认领的团期需求调用本接口恒返 808001)依赖的判定逻辑早于本工单已存在,不因本次改动而首次生效,无需部署即对前端成立。本单未在测试服做端到端真实调用复核,第八节如实写明未验证,本 changelog 只同步契约理解,不构成已实测的结论。" +updated_at: "2026-09-16" +base: "dev-v3" +--- + +# 转单端点团期需求作用域限制说明(含 808001 文案在该场景下的误导,接口无变化) + +> **存放目录**: 二期(v3) → `changelogs-v2/2026-09/` +> **服务**: hl-order-service-v3(HOUSE 抢单池 §1.3) +> **PR**: #7623(契约说明,已合 dev-v3)/ #7782(补充测试用例) +> **Issue**: #7621 +> **日期**: 2026-09-16 +> **影响范围**: 仅补充"转单/超管强制指派"端点对团期需求这一类调用对象的作用域说明;请求参数、响应结构、错误码集合、路由均无变化 + +--- + +## ⚠️ 关键变化 + +🔴 这个端点从来就转不了"团期需求",但此前的契约文档没写清楚,而它失败时给出的错误码文案在这一场景下会误导人。 + +- 之前你可能以为:只要一个需求处于"待处理"状态,超管就能用本接口把它强制指派给任意房务,不区分它是不是团期产品下的需求。 +- 实际一直是:本接口只负责转走一个已经被某个房务实际抢到手的需求,不负责建立归属。团期产品下的需求,逐户抢单这条路本身就是被拒的,所以它们结构上到不了"已被抢到"这个状态。 +- 结果: + - 普通房务对任何团期需求调用本接口,恒返 `808650`(文案已明确说明"请到团期抢单池整团认领",不会误导)。 + - 超管对"尚未被整团认领"的团期需求调用本接口,如果这条需求本身也从未被单独抢到过,会恒返 `808001`,但 `808001` 的文案"需求已被其他房务抢到"在这个场景下是不准确的——事实是从来没有人抢到过它,不是被人抢走了。 + - 唯一的例外:如果这条需求是在被并入团期产品之前就已经被某个房务抢到(历史遗留数据),超管调用本接口在满足"该团尚未被整团认领"的前提下真的能转成功(`200`)。这是这类历史脏数据唯一的无损转出口,不是一般规律,别照抄。 +- 正确路径:团期需求的房务归属,一律通过团期抢单池的"整团认领"完成——`POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/claim`(无请求体;路径参数 `groupBatchId` 是**团期 ID**(`group_batch.id`),不是订单 ID——后端 `@ApiParam` 把它标成「团期主订单 ID」,那个标签会误导,以实测为准:`POST /v3/admin/order/grab-pool/group-batches/2099715422872834050/claim` 返回 200,路径里那个值是团期 ID)。 + +--- + +## 一、背景 + +本次不涉及任何代码行为变化,起因是工单 #7621 发现:现有联调/排查资料里没有一处写明"转单接口对团期需求会怎样",导致排查时容易把 `808001` 误判成"并发抢单竞争"去查,而实际根因是这条路径对团期需求本来就走不通。#7623 把这段边界写进了后端 javadoc;#7782 给这两种成因(超管遇到前置条件不满足 / 普通并发场景)各补了显式单测,证明代码本身没有被这次说明"顺手改坏"。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 转单/超管强制指派 | POST | `/v3/admin/order/hotel-requirements/{requirementId}/transfer` | 契约说明补充 | 补充团期需求作用域限制与 808001 文案误导说明,接口本身零变化 | + +--- + +## 三、接口详情 + +### 1. 转单/超管强制指派 `POST /v3/admin/order/hotel-requirements/{requirementId}/transfer` + +**VO**: `HouseTransferReqVO → Result` + +#### 使用场景 + +管理后台房务工作台"我的接单"列表里,房务对自己已抢到的需求点"转单";或超管在全局需求视图/团期需求视图里对某个需求点"强制指派"。后端按调用者角色(普通房务 / 超管)自动分流校验规则,前端始终调同一个端点、同一套请求体。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| requirementId | Path | Long | ✅ | - | 需求 ID | +| toUserId | Body | String(数字,Long 序列化为字符串) | ✅ | 必须是有效的房务人员 ID,且不能等于当前持有人 | 接收人房务 ID | +| reason | Body | String | 否 | ≤200 字;超管调用时 ≥10 字,否则 `808016`;普通房务留空时后端兜底记为"转单" | 转单/指派原因 | +| skipUpperLimit | Body | Boolean | 否,默认 false | 历史字段,转单次数上限已下线,当前不影响转单结果,仅留痕审计 | 跳过单量上限(历史兼容字段) | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | null | 本接口恒无业务数据,成功与失败时 data 均为 null,以 code/success 判定结果 | + +#### 请求示例 + +普通房务转单(toUserId 为字符串,reason 可留空): + +```json +{ + "toUserId": "1003", + "reason": "我今天临时请假,转给小图接手", + "skipUpperLimit": false +} +``` + +超管强制指派团期需求(本条会命中"关键变化"里的边界场景,reason 必须 ≥10 字): + +```json +{ + "toUserId": "1008", + "reason": "该团期需求历史数据清理,统一归口给在职房务", + "skipUpperLimit": false +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": null, + "traceId": "a1b2c3d4-e5f6-0000", + "success": true +} +``` + +#### 空数据 / 降级响应 + +本接口不是列表/分页接口,没有空数据形态;成功时 data 恒为 null(与响应示例一致)。转单是本服务内部的同步事务,不经过网关/下游服务的降级路径。唯一的降级点在接收人姓名解析:如果下游用户服务查不到接收人姓名(服务不可用或该房务已离职),后端不会阻断转单,会用一个兜底展示名继续完成转单,调用方看到的仍是 200 成功。 + +#### 错误响应 + +| 错误码 | 文案 | 触发条件 | 是否本次新增 | +|---|---|---|---| +| 808090 | 未登录或非房务角色,无权操作 | 未登录/token 无效 | 否,既有码 | +| 808091 | 房务组长为只读监督角色,无权执行该操作 | 房务组长(只读监督角色)调用 | 否,既有码 | +| 808002 | 需求已不存在 | 需求已被取消/已配完,查不到有效需求 | 否,既有码 | +| 808650 | 团期订单不支持逐户抢单/转单,请到团期抢单池整团认领 | 普通房务对任意团期需求调用;或超管对已被整团认领的团期需求调用 | 否,既有码,本次首次写清适用场景 | +| 808010 | 需求不属于当前用户,无法转单 | 普通房务调用,但当前持有人不是自己 | 否,既有码 | +| 808014 | 接收人就是当前归属人,无需操作 | toUserId 等于当前持有人 | 否,既有码 | +| 808016 | 超管指派原因长度不足 10 字 | 超管调用且 reason 少于 10 字 | 否,既有码 | +| 808013 | 一单转单次数达上限(3 次) | 普通房务累计转单已达 3 次 | 否,既有码 | +| 808001 | 需求已被其他房务抢到 | 并发场景下持有人被抢先转走/释放;或超管对尚未被整团认领、且本身从未被单独抢到的团期需求调用(此场景下文案不准确,见关键变化章节) | 否,既有码,本次首次写清团期场景下文案不准确 | + +代表性示例 1(普通房务对团期需求调用,文案本身准确): + +```json +{ + "code": 808650, + "message": "团期订单不支持逐户抢单/转单,请到团期抢单池整团认领", + "data": null, + "traceId": "a1b2c3d4-e5f6-0001", + "success": false +} +``` + +代表性示例 2(超管对"从未被抢到"的团期需求调用,文案不准确,需特殊处理): + +```json +{ + "code": 808001, + "message": "需求已被其他房务抢到", + "data": null, + "traceId": "a1b2c3d4-e5f6-0002", + "success": false +} +``` + +上面这条 808001 在这个场景下不能按字面展示给用户——真实原因是这条需求从来没有被任何人抢到过,不是被抢走了。前端识别到目标需求属于团期产品时,应改走"团期抢单池 → 整团认领"入口,不要展示/触发本接口,也不要把这条文案原样透传。 + +#### 业务边界 + +- 鉴权:必须是已登录且具备房务写权限的账号(808090);房务组长为只读监督角色,禁止调用(808091);接收人不能是当前持有人(808014)。 +- 普通房务身份对任何团期需求(不论该团是否已被整团认领、这条需求本身是否早年已被某房务抢到)调用本接口,一律 808650,不进入后续任何判断,零写入。 +- 超管身份:仅当目标团期尚未被整团认领时才可能继续往下走;若该团已被整团认领,同样 808650。 +- 通过上一步之后,后端仍会原子性地重新核对这条需求当前是否处于可转状态: + - 若这条需求是在并入团期产品之前就已经被某个房务抢到(历史遗留数据),转单会真实生效(200)——这是这类历史数据唯一的无损转出口。 + - 若这条需求是刚进团期、从未被任何房务单独抢到(结构上到不了可转状态),恒返 808001,但文案不准确(见上)。 +- 转单次数上限:仅普通房务受限,累计已转单 3 次后再转禁止(808013);超管指派不受此限制。 +- 幂等/并发:后端对当前持有人是否仍是预期的那个人做原子校验,校验失败直接返回错误、不写库;两个人同时对同一需求发起转单,只有一个会成功,另一个收到 808001(此时文案准确,是真的被抢先了)。 +- 失败零写入:所有前置校验(鉴权/团期作用域/持有人/接收人/次数上限)失败均不产生任何数据变化。 + +--- + +## 四、契约约束与正确调用方式 + +本节只写后端接受/拒绝调用的规则,不写 UI 渲染建议。 + +### 正确 / 错误调用场景对照 + +| 场景 | 结果 | +|------|------| +| 正确:对一个已被某房务抢到的非团期需求调用转单 | 200,转单成功 | +| 正确:超管对一个合并进团期之前就已被某房务抢到的团期需求调用(该团尚未被整团认领) | 200,转单成功(历史数据唯一转出口) | +| 错误:普通房务对任意团期需求调用 | 808650,零写入 | +| 错误:超管对已被整团认领的团期需求调用 | 808650,零写入 | +| 错误:超管对尚未被整团认领、且本身从未被抢到的团期需求调用 | 808001(文案不准确,见关键变化),零写入 | +| 错误:对一个尚未被任何房务抢到的非团期需求调用转单 | 808001(此场景下文案准确:确实没人抢过,应引导去抢单而非转单) | + +### 团期需求的正确处理路径 + +前端在渲染需求列表/详情时,只要能判断出该需求属于团期产品下的子订单,就不应该展示或触发本接口的转单/强制指派入口,一律引导到团期抢单池的整团认领:`POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/claim`(无请求体;路径参数 `groupBatchId` 是**团期 ID**(`group_batch.id`),不是订单 ID,后端 `@ApiParam` 的「团期主订单 ID」标签会误导;成功后整团房务归属一次性建立,个体需求不再需要单独转单)。 + +--- + +## 五、数据库行为 + +本次不产生任何数据库变化。转单成功后对外可观察的行为:接收人成为该需求新的持有人,原持有人不再能对该需求执行释放/确认/标记完成等操作;转单失败(含本文所述的团期作用域限制)时不产生任何数据变化。 + +--- + +## 六、边界行为 + +- 未登录/token 无效 → 网关层 401;到达服务后 808090 +- 需求不存在或已失效(如所在订单已取消)→ 808002 +- 调用者是房务组长(只读监督角色)→ 808091 +- 团期需求 + 普通房务 → 808650(业务边界详见三.1) +- 团期需求 + 超管 + 该团已被整团认领 → 808650 +- 团期需求 + 超管 + 该团未被整团认领 + 需求本身从未被单独抢到 → 808001(文案不准确) +- 团期需求 + 超管 + 该团未被整团认领 + 需求本身是历史遗留的已被抢到数据 → 200,转单真实生效 +- 下游用户服务解析接收人姓名失败 → 不阻断,退回兜底展示名继续完成转单,仍返回 200 + +--- + +## 六.5、枚举 / 数据字典 + +本接口入参(toUserId/reason/skipUpperLimit)与出参(恒为 Result,data 无字段)均不涉及新增枚举值,本节不适用。 + +## 六.6、修改前后对比 + +### 字段级对比 + +无字段变化。 + +### 行为级对比 + +| 行为 | 改前(契约文档口径) | 改后(契约文档口径,实际运行时行为未变) | +|------|------|------| +| 普通房务对团期需求调用转单 | 文档未提及,容易误以为和非团期需求走同一套判权规则 | 明确写清:恒返 808650,不进入持有人校验等后续步骤 | +| 超管对从未被抢到的团期需求调用转单 | 文档未提及 808001 在此场景下的特殊性 | 明确写清:恒返 808001,但文案"需求已被其他房务抢到"在此场景不准确,真实原因是从未有人抢过它;正确处理是改走整团认领 | +| 超管对合并进团期前已被抢到的团期需求调用转单 | 文档未提及 | 明确写清:这类历史遗留数据可以真实转单成功,是唯一的无损转出口,不代表新进团期的需求也能这样处理 | + +## 六.7、影响评估 + +- 是否破坏向后兼容: 否——请求参数、响应结构、错误码集合、路由均未改变,逐字节一致。 +- 前端是否必须同步上线: 否——本单不产生任何需要部署的后端代码变化,没有上线这一步。 +- 前端 workaround 清理点: 如果前端此前把本接口的 808001 一律按字面文案展示给用户、并允许对团期需求的失败结果直接重试,建议改为:①渲染需求列表/详情时,能判断出目标需求属于团期产品的,就不展示/不触发转单/强制指派入口,直接引导到团期抢单池整团认领;②即便被动收到这个场景下的 808001,也不要照抄文案展示,改成"该团期需求不支持单户转单,请使用整团认领",避免用户误以为是竞态失败、反复重试。 + +--- + +## 七、不影响范围 + +- 仅影响: 管理后台超管/房务对团期需求调用转单/强制指派端点时,如何理解与处理返回的错误码(以及对应的 UI 引导逻辑)。 +- 零影响: + - 对非团期需求的转单/超管指派全流程——请求、响应、判权规则逐字节不变 + - 抢单(claim)/释放(release)/我的接单等 HOUSE 抢单池其余 4 个端点 + - 团期抢单池自身的 5 个端点(#7322:列表/整团认领/整团释放/团级接管/我的团) + - 小程序端(本次涉及端点仅管理后台) + - 请求/响应字段结构、错误码集合——无新增、无删除、无字段变化 + +--- + +## 八、测试环境已验证 + +本单未部署、未在测试服做端到端真实调用验证。以下只是仓内测试结果,不构成测试服实测证据: + +- HouseGrabServiceImplTest 新增 2 条(团期需求转单前置入参 fromClaimerId=null 校验;并发失配场景入参为操作人本人) +- HotelRequirementTransferCasMysqlTest(真库集成测试)新增 5 条(两条前置条件各设计一个只违反它自己的探针 + 两条阳性对照) +- 定向跑:124 / 0 / 0 / 0(Tests / Failures / Errors / Skipped) +- ArchTest:63 / 0 / 0 / 0 + +--- + +## 九、相关历史 PR + +| PR | Issue | 说明 | 是否仍有效 | +|----|-------|------|------------| +| #7623 | #7621 | 给转单端点的 javadoc / @ApiOperation 补作用域说明,已合 dev-v3 | 有效 | +| 本次 #7782 | #7621 | 给上面的说明补充显式单测(Service 2 条 + 真库集成 5 条),证明代码未被顺手改坏 | 有效,未部署测试服 | + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#7621](https://git.1814.love:8443/wx/HL/issues/7621) +- 关联 PR: [wx/HL#7623](https://git.1814.love:8443/wx/HL/pulls/7623)(契约说明)、[wx/HL#7782](https://git.1814.love:8443/wx/HL/pulls/7782)(补充测试) +- 后续计划: 若后续有人对 dev-v3 做常规部署并顺带把本单一起带上测试服,请在测试服对"超管调用团期需求 transfer 恒返 808001"和"普通房务调用团期需求 transfer 恒返 808650"两条各补一次真实网关调用记录,回填本文件第八节并把 backend_status 视情况改为 deployed + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#7621](https://git.1814.love:8443/wx/HL/issues/7621) +- **PR**: [#7623](https://git.1814.love:8443/wx/HL/pulls/7623) / [#7782](https://git.1814.love:8443/wx/HL/pulls/7782) + +### 联系人 + +- **后端负责人**: @wx