文件
hl-api-changelog/changelogs-v2/2026-09/06_frontend_团期产品新建订单向导创单漏传productBatchId-前端缺陷-管理后台.md
2026-09-06 16:33:44 +08:00

12 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 frontend 团期产品新建订单向导创单漏传 productBatchId admin wx(GIT) 前端缺陷 not_required not_required verified mmg 03a985b6 2026-09-06 后端已随 #7135/#7159 加固并部署测试服 dev-v3、网关复测 15/15 通过(新增 581055/581056/581057、修复 581034 人数预查,合并提交 977be08e);前端可据此联调:在 order-v2/new 向导创单 payload 补 productBatchId(仅 GROUP 产品,且必须是该产品的班期),并修团期看板「新增子订单」入口带团期上下文。 2026-09-06 dev-v3

团期订单:新建订单向导对 GROUP 产品创单漏传 productBatchId(前端缺陷)

⚠️ 关键变化

现象:团期(GROUP)产品在管理后台新建订单向导完成表单、点"确认创建"后报 HTTP 200 code=581026 message='团期产品必须选择团期',无法创建。

根因:前端 src/views/order-v2/new/index.vue handleCreate() 组装的创单 payload 漏传 productBatchId 字段。而向导其实已在报价阶段成功拿到班期 ID(存于 pricingContext.batchId),报价后端响应 ¥5,850 正确,但创建时没把它放入请求体。

结论:前端补 productBatchId 是主修复。后端已随 #7135/#7159 同步加固(不再是「零改动」):现在仅 GROUP 产品可传 productBatchId,CORE/CUSTOM 传了会被拒(581056);班期必须属于所选产品(否则 581055);tierSeq 必须在产品配置内(否则 581057);人数超班期剩余名额会被拒(581034,此前因字段名漂移长期失效,#7159 修复)。因此前端务必只对 GROUP 产品传 productBatchId,且取自该产品价格日历的 items[].batchId。


一、背景

复现步骤

页面:管理后台 192.168.100.219:9527/order-v2/new(hl-ui 路由 orderV2NewRoute,src/router/routes.js:166-175,四步向导:选主题 → 选产品/档位 → 基本信息 → 确认创建)。

  1. Step 0 选主题:「阿斯蒂芬撒点」(GROUP 产品线 lineId=2044248925572919297)
  2. Step 1 选产品:「冻干粉发短信给」(productId=2044306857534636034,GROUP,tierSeq=1 档位「轻奢」)
  3. Step 2 基本信息:出发日期 2026-10-01,成人 1 名、儿童 1 名 → 系统报价 ¥5,850.00(其中单房差 +¥500.00)
  4. Step 3 确认创建:填客户姓名、手机号 → 点「确认创建订单」
  5. 结果:Toast 弹窗「团期产品必须选择团期」,订单创建失败

