文件
hl-api-changelog/changelogs-v2/2026-09/07_7244_选品接口透出产品类型与团期班期列表-修改接口-管理后台.md
T
Mimingguang 689df8b520
changelog-filename-gate / validate (push) Failing after 2s
docs(changelog): #7274/#7244 前端 verified 回写
2026-09-07 17:35:20 +08:00

20 KiB
原始文件 Blame 文件历史

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 7244 订单选品接口透出 productType 与团期班期列表 batches admin wx(GIT) 修改接口 deployed verified verified mmg ccbbc446 2026-09-07 前端已交付并验证(功能主体 ccbbc446 + API 契约注释 7894071e):新增 groupBatch utils(可选仅 ENROLLING/NEARLY_FULL、剩房 ?? 不吞 0、期号取 batchLabel、batchId 全程字符串)与 useOrderDeepLink(详情 lineId 拉选品 batches 定位预选,缺期/已满警告转 Step1);Step1Sku GROUP 卡展开班期行、Step2Info batchLocked 锁出发日跳日历反解、Step3Confirm 增「第N期」、index canNext 缺班期拦截+创单 productBatchId 优先 draft、报价 quote 传所选 batchId。order-v2/new 定向 Vitest 29 绿 + checkpoint 全绿(含 Vitest 全量+生产构建)。 2026-09-07 dev-v3

订单选品接口: 新增 productType 与内嵌可售班期列表 batches

服务: hl-product-service-v2 | PR: #7259 | Issue: #7244 | 合并提交: 9800cddde 影响范围: 管理后台「新建订单 → 选择产品」页


⚠️ 关键变化

新建订单时,团期产品(productType === "GROUP")必须让运营选定一个班期,不选不允许进入下一步。班期数据现在直接内嵌在选品接口的每个产品项里,前端不需要再单独请求班期接口。

响应新增两个字段:productType(产品类型)与 batches(该产品当前可售班期列表)。11 个旧字段的名称、类型、顺序一字未动,不消费新字段的页面不受影响。


一、背景

新建订单页选完产品后直接让运营手填出发日期,团期产品因此可能建出「没有归属班期」或「出发日与任何班期都对不上」的订单,后续团期看板认领不到该单。

要在选品页当场选班期,就需要选品接口把班期带出来。原本可以再加一个「按产品查班期」的接口,但选品页是分页列表,每个产品一次请求会退化成 N+1;因此改为在选品接口内嵌,一次请求把本页所有团期产品的可售班期全部带回。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 订单选品列表 GET /admin/product/item/order-picker 响应新增字段 每个产品项新增 productType 与 batches[]

入参、路径、错误码均未变。


三、接口详情

1. 订单选品列表 GET /admin/product/item/order-picker

VO: OrderPickerProductVO(本次新增两个静态嵌套类 OrderPickerProductVO.BatchItem、OrderPickerProductVO.BatchTierPrice)

使用场景

管理后台「新建订单」流程第 1 步「选择产品」页加载与翻页时调用。运营按主题或关键字筛出产品,选中产品后进入第 2 步选档位。本次改动后,团期产品的可售班期随产品一起返回,运营在同一页选定班期,不再需要单独请求班期接口,也不再手填出发日期。

入参

字段 位置 类型 必填 约束 说明
lineId query Long 否 - 主题/线路 ID,按主题筛选产品
keyword query String 否 - 产品名关键字
page query Integer 否 默认 1 页码
pageSize query Integer 否 默认 10 每页条数

出参 Result<PageResult<OrderPickerProductVO>>

data.records[] 的每一项:

字段 类型 说明
productId String 产品 ID(雪花,字符串序列化)。非空
name String 产品名。非空
subtitle String 副标题。可空
tripDays Integer 行程天数。可空
tripNights Integer 行程晚数。可空
tags String[] 标签。可空
coverImageUrl String 封面图。可空
startPrice String 起价(金额字符串)。可空
tierPrices Object[] 产品档位起价(tierSeq / tierName / tierDescription / startPrice)。可空
hot Boolean 是否热门。非空
nextSaleDate String 最近可售日。可空
productType String 新增。产品类型,见六.5。非空
batches Object[] 新增。当前可售班期列表。非 GROUP 产品恒为 [](空数组,不是 null)。非空

batches[] 每一项

