hl-api-changelog/changelogs/2026-04/2026-04-13_product-v2_frontend_guide.md
API Changelog Bot edf25422b6 feat: 产品模块v2前端联调指南(按页面场景组织)
管理端7个页面 + C端6个页面的完整接口对照,含请求/响应示例、字典速查、切换说明。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 08:58:57 +08:00

26 KiB

产品模块v2 — 前端联调指南

日期2026-04-13 服务hl-product-service-v2端口待定,Knife4j: http://localhost:{port}/doc.html 网关路由/product-v2/** → hl-product-service-v2 重要:本模块是全新服务,与旧 hl-product-service 并行运行。所有接口路径不同,不会冲突。 参考:完整接口字段定义见同目录 2026-04-12_1538_273bef51_feat_product-v2_Co.md2026-04-12_1538_ff42bed5_feat_product-v2_VO.md


一、整体架构变化

旧服务 vs 新服务

维度 旧 hl-product-service 新 hl-product-service-v2
产品类型 CORE/GROUP/CUSTOM/ROUTE4种 CORE/GROUP/CUSTOM3种,ROUTE废弃
保存方式 每个字段独立接口 5个业务域整体保存(基础/行程/路线/定价/补充)
定价模型 统一价格日历 双模型价格区间CORE/CUSTOM+ 班期GROUP
住宿 统一 支持多档位(舒适/豪华/高端等)
人群分档 成人/儿童 4档:成人/儿童/小童/幼童
并发控制 乐观锁version字段

乐观锁机制(所有保存接口通用)

1. GET 详情 → 拿到 version 字段
2. PUT 保存 → 请求体带 version
3. 成功 → version+1下次保存用新version
4. 失败(409) → 提示"数据已被其他人修改,请刷新",重新GET

已上架产品调价:用 priceVersion 而非 version调价不占主version,允许调价和内容编辑互不阻塞


二、管理端页面接口对照

页面1产品列表

┌──────────────────────────────────────────────────────────────┐
│  [搜索: 名称/编号]  [类型▼]  [产品线▼]  [状态▼]  [标签▼]    │
│                                                    [+ 新建产品] │
├──────────────────────────────────────────────────────────────┤
│  封面  │ 名称/编号  │ 类型  │ 产品线 │ 状态 │ 操作          │
│  ...   │ ...        │ ...   │ ...    │ ...  │ 编辑/复制/删除│
└──────────────────────────────────────────────────────────────┘

接口

场景 方法 路径 说明
产品列表 GET /admin/product/item/list 分页,支持keyword/productType/lineId/status/tag筛选
产品线下拉 GET /admin/product/line/simple-list 返回 [{lineId, name}],用于筛选和创建
创建产品 POST /admin/product/item 传 name+productType+lineId+tripDays,返回productId
删除产品 DELETE /admin/product/item/{id} 需先下架
复制产品 POST /admin/product/item/{id}/copy 返回新productId
状态变更 PUT /admin/product/item/{id}/action 传 action 枚举,见下方状态操作表
操作记录 GET /admin/product/item/{id}/operation-logs 分页,查看历史操作

状态操作枚举action字段

action值 操作 前置状态 目标状态
SUBMIT_PUBLISH 提交上架 DRAFT PENDING_REVIEW
WITHDRAW 撤回审批 PENDING_REVIEW DRAFT
DIRECT_PUBLISH 直接上架(超管) DRAFT PUBLISHED
UNPUBLISH 申请下架 PUBLISHED PENDING_REVIEW(下架)
FORCE_UNPUBLISH 强制下架(超管) PUBLISHED UNPUBLISHED
COMPLETE 完成设计(定制) DRAFT COMPLETED

页面2产品编辑 — Step1 基础信息

┌──────────────────────────────────────────────────┐
│  Step1   Step2   Step3   Step4   Step5           │
│  ●基础   ○行程   ○路线   ○定价   ○补充           │
├──────────────────────────────────────────────────┤
│  产品名称: [____________]                         │
│  副标题:   [____________]                         │
│  产品类型: CORE (只读,创建时选定)                 │
│  产品线:   [下拉选择▼]                            │
│  行程天数: [6] 晚数: [5]                          │
│  住宿规格: ○单一 ○多档 → [舒适][豪华][+添加档位]  │
│  季节:     □春 ☑夏 ☑秋 □冬                       │
│  标签:     ☑亲子 ☑研学 □露营                      │
│  封面图:   [上传]                                  │
│  轮播图:   [上传1] [上传2] ... (≤10张)            │
│  年龄设置: 幼童≤[1]岁  小童≤[3]岁  儿童[4]-[12]岁│
│  幼童默认价: [0]元                                 │
│  支付方式:  ○全款 ○订金([30]%/固定[___]元)        │
│  产品简介:  [富文本编辑器]                         │
│                                    [保存]          │
└──────────────────────────────────────────────────┘

