docs(order-v3): #7746 取消成团 R10 导游/摄影就绪位判定修正 changelog
changelog-filename-gate / validate (push) Failing after 2s
changelog-filename-gate / validate (push) Failing after 2s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -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<Void>`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
团期详情页「取消成团」按钮,把已成团(`RESOURCE_PREPARING`)的团期打回招募中(`RECRUITING`),同一事务内清零四个资源就绪位与物资确认位、登记 Fleet 配车释放。判权 `group-batch:manage`(灰度开关控制,见 `#7608` changelog,本单未改)。本单只改 R10 守卫本身对导游/摄影两项就绪位的判定口径。
|
||||
|
||||
#### 入参字段表
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | 是 | 团期主订单 ID(不是产品侧排期 ID) | 不变 |
|
||||
|
||||
(无请求体,本单未改。)
|
||||
|
||||
#### 出参字段表
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| data | null | 结构完全不变,`Result<Void>` |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```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
|
||||
|
||||
在新工单中引用
屏蔽一个用户