调用链

  1. hl-ui src/views/order-v2/new/index.vue:405-450 handleCreate() 组装 payload → createOrder(payload)
  2. src/api/orderV2.js:79-81 createOrder(data) = http.post('/v3/admin/order', data, V3)
  3. hl-gateway application.yml:246-249 路由 /v3/admin/** → lb://hl-order-service-v3
  4. hl-order-service-v3 OrderController.java:86-91 接收 → orderService.createOrder(req, ...)
  5. OrderService.java:630 直接 ctx.setProductBatchId(req.getProductBatchId()),无兜底反查
  6. OrderService.java:635 strategy.validate(ctx, productDetail)
  7. GroupOrderStrategy.java:46-49 校验失败:if (ctx.getProductBatchId() == null) throw new BusinessException(OrderCoreErrorCode.GROUP_BATCH_REQUIRED)
  8. OrderCoreErrorCode.java:102-103 返回 581026「团期产品必须选择团期」

地面真相(测试库验证)

属性 值
主题(product_line) 阿斯蒂芬撒点 2044248925572919297
产品(product) 冻干粉发短信给 2044306857534636034,product_type=GROUP
档位(tier) tierSeq=1 轻奢
班期(group_tour_batch) batch_id=2052935476557328386,batch_no=Q202610012052935476548939777,departure_date=2026-10-01,batch_status=ENROLLING,end_date=2026-10-03,adult_price=2925,child_price=2425,single_room_diff=500
预计金额 2925 + 2425 = 5,350,加单房差 500 = 5,850 ✓ (与前端报价一致,证明向导已成功拿到班期信息计价)

二、变更接口清单

# 接口名 方法 网关路径 前端函数 变更 说明
1 创建订单 POST /v3/admin/order createOrder() 调用方式修正 GROUP 产品必传 productBatchId;CORE/CUSTOM 必须不传
2 统一价格日历 GET /admin/product/item/{productId}/pricing-calendar getUnifiedPricingCalendar() 无变更 GROUP 时返回班期列表,items[].batchId 即为所需班期 ID
3 报价 POST /admin/product/item/{productId}/quote quoteProduct() 无变更 GROUP 时现已传 batchId,保持即可

三、接口详情

1. 创建订单 POST /v3/admin/order

VO: OrderCreateReqVO → Result<OrderCreateRespVO>

使用场景

新建订单向导 Step 3 确认创建时调用,后端落库并触发团期聚合等副作用。

入参

字段 类型 必填 约束 说明
productId String(Long 值) ✓ 正整数 产品 ID
tierSeq Integer ✓ 1~N,且必须在产品已配档位内 档位序号;不在产品 tierPrices ∪ tiers 配置内 → 581057(#7135 起全类型校验;产品未配任何档位时不拦)
departureDate String (yyyy-MM-dd) ✓ 不早于今天 出发日期;GROUP 下服务端会用班期权威出发日落库,但仍必填;CORE/CUSTOM 按字面值
adultCount Integer ✓ ≥1 成人数
childCount Integer — ≥0,默认 0 儿童数(6~12 岁)
youngChildCount Integer — ≥0,默认 0 小童数(2~5 岁)
babyCount Integer — ≥0,默认 0 婴儿数;GROUP 下服务端强制置为 0,不计入名额和价格
customerName String ✓ 非空,≤50 客户姓名
customerPhone String ✓ 格式 ^1[3-9]\d{9}$ 手机号明文
customerRemark String — ≤500 订单备注
createSource String — ≤20,默认 CONSULTANT 创建来源标记
productBatchId String(Long 值) GROUP ✓ / CORE、CUSTOM ✗ 仅 GROUP 可传且必传;班期须属于本 productId GROUP 产品必传班期 ID(来自价格日历 items[].batchId),非 GROUP 产品禁止传递(CORE/CUSTOM 带了 → 581056);班期 productId 须等于请求 productId(否则 581055);建议用字符串如 "2052935476557328386"(JSON Number 亦可,后端接受,但字符串防前端精度丢失)
roomCount Integer — ≥1,默认 1 房间数;超过班期剩余房间数报 581031
tags Array — — 订单标签

出参 Result<OrderCreateRespVO>

字段 类型 说明
code Integer 200 = 成功,否则为业务错误码
data.id String 订单 ID(雪花 Long)
data.orderNo String 订单编号(HL+时间+序号,如 HL20260906093733163)
data.orderStatus String 订单状态(创建初态 = PENDING_PAY)
data.totalAmount String 订单总金额,格式 decimal(12,2)
data.depositAmount String 订金额
data.departureDate String 出发日期(yyyy-MM-dd);GROUP 以班期为准,回显班期出发日
data.returnDate String 归程日期(yyyy-MM-dd);根据 tripDays = endDate - departureDate + 1 推算
data.groupBatchName String/null 团批次名称(当前实现为 null)
data.tierName String/null 档位名称(#7135 起 GROUP 也回显来自 tiers 配置的档位名;产品未配档位时为 null)
data.consultantId String 发单定制师 ID(创建时落 1001)

请求示例(GROUP 产品,正例)

{
  "productId": "2044306857534636034",
  "tierSeq": 1,
  "departureDate": "2026-10-01",
  "adultCount": 1,
  "childCount": 1,
  "youngChildCount": 0,
  "babyCount": 0,
  "customerName": "张三",
  "customerPhone": "13800009601",
  "createSource": "CONSULTANT",
  "customerRemark": "团期测试订单",
  "productBatchId": "2052935476557328386",
  "roomCount": 1
}

响应示例

{
  "code": 200,
  "message": "成功",
  "data": {
    "id": "2096412454488612866",
    "orderNo": "HL20260906093733163",
    "orderStatus": "PENDING_PAY",
    "totalAmount": "5850.00",
    "depositAmount": "1000.00",
    "departureDate": "2026-10-01",
    "returnDate": "2026-10-03",
    "groupBatchName": null,
    "tierName": null,
    "consultantId": "1001"
  },
  "success": true
}

错误响应

code message 触发条件
581026 团期产品必须选择团期 productBatchId 为 null 且 productType = GROUP
581027 团期信息获取失败 Feign 调班期服务异常
581028 团期状态不允许报名 班期状态不在可订范围
581029 团期已过报名截止日 当前日期 > enrollment_deadline
581031 团期剩余房间不足 roomCount > 班期剩余房间数
581034 团期剩余名额不足 成人+儿童+小童 > 班期剩余名额(#7159 修复:此前字段名漂移致此校验长期失效;人数不限的班期不拦)
581055 所选团期不属于该产品 班期 productId ≠ 请求 productId(跨产品串号;#7135 新增)
581056 非团期产品不能指定团期 CORE/CUSTOM 请求带了 productBatchId(#7135 新增)
581057 所选档位不存在 tierSeq 不在产品 tierPrices ∪ tiers 配置内(#7135 新增;产品未配档位时不拦)

业务边界

  • 后端已校验 tierSeq 存在性(581057)与班期归属产品(581055);GROUP 出发日/返团日以班期为准回显(#7135)
  • babyCount 强制 0,不参与计价
  • returnDate 按班期 tripDays 计算
  • 创建后触发 staff 分配、group_batch 懒建

2. 统一价格日历 GET /admin/product/item/{productId}/pricing-calendar

GROUP 时返回班期列表,items[] 每条含:

  • date、batchId(JSON Number,前端必须 String() 转换)
  • batchNo、batchName、batchStatus、endDate、enrollmentDeadline
  • maxRooms、bookedRooms、remainParticipants(null 表不限)
  • 价格字段、sellable(仅 ENROLLING 为 true)

3. 报价 POST /admin/product/item/{productId}/quote

GROUP 时入参需 batchId,出参 grandTotal、singleRoomSurcharge 等。


四、前端修复要点与自测清单

修复要点

  1. index.vue handleCreate():GROUP 时 payload.productBatchId = String(pricingContext.batchId)
  2. Step3 确认前二次校验 pricingContext 非空且与当前表单一致
  3. Step3 展示 batchNo/batchName
  4. 团期看板 onAddSub() 带深链 ?productBatchId&departureDate

自测清单

  • ✓ GROUP 选日期 → 报价 → 创建成功
  • ✓ CORE 创建不含 productBatchId
  • ✓ 日期未选时按钮拦截

五、验证证据

场景矩阵(测试服 2026-09-06)

14 场景通过,1 场景失败(S8),10 单已清理。

DB 落库

orderNo HL20260906093733163,product_batch_id=2052935476557328386


六、影响与不影响范围

不影响:

  • 正常 CORE/CUSTOM 创单(不带 productBatchId):完全不变
  • 订单读接口(列表 / 详情 / 编辑):不变
  • 前端改动只涉及 order-v2/new 向导创单 payload 与团期看板「新增子订单」入口

受后端加固影响(前端需知晓):

  • 小程序端 POST /v3/mp/order 与管理端共用内核,581055/581056/581057/581034 同样生效,但小程序请求契约不变(groupBatchId 语义不变)
  • CORE/CUSTOM 若误带 productBatchId:此前静默落库,现在直接 581056 拒单

七、关联

  • 前端向导既有工单 #7083(已合);后端判团字段透出 #7142、看板 productId 深链 #7143(均已合 dev-v3)
  • 后端加固 #7135(收紧 productBatchId 校验:581055/581056/581057、日期按班期)+ #7159(人数预查改读 remainingParticipants 使 581034 生效),随 PR #7169 合入 dev-v3
  • 证据:gateway-verify.txt、gateway-matrix.txt