文件
hl-api-changelog/changelogs-v2/2026-09/06_7135_团期创单收紧productBatchId校验-修改接口-管理后台.md
jw d795a2f0bb
changelog-filename-gate / validate (push) Successful in 3s
docs(changelog): #7159 补齐 581034 网关端到端实测证据,作废「未实跑」旧记录
#7135/#7159 同 PR #7169 交付,本文档是 #7159 的口径落点。2026-09-06 记录的
「581034 线上端到端未实跑」已于 2026-09-07 09:2x 由网关实测补齐(12-01 期
maxParticipants 改 3 → adultCount=4 返 581034、=2 返 200 建单后取消复原),
原文表述已作废。同时补 Issue #7159 反链,并按前端交付实况回填 target_release。
2026-09-08 16:29:47 +08:00

20 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 7135 团期创单收紧 productBatchId 校验(班期归属产品 / 非 GROUP 拒收 / tierSeq 存在性 / 日期按班期) admin wx(GIT) 修改接口 deployed verified verified mmg 03a985b6 hl-ui@03a985b6 2026-09-06 服务端对 POST /v3/admin/order 新增三个校验错误码 581055/581056/581057,响应 departureDate/returnDate 改为落库值;前端新建订单向导须仅对 GROUP 产品传 productBatchId 且班期必须属于所选产品。 2026-09-08 dev-v3

订单模块:团期创单校验收紧(班期归属产品 / 非 GROUP 拒收 / tierSeq 存在性 / 日期按班期)

管理后台创单接口 POST /v3/admin/order 对 GROUP 产品的团期校验收紧,新增三个拒单错误码(581055/581056/581057),同时出发日期、返团日期改为班期的权威值。修复跨产品班期串号导致订单错误归团、CORE/CUSTOM 单被误命中团期逻辑等问题。

