docs(changelog): #7283 流团后班期自动置为不可售 + 已流团团期下单返 589551
changelog-filename-gate / validate (push) Successful in 2s
changelog-filename-gate / validate (push) Successful in 2s
后端已部署测试服并逐条实测(AC-1~AC-18 全过)。接口出入参零变化, 前端两项待办:补录错误码 589551、知悉班期会显示「取消中/已取消」。 上线顺序硬约束:product-v2 先、order-v3 后,回滚反序。
这个提交包含在:
@@ -0,0 +1,365 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "7283"
|
||||||
|
title: "流团批准后自动把对应班期置为不可售,并给已流团团期的下单补 589551 硬闸"
|
||||||
|
consumer: "multiple"
|
||||||
|
author: "jw(GIT)"
|
||||||
|
change_type: "修改接口"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "verified"
|
||||||
|
frontend_status: "pending"
|
||||||
|
frontend_owner: "mmg"
|
||||||
|
frontend_ref: ""
|
||||||
|
target_release: ""
|
||||||
|
verified_at: "2026-09-08"
|
||||||
|
status_note: "后端已部署测试服并逐条实测(AC-1~AC-18 全过)。前端待办两项:①错误码字典补录 589551「该团期已流团,不可下单」;②知悉流团批准后该班期在小程序日历/班期列表会变为不可选、状态文案显示「取消中/已取消」。接口出入参结构零变化,前端不改也不会报错。"
|
||||||
|
updated_at: "2026-09-08"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 团期: 流团后班期自动不可售 + 下单侧 589551 硬闸
|
||||||
|
|
||||||
|
> **服务**: hl-order-service-v3、hl-product-service-v2(**必须一起部署**)
|
||||||
|
> **PR**: #7303
|
||||||
|
> **Issue**: #7283
|
||||||
|
> **日期**: 2026-09-08
|
||||||
|
> **影响范围**: 团期流团审批、GROUP 产品下单(管理后台代客下单 + 小程序下单)、小程序班期日历
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 关键变化
|
||||||
|
|
||||||
|
**出入参结构一个字段都没改。** 前端不改不报错,但有两件事必须知道:
|
||||||
|
|
||||||
|
1. **流团批准后,该班期会自动变为不可售**——小程序日历上该出发日 `isSelectable=false`,
|
||||||
|
班期状态文案显示「取消中」或「已取消」。这是现有枚举字典的限制,不是产品下架,
|
||||||
|
**同产品的其它班期不受任何影响**。
|
||||||
|
2. **已流团团期再下单会被拒**,新增错误码 **589551**「该团期已流团,不可下单」,
|
||||||
|
前端错误码字典需补录。
|
||||||
|
|
||||||
|
改前:流团批准后班期仍停在「报名中」,客户能成功下单到一个已经解散的团,钱收得进来、退不回去。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、背景
|
||||||
|
|
||||||
|
流团在 #7196 之后已是一条完整审批链:发起申请 → 管理员审批 → 通过后系统自动置团期取消、
|
||||||
|
批量退各户定金、释放配车占用。这条链唯一漏掉的一步是「把对应班期置为不可售」。
|
||||||
|
|
||||||
|
后果实测(AC-5 改前对照,在部署本次改动**之前**于测试服复现):批准流团后,
|
||||||
|
`group_tour_batch.batch_status` 仍是 `ENROLLING`,对同一 `productBatchId` 再下单返回
|
||||||
|
`code=200` 且订单创建成功(订单号 `HL20260907222635897`)。新单挂到一个终态团期上,
|
||||||
|
团期看板、配房、配车、合同保险等所有按团聚合的链路都会拿到错的事实,需要人工善后并再退一次款。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||||
|
|---|------|------|------|----------|------|
|
||||||
|
| 1 | 同意流团申请 | POST | `/v3/admin/order/group-batch/disband/:approvalId/approve` | 行为变化 | 批准后额外把对应班期置为不可售;出入参与错误码集合**不变** |
|
||||||
|
| 2 | 管理后台代客下单 | POST | `/v3/admin/order` | 新增错误码 | 已流团团期下单被拒,返 589551 或 581028;入参出参**不变** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、接口详情
|
||||||
|
|
||||||
|
### 1. 同意流团申请 `POST /v3/admin/order/group-batch/disband/:approvalId/approve`
|
||||||
|
|
||||||
|
**VO**: `DisbandApprovalRespVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
管理后台团期「流团审批」中同意一条待审流团申请。调用方 hl-ui 团期审批页。
|
||||||
|
权限:仅管理员角色(不满足抛 589547)。
|
||||||
|
|
||||||
|
#### 入参
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| approvalId | Path | Long | ✅ | — | 流团审批单 ID;**入参一个都没变** |
|
||||||
|
| remark | Body | String | ❌ | — | 批复备注;请求体整体可不传 |
|
||||||
|
|
||||||
|
#### 出参 `Result<DisbandApprovalRespVO>`
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| approvalId | Long | 审批单 ID;**响应结构完全不变** |
|
||||||
|
| approvalStatus | String | PENDING / APPROVED / REJECTED |
|
||||||
|
| affectedOrderCount | Integer | 影响子订单户数 |
|
||||||
|
| actualRefundAmount | BigDecimal | 执行后回填的实际退款合计 |
|
||||||
|
| 其余全部字段 | — | **完全不变** |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /v3/admin/order/group-batch/disband/2096968377787392001/approve HTTP/1.1
|
||||||
|
Authorization: Bearer <admin-token>
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{"remark":"确认无法成团"}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"approvalId": "2096971122355458049",
|
||||||
|
"groupBatchId": "2096971120853897217",
|
||||||
|
"approvalStatus": "APPROVED",
|
||||||
|
"affectedOrderCount": 1,
|
||||||
|
"actualRefundAmount": 0.0,
|
||||||
|
"approvedByName": "admin",
|
||||||
|
"approvedAt": "2026-09-07 22:37:30"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
**置班期不可售失败不影响本接口返回值**,仍返回 200 且 `approvalStatus=APPROVED`。
|
||||||
|
失败只记 ERROR 日志并往团期状态流水写一条 `changeType=DATA` 的记录,
|
||||||
|
内容为「班期置不可售失败,请到产品后台手动取消该班期(班期 ID:…)」,
|
||||||
|
由运营到产品后台对该班期手动执行取消收尾。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"success": true,
|
||||||
|
"data": { "approvalId": "2097162345619902466", "approvalStatus": "APPROVED" }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 589546,
|
||||||
|
"message": "该流团申请已处理",
|
||||||
|
"success": false,
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**错误码集合无新增**。既有:589545 申请不存在、589546 已处理、589547 非管理员、
|
||||||
|
589500 团期不存在、589501 团期已处于终态。
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- **只写该班期一行**:作用域严格限定为该 `batchId` 的班期状态列,不触产品表、不影响同产品的其它班期。
|
||||||
|
- **目标值两种**:该班期仍有活跃订单时置「取消中」,无活跃订单时置「已取消」,两者都不可售。
|
||||||
|
实测中流团路径**几乎恒落「取消中」**——判定读的是订单域持久计数,而扣减计数与本动作同为提交后回调、
|
||||||
|
先后不保证,通常本动作先跑。两者对售卖的效果一致,前端不必区分。
|
||||||
|
- **不回滚**:跨服务同步在流团事务提交后执行,失败无从回滚,故只告警不回滚,由下单侧硬闸兜底。
|
||||||
|
- **上线期间的预期毛刺**:流团审批执行期间(含逐户退款,秒级)该班期的下单会短暂阻塞,
|
||||||
|
热门班期可能出现下单毛刺甚至锁等待超时。这是**预期行为不是故障**,客服口径需同步。
|
||||||
|
|
||||||
|
### 2. 管理后台代客下单 `POST /v3/admin/order`
|
||||||
|
|
||||||
|
**VO**: `OrderCreateRespVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
管理后台定制师代客下单。GROUP 产品必须带 `productBatchId`。
|
||||||
|
同一行为变化也适用于小程序下单链路(经 mp BFF 的 `/v3/internal/mp/order/create`),
|
||||||
|
错误码与文案一致。
|
||||||
|
|
||||||
|
#### 入参
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| productId | Body | Long | ✅ | — | **入参一个都没变** |
|
||||||
|
| productBatchId | Body | Long | GROUP 必填 | — | 产品侧班期 ID;本次新增的两道拒单都按它判定 |
|
||||||
|
| departureDate | Body | String(date) | ✅ | yyyy-MM-dd | 不变 |
|
||||||
|
| adultCount / childCount / youngChildCount / babyCount | Body | Integer | 成人必填 | — | 不变 |
|
||||||
|
| customerName / customerPhone | Body | String | ✅ | — | 不变 |
|
||||||
|
| 其余字段 | — | — | — | — | 不变 |
|
||||||
|
|
||||||
|
#### 出参 `Result<OrderCreateRespVO>`
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | Long | 订单 ID;**成功响应结构完全不变** |
|
||||||
|
| orderNo | String | 订单号 |
|
||||||
|
| orderStatus | String | PENDING_PAY 等 |
|
||||||
|
| 其余全部字段 | — | **完全不变** |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"productId": "2056947670512971778",
|
||||||
|
"tierSeq": 1,
|
||||||
|
"departureDate": "2026-12-18",
|
||||||
|
"adultCount": 2,
|
||||||
|
"childCount": 0,
|
||||||
|
"customerName": "张三",
|
||||||
|
"customerPhone": "13900000000",
|
||||||
|
"productBatchId": "2096971118245015554",
|
||||||
|
"roomCount": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"id": "2096971120677736450",
|
||||||
|
"orderNo": "HL20260907223729183",
|
||||||
|
"orderStatus": "PENDING_PAY",
|
||||||
|
"totalAmount": "6000.00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
无空数据形态。产品域同步失败导致班期仍显示可选时,下单仍会被本地硬闸拒掉(返 589551),
|
||||||
|
不会漏出一笔挂在已解散团期上的订单。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
班期已成功置为不可售时(正常路径):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 581028,
|
||||||
|
"message": "团期状态不允许报名",
|
||||||
|
"success": false,
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
班期置不可售失败、产品域仍显示报名中时(兜底路径):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 589551,
|
||||||
|
"message": "该团期已流团,不可下单",
|
||||||
|
"success": false,
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**新增错误码 589551**,前端错误码字典需补录。既有 581028 语义不变。
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- **两道闸,命中哪一道取决于产品域是否同步成功**:同步成功走 581028(产品域状态不允许报名),
|
||||||
|
同步失败走 589551(订单域本地判定)。前端两者都要能展示。
|
||||||
|
- **拒单零副作用**:整单回滚,不留脏订单、不误扣产品域名额(实测 `order_main` 无新增、名额未变动)。
|
||||||
|
- **成团后加单不受影响**:判定只认「已流团」这一个终态,团期成团后(资源准备中及之后)的正常加单照常放行。
|
||||||
|
- **转期入口不变**:转入已流团团期早已由 589510 拦下,本次不改。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、契约约束与正确调用方式
|
||||||
|
|
||||||
|
| 场景 | 做法 |
|
||||||
|
|------|------|
|
||||||
|
| ✅ 错误码字典 | 补录 `589551 = 该团期已流团,不可下单`,与既有 `581028 = 团期状态不允许报名` 并列 |
|
||||||
|
| ✅ 下单失败提示 | 581028 与 589551 都提示「该班期已不可报名,请换一期」即可,不必向客户区分两者 |
|
||||||
|
| ✅ 班期状态展示 | 该班期会显示「取消中」或「已取消」,**两者都表示不可售**,不要只把「已取消」当不可售 |
|
||||||
|
| ✅ 流团审批结果 | 仍以接口返回的 200 为准;置班期不可售是尽力而为的后续动作,不改变审批成败 |
|
||||||
|
| ❌ 把「取消中」当成「还能报名」 | 「取消中」表示仍有活跃订单待处理,售卖上与「已取消」等价,一律不可选 |
|
||||||
|
| ❌ 把班期不可售理解成产品下架 | 只是这一条班期,同产品其它班期与产品本身状态一字未动 |
|
||||||
|
| ❌ 依赖流团后班期一定变成「已取消」 | 实际几乎恒为「取消中」,见接口 1 的业务边界 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、数据库行为
|
||||||
|
|
||||||
|
- **写**:目标班期的状态列(既有列、既有取值,「取消中」或「已取消」),**单行**;
|
||||||
|
团期状态流水表新增一条记录(正常路径为流团记录,同步失败时额外一条 DATA 类留痕记录)。
|
||||||
|
- **读**:团期主订单状态、该班期下的子订单集合。
|
||||||
|
- **不写**:产品表、同产品的其它班期、班期的名额与价格等任何其它列。
|
||||||
|
- **无表结构变更、无数据迁移。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、边界行为
|
||||||
|
|
||||||
|
- **小程序链路同步生效**:经 mp BFF 的下单同样返 589551 / 581028,文案一致。
|
||||||
|
- **已付款子订单在流团时仍不会被自动取消**——这是既有缺陷(已另立工单 #7294 跟进),
|
||||||
|
与本次改动无关,本次也未修改流团取消哪些户。
|
||||||
|
- **团期主订单未绑定产品班期时**(历史脏数据):置不可售无处可写,按同步失败处理,记 ERROR 与流水留痕。
|
||||||
|
- **重复批准**:审批入口本就有幂等双保险,重复触发不会重复置状态;置不可售动作本身也幂等,
|
||||||
|
班期已是不可售状态时直接返回当前值、不重复写。
|
||||||
|
|
||||||
|
## 六.6、修改前后对比
|
||||||
|
|
||||||
|
| 维度 | 改前 | 改后 |
|
||||||
|
|------|------|------|
|
||||||
|
| 流团批准后产品域班期状态 | 停在「报名中」 | 自动置「取消中」/「已取消」 |
|
||||||
|
| 小程序日历该出发日 | 可选 | `isSelectable=false` |
|
||||||
|
| 已流团团期再下单 | **成功建单**(实测 `code=200`,订单号 `HL20260907222635897`) | 被拒:581028(正常)或 589551(同步失败兜底) |
|
||||||
|
| 运营收尾动作 | 必须另外去产品后台手动取消该班期 | 系统自动完成;仅同步失败时才需人工兜底 |
|
||||||
|
| 审批接口出入参 | — | **完全不变** |
|
||||||
|
| 下单接口出入参 | — | **完全不变**,仅多一个错误码 |
|
||||||
|
|
||||||
|
## 六.7、影响评估
|
||||||
|
|
||||||
|
- **前端**:无接口结构变化,不改也不报错;两项待办是「补录 589551 文案」与「知悉班期会显示取消中/已取消」。
|
||||||
|
- **后端**:`hl-order-service-v3` 与 `hl-product-service-v2` **必须一起部署**,
|
||||||
|
且**上线顺序为 product-v2 先、order-v3 后**(回滚反序)——order-v3 的新调用依赖 product-v2 本次新增的内部端点。
|
||||||
|
- **运营**:流团审批期间该班期下单会短暂阻塞(秒级),属预期行为;
|
||||||
|
团期流水出现「班期置不可售失败」时,需到产品后台对该班期手动执行取消。
|
||||||
|
- **数据**:存量已流团但班期仍显示报名中的团期**不会被自动修复**,需要时由运营手动取消该班期。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、不影响范围
|
||||||
|
|
||||||
|
- 产品状态、产品上下架、同产品其它班期:一字未动。
|
||||||
|
- 流团审批的响应结构、错误码集合、权限规则:全部不变。
|
||||||
|
- 下单接口的入参与成功响应结构:全部不变。
|
||||||
|
- 流团取消哪些子订单、退款金额口径:不变(已付款户仍不被自动取消,见 #7294)。
|
||||||
|
- 定制师提需求、团期成团、调名额、转期等其它团期动作:不变。
|
||||||
|
- 取消成团(资源准备中回退到招募中):不联动置班期不可售——班期本就应继续可售。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、测试环境已验证
|
||||||
|
|
||||||
|
测试服 `https://api.test.1814.love:9443`,order-v3 与 product-v2 已一起部署。工单 AC-1~AC-18 全部逐条取证:
|
||||||
|
|
||||||
|
| 项 | 结果 |
|
||||||
|
|---|---|
|
||||||
|
| 改前对照(部署本次改动前) | 流团批准后班期仍 `ENROLLING`,再下单 `code=200` 成功建单 `HL20260907222635897` |
|
||||||
|
| 流团后班期状态 | 产品域「取消中」、订单域「已取消」 |
|
||||||
|
| 作用域未越界 | 同产品 17 条班期只有目标那条变化,产品状态仍为已上架 |
|
||||||
|
| 展示层不可选 | 班期日历状态「已取消」;小程序价格日历该出发日 `isSelectable=false` |
|
||||||
|
| 改后下单 | `code=581028` |
|
||||||
|
| 硬闸兜底 | 手工把班期改回报名中后下单 → `code=589551`,无新增订单、名额未变动 |
|
||||||
|
| 幂等 | 连续两次置不可售均 200 且结果相同,第二次未产生任何写入 |
|
||||||
|
| 班期不存在 | 返 `code=200` + `data=null`,由调用方按同步失败告警 |
|
||||||
|
| 同步失败留痕 | 审批仍 200、订单域仍「已取消」、ERROR 日志含两个 ID、状态流水出现 DATA 类「班期置不可售失败…」记录 |
|
||||||
|
| 并发正确性 | 真实 MySQL 并发集成测试:流团先提交 → 创单被拒且整单回滚;创单后提交 → 被流团一并取消;相反加锁顺序产生的真实死锁下,创单侧整单回滚、无脏订单 |
|
||||||
|
| 隔离级别 | 测试库实测 `REPEATABLE-READ` |
|
||||||
|
| 全量回归 | order-v3 全量单测 9008 例 0 失败 0 错误;product-v2 1592 例 0 失败 0 错误 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、相关文档
|
||||||
|
|
||||||
|
- Issue: #7283
|
||||||
|
- PR: #7303
|
||||||
|
- 后续工单: #7294(流团不取消已付款子订单)、#7293(库存聚合降级改写满员班期)、#7284(下单校验改判实时库存)、#7304(团期状态流水只读端点)
|
||||||
|
- 设计文档: `docs/order-v3/notes/14-group-tour-order-design.md` 流团章节
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
- 后端: jw
|
||||||
|
- 前端: mmg(hl-ui / 小程序)
|
||||||
|
- 业务口径: wx
|
||||||
在新工单中引用
屏蔽一个用户