字段 类型 可空 说明
batchId String 否 班期 ID(雪花,字符串序列化)。创建订单时作为 productBatchId 回传
batchNo String 否 班期编号
batchLabel String 否 期号,如 "7" 表示第 7 期。按该产品全部未删班期的出发日升序位置计算,与团期看板显示的期号一致
batchName String 是 班期名(后端已去除前后空格)
departureDate String 否 出发日 yyyy-MM-dd
endDate String 是 返团日 yyyy-MM-dd
enrollmentDeadline String 是 报名截止日 yyyy-MM-dd
maxRooms Integer 否 房间数上限。本接口恒非 null,0 = 不限
remainRooms Integer 是 剩余房数。null = 不限,0 = 已满。含运营线下占位房数
maxParticipants Integer 是 人数上限,null 或 0 = 不限
remainSlots Integer 是 剩余人数。null = 不限,0 = 已满
batchStatus String 否 班期状态,见六.5
batchStatusLabel String 否 班期状态中文文案,可直接显示
tierPrices Object[] 否 该班期的档位价,至少 1 项

batches[].tierPrices[] 每一项

字段 类型 可空 说明
tierSeq Integer 否 档位序号,未配置档位时为 1
tierName String 否 档位名,未配置档位时为 "默认档"
adultPrice String 是 成人价(金额字符串)
childPrice String 是 儿童价(金额字符串)

请求示例

GET /admin/product/item/order-picker?lineId=2044248925572919297&page=1&pageSize=20
Authorization: Bearer {token}

响应示例

测试环境实测,团期产品:

{
  "code": 200,
  "msg": null,
  "data": {
    "total": 1,
    "records": [
      {
        "productId": "2044306857534636034",
        "name": "冻干粉发短信给",
        "subtitle": "发短信给对方搞定",
        "tripDays": 3,
        "tripNights": 2,
        "tags": ["亲子", "研学", "露营"],
        "coverImageUrl": "https://hlgl-test.oss-cn-beijing.aliyuncs.com/test/material/2026/03/07/ed278d1da74c3827cb06b7f0cf6091fe.jpg",
        "startPrice": "1000.00",
        "tierPrices": [
          { "tierSeq": 1, "tierName": "轻奢", "tierDescription": null, "startPrice": "1000.00" }
        ],
        "hot": true,
        "nextSaleDate": "2026-10-01",
        "productType": "GROUP",
        "batches": [
          {
            "batchId": "2052935476557328386",
            "batchNo": "Q202610012052935476548939777",
            "batchLabel": "7",
            "batchName": "没,那你",
            "departureDate": "2026-10-01",
            "endDate": "2026-10-03",
            "enrollmentDeadline": "2026-09-30",
            "maxRooms": 80,
            "remainRooms": 25,
            "maxParticipants": 0,
            "remainSlots": null,
            "batchStatus": "ENROLLING",
            "batchStatusLabel": "报名中",
            "tierPrices": [
              { "tierSeq": 1, "tierName": "轻奢", "adultPrice": "2925.00", "childPrice": "2425.00" }
            ]
          },
          {
            "batchId": "2096631555760807938",
            "batchNo": "Q202612012096631555752419329",
            "batchLabel": "8",
            "batchName": "QA-7189-1201",
            "departureDate": "2026-12-01",
            "endDate": "2026-12-03",
            "enrollmentDeadline": "2026-11-30",
            "maxRooms": 4,
            "remainRooms": 4,
            "maxParticipants": 6,
            "remainSlots": 6,
            "batchStatus": "ENROLLING",
            "batchStatusLabel": "报名中",
            "tierPrices": [
              { "tierSeq": 1, "tierName": "轻奢", "adultPrice": "1000.00", "childPrice": "600.00" }
            ]
          }
        ]
      }
    ]
  }
}

空数据 / 降级响应

非团期产品的 batches 是空数组而不是 null(测试环境实测):

{
  "productId": "2049754271968047106",
  "name": "的风格发多少",
  "productType": "CORE",
  "batches": []
}

该产品全部班期都已返团或已取消时,batches 同样是 [],产品本身仍出现在列表里。

整页无匹配产品时:

{ "code": 200, "msg": null, "data": { "total": 0, "records": [] } }

订单侧班期统计服务不可用时,本接口不失败:该批班期按「无活跃订单」计算,remainRooms / remainSlots 可能偏大(偏向显示为可订),响应结构与 code 不变,服务端记 ERROR 日志。

错误响应

未新增错误码,沿用既有:

{
  "code": 401,
  "msg": "未登录或登录已过期",
  "data": null
}
{
  "code": 403,
  "msg": "无权限访问该数据",
  "data": null
}

业务边界

  • 鉴权: 需登录;@DataScope 行级校验产品数据权限,越权返 403。
  • 只读: 无任何写操作,不产生数据变更,可重复调用。
  • 班期可见性: 只返回「未取消 且 返团日未过(返团日为空视为未过)」的班期;已返团、取消中、已取消、已软删的班期不出现。满员(FULL)的班期仍然返回,由前端灰显。
  • 期号口径: batchLabel 按该产品全部未删班期的出发日升序位置计算,再做可售过滤——所以它与数组下标不对应,与团期看板显示的期号一致。
  • 空值语义: remainRooms / remainSlots 为 null 表示不限,为 0 表示已满;maxRooms 恒非 null,0 表示不限。
  • 降级: 订单侧统计不可用时按无活跃订单计算并记 ERROR,不 500、不阻断页面。
  • 兼容: 11 个旧字段的名称、类型、顺序完全未变,不消费新字段的调用方无需改动。

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