接口

场景 方法 路径 说明
获取详情(回显) GET /admin/product/item/{id} 返回全部5步数据,取basic部分回显
保存基础信息 PUT /admin/product/item/{id}/basic ProductBasicSaveReqVO,带version
产品线下拉 GET /admin/product/line/simple-list 创建/修改时选产品线

关键字段

{
  "version": 1,
  "name": "额吉的故乡·亲子版",
  "subtitle": "6天5晚草原环线",
  "tripDays": 6,
  "tripNights": 5,
  "lineId": 100,
  "tiers": [
    {"tierSeq": 1, "tierName": "舒适"},
    {"tierSeq": 2, "tierName": "豪华"}
  ],
  "seasons": ["summer", "autumn"],
  "tags": ["亲子", "研学"],
  "coverImageUrl": "https://...",
  "carouselImages": ["https://...", "https://..."],
  "infantAgeMax": 1,
  "toddlerAgeMax": 3,
  "childAgeMin": 4,
  "childAgeMax": 12,
  "infantDefaultPrice": 0,
  "paymentType": "FULL",
  "showReview": true
}

页面3产品编辑 — Step2 行程编排

┌──────────────────────────────────────────────────┐
│  DAY1  DAY2  DAY3  DAY4  DAY5  DAY6  [+添加]    │
├──────┬───────────────────────────────────────────┤
│      │  标题: [海拉尔→额尔古纳]                   │
│ DAY1 │  金句: [让草原成为孩子的第一个课堂]         │
│      │  描述: [富文本]                             │
│      │  封面: [上传]    里程: [290]km              │
│      │                                             │
│      │  途经路线:                                   │
│      │  [海拉尔] → [莫日格勒河] → [额尔古纳]      │
│      │                                             │
│      │  活动安排:                                   │
│      │  ☐ 10:00 莫日格勒河观景台 (景区)  60分钟    │
│      │  ☐ 14:00 骑马体验 (活动)  120分钟           │
│      │  [+ 从资源库添加] [+ 自定义活动]            │
│      │                                             │
│      │  住宿安排: (多档时按档位Tab切换)             │
│      │  [舒适] 额尔古纳大酒店                      │
│      │  [豪华] 额尔古纳国际酒店                    │
│      │                                             │
│      │  餐饮: 早[酒店▼] 中[自理▼] 晚[特色餐▼]     │
│      │  照片: [上传1] [上传2]                      │
│      │                                             │
│      │                               [整体保存]    │
└──────┴───────────────────────────────────────────┘

接口

场景 方法 路径 说明
获取行程(回显) GET /admin/product/item/{id} 从详情中取 itinerary 部分
整体保存行程 PUT /admin/product/item/{id}/itinerary 整棵行程树一次提交,后端用EntityDiffUtil对比增删改
增加一天 POST /admin/product/item/{id}/itinerary/day 追加末尾天,返回dayId
删除一天 DELETE /admin/product/item/{id}/itinerary/day/{dayNumber} 级联删节点+住宿+路线点
费用推导预览 GET /admin/product/item/{id}/fee-deduction-preview 根据行程自动推导费用包含,进入Step5前调用

整体保存请求结构

{
  "productId": 123,
  "version": 2,
  "days": [
    {
      "dayId": null,          // 新增不传,已有传ID
      "dayNumber": 1,
      "dayTitle": "海拉尔→额尔古纳",
      "quoteText": "让草原成为孩子的第一个课堂",
      "description": "...",
      "coverImageUrl": "https://...",
      "dailyMileage": 290,
      "breakfast": "HOTEL",   // HOTEL/CAMP/SPECIAL/SELF
      "lunch": "SELF",
      "dinner": "SPECIAL",
      "photoUrls": ["https://..."],
      "nodes": [
        {
          "nodeId": null,     // 新增不传
          "sortOrder": 1,
          "nodeType": "SCENIC",
          "nodeName": "莫日格勒河观景台",
          "resourceType": "SCENIC_SPOT",
          "resourceId": 456,
          "startTime": "10:00",
          "durationMinutes": 60,
          "description": "...",
          "images": ["https://..."]
        }
      ],
      "hotels": [
        {
          "id": null,
          "tierSeq": 1,       // 档位序号,单一档=1
          "hotelId": 789,
          "roomTypeId": null,  // 私人定制才选房型
          "isDefault": true,
          "sortOrder": 1
        }
      ],
      "routePoints": [
        {
          "resourceId": 456,
          "name": "莫日格勒河",
          "longitude": 119.123,
          "latitude": 49.456,
          "sortOrder": 1
        }
      ]
    }
  ]
}

