15 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 7283 | 流团批准后自动把对应班期置为不可售,并给已流团团期的下单补 589551 硬闸 | multiple | jw(GIT) | 修改接口 | deployed | verified | not_required | mmg | 2026-09-08 | 后端已部署测试服并逐条实测(AC-1~AC-18 全过)。前端待办两项:①错误码字典补录 589551「该团期已流团,不可下单」;②知悉流团批准后该班期在小程序日历/班期列表会变为不可选、状态文案显示「取消中/已取消」。接口出入参结构零变化,前端不改也不会报错。 | 2026-09-08 | dev-v3 |
团期: 流团后班期自动不可售 + 下单侧 589551 硬闸
服务: hl-order-service-v3、hl-product-service-v2(必须一起部署) PR: #7303 Issue: #7283 日期: 2026-09-08 影响范围: 团期流团审批、GROUP 产品下单(管理后台代客下单 + 小程序下单)、小程序班期日历
⚠️ 关键变化
出入参结构一个字段都没改。 前端不改不报错,但有两件事必须知道:
- 流团批准后,该班期会自动变为不可售——小程序日历上该出发日
isSelectable=false, 班期状态文案显示「取消中」或「已取消」。这是现有枚举字典的限制,不是产品下架, 同产品的其它班期不受任何影响。 - 已流团团期再下单会被拒,新增错误码 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 | 执行后回填的实际退款合计 |
| 其余全部字段 | — | 完全不变 |
请求示例
POST /v3/admin/order/group-batch/disband/2096968377787392001/approve HTTP/1.1
Authorization: Bearer <admin-token>
Content-Type: application/json
{"remark":"确认无法成团"}
响应示例
{
"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:…)」,
由运营到产品后台对该班期手动执行取消收尾。
{
"code": 200,
"message": "成功",
"success": true,
"data": { "approvalId": "2097162345619902466", "approvalStatus": "APPROVED" }
}
错误响应
{
"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 等 |
| 其余全部字段 | — | 完全不变 |
请求示例
{
"productId": "2056947670512971778",
"tierSeq": 1,
"departureDate": "2026-12-18",
"adultCount": 2,
"childCount": 0,
"customerName": "张三",
"customerPhone": "13900000000",
"productBatchId": "2096971118245015554",
"roomCount": 1
}
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"id": "2096971120677736450",
"orderNo": "HL20260907223729183",
"orderStatus": "PENDING_PAY",
"totalAmount": "6000.00"
}
}
空数据 / 降级响应
无空数据形态。产品域同步失败导致班期仍显示可选时,下单仍会被本地硬闸拒掉(返 589551), 不会漏出一笔挂在已解散团期上的订单。
错误响应
班期已成功置为不可售时(正常路径):
{
"code": 581028,
"message": "团期状态不允许报名",
"success": false,
"data": null
}
班期置不可售失败、产品域仍显示报名中时(兜底路径):
{
"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