文件
hl-api-changelog/changelogs-v2/2026-09/12_7513_团期转期同步回写归属两列-修改接口-管理后台.md
Mimingguang 10502ccdd3
changelog-filename-gate / validate (push) Failing after 2s
chore(changelog): 补回写漏网 8 条(7510×2/7511/7512 verified+not_required;7513/7530/7531/7535 not_required)
7510/7511/7512 代码早已随供应商关系系列交付(a17a2c2a/4bedc8be),实证后回写 verified 挂 a17a2c2a;
11_7510 为正本 12_7510 的重复副本(canonical_path),标 not_required 消除 pending;
7513/7530/7531/7535 实证前端零改动 not_required。
2026-09-13 09:51:52 +08:00

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 7513 团期转期同步回写归属两列(group_batch_id 不再停留源期)+ 在途退单转期守卫 589589 admin jw(GIT) 修改接口 deployed verified not_required mmg 2026-09-12 转订单端点的请求/响应结构一行未动,前端无需改造即可继续运行。两处需要知道:①转期后订单详情的团期编号/名称会正确显示为【目标期】(此前错显源期,属修复非回归);②新增拒绝码 589589——该户有在途退单审核时转期会被拒,前端需按普通业务错误弹提示,文案后端已给全。 前端 2026-09-13 闭环 not_required:转期 TransferOrderModal 纯透传,589589 走拦截器通用业务码兜底弹后端原文;订单详情团期编号/名称由后端按 group_batch_id 解析下发回写后自动正确;历史脏数据归后端 #7586。零业务代码改动。 2026-09-13 dev-v3

order-v3: 团期转期同步回写归属两列

服务: hl-order-service-v3 PR: #7589 Issue: #7513


⚠️ 关键变化

🟢 请求与响应结构零改动。 transfer-in 的 path、body、Result<TransferSubOrderRespVO> 的字段名/类型/null 语义一行没动,前端不改任何代码即可继续工作。

🔴 转期后订单的团期归属显示会变:此前转期只改了 order_main.product_batch_id、没改 order_main.group_batch_id,导致同一订单两列指向两个不同的期。凡按 group_batch_id 取团期编号/名称的展示位(订单详情、聊天会话归属等)此前显示的是源期。本次修复后显示目标期。这是纠错,不是回归——如果 QA 对比历史截图发现团号变了,是本次修好了。

🔴 新增拒绝码 589589:该户存在在途(PENDING)退单审核时,转期会被直接拒绝。

{"code": 589589, "message": "该户有退单审核在途,请先在原团期驳回或完成审核后再转期", "success": false}

前端按普通业务错误弹 message 即可,文案已含出口指引,无需自造提示。 为什么加:此前「先提退单 → 被转走 → 审批通过」这条路能跑通,但跑出来是错账——按源期阶段算退款、时间线记源期,而人数扣减按 product_batch_id 扣到目标期,钱与账落在两个团,接口却返回成功。修好归属两列后,这种审批会在归属校验处抛 589512,那张申请再也批不掉;故把冲突提前到转期入口,给运营看得懂的提示与出口。

🔴 历史脏数据本次不修:本次只保证新发生的转期两列一致。修复上线之前已转过期的订单,两列仍分叉,需 #7586 单独处理(不能与本次同版发迁移,理由见该单)。


一、背景