本接口是只读 GET,无请求体。约束体现在怎么读响应,下面是三条读法对照。

✅ 正确 / ❌ 错误 读法对照

场景 读法
✅ 判断班期能否选中 ["ENROLLING", "NEARLY_FULL"].includes(batch.batchStatus)
❌ 判断班期能否选中 batch.remainRooms > 0 → 不限容量时 remainRooms 为 null,null > 0 为 false,会把不限的班期误判成不可选
✅ 显示剩余房数 batch.remainRooms ?? "不限"
❌ 显示剩余房数 batch.remainRooms || "不限" → 0(已满)会被显示成「不限」
✅ 取期号 batch.batchLabel
❌ 取期号 index + 1 → batches 已过滤掉已返团班期,下标与真实期号对不上
✅ 回传班期 productBatchId: String(batch.batchId)
❌ 回传班期 productBatchId: Number(batch.batchId) → 雪花 ID 超出 JS 安全整数范围,精度丢失

哪些班期会出现在 batches 里

后端已过滤,只返回「未取消 且 返团日未过(返团日为空视为未过)」的班期。已返团、取消中、已取消、已软删的班期不会出现,前端不需要再过滤一次。


五、数据库行为

本接口为只读查询,无任何写操作,不产生数据变更。


六、边界行为

  • 未登录 → 401(网关拦截)
  • 无该产品数据权限 → 403(@DataScope 行级校验)
  • 该产品无任何可售班期(全部已返团/已取消)→ batches 为 [],产品本身仍出现在列表里
  • 非 GROUP 产品 → batches 恒为 [],不是 null
  • 班期未配置档位 → tierPrices 返回一项 tierSeq=1、tierName="默认档" 的兜底档
  • 订单侧统计服务降级 → 该批班期按「无活跃订单」计算,剩余数可能偏大,接口不 500 不阻断页面(服务端记 ERROR 日志)
  • 本页可售班期总数超过 500 → 服务端记 warn,不截断,仍全量返回

六.5、枚举 / 数据字典

productType(com.hulalv.enums.ProductTypeEnum)

所属字段: records[].productType | 类型: String

值 中文 说明
CORE 核心产品 batches 恒为 []
GROUP 小蒙马(即团期产品) batches 为可售班期列表,创建订单必须选定其一
CUSTOM 私人定制 batches 恒为 []

(中文名取自枚举定义本身,与后台产品管理页的类型显示一致。)

batchStatus(com.hulalv.enums.BatchStatusEnum)

所属字段: records[].batches[].batchStatus | 类型: String

值 中文 说明
ENROLLING 报名中 可选中下单;会出现在 batches
NEARLY_FULL 即将满额 可选中下单;会出现在 batches
FULL 已满额 不可选(灰显);仍会出现在 batches
FINISHED 已结束 不可选;一般不会出现(返团日已过会被过滤)
CANCELLING 取消中 不可选;不会出现(后端已过滤)
CANCELLED 已取消 不可选;不会出现(后端已过滤)

六.6、修改前后对比

字段级对比

字段 改前 改后
records[].productType 不存在 新增,CORE / GROUP / CUSTOM
records[].batches 不存在 新增,可售班期数组(非 GROUP 恒 [])
其余 11 个字段 有 完全不变(名称、类型、顺序、取值均未动)

行为级对比

行为 改前 改后
选品页拿班期 拿不到,需另外调接口或让运营手填出发日 一次请求随产品带回
班期期号 无 batchLabel,与团期看板一致
请求次数 1 次 仍是 1 次(班期用一次 IN 查询取回,不随产品数增长)

六.7、影响评估

  • 是否破坏向后兼容: 否。11 个旧字段完全未动,只追加两个新字段。
  • 前端是否必须同步上线: 否(不上线则维持现状,选品页看不到班期)。但团期订单必须选班期这条业务要求要靠前端落地,见八.5。
  • 前端 workaround 清理点: 若前端此前为团期产品单独请求过班期接口或让用户手填出发日,改用本接口的 batches 后可撤除。

