#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。
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)。
关联 / 联系人
链接
联系人
- 后端负责人: @wx