转订单功能(#7095)合入于 2026-09-04 17:32,而 order_main.group_batch_id 这一「一跳直连」列由 #7083 于同日 18:09 才加上——相差 37 分钟,两个工单并行落地,此后无人回头补转期链路的回写。

后果比「两列不一致」更重:团期在团名册走的是「一跳列 ∪ 两跳列(且一跳为 NULL)」的双通道并集读,回退通道带 group_batch_id IS NULL 硬闸。转期户该列非空被排除,于是在目标期的在团名册里整体消失(房务需求、整团需求确认、团期核单、用车派单全看不到),同时仍留在源期;而人数计数走 product_batch_id 已正确搬走——「人数」与「成员名单」互相矛盾。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 转订单(子订单跨期转入) POST /v3/admin/order/group-batch/:groupBatchId/sub-order/:orderId/transfer-in 修改接口 请求/响应结构不变;写库时同步回写归属第二列,并新增拒绝码 589589

三、接口详情

1. 转订单(子订单跨期转入) POST /v3/admin/order/group-batch/:groupBatchId/sub-order/:orderId/transfer-in

VO: TransferSubOrderReqVO → Result<TransferSubOrderRespVO>

使用场景

团期管理员把同一产品其他期的一个子订单转入本期:不退钱、订金跟人走、尾款按目标期价重算、不可跨产品转。path 上的 :groupBatchId 是转入期,源期在 body 的 fromGroupBatchId。

入参

字段 位置 类型 必填 约束 说明
groupBatchId path Long 是 团期主订单 ID,非产品侧班期 ID 转入期
orderId path Long 是 须为源期下的在团子订单 待转入的子订单
fromGroupBatchId body Long 是 团期主订单 ID;须与订单当前排期一致 源期
reason body String 否 ≤512 字 转期原因;不传记「管理员手动转期」
notify body Boolean 否 — 转入后是否通知客户;只落标志位,不实发

本次未改动任何入参。

出参 Result<TransferSubOrderRespVO>

字段 类型 说明
orderId String 子订单 ID
fromBatchNo / toBatchNo String 源期 / 目标期期号
oldOrderAmount / newOrderAmount BigDecimal 转期前 / 后应收
paidAmount BigDecimal 已付(转期不动钱)
newBalanceDue BigDecimal 新待收尾款
warnings String[] 非阻断提示

本次未改动任何出参字段。

请求示例

POST /v3/admin/order/group-batch/2096412454643802114/sub-order/2097498673159028737/transfer-in
Content-Type: application/json

{
  "fromGroupBatchId": 2097498512387104770,
  "reason": "客户改期,转到第 7 期",
  "notify": false
}

响应示例

{
  "code": 200,
  "message": "成功",
  "data": {
    "orderId": "2097498673159028737",
    "fromBatchNo": "Q202611202097498511120478209",
    "toBatchNo": "Q202610012052935476548939777",
    "oldOrderAmount": 1000.0,
    "newOrderAmount": 3425.0,
    "paidAmount": 0.0,
    "newBalanceDue": 3425.0,
    "warnings": []
  },
  "success": true
}

空数据 / 降级响应

  • 目标期班期天数取不到(产品域 Feign 失败)时不阻断:订单沿用原天数,warnings 追加「目标团期行程天数未取到,订单沿用原天数,请人工核对」,code 仍为 200。
  • 本接口无「空数据」形态:要么整体成功,要么抛业务错误码,不返回半截数据。

错误响应

code message 何时出现 本次变化
589589 该户有退单审核在途,请先在原团期驳回或完成审核后再转期 该子订单存在 PENDING 退单审批 🆕 本次新增
589510 团期状态不允许转期 源期或目标期不在「招募中 / 资源准备中」 不变
589512 该订单不属于本团期 源期与订单当前排期不一致 不变
589560 该户在源期已分房,不可转期({0} 条) 源期已有生效分房行 不变
589527 获取目标团期报价失败,请稍后重试 目标期报价取不到或非正 不变
589528 目标团期价低于该户已付金额,需先走退款流程 目标期价 < 已付 不变

本次新增的 589589 实际响应体(TEST 网关实测原样):

{
  "code": 589589,
  "message": "该户有退单审核在途,请先在原团期驳回或完成审核后再转期",
  "data": null,
  "success": false
}

业务边界

  • 单单语义:一次调用只转一个子订单,请求体内没有订单列表。
  • 幂等:按 orderId 5 秒防重;同时按两期 ID 升序各下一次行栅栏,避免两期互转死锁。
  • 可转订单状态白名单:PENDING_PAY / PENDING_DEPARTURE。
  • 可转团期状态白名单:RECRUITING / RESOURCE_PREPARING。因此已出账 / 已配房 / 已出行的团在守卫层就转不了。

四、契约约束与正确调用方式

✅ 正确 / ❌ 错误 payload 对照

❌ 错误——把源期写在 path 上(这是最容易搞反的一处):

POST /v3/admin/order/group-batch/{源期ID}/sub-order/{orderId}/transfer-in
{"fromGroupBatchId": 目标期ID}

✅ 正确——path 是转入期,body 是源期:

POST /v3/admin/order/group-batch/{目标期ID}/sub-order/{orderId}/transfer-in
{"fromGroupBatchId": 源期ID}

两个 ID 都是团期主订单 ID(groupBatchId),不是产品侧班期 ID(productBatchId)。一个接口不混用两套口径。

切换状态时的必要动作

前端收到 589589 时的正确引导:先在源期把该户的退单申请驳回(驳回路径不校验归属,任何时候都可用),再重新发起转期;或等该退单审批走完(走完即离团,也就不需要转期了)。不要引导用户去目标期重新提交退单——会被 589529「该户已有退单审核在途」拦。


五、数据库行为

表 列 变更
order_main product_batch_id 转期时改写为目标期排期 ID(行为不变)
order_main group_batch_id 🆕 本次起同步改写为目标期团期 ID;此前不写,停留源期

两列在同一条 UPDATE、同一个事务内落库(转期方法整体 @Transactional(rollbackFor = Exception.class)),不存在「只更新了一列」的中间态。未新增 SQL 语句、未加表、未加列、无 DDL。

group_batch_transfer 转期流水表行为不变,仍逐次留痕。


六、边界行为

  • 多次转期:每次转期两列同步搬到当次目标期,流水表逐条累积,不做合并。
  • 目标期 ID 不可能为空:目标期对象按主键查出,查不到直接抛「团期不存在」,因此不存在「把归属清成 null」的路径。
  • 归属守卫仍比 product_batch_id(不比 group_batch_id):这样 group_batch_id 尚未回填的历史订单仍能正常转期,不会被挡死。
  • 人数计数(enrolled)口径不变:仍按显式 ±N 搬移,本次未改。
  • 在途退单守卫只拦 PENDING:已通过 / 已驳回 / 已撤销的审批行不影响转期。

六.6、修改前后对比

字段级对比

数据位 修复前 修复后
order_main.product_batch_id 目标期排期 目标期排期(不变)
order_main.group_batch_id 停留源期 ❌ 目标期 ✅

行为级对比

场景 修复前 修复后
目标期在团名册是否含该户 否(被两跳回退的空值闸排除) 是
源期在团名册是否仍含该户 是(错误滞留) 否
订单详情展示的团期编号 源期(错) 目标期
该户有在途退单时转期 允许,且后续审批产生错账且返回成功 拒绝 589589,给出口
每日对账 inconsistent 指标 每转一次 +1,永不归零 新转期不再增加

六.7、影响评估

受影响面 影响 需要前端动作
transfer-in 请求/响应结构 无变化 无
转期后团期编号/名称展示 由源期改为目标期(纠错) 无,但 QA 对比历史截图时需知情
转期错误码集合 新增 589589 有:按普通业务错误弹 message
房务需求 / 整团需求 / 团期核单 / 用车派单的在团名册 转期户从此正确出现在目标期 无(后端口径修正)

七、不影响范围

  • 不影响任何接口的请求参数、响应字段名/类型/null 语义。
  • 不影响判团读写法:本次一行未动各处「这张订单属于哪个团」的判据写法,只保证两列数据一致。
  • 不影响退款金额计算、退改政策、订金/尾款口径。
  • 不影响人数计数(enrolled)的搬移口径。
  • 不影响小程序端:本次改动不涉及 mp 链路。
  • 不影响历史已转期订单的存量数据(见 #7586)。
  • 无 DDL、无新增表/列、无网关路由变更、无 Feign 契约变更、无 MQ 变更。

八、测试环境已验证

部署:hl-order-service-v3 ← 分支 fix/7513-transfer-groupbatchid,双实例滚动,2026-09-12 17:01 在线。取证经真实网关 api.test.1814.love + TEST 库直查。

# 用例 结果
1 真实转期 transfer-in code 200,金额 1000.0 → 3425.0
2 转期后两列归属 group_batch_id 与 product_batch_id 同指目标期(SQL 判定 CONSISTENT)
3 全局两列分叉计数 转期前 1 → 转期后 1(新转期未制造新分叉;那 1 条是历史脏行,归 #7586)
4 在团名册(双通道并集口径,经 requirement-summary 的 activeOrderCount) 目标期 56→55、源期 3→4,名册随转期正确移动
5 五种判团形态一致性 A / B / C / D / E 五种口径全部指向同一个团
6 人数计数 源期 6→5、目标期 110→111
7 589589 负向 先提交退单(200)→ 转期得 589589,文案一致
8 589589 逃生口 驳回退单(200)→ 再转期得 200,确认是卡壳不是死锁
9 全量单测 + ArchTest 基线 10132/0/13/49 → 本分支 10135/0/13/49,仅 +3(本次新增用例),零新增失败;13 条 Errors 两边逐类一致,均为本机无 Docker 的 Testcontainers 类

取证用的既有订单已转出后转回,两列、金额、两侧人数、分叉计数全部回到基线值。


九、相关历史 PR

  • #7095 转订单功能初版(V20260904_002,2026-09-04 17:32)——设计时 order_main.group_batch_id 尚不存在
  • #7083 新增 order_main.group_batch_id 一跳直连列(V20260904_004,同日 18:09)——回填了当时存量、接管创单写入,但未回头改转期链路

十、相关文档

  • 工单 #7513(缺陷与口径定案)
  • 工单 #7586(存量数据修复,代码全量滚完后执行)
  • docs/group/团期模块详细设计-v3.0.html — 归属列写入时机与一致性约束的冻结口径

关联 / 联系人

链接

  • Issue: #7513
  • PR: #7589
  • 后续单: #7586

联系人

  • 后端: jw
  • 前端: mmg