diff --git a/changelogs-v2/2026-09/15_7746_取消成团导游摄影就绪位判定修正-修改接口-管理后台.md b/changelogs-v2/2026-09/15_7746_取消成团导游摄影就绪位判定修正-修改接口-管理后台.md new file mode 100644 index 00000000..51cd8fca --- /dev/null +++ b/changelogs-v2/2026-09/15_7746_取消成团导游摄影就绪位判定修正-修改接口-管理后台.md @@ -0,0 +1,251 @@ +--- +schema: "hl-changelog/v2" +ticket: "7746" +title: "取消成团 R10 守卫修正——不需要导游/摄影的团期不再恒返回 589502" +consumer: "admin" +author: "wx(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "2026-09-15" +status_note: "测试服 dev-v3(合入提交 6976f68fd)已取得网关调用与前后 SQL 快照证据,见工单 #7746 验收评论。frontend_status 为 pending:本变更放开了此前恒被拦的取消成团路径,若 hl-ui 按 guideReady/photographerReady 隐藏或禁用「取消成团」按钮,需同步放开,请 mmg 核实后回写。" +updated_at: "2026-09-15" +base: "dev-v3" +--- + +# order-v3: 取消成团 R10 守卫修正 + +> **存放目录**: `changelogs-v2/{YYYY-MM}/`(管理后台,二期 order-v3) +> +> **服务**: hl-order-service-v3 (端口 8083) +> **PR**: #7755 +> **Issue**: #7746 +> **日期**: 2026-09-15 +> **影响范围**: 既有端点 `POST /v3/admin/order/group-batch/{groupBatchId}/cancel-group` 的 R10「已派单资源」判定逻辑;请求/响应结构、路径、判权码均未改 + +--- + +## ⚠️ 关键变化 + +🔴 **不需要导游、不需要摄影的团期,改前一旦成团就再也无法取消成团(恒返回 `589502`),本次修复后可以正常取消。** 根因:成团时的「免闸」逻辑会把 `needs_guide=false`/`needs_photographer=false` 的团期对应 `guide_ready`/`photographer_ready` 直接置 `true`(语义是「这一项不需要,闸门免检」),而取消成团的 R10 守卫把这两列的 `true` 一律当成「已派单资源」(语义是「已经有资源在准备/已派单」)——同一列被两种相反的语义共用,R10 读错了其中一种。 + +🟢 **有权限的正常场景不变**:需要导游/摄影、且真的已经配了人的团期,取消成团仍然会被拦(`589502`),这正是 R10 该拦的场景,行为逐字节不变。 + +🟡 **响应结构、判权(`group-batch:manage`,见 `#7608`)、成功码全部不变**。本单只改「已派单资源」这一项判定的口径,不改端点契约。 + +--- + +## 一、背景 + +`R10` 守卫是取消成团(把已成团团期打回招募中)前的硬性前置检查:「无已确认子订单 且 无已派单资源」,任一不满足即拦截。它读四个资源就绪标志位(住宿/车/导游/摄影)判断「是否已派单」。 + +但导游/摄影两个标志位还有另一个写口:成团时,若该团期不需要导游/摄影(`needs_guide=false`/`needs_photographer=false`),系统会直接把对应的 `ready` 位置为 `true`,含义是「这一项不需要,闸门免检」,用来避免「没有员工角色配置时四个 ready 位永远凑不齐」的死锁。 + +R10 直接读这两个标志位的原值,无法分辨它是「真的已经排了人」还是「不需要、被免闸置位」,于是把后者也当成「已派单」,把这类团期永久拦死——只剩流团(需审批、会退款关单,是破坏性动作)一条路。该缺陷此前被三次撞到(`#7060`/`#7608`/`#7287`)但一直没有独立单跟进,本单是收口。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 取消成团 | POST | `/v3/admin/order/group-batch/{groupBatchId}/cancel-group` | 判定逻辑修正 | R10「已派单资源」判定对导游/摄影两项只在「需要该资源」时才看就绪位 | + +--- + +## 三、接口详情 + +### 1. 取消成团 `POST /v3/admin/order/group-batch/{groupBatchId}/cancel-group` + +**VO**: `无请求体 → Result` + +#### 使用场景 + +团期详情页「取消成团」按钮,把已成团(`RESOURCE_PREPARING`)的团期打回招募中(`RECRUITING`),同一事务内清零四个资源就绪位与物资确认位、登记 Fleet 配车释放。判权 `group-batch:manage`(灰度开关控制,见 `#7608` changelog,本单未改)。本单只改 R10 守卫本身对导游/摄影两项就绪位的判定口径。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | 是 | 团期主订单 ID(不是产品侧排期 ID) | 不变 | + +(无请求体,本单未改。) + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | null | 结构完全不变,`Result` | + +#### 请求示例 + +```http +POST /v3/admin/order/group-batch/2099716954674597889/cancel-group HTTP/1.1 +Authorization: Bearer {token} +``` + +(无请求体,仅 Path 参数 `groupBatchId`。) + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": null, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无列表/分页语义,不存在空数据形态。灰度开关关闭时判权退化为旁路观察日志(见 `#7608` changelog),本单未改该降级逻辑。 + +```json +{ "code": 200, "success": true, "data": null } +``` + +#### 错误响应 + +| 码 | 符号 | 触发 | 本单 | +|----|------|------|------| +| 589507 | GROUP_BATCH_PERMISSION_DENIED | 灰度开关开且无 `group-batch:manage` | 不变,见 #7608 | +| 589500 | GROUP_BATCH_NOT_FOUND | 团期不存在 | 不变 | +| 589501 | GROUP_BATCH_STATUS_INVALID | 团期不在 `RESOURCE_PREPARING` | 不变 | +| 589502 | GROUP_BATCH_CANCEL_GROUP_BLOCKED | 有已确认子订单,或住宿/车/导游/摄影任一被判定为「已派单」 | 触发条件收窄,见下 | + +```json +{ + "code": 589502, + "message": "取消成团被阻塞(有已确认子订单或已派单资源)", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- **R10 判定口径**:「已派单资源」= 已确认子订单数 > 0,或住宿/车两项 `ready=true`,或(导游 `needs_guide=true` 且 `guide_ready=true`),或(摄影 `needs_photographer=true` 且 `photographer_ready=true`)。住宿/车两项当前没有免闸置位,仍直接看 `ready` 位,不受本单影响。 +- **`needs=false` 但管理员仍配置了导游/摄影位的团期,本单放行取消成团**:导游/摄影配置保存时不看 `needs`,一律置 `ready=true`,这类团期的 `ready=1` 在数据列上与免闸置位无法区分。本单接受这一放行结果(wx 2026-09-15 拍板方案 A),取消后配置行本身保留在原表,不被清除。 +- **取消成团后重置**:四个资源就绪位与物资确认位随取消成团清零;再次成团时按当时的 `needs` 重新免闸置位——不存在绕过免闸的回退后再成团路径。 +- **拒绝时零写入**:判权与 R10 守卫均排在任何库写入之前,被拒的请求不改团期任何列(含 `update_time`),不写状态流水。 +- **不在本单范围**:住宿/车两项当前没有免闸置位问题;若未来「免车」等场景为车项引入类似免闸置位,需要同步修改本方法的判定,否则会以同一形态复现本缺陷(`#7441` PR-2e 已引入车项免闸置位并同步处理,另见 `#7441` changelog)。 + +--- + +## 四、契约约束与正确调用方式 + +### 正确 / 错误 调用结果对照 + +| 场景(团期形态) | 结果 | +|------|------| +| 不需要导游、不需要摄影,住宿/车未就绪,无已确认子订单(正确) | 200,落 `RECRUITING` | +| 只有一项不需要(如只不需要导游),其余按需就绪(正确) | 200,落 `RECRUITING` | +| 需要导游且已配置导游位(真实已派单,正确应拦) | `589502` | +| 有已确认子订单(正确应拦) | `589502`,零写入 | +| 不需要导游但管理员仍手动配置了导游位(本单接受放行) | 200,落 `RECRUITING`,配置行保留 | + +### 切换状态时的必要动作 + +前端无需改动任何请求参数——本端点入参/路径未变,判定完全由后端按团期当前 `needs`/`ready` 状态计算。前端唯一需要核实的是:若此前为了规避「不需要导摄的团期点了也白点」而对「取消成团」按钮做过基于 `guideReady`/`photographerReady` 原值的隐藏/禁用逻辑,需要同步放开或调整判断依据,否则会出现「后端已允许但前端仍不让点」的体验倒退。 + +--- + +## 五、数据库行为 + +本单**无表结构变更、无 Flyway、无数据迁移**。写行为本身不变:取消成团成功后仍是「团期状态回退 + 四个资源就绪位与物资确认位清零 + 登记配车释放」这一套既有动作;本单只改变判定这套动作能否执行的前置条件。 + +--- + +## 六、边界行为 + +- 未登录 → 401(网关拦截) +- 无权限(灰度开关开)→ `589507`(不变) +- 团期不存在 → `589500`(不变) +- 团期非 `RESOURCE_PREPARING` → `589501`(不变) +- 有已确认子订单 → `589502`,零写入(不变) +- 需要且已就绪的导游/摄影/住宿/车任一 → `589502`,零写入(不变) +- 不需要的导游/摄影(免闸置位)→ 本单起不计入「已派单」判定(**变化点**) +- 老数据兼容:`V20260616_001` 之前成团的存量团期,`needs` 列按 `DEFAULT 0` 回填,同样落入「不需要」分支,按本单新口径放行 + +--- + +## 六.6、修改前后对比 + +### 字段级对比 + +本单不改任何请求/响应字段,仅改判定逻辑读取既有字段(`needs_guide`/`needs_photographer`/`guide_ready`/`photographer_ready`)的组合方式,无字段级增删。 + +### 行为级对比 + +| 团期形态 | 改前 | 改后 | +|------|------|------| +| 不需要导游、不需要摄影,其余条件满足 | `589502`(恒拦) | 200,落 `RECRUITING` | +| 只不需要导游(其余同上) | `589502` | 200 | +| 只不需要摄影(其余同上) | `589502` | 200 | +| 需要导游且已配置(真实已派单) | `589502` | `589502`(不变) | +| 有已确认子订单 | `589502` | `589502`(不变) | +| 不需要导游但已手动配置导游位 | `589502` | 200(本单接受放行,见业务边界) | + +--- + +## 六.7、影响评估 + +- **是否破坏向后兼容**: 否(对已能取消成团的场景无影响);对此前被误拦的场景是**放宽**——这类团期从「恒不可取消成团」变为「可以取消成团」。 +- **前端是否必须同步上线**: 视前端现有实现而定。若前端存在基于 `guideReady`/`photographerReady` 原值隐藏或禁用「取消成团」按钮的逻辑,需要同步放开;若前端本就是「可见即可点,后端兜底报错」的口径(不做前置隐藏),则零改动。本草稿未接触 hl-ui 代码库,无法从后端仓库判定该假设是否成立,需前端自行核实。 +- **前端 workaround 清理点**: 若前端此前为「点了也白点」的已知问题做过提示语/禁用态特殊处理,可以在确认后端已放开后清理;具体清理点需前端自查。 + +--- + +## 七、不影响范围 + +- **仅影响**: `POST /v3/admin/order/group-batch/{groupBatchId}/cancel-group` 端点 R10 守卫对导游/摄影两项的判定口径。 +- **零影响**: + - 成团(`POST .../group`)、流团、名额调整、预支、物料门复判等其余团期端点——判定口径不变(`#7441` PR-2e 另行为车项引入免闸置位联动,见另一份 changelog,与本单各自独立)。 + - 住宿、车两项在 R10 中的判定——本单未改(车项的免闸联动归 `#7441` PR-2e,另案)。 + - 判权机制(`group-batch:manage` 灰度开关)——未改,见 `#7608`。 + - `hl-common-*`、`hl-fleet-service`、`hl-gateway` 路由——本单只改 `hl-order-service-v3` 一个服务的判定逻辑,未新增路由配置。 + - 小程序端(`consumer: mp`)——本端点为管理后台端点,小程序无影响。 + +--- + +## 八、测试环境已验证 + +**取证环境**:测试服 dev-v3(合并提交 `6976f68fd`,已在 dev-v3 主线 `870610927` 中)。 + +| 验收项 | 场景 | 结果 | +|---|---|---| +| AC-1 | `needs_guide=0、needs_photographer=0`,子订单均未确认,住宿/车未就绪,未配置任何导摄位 | `code=200`,团期落 `RECRUITING`,四个 ready 位与 `material_confirmed` 均为 0,状态流水新增 1 行 `BATCH_CANCEL_GROUP`;附改前同一夹具返回 `589502` 的对照 | +| AC-2 | 只不需要导游(`needs_photographer=1`)与只不需要摄影(`needs_guide=1`)两个团期各测一次 | 均 `code=200`,落 `RECRUITING` | +| AC-3 | 对 AC-1 取消后的团期再次调用「成团」 | `code=200`,团期回到 `RESOURCE_PREPARING` 且 `guide_ready=1、photographer_ready=1`(免闸重新生效) | +| AC-4 | 反例对照:`needs_guide=1` 且已配置导游位(真实已派单),其余 ready=0,子订单未确认 | `589502`;团期行取消前后逐列相同,状态流水新增 0 行。摄影同构反例同样 `589502` | +| AC-5 | 反例对照:`needs_guide=0、needs_photographer=0` 但至少一户子订单已确认 | `589502`,零写入 | +| AC-6 | `needs_guide=0` 但已保存含导游位的导摄配置(方案 A 放行场景) | `code=200`,落 `RECRUITING` | +| AC-9 | 网关实测 AC-1/AC-3/AC-4,附请求/响应、取消前后 SQL 快照、`deploy-status.sh` 两行(`hl-order-service-v3`/`hl-gateway`) | 已完成 | + +**其余读就绪位的消费方**(物资门推进、`recheck-material-gate`、预支闸门、合同可出判定、出发门、团期详情 / 看板展示)行为不变,由单测覆盖;本单全量测试结果见工单 #7746 验收评论。 + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#7746](https://git.1814.love:8443/wx/HL/issues/7746) +- 关联 PR: [wx/HL#7755](https://git.1814.love:8443/wx/HL/pulls/7755) +- 相关工单:`#7608`(取消成团判权改造,评论中记录的「AC-3 ready 位重置结构性不可观测」前提,本单合入后该前提不再成立,需 `#7608` 执行方重新判定,本单不改 `#7608` 正文)、`#7060`/`#7287`(本缺陷此前被撞到但未独立跟进的历史记录)、`#7441` PR-2e(车项在同一守卫上引入的另一处免闸联动,另案,见另一份 changelog) + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#7746](https://git.1814.love:8443/wx/HL/issues/7746) +- **PR**: [#7755](https://git.1814.love:8443/wx/HL/pulls/7755) +- **Merge commit**: [6976f68fd](https://git.1814.love:8443/wx/HL/commit/6976f68fd)(已在 dev-v3 主线) + +### 联系人 + +- **后端负责人**: @wx +