diff --git a/changelogs-v2/2026-09/08_7344_取消班期活跃订单判据failclosed聚合失败取保守侧CANCELLING-修复-管理后台.md b/changelogs-v2/2026-09/08_7344_取消班期活跃订单判据failclosed聚合失败取保守侧CANCELLING-修复-管理后台.md new file mode 100644 index 00000000..02ae4dee --- /dev/null +++ b/changelogs-v2/2026-09/08_7344_取消班期活跃订单判据failclosed聚合失败取保守侧CANCELLING-修复-管理后台.md @@ -0,0 +1,93 @@ +--- +schema: "hl-changelog/v2" +ticket: "7344" +title: "管理端取消班期:order-v3 活跃订单聚合失败时不再落终态 CANCELLED,改取保守侧 CANCELLING(无接口、字段、路径、参数、枚举、错误码变化)" +consumer: "admin" +author: "jw(GIT)" +change_type: "修复" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "not_required" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "纯后端修复(PR #7348 合入 dev-v3,合并提交 2071286a1),不动接口/字段/路径/参数/枚举/错误码,只在「order-v3 活跃订单聚合失败」这一条失败路径上把落库值由 CANCELLED 改为更保守的 CANCELLING。batchStatus 的取值集合与语义均不变(CANCELLING/CANCELLED 在展示态解析、可报名谓词、选品列表过滤、order-v3 下单闸四处判定中一律同等排除,差异只在展示文案),前端读后端值直接渲染、无兜底反推逻辑,故 not_required。已部署测试服并经真实网关三条路径实测(无订单→CANCELLED / 有真实订单→CANCELLING / 聚合失败→CANCELLING+WARN)。" +updated_at: "2026-09-08" +base: "dev-v3" +--- + +# 取消班期的活跃订单判据改 fail-closed(修复) + +## 一、给前端的一句话 + +**前端无需任何改动。** 本次修复不动接口、不动字段、不动路径、不动参数、不动枚举、不动错误码。`POST /admin/product/item/:id/schedule/:scheduleId/cancel` 的响应在全部场景下仍是 `code=200` / `message="班期取消成功"` / `data=null`;`batchStatus` 的取值集合(`ENROLLING` / `NEARLY_FULL` / `FULL` / `FINISHED` / `CANCELLING` / `CANCELLED`)与语义也不变。变的只是**订单服务聚合失败时落库取哪一个取消态**。 + +## 二、症状(改前) + +管理端「取消班期」要先问 order-v3「这个班期还有没有活跃订单」,再决定落 `CANCELLING`(还有订单待订单域处理)还是 `CANCELLED`(确实没订单)。 + +这次取数走的是 fail-open 的降级路径:向 order-v3 的实时聚合一旦失败(Feign 超时 / 5xx / 返回 `code != 200` / 目标班期缺席结果),失败分片的班期就退化为「无活跃订单」,活跃数被当成 0,班期于是被落成**终态 `CANCELLED`**。 + +后果不是钱退不出去(取消班期本来就不发起任何退款,见第五节),而是三件事: + +1. **状态语义错误**:明明还有活跃订单的班期,被记成「已取消、无遗留」。 +2. **与订单域口径分裂**:order-v3 流团触发的同语义入口(内部接口)早已收口为 fail-closed,两条路径对同一件事给出相反结论。 +3. **写错后不自愈**:全仓没有任何路径会把 `CANCELLED` 改回来——班期状态重算只在 `ENROLLING` ↔ `FULL` 之间流转、明确不碰取消态;编辑班期会保留原状态;订单域再次回调时对已取消态幂等直接返回。一旦写错,只能手工改库。 + +触发窗口窄(order-v3 抖动期间恰好有人点取消),但一旦命中就是永久性的错误状态。 + +## 三、修复(改后) + +活跃数取数改用严格版本:聚合成功才按真实活跃订单数判定,聚合失败取**保守侧 `CANCELLING`** 并记一条 WARN 日志,与订单域内部入口逐字对齐。**不新增任何接口、不新增任何方法。** + +| 场景 | 改前落库 `batchStatus` | 改后落库 `batchStatus` | +|---|---|---| +| 聚合成功且有活跃订单 | `CANCELLING` | `CANCELLING`(不变) | +| 聚合成功且无活跃订单 | `CANCELLED` | `CANCELLED`(不变) | +| **聚合失败**(超时 / 5xx / `code != 200` / 班期缺席结果) | `CANCELLED` | **`CANCELLING`** + 一条后端 WARN | + +保守侧的代价是:order-v3 抖动窗口内被取消的空班期会显示「取消中」而不是「已取消」。这是刻意接受的——两个状态在全部下游判定里等价(下一节),差异只是展示文案;而反方向的错误会丢失「这批还有订单要处理」这一运营信号,且同样不自愈。 + +## 四、受影响接口(全部「复用不改」) + +| 端 | 接口 | 变化 | +|---|---|---| +| 管理后台 | `POST /admin/product/item/:id/schedule/:scheduleId/cancel` | method / path / 入参 / 出参 / 错误码均无变化;只有「聚合失败」这一条路径的落库值变了。Swagger `notes` 文案一并修正(原文误称「标记为 CANCELLING 触发退款」,实际不触发任何退款) | +| 管理后台 | `GET /admin/product/item/:id/schedule/list` | 无变化。仅作为取证入口,`batchStatus` 字段读到的就是上面落库的值 | + +`CANCELLING` 与 `CANCELLED` 在下游四处判定中**完全等价**,前端已有的渲染逻辑不受影响: + +- 班期展示态解析:两者都原样保留持久值,不参与实时库存重算 +- 可报名谓词:两者都返回不可报名 +- 下单选品列表:两者都被排除 +- order-v3 下单闸:可下单状态只含 `ENROLLING` / `NEARLY_FULL`,两者都不在其中 + +## 五、澄清:取消班期不触发退款 + +修复顺带纠正了两处误导性文案(服务层注释与 Swagger `notes`)。它们原本写「CANCELLING → 触发退款 / 等退款流程异步处理」,但产品域取消班期的方法体只有一次班期状态落库,不发 MQ、不调 Feign、不发事件;全仓也没有任何代码读产品侧的 `CANCELLING` 去发起退款。团期退款实际由订单域的流团审批链路驱动。前端如果据旧文案做过「取消班期后等退款」的假设,可以去掉。 + +## 六、影响范围 + +- 只影响管理端「取消班期」这一个动作,且只在 order-v3 聚合失败时表现不同。 +- 后端只滚 `hl-product-service-v2`;order-v3 / 网关 / 小程序端 / 其它服务一律不动。 +- 无表结构变更、无 Flyway、无数据迁移;`group_tour_batch.batch_status` 的列定义与取值字典不变。 + +## 七、测试环境已验证 + +测试环境(`api.test.1814.love`)经真实网关实测三条路径,落库均以 SQL 复核: + +| 路径 | 实测结果 | +|---|---| +| 无活跃订单 → 取消 | `code=200`,`batchStatus=CANCELLED`(不回归) | +| 真实下单后 → 取消 | `code=200`,`batchStatus=CANCELLING`(不回归) | +| 注入聚合失败 → 取消无订单班期 | `code=200`,`batchStatus=CANCELLING`,后端 WARN「取消班期: 活跃订单数聚合失败, 取保守侧 CANCELLING」 | + +聚合失败用「只压单个 Feign 客户端超时」的方式构造,未停 order-v3;取证后配置已还原并复核一致。合并进 `dev-v3` 后又在 `dev-v3` 构建上复核过正常路径。本机单测 `BUILD SUCCESS`(1621 例,0 失败 0 错误),含新增的聚合失败用例与架构门禁。 + +## 关联 / 联系人 + +- 工单:https://git.1814.love:8443/wx/HL/issues/7344 +- PR:https://git.1814.love:8443/wx/HL/pulls/7348 (合并提交 2071286a1 合入 dev-v3) +- 同源三单:#7283(订单域内部入口先行收口)、#7293(状态重算同构降级)、#7263(分片修复同一聚合链路) +- 后端:jw