diff --git a/changelogs-v2/2026-09/18_7946_团期转订单收紧为只允许招募中团期之间转-修改接口-管理后台.md b/changelogs-v2/2026-09/18_7946_团期转订单收紧为只允许招募中团期之间转-修改接口-管理后台.md new file mode 100644 index 00000000..fb873bb7 --- /dev/null +++ b/changelogs-v2/2026-09/18_7946_团期转订单收紧为只允许招募中团期之间转-修改接口-管理后台.md @@ -0,0 +1,369 @@ +--- +schema: "hl-changelog/v2" +ticket: "7946" +title: "团期转订单收紧为只允许招募中(未成团)团期之间转" +consumer: "admin" +author: "jw(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "mmg" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "团期转订单 POST .../sub-order/{orderId}/transfer-in 与候选列表 GET .../transfer-candidates 的可转团期状态由「招募中 + 资源准备中」收紧为只允许招募中(未成团),源期与目标期都适用;同产品限制不变。入参、出参、路径均不变,589510 文案改为「须为招募中」。后端已合并 dev-v3(843e81712)并部署 TEST,网关实测 7/7 通过。前端待办(一行):团期详情「更多操作 → 转订单」入口白名单 canTransferOrder 由 ['RECRUITING','RESOURCE_PREPARING'] 改为 ['RECRUITING'],见第四节。" +updated_at: "2026-09-18" +base: "dev-v3" +--- + +# order-v3: 团期转订单收紧为只允许招募中(未成团)团期之间转 + +> **存放目录**: `changelogs-v2/{YYYY-MM}/`(管理后台,二期 order-v3) +> +> **服务**: hl-order-service-v3 (端口 8086) +> **PR**: #7948 +> **Issue**: #7946 +> **日期**: 2026-09-18 +> **影响范围**: 团期详情「更多操作 → 转订单」弹窗 + +--- + +## ⚠️ 关键变化 + +1. **只有招募中(未成团)的团期之间才能转订单**:订单所在的原团期、要转入的本团期,两个都必须是招募中。 +2. 已成团的团期(资源准备中及之后)**既不能转出,也不能转入**:候选列表里不再出现这些团期的订单;本团期已成团时候选列表直接为空。 +3. 同一产品的限制不变;入参、出参、路径都不变。 +4. **前端待办(一行)**:转订单入口的显示条件改为只在招募中显示,见第四节。 + +--- + +## 一、背景 + +转订单(#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`(不变) + +#### 使用场景 + +团期详情「更多操作 → 转订单」弹窗,选中候选订单后确认:把同一产品另一个团期的一户转到本团期。不退钱、订金跟人走、尾款按本团期价格重算。 + +#### 入参 + +本次入参**不变**。 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| 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 | 非阻断提示 | + +#### 请求示例 + +```http +POST /v3/admin/order/group-batch/2100866001561128962/sub-order/2100866000617414658/transfer-in +Authorization: Bearer +Content-Type: application/json + +{ "fromGroupBatchId": "2100866000667746306", "reason": "#7946 验收", "notify": false } +``` + +#### 响应示例 + +(测试服真实响应,2026-09-18,两个团期都是招募中) + +```json +{ + "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 +} +``` + +#### 空数据 / 降级响应 + +写接口,无空数据形态。被拒时整笔回滚:订单归属、两个团期的报名人数、产品库存、转期流水都不变。 + +```json +{ "code": 589510, "message": "转订单时源期或目标期状态不满足条件(须为招募中)", "data": null, "success": false } +``` + +#### 错误响应 + +(测试服真实响应:源期已成团) + +```json +{ "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>`(不变) + +#### 使用场景 + +转订单弹窗的搜索下拉:列出可以转入本团期的订单。 + +#### 入参 + +本次入参**不变**。 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| 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 | 源期出发日 | + +#### 请求示例 + +```http +GET /v3/admin/order/group-batch/2100866001561128962/transfer-candidates +Authorization: Bearer +``` + +#### 响应示例 + +(按测试服验收时的真实值整理,只列本单造数那一条;同产品已成团团期 C 的订单已不在列表中) + +```json +{ + "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 条): + +```json +{ "code": 200, "message": "成功", "data": [], "success": true } +``` + +#### 错误响应 + +```json +{ "code": 589500, "message": "团期不存在", "data": null, "success": false } +``` + +| code | 触发条件 | +|------|----------| +| 589500 | 本期团期不存在(不变) | +| 401 | 未登录(网关拦截) | + +#### 业务边界 + +- 本期非招募中 → `[]`,不查订单。 +- 源期只取同产品、招募中、非本期的团期;已成团团期的订单不再出现。 +- 关键词匹配规则不变。 + +--- + +## 四、契约约束与正确调用方式 + +### 前端需要改的一行 + +hl-ui `v2.1`:`src/views/order-v2/batch/detail/index.vue` 约第 657 行 + +```js +// 改前 +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](https://git.1814.love:8443/wx/HL/issues/7946) +- 关联 PR: [wx/HL#7948](https://git.1814.love:8443/wx/HL/pulls/7948) +- 接口文档:`docs/group/团期模块接口文档-v2.0.html` GB-ADM-071 小节与错误码表已同步 + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#7946](https://git.1814.love:8443/wx/HL/issues/7946) +- **PR**: [#7948](https://git.1814.love:8443/wx/HL/pulls/7948) +- **Merge commit**: [843e81712](https://git.1814.love:8443/wx/HL/commit/843e81712) + +### 联系人 + +- **后端负责人**: @jw +- **前端负责人**: @mmg