增删改对比规则后端按ID判断——有ID且在提交中=更新,有ID但不在提交中=删除,无ID=新增。前端直接提交当前完整状态,不需要自己标记增删改。


页面4产品编辑 — Step3 路线与备品

┌──────────────────────────────────────────────────┐
│  路线信息                                          │
│  路线名称: [草原环线体验]                          │
│  路线图:   [上传]                                  │
│  总里程:   [1200] km (自动计算,可手动改)           │
│  线路描述: [____________]                          │
│                                                    │
│  备品清单                                          │
│  名称         │ 涉及成本 │ 计费方式  │ 单价 │ 数量 │
│  防晒霜SPF50+ │ ☐        │ —        │ —    │ —    │
│  一次性雨衣   │ ☑        │ 按人头    │ 5    │ —    │
│  [+ 从资源库选] [+ 自定义]                         │
│                                                    │
│  全程成本项                                        │
│  实际用车: [车型下拉▼]                              │
│  服务人员: [选择▼]                                  │
│  额外费用: [名称] [单价] [数量] [按天☐]            │
│                                    [保存]          │
└──────────────────────────────────────────────────┘

接口

场景 方法 路径 说明
保存路线与备品 PUT /admin/product/item/{id}/route ProductRouteSaveReqVO,一次提交全部
车型下拉 调资源服务 /internal/vehicle-model/list 从资源模块获取车型列表
人员下拉 调资源服务 /internal/staff/list 从资源模块获取人员列表
备品下拉 调资源服务 /internal/supplies/list 从资源模块获取备品列表

页面5产品编辑 — Step4 定价管理

核心产品/私人定制 → 价格区间

┌──────────────────────────────────────────────────┐
│  价格区间列表                                      │
│  日期范围      │ 类型 │ 成人价 │ 儿童价 │ 库存    │
│  07.01 - 07.31 │ 旺季 │ 4980   │ 3980   │ 10     │
│  08.01 - 08.31 │ 旺季 │ 5280   │ 4280   │ 10     │
│  09.01 - 09.30 │ 平日 │ 3980   │ 2980   │ 不限   │
│  [+ 新增区间]                                      │
│                                                    │
│  (多档时每个区间按档位平铺多组价格)                 │
│                                    [保存]          │
└──────────────────────────────────────────────────┘

接口

场景 方法 路径 说明
查询价格日历 GET /admin/product/item/{id}/price-calendar 参数 month(yyyy-MM)、tierSeq(档位)
批量设置价格 POST /admin/product/item/{id}/price-calendar/batch 按日期范围+档位UPSERT
删除价格区间 DELETE /admin/product/item/{id}/price-calendar 按日期范围+档位删除
报价测算 POST /admin/product/item/{id}/quote 输入日期+人数,预览总价

批量设置请求

{
  "productId": 123,
  "version": 3,           // 草稿用version,已上架用priceVersion
  "startDate": "2026-07-01",
  "endDate": "2026-07-31",
  "tierSeq": 1,           // 档位序号,单一=1
  "priceType": "PEAK",    // NORMAL/PEAK/HOLIDAY/SPECIAL
  "adultSellPrice": 4980,
  "childSellPrice": 3980,
  "toddlerDiscount": -500, // 小童优惠额(负数),小童价=儿童价-|优惠额|
  "infantPrice": 0,        // 幼童价,0=免费
  "dailyStock": 10         // NULL=不限量
}

小蒙马 → 班期管理

┌──────────────────────────────────────────────────┐
│  班期列表                                          │
│  班期名   │ 出发日  │ 状态 │ 成人价 │ 已报/上限   │
│  第1期    │ 07.01  │ 报名中│ 4980  │ 8/20        │
│  第2期    │ 07.15  │ 报名中│ 4980  │ 3/20        │
│  [+ 新增] [批量创建]                               │
└──────────────────────────────────────────────────┘

接口

场景 方法 路径 说明
班期列表 GET /admin/product/item/{id}/schedule/list 返回全部班期
创建班期 POST /admin/product/item/{id}/schedule ScheduleSaveReqVO,不传batchId
修改班期 PUT /admin/product/item/{id}/schedule 传batchId
删除班期 DELETE /admin/product/item/{id}/schedule/{scheduleId} 有订单不可删
取消班期 POST /admin/product/item/{id}/schedule/{scheduleId}/cancel 有订单触发退款
批量创建 POST /admin/product/item/{id}/schedule/batch-create 按重复模式批量
团队查询 GET /admin/product/item/{id}/schedule/team?batchId=xxx 班期团队成员
团队保存 PUT /admin/product/item/{id}/schedule/team?batchId=xxx 全量替换

