Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
14 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 | 7946 | 团期转订单收紧为只允许招募中(未成团)团期之间转 | admin | jw(GIT) | 修改接口 | deployed | verified | pending | mmg | 团期转订单 POST .../sub-order/{orderId}/transfer-in 与候选列表 GET .../transfer-candidates 的可转团期状态由「招募中 + 资源准备中」收紧为只允许招募中(未成团),源期与目标期都适用;同产品限制不变。入参、出参、路径均不变,589510 文案改为「须为招募中」。后端已合并 dev-v3(843e81712)并部署 TEST,网关实测 7/7 通过。前端待办(一行):团期详情「更多操作 → 转订单」入口白名单 canTransferOrder 由 ['RECRUITING','RESOURCE_PREPARING'] 改为 ['RECRUITING'],见第四节。 | 2026-09-18 | dev-v3 |
order-v3: 团期转订单收紧为只允许招募中(未成团)团期之间转
存放目录:
changelogs-v2/{YYYY-MM}/(管理后台,二期 order-v3)服务: hl-order-service-v3 (端口 8086) PR: #7948 Issue: #7946 日期: 2026-09-18 影响范围: 团期详情「更多操作 → 转订单」弹窗
⚠️ 关键变化
- 只有招募中(未成团)的团期之间才能转订单:订单所在的原团期、要转入的本团期,两个都必须是招募中。
- 已成团的团期(资源准备中及之后)既不能转出,也不能转入:候选列表里不再出现这些团期的订单;本团期已成团时候选列表直接为空。
- 同一产品的限制不变;入参、出参、路径都不变。
- 前端待办(一行):转订单入口的显示条件改为只在招募中显示,见第四节。
一、背景
转订单(#7095)原来允许在「招募中」和「资源准备中」两个状态之间转。资源准备中其实就是已成团(看板「已成团」这一栏 = 资源准备中 + 物料准备中),成团后该户已进入资源采购与配房,业务要求不再跨团期挪动。jw 2026-09-18 定案:只允许招募中。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 转订单(子订单跨期转入) | POST | /v3/admin/order/group-batch/{groupBatchId}/sub-order/{orderId}/transfer-in |
修改 | 源期、目标期都须为招募中,否则 589510 |
| 2 | 转订单候选子订单列表 | GET | /v3/admin/order/group-batch/{groupBatchId}/transfer-candidates |
修改 | 本期非招募中返回 [];候选只来自同产品其他招募中团期 |
三、接口详情
1. 转订单(子订单跨期转入) POST /v3/admin/order/group-batch/{groupBatchId}/sub-order/{orderId}/transfer-in
VO: Result<TransferSubOrderRespVO>(不变)
使用场景
团期详情「更多操作 → 转订单」弹窗,选中候选订单后确认:把同一产品另一个团期的一户转到本团期。不退钱、订金跟人走、尾款按本团期价格重算。
入参
本次入参不变。
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | Path | Long | ✅ | 转入期(本期)团期 ID | 须为招募中 |
| orderId | Path | Long | ✅ | 待转入的子订单 ID | 订单状态须为待支付 / 待出行 |
| fromGroupBatchId | Body | Long | ✅ | 源期团期 ID | 须为招募中,且与本期同一产品 |
| reason | Body | String | 否 | ≤512 字 | 不传记「管理员手动转期」 |
| notify | Body | Boolean | 否 | 缺省 true | 是否通知客户 |
出参
本次出参不变。
| 字段 | 类型 | 说明 |
|---|---|---|
| orderId | String | 子订单 ID |
| fromBatchNo | String | 源期期号 |
| toBatchNo | String | 目标期期号 |
| oldOrderAmount | Number | 转期前应收 |
| newOrderAmount | Number | 转期后应收(按目标期价格重算) |
| paidAmount | Number | 已付(转期不动钱) |
| newBalanceDue | Number | 转期后待收尾款 = 新应收 − 已付 |
| warnings | List | 非阻断提示 |
请求示例
POST /v3/admin/order/group-batch/2100866001561128962/sub-order/2100866000617414658/transfer-in
Authorization: Bearer <token>
Content-Type: application/json
{ "fromGroupBatchId": "2100866000667746306", "reason": "#7946 验收", "notify": false }
响应示例
(测试服真实响应,2026-09-18,两个团期都是招募中)
{
"code": 200,
"message": "成功",
"data": {
"orderId": "2100866000617414658",
"fromBatchNo": "Q202705102100866000156069890",
"toBatchNo": "Q202705172100866000994930690",
"oldOrderAmount": 6000.0,
"newOrderAmount": 6000.0,
"paidAmount": 0.0,
"newBalanceDue": 6000.0,
"warnings": []
},
"success": true
}
空数据 / 降级响应
写接口,无空数据形态。被拒时整笔回滚:订单归属、两个团期的报名人数、产品库存、转期流水都不变。
{ "code": 589510, "message": "转订单时源期或目标期状态不满足条件(须为招募中)", "data": null, "success": false }
错误响应
(测试服真实响应:源期已成团)
{ "code": 589510, "message": "转订单时源期或目标期状态不满足条件(须为招募中)", "data": null, "success": false }
| code | 触发条件 |
|---|---|
| 589510 | 源期或目标期不是招募中(本次收紧:资源准备中也会触发;文案改为「须为招募中」) |
| 589525 | 源期与目标期不是同一产品(不变) |
| 589524 | 源期与目标期是同一团期(不变) |
| 589512 | 子订单不属于源期(不变) |
| 589501 | 订单状态不可转(不变) |
| 589560 | 该户已在源期分到房(不变) |
业务边界
- 状态判断在读订单之前完成(事务里只先对两个团期各取一次行锁);被拒时整笔回滚,净写入为零。
- 同产品、订单状态、容量、价格不低于已付等其余守卫全部不变。
- 已分房守卫(589560)保留:用于「取消成团回到招募中」但源期仍残留分房行的情形。
2. 转订单候选子订单列表 GET /v3/admin/order/group-batch/{groupBatchId}/transfer-candidates
VO: Result<List<TransferCandidateVO>>(不变)
使用场景
转订单弹窗的搜索下拉:列出可以转入本团期的订单。
入参
本次入参不变。
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | Path | Long | ✅ | 转入期(本期)团期 ID | 非招募中时直接返回 [] |
| keyword | Query | String | 否 | 手机号须整串 | 订单号 / 客户名 / 期号 / 手机号 |
出参
本次出参不变。
| 字段 | 类型 | 说明 |
|---|---|---|
| orderId | String | 子订单 ID |
| orderNo | String | 订单号 |
| customerName | String | 客户名 |
| peopleCount | Integer | 人数 |
| paidAmount | Number | 已付 |
| fromGroupBatchId | String | 所在源期团期 ID(只会是招募中的团期) |
| batchNo | String | 源期期号 |
| batchName | String | 源期名称 |
| departDate | String | 源期出发日 |
请求示例
GET /v3/admin/order/group-batch/2100866001561128962/transfer-candidates
Authorization: Bearer <token>
响应示例
(按测试服验收时的真实值整理,只列本单造数那一条;同产品已成团团期 C 的订单已不在列表中)
{
"code": 200,
"message": "成功",
"data": [
{
"orderId": "2100866000617414658",
"orderNo": "HL20260918163421351",
"customerName": "测试七九四六A",
"peopleCount": 2,
"paidAmount": 0.0,
"fromGroupBatchId": "2100866000667746306",
"batchNo": "Q202705102100866000156069890",
"batchName": "#7946-A",
"departDate": "2027-05-10"
}
],
"success": true
}
空数据 / 降级响应
本团期已成团(资源准备中及之后)时,候选直接为空(测试服真实响应,改前同一团期返回 9 条):
{ "code": 200, "message": "成功", "data": [], "success": true }
错误响应
{ "code": 589500, "message": "团期不存在", "data": null, "success": false }
| code | 触发条件 |
|---|---|
| 589500 | 本期团期不存在(不变) |
| 401 | 未登录(网关拦截) |
业务边界
- 本期非招募中 →
[],不查订单。 - 源期只取同产品、招募中、非本期的团期;已成团团期的订单不再出现。
- 关键词匹配规则不变。
四、契约约束与正确调用方式
前端需要改的一行
hl-ui v2.1:src/views/order-v2/batch/detail/index.vue 约第 657 行
// 改前
const canTransferOrder = computed(() =>
['RECRUITING', 'RESOURCE_PREPARING'].includes(detail.value?.batchStatus))
// 改后
const canTransferOrder = computed(() => detail.value?.batchStatus === 'RECRUITING')
不改的后果:资源准备中的团期仍会显示「转订单」入口,点开后候选列表永远是空的,运营会以为「搜不到单」。不会转错数据(后端已拦)。
✅ 正确 / ❌ 错误 用法
| 场景 | 做法 |
|---|---|
| ✅ 决定是否显示转订单入口 | 只在 batchStatus === 'RECRUITING' 时显示 |
| ✅ 589510 的提示 | 直接展示后端 message「须为招募中」 |
| ❌ 在资源准备中团期显示转订单入口 | 候选恒为空,点了没用 |
| ❌ 前端自己拼「须为招募中/资源准备中」的提示文案 | 旧口径,已作废 |
五、数据库行为
- 无表结构变更,无 Flyway。
- 转入成功时的写入与改前完全相同:订单改归属到目标团期并刷新出发 / 返程日期,两个团期报名人数一增一减,记一条转期记录,两个团期时间线各记一条。
- 本次只是把一部分请求提前拒掉:被拒时事务在状态守卫处整笔回滚,零写入(测试服已核对订单、两期报名人数、转期流水均未变化)。
六、边界行为
- 源期、目标期都招募中且同产品 → 成功。
- 源期资源准备中(已成团)→ 589510,零写入。
- 目标期资源准备中(已成团)→ 589510,零写入。
- 物料准备中及之后 → 589510(与改前相同)。
- 跨产品 → 589525(与改前相同,先于状态判断)。
- 候选列表:本期非招募中 →
[];本期招募中 → 只含同产品其他招募中团期的订单。
六.6、修改前后对比
字段级对比
| 项 | 改前 | 改后 |
|---|---|---|
| 入参 / 出参 / 路径 | — | 不变 |
| 589510 文案 | 须为招募中/资源筹备中 | 须为招募中 |
行为级对比
| 行为 | 改前 | 改后 |
|---|---|---|
| 可转团期状态 | 招募中、资源准备中 | 只有招募中 |
| 本期资源准备中的候选列表 | 同产品这两种状态其他团期的订单 | [] |
| 源期资源准备中的订单 | 出现在候选中,可转 | 不出现,转入被 589510 拒 |
六.7、影响评估
- 是否破坏向后兼容: 结构上兼容(字段不变);行为上收紧,原来能在资源准备中团期之间转的现在会被拒。
- 前端是否必须同步上线: 否,不改也不会出错;但需要前端改那一行,入口才不会出现在已成团的团期。
- 回滚: 回滚 PR #7948 即可,无数据变更。
七、不影响范围
- 仅影响: 上述两个接口的可转状态判断与 589510 文案。
- 零影响: 退单户(GB-ADM-070)、成团 / 取消成团、调整满团名额、发起流团;转订单的金额计算、库存、配房联动、行程与用车重排;数据库结构。
八、测试环境已验证
被测版本:hl-order-service-v3 = dev-v3 843e81712(2026-09-18 17:09 部署,双实例滚动完成),网关实测 7/7 通过。
造数:同一产品建三个班期 A / B(招募中)、C(经成团接口推到资源准备中),各下一张真实订单。
改前(部署前,同一批数据)
候选 本期=C(资源准备中) → 9 条(含 A、B 的单)
候选 本期=B(招募中) → 含 C(资源准备中) 的单
改后
AC-4 候选 本期=C → [];候选 本期=B → 含 A 单、不含 C 单 ✓
AC-2 C(资源准备中)→B → 589510「须为招募中」,订单 / 两期报名人数 / 转期流水零变化 ✓
AC-3 A→C(资源准备中) → 589510,零变化 ✓
AC-5 跨产品源期→B → 589525,零变化 ✓
AC-1 A→B(均招募中) → 200;订单归属与出发日改到 B;报名人数 A 2→0、B 2→4;流水 1 条 ✓
本地:GroupBatchTransferServiceTest 50/0/0(新增 5 例)、GroupBatchTransferDayConfirmConcurrencyIT 3/0/0(真实 MySQL);全量 Half A 7955/0/0、Half B 3967/F1(MapperBoundaryArchTest dev-v3 既存,与本次无关)。
验收后已清理:三张订单取消,三个班期在产品侧取消(CANCELLED);订单侧团期行不随产品侧取消同步,停在原状态。
十、相关文档
- 关联 Issue: wx/HL#7946
- 关联 PR: wx/HL#7948
- 接口文档:
docs/group/团期模块接口文档-v2.0.htmlGB-ADM-071 小节与错误码表已同步