七、不影响范围

  • 仅影响: 管理后台「新建订单 → 选择产品」页调用的 GET /admin/product/item/order-picker
  • 零影响:
    • 订单创建接口 POST /v3/admin/order(本次未改 order-v3 任何代码)
    • 报价 POST /admin/product/item/{id}/quote、价格日历 pricing-calendar
    • 团期看板全部读端点(/v3/admin/order/group-batch 系列)
    • 产品侧班期管理接口(/admin/product/item/{id}/schedule 系列)
    • 小程序 C 端全部接口
    • 存量数据(不做任何数据迁移,班期名的前后空格只在输出时去除,不回写库)

八、测试环境已验证

2026-09-07 测试环境(api.test.1814.love)网关实测 15/15 通过:

GET /admin/product/item/order-picker?lineId=2044248925572919297&page=1&pageSize=20  → 200 ✓
  productType=GROUP,batches 14 个字段齐全                                          ✓
  各字段与 GET /admin/product/item/2044306857534636034/schedule/list 逐字段一致      ✓
  6 个已返团班期被过滤,10-01 与 12-01 期在列,按 departureDate 升序                 ✓
  期号 10-01="7"、12-01="8",与 /v3/admin/order/group-batch/board?scope=ALL 逐一相等 ✓
  班期名 " 没,那你" → "没,那你"(已去空格)                                        ✓
  11 个旧字段一个不少                                                                ✓
线下占位: 容量 8 房 + 占位 3 房 + 线上 0 单 → remainRooms=5                          ✓
不限容量: maxRooms=0 → remainRooms=null, remainSlots=null, batchStatus=ENROLLING     ✓
满员:     容量 1 房 + 占位 1 房 → remainRooms=0, FULL, "已满额", 且仍在 batches 里    ✓
取消与软删的班期不再出现在 batches                                                   ✓
非 GROUP 产品 batches 为 [] 而非 null                                                ✓

验证产品: productId=2044306857534636034(冻干粉发短信给,GROUP,8 期)、productId=2049754271968047106(的风格发多少,CORE)

单元测试: mvn -pl hl-product-service-v2 test → Tests run: 1574, Failures: 0, Errors: 0, Skipped: 0


八.5、前端配套(mmg 待办)

2026-09-07 13:50 复测结论:后端接口已就绪。GET /admin/product/item/order-picker?lineId=2044248925572919297 对产品「冻干粉发短信给」返回 productType=GROUP 与 2 期可售班期——第 7 期「没,那你」2026-10-01→2026-10-03 剩 25 房 ENROLLING;第 8 期「QA-7189-1201」2026-12-01→2026-12-03 剩 4 房 ENROLLING。但此刻管理后台「新建订单 → 选择产品」页展开产品后仍只显示档位、没有班期列表,经查 origin/master 的 src/views/order-v2/new/components/Step1Sku.vue 中 batches / productType / batchLabel / productBatchId 均未出现(0 次命中),即前端尚未消费本契约。这不是后端缺陷,下面 5 条待办完成后该页即可选班期。

  1. Step1Sku.vue: productType === "GROUP" 的产品卡展开显示 batches[],每行建议格式 第{batchLabel}期 · {batchName} · {departureDate} 到 {endDate} · 剩 {remainRooms ?? "不限"} 房 · {batchStatusLabel}; batchStatus 不属于 {ENROLLING, NEARLY_FULL} 的班期灰显不可选。选中档位与班期后 emit 带上 productBatchId 与 batchDepartureDate。
  2. GROUP 未选班期不允许进入第 3 步。第 3 步的出发日直接取所选班期的 departureDate(不再让用户手选日期),报价 POST /admin/product/item/{id}/quote 传 batchId。
  3. 创建订单 payload: productBatchId = String(batch.batchId);非 GROUP 产品不传该字段。
  4. 深链: 带 ?productBatchId=… 进入时,在 batches 中预选该期;若该期不在列表里(已结束或已取消),给出提示而不是静默忽略。
  5. 金额只显示后端报价结果,batches[].tierPrices 仅用于展示参考价,不参与前端算价。

九、相关历史 PR

PR Issue 说明 是否仍有效
#7219 #7189 团期看板以产品全班期为基底 + scope 筛选,batchLabel 期号口径在此确立 ✅ 有效
#7256 #7245 班期名去空格 + 不限容量 remain 统一 null + board 库存同源 ✅ 有效(本接口沿用同一口径)
本 PR #7259 #7244 选品接口透出 productType 与 batches ✅ 最新

十、相关文档

  • 关联 Issue: wx/HL#7244
  • 关联 PR: wx/HL#7259
  • 期号口径来源: changelogs-v2/2026-09/07_7189_团期看板产品全班期基底与scope范围筛选-修改接口-管理后台.md
  • 剩余库存口径来源: changelogs-v2/2026-09/07_7245_团期看板班期名与不限容量口径修正-修改接口-管理后台.md

关联 / 联系人

链接

联系人

  • 后端负责人: @wx
  • 前端负责人: @mmg