页面6产品编辑 — Step5 补充信息

┌──────────────────────────────────────────────────┐
│  费用包含  (自动推导+手动编辑)                     │
│  ☑ 门票 (自动) │ ☑ 住宿 (自动) │ ☐ 旅拍 (手动)  │
│                                                    │
│  费用不含                                          │
│  ☑ 个人消费 │ ☑ 自费项目                           │
│                                                    │
│  人群优惠                                          │
│  儿童门票: [免票▼]  住宿: [不占床▼]  餐饮: [半额▼]│
│  老人门票: [半价▼]  住宿: [同成人▼]                │
│                                                    │
│  车辆展示: [选择车型▼] (纯展示)                    │
│  退改政策: [选择模板▼]                              │
│  预订条款: [选择模板▼]                              │
│  装备建议: [富文本]                                 │
│  保险告知: ○含 ○不含 ○可选升级                     │
│                                                    │
│  产品卖点                                          │
│  快速理解: [富文本+图片]                            │
│  孩子经历: [富文本+图片]                            │
│  成长收获: [探索力] [协作力] [+添加]                │
│                                    [保存]          │
└──────────────────────────────────────────────────┘

接口

场景 方法 路径 说明
保存补充信息 PUT /admin/product/item/{id}/supplement ProductSupplementSaveReqVO
费用推导预览 GET /admin/product/item/{id}/fee-deduction-preview 根据行程自动计算费用包含
退改政策模板 GET /admin/product/refund-policy/enabled 下拉选择
预订条款模板 GET /admin/product/booking-terms/enabled 下拉选择
温馨提示模板 GET /admin/product/warm-tips/enabled 下拉选择

页面7产品线管理

┌──────────────────────────────────────────────────┐
│  [+ 新增产品线]                                    │
│  ┌──────┐ ┌──────┐ ┌──────┐                      │
│  │ 封面 │ │ 封面 │ │ 封面 │                      │
│  │ 名称 │ │ 名称 │ │ 名称 │                      │
│  │ 类型 │ │ 类型 │ │ 类型 │                      │
│  └──────┘ └──────┘ └──────┘                      │
└──────────────────────────────────────────────────┘

接口

场景 方法 路径 说明
产品线列表 GET /admin/product/line/list 分页
创建产品线 POST /admin/product/line name+productType+coverImageUrl+seasons+tags
编辑产品线 PUT /admin/product/line/{lineId}
删除产品线 DELETE /admin/product/line/{lineId} 有产品时不可删
简单列表(下拉) GET /admin/product/line/simple-list 返回 [{lineId, name, productType}]

三、C端小程序页面接口对照

页面1主题精选页产品线列表

接口GET /mp/product/line/list

  • 参数productType(可选)、season(可选)、page、pageSize
  • 返回产品线卡片列表name, description, coverImageUrl, startPrice, productType, seasons

页面2产品列表产品线下的产品

接口GET /mp/product/list

  • 参数lineId(产品线ID)、tag(产品标签Tab)、page、pageSize
  • 返回:产品卡片列表

产品卡片关键字段

{
  "productId": 123,
  "name": "额吉的故乡·亲子版",
  "subtitle": "6天5晚草原环线",
  "coverImageUrl": "https://...",
  "tripDays": 6,
  "productType": "CORE",
  "tags": ["亲子", "研学"],
  "startPrice": 3980,         // 起步价(¥X起)
  "startPriceLabel": "¥3980起/人"
}

页面3产品详情页

接口GET /mp/product/{productId}

  • 返回:MpProductDetailRespVO(聚合全部展示数据)

返回结构

{
  "productId": 123,
  "productType": "CORE",
  "name": "额吉的故乡·亲子版",
  "subtitle": "...",
  "coverImageUrl": "...",
  "carouselImages": ["...", "..."],
  "tripDays": 6,
  "tripNights": 5,
  "routeMapUrl": "...",
  "creatorAvatarUrl": "...",
  "creatorIntro": "...",
  
  "itinerary": [
    {
      "dayNumber": 1,
      "dayTitle": "海拉尔→额尔古纳",
      "description": "...",
      "coverImageUrl": "...",
      "breakfast": "HOTEL",
      "lunch": "SELF",
      "dinner": "SPECIAL",
      "nodes": [
        {
          "nodeType": "SCENIC",
          "nodeName": "莫日格勒河观景台",
          "startTime": "10:00",
          "durationMinutes": 60,
          "description": "..."
        }
      ]
    }
  ],
  
  "includedFees": [
    {"feeType": "TICKET", "name": "门票", "source": "AUTO"},
    {"feeType": "ACCOMMODATION", "name": "住宿", "source": "AUTO"}
  ],
  "excludedFees": [
    {"feeType": "PERSONAL", "name": "个人消费"}
  ],
  
  "crowdBenefit": {
    "childTicket": "FREE",
    "childAccommodation": "NO_BED",
    "childMeal": "HALF",
    "elderTicket": "HALF_PRICE",
    "tips": "..."
  }
}