⚠️ 关键变化

  • 仅 GROUP 产品可传 productBatchId:CORE/CUSTOM 请求含 productBatchId → 拒单 581056「非团期产品不能指定团期」(改前静默落库并误命中团期分支)。
  • 班期必须属于所选产品:productBatchId 对应班期的 productId ≠ 请求 productId → 拒单 581055「所选团期不属于该产品」(改前会按别家班期计价并挂到别家的团)。
  • tierSeq 必须在产品配置内(全产品类型):不在 tierPrices ∪ tiers 并集中 → 拒单 581057「所选档位不存在」;产品未配档位时不拦(改前 tier_name 落 NULL)。
  • 响应 departureDate/returnDate 改为班期权威值:GROUP 单出发日 = 班期出发日;返团日 = 班期 endDate,或 班期出发日 + 行程天数 − 1(班期无 endDate 时)。改前响应回显请求日期,落库日期与班期脱钩。
  • GROUP 单响应 tierName 现有值:来自产品 tiers 配置,改前恒 null。
  • 人数超班期剩余名额拒单(581034)(#7159 并入本 PR):成人+儿童+小童 > 班期剩余名额 → 581034;此前订单侧读的 remainingSlots 字段在产品侧「库存改造 Phase 2」后已无来源、恒 null,致该预查长期 no-op,现改读产品侧权威字段 remainingParticipants(null=人数不限,跳过校验)。

二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 创建订单(管理端) POST /v3/admin/order 请求新增校验 / 响应日期改值 productBatchId 仅 GROUP 可传;班期归属;tierSeq 存在性;日期按班期

三、接口详情

1. 创建订单(管理端) POST /v3/admin/order

VO: OrderCreateReqVO → OrderCreateRespVO

使用场景

管理端新建订单向导或团期看板新增子订单调用。创建跟团游、定制游、线路游订单,GROUP 产品必须指定班期,CORE/CUSTOM 产品不允许指定班期。

入参

字段 位置 类型 必填 约束 说明
productId Body Long(String) ✅ 正整数 产品 ID
tierSeq Body Integer ✅ ≥ 1;必须存在于产品配置档位 档位序号,不在产品 tierPrices ∪ tiers 配置内 → 581057
departureDate Body yyyy-MM-dd ✅ 不早于当天;日期格式 出发日期;出发日早于今天 → 581011
adultCount Body Integer ✅ ≥ 1 成人数
childCount Body Integer 否 ≥ 0,默认 0 儿童数(5-12 岁)
youngChildCount Body Integer 否 ≥ 0,默认 0 小童数(3-4 岁)
babyCount Body Integer 否 ≥ 0,默认 0 婴儿数(0-2 岁)
customerName Body String ✅ @NotBlank 客户姓名
customerPhone Body String ✅ ^1[3-9]\d{9}$ 客户手机号(明文传,DB 层 AES 加密)
customerRemark Body String 否 ≤ 500 客户备注
createSource Body String 否 ≤ 20 字;枚举:CUSTOMER / CONSULTANT / OTA / WALK_IN / B2B / VIP_REPURCHASE / REFERRAL / PROMOTION / INTERNAL;默认 CONSULTANT 创建来源
productBatchId Body Long(String) 条件必填 仅 GROUP 产品可传;CORE/CUSTOM 传了 → 581056;班期 productId 必须等于请求 productId,否则 → 581055 团期 ID(product 侧班期 batchId);GROUP 产品必传,CORE/CUSTOM/ROUTE 禁传;值须属于请求 productId 对应产品
roomCount Body Integer 否 ≥ 1 房间数
tags Body List 否 无约束 订单标签名列表
sharerOpenid Body String 否 无约束 分享人 openid(C 端裂变追踪)
customizerId Body Long 否 无约束 分享归因定制师 ID

出参 Result<OrderCreateRespVO>

字段 类型 说明
data.id Long 订单主键(雪花 ID 序列化为 String)
data.orderNo String 订单号(创单瞬间生成,永不变)
data.orderStatus String 粗状态枚举(创单后 = PENDING_PAY)
data.orderStatusName String 粗状态中文名(创单后 = 待支付)
data.flowStatus String 细状态枚举值(创单后 = AWAITING_PAY)
data.flowStatusName String 细状态中文名
data.flowStep Integer 线性 6 步当前步序号(创单初态 = 0)
data.flowStepTotal Integer 线性 6 步总步数(固定 6)
data.flowStepName String 线性 6 步当前步中文名
data.consultantId Long 实际绑定定制师 ID(序列化为 String)
data.consultantSource String 定制师来源(DEFAULT_ASSIGNED / LINK_BOUND / MANUAL / SHARED)
data.tags List 系统自动打的标签
data.createdAt yyyy-MM-ddTHH:mm:ss 创单时间
data.productName String 产品名称
data.tierName String 档位名(v5.18 新增,GROUP 单现有值来自产品 tiers 配置,CORE/CUSTOM/ROUTE 仍为 null)
data.groupBatchName String 拼团批次名(仅供兼容,恒 null,勿读)
data.departureDate yyyy-MM-dd 出发日期(v5.18;改后 = 班期权威出发日,与请求值可能不同)
data.returnDate yyyy-MM-dd 返团日期(v5.18;改后 = 班期 endDate 或班期出发日 + 行程天数 − 1,与请求值可能不同)
data.totalAmount BigDecimal(String) 订单总价(元)
data.depositAmount BigDecimal(String) 建议定金金额(元)
data.depositRatio Integer 定金比例百分比;RATIO 模式有值,FIXED 模式为 null,FULL 模式 = 100
data.depositMode String 定金计算模式(FIXED / RATIO / FULL)
data.paymentMode String 支付模式(DEPOSIT / FULL)
data.expiryMinutes Integer 支付时限分钟数(默认 1440 = 24h)
data.payUrl String 支付页绝对 URL
data.customerName String 客户姓名(回显)
data.groupBatchId Long(String) 运营团期 ID(order 侧),非空 = 团订单,为空 = 普通订单,这是唯一判别
data.productBatchId Long(String) 团期产品排期 ID(product 侧 batchId,仅供溯源)
data.groupOrder Boolean 是否团订单(= groupBatchId 非空的派生值)

请求示例

{
  "productId": "2044306857534636034",
  "tierSeq": 1,
  "departureDate": "2026-10-01",
  "adultCount": 1,
  "childCount": 1,
  "youngChildCount": 0,
  "babyCount": 0,
  "customerName": "张三",
  "customerPhone": "13800009601",
  "createSource": "CONSULTANT",
  "productBatchId": "2052935476557328386"
}

响应示例(成功)

{
  "code": 200,
  "message": "成功",
  "data": {
    "id": "2096414445365338113",
    "orderNo": "HL20260906094527867",
    "orderStatus": "PENDING_PAY",
    "orderStatusName": "待支付",
    "flowStatus": "AWAITING_PAY",
    "flowStatusName": "待支付",
    "flowStep": 0,
    "flowStepTotal": 6,
    "flowStepName": "待支付",
    "consultantId": "50001234567890",
    "consultantSource": "DEFAULT_ASSIGNED",
    "tags": [],
    "createdAt": "2026-09-06T09:45:27",
    "productName": "冻干粉发短信给",
    "tierName": "标准档",
    "groupBatchName": null,
    "departureDate": "2026-10-01",
    "returnDate": "2026-10-03",
    "totalAmount": "5850.00",
    "depositAmount": "1000.00",
    "depositRatio": null,
    "depositMode": "FIXED",
    "paymentMode": "DEPOSIT",
    "expiryMinutes": 1440,
    "payUrl": "https://pay.hulalv.com/pay/HL20260906094527867",
    "customerName": "张三",
    "groupBatchId": "2096412454643802114",
    "productBatchId": "2052935476557328386",
    "groupOrder": true
  },
  "success": true
}

空数据 / 降级响应

创建接口无「空数据」场景(成功即返回订单对象)。降级路径:所依赖的产品服务 Feign 不可用时按错误码降级而非返回空——拉班期失败返 581027、拉报价失败返 581032,前端据 code 提示重试,不会返回 data=null 的成功包。

错误响应

出发日期早于今天

{
  "code": 581011,
  "message": "出发日期不能早于今天",
  "data": null,
  "success": false
}

档位不存在(tierSeq 未在产品配置内)

{
  "code": 581057,
  "message": "所选档位不存在",
  "data": null,
  "success": false
}

非团期产品不能指定团期

{
  "code": 581056,
  "message": "非团期产品不能指定团期",
  "data": null,
  "success": false
}

所选团期不属于该产品

{
  "code": 581055,
  "message": "所选团期不属于该产品",
  "data": null,
  "success": false
}

既有 GROUP 校验错误(团期缺失)

{
  "code": 581026,
  "message": "团期产品必须选择团期",
  "data": null,
  "success": false
}

参数校验错误(如手机号格式错误)

{
  "code": 400,
  "message": "手机号格式错误",
  "data": null,
  "success": false
}

业务边界

  • productBatchId 传值规则:仅 GROUP 产品可传且必传(除非为空表示不下单);CORE/CUSTOM/ROUTE 产品绝不能带。
  • 班期归属校验:productBatchId 对应班期的 productId 必须等于请求 productId;不同则拒单 581055,不会创建订单或部分落库。
  • tierSeq 校验:必须存在于产品的 tierPrices JSON(CORE/CUSTOM/ROUTE)或 tiers JSON(GROUP);产品未配任何档位时保持放行(存量产品兼容)。
  • 出发日期与返团日期:响应值为班期或产品的权威日期,可能与请求值不同。前端展示及后续行程渲染必须以响应值为准,不要缓存请求值。
  • 错误码优先级顺序:出发日期 581011 → 非 GROUP 拒收 581056 → tierSeq 581057 → GROUP 缺 productBatchId 581026 → 拉班期 581027 → 班期归属 581055 → 后续班期状态 / 截止 / 库存检查。
  • 鉴权:网关注入 X-Admin-Id 头,consultantId 无法解析返 581013。
  • 幂等性:客户端不得重试;同一 orderNo 存量幂等托管由 order-v3 内核保证。

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

场景 正确调用 错误调用 结果
GROUP 产品创单 传 productBatchId,值取价格日历 items[].batchId 不传 productBatchId 581026 团期产品必须选择团期
CORE 产品创单 不传 productBatchId / productBatchId = null 传 productBatchId(任何值) 581056 非团期产品不能指定团期
CUSTOM 产品创单 不传 productBatchId / productBatchId = null 传 productBatchId(任何值) 581056 非团期产品不能指定团期
档位选择 tierSeq 必须在产品已配档位内 tierSeq 超过产品最大档位序号 581057 所选档位不存在
班期串号防护 班期 batchId 必须属于所选 productId 前端用不同产品的 batchId 581055 所选团期不属于该产品
日期展示 使用响应 departureDate / returnDate 使用请求的出发日期 行程日期与班期脱钩,退款档位错位

五、数据库行为

操作 落库字段 说明
GROUP 创单(班期出发 2026-10-01,endDate 2026-10-03,tripDays=3) order_main.depart_date = 2026-10-01(班期值) 请求可能为 2026-10-02,但落库为班期出发日
GROUP 创单(班期无 endDate) order_main.return_date = 班期出发日 + tripDays - 1 如班期 2026-10-01,tripDays=3,则 return_date=2026-10-03
GROUP 创单(班期有 endDate) order_main.return_date = 班期 endDate endDate 为准,与 tripDays 无关
GROUP 创单 order_main.tier_name = 产品 tiers JSON 对应 tierSeq 的值 改前恒 null;CORE/CUSTOM 仍为 null(无 tiers JSON)
CORE 创单带 productBatchId 不创建,拒单 581056 改前会落库 productBatchId,误命中团期逻辑
班期串号 productBatchId 错 不创建,拒单 581055 改前会按班期产品计价,订单挂错团

六、边界行为

  • 业务失败仍 HTTP 200,必须检查 code 字段判成败。
  • 存量订单不回溯修正;仅新建订单应用新规则。
  • 产品未配档位时保持放行(向后兼容存量产品);即使 tierSeq 传 99 也不拦。
  • 班期 productId 缺失时仅告警日志,不拦单(product 侧老数据未回填,宁缺毋滥)。
  • 网关注入 X-Admin-Id 为 null 时返 581013,改前静默取 JWT;两者互斥。

六.6、修改前后对比

针对 POST /v3/admin/order(GROUP 产品):

维度 修改前 修改后
CORE/CUSTOM 带 productBatchId 静默落库 product_batch_id,误命中团期 staff 扇出分支 拒单 581056「非团期产品不能指定团期」,不落库
跨产品班期(batchId 属别的产品) 按别家班期计价,订单挂错团 拒单 581055「所选团期不属于该产品」(任一侧 productId 为空时仅告警放行)
tierSeq 不在产品档位内 不校验,tier_name 落 NULL 拒单 581057「所选档位不存在」(产品未配档位时不拦)
GROUP 单响应/落库出发日 回显请求出发日,可能与班期脱钩 = 班期出发日
GROUP 单响应/落库返团日 按请求出发日推算 = 班期 endDate(缺则 班期出发日 + 行程天数 − 1)
GROUP 单响应 tierName 恒 null 现有值(来自产品 tiers 配置)
人数超班期剩余名额(#7159) 预查读 remainingSlots 恒 null,整段 no-op(不拦) 读 remainingParticipants,超额拒单 581034(人数不限的班期跳过)

六.7、影响评估

  • 前端必改:新建订单向导与团期看板「新增子订单」仅对 GROUP 产品传 productBatchId,且取该产品价格日历的 items[].batchId;CORE/CUSTOM 绝不能带,否则 581056。
  • 前端展示:出发日期 / 返团日期以创单响应值为准(GROUP 单被班期覆盖),不要回显请求值。
  • 向后兼容:正常 CORE/CUSTOM 创单(不带 productBatchId)行为完全不变;错误码新增不影响既有成功路径。
  • 其他端:小程序 POST /v3/mp/order 共用内核,四个校验同样生效,请求契约不变。
  • 数据安全:跨产品串号单此前会把订单挂到别家团、扣错名额,本次从源头拒绝。
  • 回滚:回滚本次发布即恢复旧行为;已按新规则创建的订单不受影响。

七、不影响范围

  • 仅影响:管理后台「新建订单向导」和「团期看板新增子订单」流程。
  • 零影响:
    • 小程序端 POST /v3/mp/order 共用内核,新校验同样生效但请求契约不变。
    • 表结构:无 Flyway 变更、无新列新表。
    • 订单列表、详情、订单编辑等读操作。
    • CORE/CUSTOM 正常创单(不带 productBatchId)完全不变。
    • 团期相关接口(看板、价格日历、班期详情)。

八、测试环境已验证

测试服创单(产品「冻干粉发短信给」productId=2044306857534636034,班期 2026-10-01 productBatchId=2052935476557328386,1 成人 1 儿童,2026-09-06 测试)

  • ✓ GROUP 单正例:响应 departureDate/returnDate 与班期一致(班期 2026-10-01 出发、2026-10-03 返);groupBatchId / productBatchId / groupOrder 三字段已填值。
  • ✓ CORE 产品 2056944461216100353 带 productBatchId → HTTP 200,code 581056「非团期产品不能指定团期」。
  • ✓ productId=2044306857534636034 + productBatchId=2089667212070612995(属另一产品) → HTTP 200,code 581055「所选团期不属于该产品」。
  • ✓ tierSeq=9(产品只配 1-3 档) → HTTP 200,code 581057「所选档位不存在」。
  • ✓ GROUP 单不传 productBatchId → HTTP 200,code 581026「团期产品必须选择团期」。

单测覆盖(定向复测全绿):OrderServiceTest 193/193 ✓ | GroupOrderStrategyTest 30/30 ✓(含 581034 三例)| OrderCreateTransactionExecutorTest 21/21 ✓ | ProductTierResolverTest 11/11 ✓ | OrderMpCreateServiceTest 6/6 ✓ | BatchInfoVODeserializationTest 2/2 ✓ | OrderMpReadServiceTest 15/15 ✓ | InternalOrderSnapshotControllerTest 4/4 ✓ | E2eScopedOrderCreateServiceTest 19/19 ✓;ArchTest 全绿(LayerEnforcement 5 / RedLine 9 / MapperBoundary 26 / HouseModuleBoundary 4 / DashboardLayer 2 / LocalCacheVetting 1)。

网关验证(已部署 dev-v3 复测,2026-09-06 15:00):合并提交 977be08e 部署测试服,双实例滚动重启完成(8086/8186 新 PID、jar 已更新)。网关实测 15/15 通过:S2 正例 200(日期=班期 10-01/10-03)、S10 跨产品班期 581055、S11 日期不一致回显班期日期、S13 tierSeq=9 581057、S14 CORE 带班期 581056、S16 GROUP tierName=轻奢;#7142 判团字段在创单响应/详情/列表三处透出、#7143 productId 在看板/分页/详情三接口透出,均已核。581034(#7159)判定逻辑经三条单测证实生效;端到端实跑当日受测试账号对产品 schedule 无编辑权限(403)与限额班期 getBatchInfo 存量异常(581027)所限未完成,已于次日补齐(见下条)。

581034 网关端到端补测(#7159,2026-09-07 09:2x,dev-v3 HEAD 9f10f44d):改用同产品 2044306857534636034 下干净的 12-01 期(班期 2096631555760807938 / QA-7189-1201,原 maxParticipants=6、0 人已报名)——经 SUPER_ADMIN 调 PUT /admin/product/item/2044306857534636034/schedule 把 maxParticipants 改为 3,随后 POST /v3/admin/order 传 adultCount=4 → 581034 团期剩余名额不足;传 adultCount=2 → 200 建单 2096769958221402114,随即 cancel/pre-trip 取消并把 maxParticipants 改回 6,测试库已复原。工单原写的 10-01 期在 2026-09-07 凌晨已被他人下 4 单(8 人已报名),改容量后正例也会被拦,故换期取证,步骤与断言按 AC-4 原文执行。至此 #7159 的行为变化在测试环境已完成网关实测,本文档第一节与「六.6 修改前后对比」所述口径全部成立。


十、相关文档

  • 关联 Issue: wx/HL#7135
  • 关联 PR: wx/HL#7169(Closes #7135、#7159;合并提交 977be08e)
  • 关联 Issue: wx/HL#7159(人数预查改读 remainingParticipants 使 581034 生效;与 #7135 同 PR 交付,行为变化由本文档覆盖,网关实测证据见「八、测试环境已验证」末条)
  • 关联工单 #7142(判团字段 groupBatchId / productBatchId / groupOrder)、#7143(看板 productId 显示)。
  • 团期接口文档:GB-ADM-00B(OpenWiki)。

关联 / 联系人

链接

  • Issue: #7135
  • PR: #7169(含 #7159)
  • Merge commit: 977be08eccd0a32e27c01dce41de210a53f13e5d

联系人

  • 后端负责人: @wx