页面4价格日历弹窗

接口GET /mp/product/{productId}/price-calendar

  • 参数month(yyyy-MM)
  • 返回:
{
  "days": [
    {
      "date": "2026-07-01",
      "priceType": "PEAK",
      "adultPrice": 4980,
      "childPrice": 3980,
      "isSelectable": true,     // 连续N天有价格且有库存
      "remainStock": 8          // null=不限量
    }
  ]
}

页面5报价计算选日期+人数后)

接口POST /mp/product/{productId}/quote

请求

{
  "departureDate": "2026-07-01",
  "adultCount": 2,
  "childCount": 1,
  "youngChildCount": 0,
  "babyCount": 0,
  "batchId": null,          // 小蒙马传班期ID
  "tierSeq": 1              // 多档时传档位序号
}

响应

{
  "totalAdultPrice": 9960,
  "totalChildPrice": 3980,
  "grandTotal": 13940,
  "dailyDetails": [
    {"date": "2026-07-01", "adultPrice": 4980, "childPrice": 3980},
    {"date": "2026-07-02", "adultPrice": 4980, "childPrice": 3980}
  ],
  "paymentType": "FULL",
  "stock": 8,
  "available": true
}

页面6小蒙马班期列表弹窗

接口GET /mp/product/{productId}/schedule/list

返回

[
  {
    "batchId": 456,
    "batchName": "第1期",
    "departureDate": "2026-07-01",
    "endDate": "2026-07-06",
    "enrollmentDeadline": "2026-06-30",
    "batchStatus": "ENROLLING",
    "adultPrice": 4980,
    "childPrice": 3980,
    "maxParticipants": 20,
    "enrolledCount": 8,
    "maxRooms": 10,
    "bookedRooms": 4
  }
]

四、字典值速查

字典 中文
product_type CORE 核心产品
product_type GROUP 小蒙马跟团游
product_type CUSTOM 私人定制
product_status DRAFT 草稿
product_status PENDING_REVIEW 待审核
product_status PUBLISHED 已上架
product_status UNPUBLISHED 已下架
product_status REJECTED 已驳回
product_status COMPLETED 已完成(定制)
product_status ORDERED 已下单(定制)
payment_type FULL 全款支付
payment_type DEPOSIT 订金+尾款
price_type NORMAL 平日
price_type PEAK 旺季
price_type HOLIDAY 节假日
price_type SPECIAL 特价
meal_option HOTEL 含(酒店)
meal_option CAMP 含(营地)
meal_option SPECIAL 含(特色餐)
meal_option SELF 自理
schedule_status ENROLLING 报名中
schedule_status NEARLY_FULL 即将满员
schedule_status FULL 已满
schedule_status FINISHED 已结束
schedule_status CANCELLED 已取消
insurance_notice INCLUDED 含保险
insurance_notice EXCLUDED 不含保险
insurance_notice OPTIONAL 可选升级

五、与旧服务的切换说明

  1. 管理端产品管理页面整体切换到v2接口,旧产品管理页面下线
  2. C端:小程序的产品列表/详情/报价/下单全部切换到v2接口
  3. 订单服务不改订单的Feign调用仍走旧接口产品v2会兼容旧Feign路径
  4. 旧产品数据清空:上线后旧产品数据全部删除,从零录入

六、联调注意事项

  1. 所有保存接口都要传version——忘传会返回400
  2. 行程整体保存——不是一个节点一个接口,是整棵行程树一次提交(PUT /itinerary
  3. 价格区间制vs班期制——看productTypeCORE/CUSTOM用价格日历接口,GROUP用班期接口
  4. 多档位——tierSeq从1开始递增,单一档位=1。价格日历和住宿都按tierSeq关联
  5. 小童价格特殊——前端传的是优惠额(负数),实际小童价=儿童价-|优惠额|
  6. C端只展示PUBLISHED产品——其他状态的产品C端接口不会返回
  7. Knife4j文档——本地启动服务后访问 http://localhost:{port}/doc.html 查看完整Swagger文档