diff --git a/changelogs/2026-04/2026-04-12_1538_273bef51_feat_product-v2_Co.md b/changelogs/2026-04/2026-04-12_1538_273bef51_feat_product-v2_Co.md new file mode 100644 index 0000000..9cdf4b5 --- /dev/null +++ b/changelogs/2026-04/2026-04-12_1538_273bef51_feat_product-v2_Co.md @@ -0,0 +1,1070 @@ +# 接口变更记录 — 2026-04-12 15:38 + +> **提交** `273bef51` · **作者** wx · **时间** 2026-04-12 15:38:54 +0800 +> +> feat(product-v2): Controller层(admin/mp分包,44个接口+完整Swagger文档) + +## 变更总览 + +- **hl-product-service-v2** + - 🆕 AdminProduct (+28 接口) + - 🆕 AdminProductLine (+5 接口) + - 🆕 AdminProductTemplate (+3 接口) + - 🆕 MpProduct (+6 接口) + - 🆕 MpProductLine (+2 接口) + +--- + +## hl-product-service-v2 + +### 🆕 新增: AdminProduct + +### 📌 `POST` /admin/product/item — 创建产品 + +> 创建产品草稿,自动生成产品编号(C/G/D+日期+序号),返回产品ID。 + +**请求体** `ProductSaveReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `name` | `String` | 产品名称(≤30字) | +| `productType` | `String` | 产品类型(字典product_type: CORE=核心产品, GROUP=小蒙马跟团游, CUSTOM=私人定制) | +| `lineId` | `Long` | 产品线ID(从 /admin/product/line/simple-list 下拉获取,私人定制可空) | +| `tripDays` | `Integer` | 行程天数(可选,创建后可在Step1修改) | + +**响应** `Result` + +### 📌 `PUT` /admin/product/item/{id}/basic — Step1 基础信息保存 + +> 保存产品基础信息。需传version做乐观锁校验,版本不一致返回错误提示刷新。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `ProductBasicSaveReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID(路径参数自动设入,前端不传) | +| `version` | `Integer` | 乐观锁版本号(从详情接口获取,提交时传回做并发校验) | +| `name` | `String` | 产品名称(≤30字) | +| `subtitle` | `String` | 副标题(一句话卖点,≤128字) | +| `introduction` | `String` | 产品简介(富文本HTML) | +| `lineId` | `Long` | 产品线ID(从产品线下拉接口获取,私人定制可空) | +| `tripDays` | `Integer` | 行程天数(≥1,晚数自动=天数-1) | +| `tripNights` | `Integer` | 行程晚数(通常=天数-1,不传自动计算) | +| `tiers` | `List` | 档位列表(JSON数组,如标准/舒适/豪华3档) | +| `seasons` | `List` | 适用季节(字典product_season: spring=春, summer=夏, autumn=秋, winter=冬) | +| `tags` | `List` | 产品标签(字典product_tag,多选) | +| `coverImageUrl` | `String` | 封面图URL | +| `carouselImages` | `List` | 轮播图URL列表(≤10张) | +| `infantAgeMax` | `Integer` | 幼童年龄上限(默认1岁,幼童≤此值免费或特价) | +| `toddlerAgeMax` | `Integer` | 小童年龄上限(默认3岁,幼童<小童<儿童) | +| `childAgeMin` | `Integer` | 儿童年龄下限(默认4岁) | +| `childAgeMax` | `Integer` | 儿童年龄上限(默认12岁,超过按成人算) | +| `infantDefaultPrice` | `BigDecimal` | 幼童默认价(元,0=免费) | +| `isBooking` | `Boolean` | 是否预约产品(true=订金可不填出行人,false=正常下单) | +| `paymentType` | `String` | 支付方式(字典payment_type: FULL=全款支付, DEPOSIT=订金+尾款) | +| `depositRatio` | `Integer` | 订金比例(%,支付方式=DEPOSIT时使用,与固定额二选一) | +| `depositAmount` | `BigDecimal` | 订金固定额(元,支付方式=DEPOSIT时使用,与比例二选一) | +| `balanceDueDays` | `Integer` | 尾款支付期限(天,出发前N天需付清尾款) | +| `defaultDailyStock` | `Integer` | 每日库存默认值(NULL=不限量,用于价格日历预填) | +| `defaultRoomCount` | `Integer` | 默认房间数(小蒙马跟团游用,每个班期的初始房间数) | +| `minGroupSize` | `Integer` | 最低成团人数(0=不限,小蒙马用) | +| `minAdultPerOrder` | `Integer` | 每单最少成人数 | +| `maxAdultPerOrder` | `Integer` | 每单最多成人数 | +| `maxChildPerOrder` | `Integer` | 每单最多儿童数 | +| `showReview` | `Boolean` | 展示用户评价(true=产品详情页显示评价模块) | +| `showChatGroup` | `Boolean` | 展示群聊入口(true=显示加入群聊按钮) | +| `showTripTime` | `Boolean` | 展示行程时间(true=行程天显示起止时间) | +| `showTripDistance` | `Boolean` | 展示行程距离(true=行程天显示里程数) | +| `creatorAvatarUrl` | `String` | 创建者头像URL(产品详情页展示的领队/设计师头像) | +| `creatorIntro` | `String` | 创建者简介(≤512字) | +| `customizerId` | `Long` | 定制师ID(私人定制产品关联的定制师) | +| `customerName` | `String` | 客户姓名(私人定制的客户) | +| `contactPhone` | `String` | 联系电话(加密存储) | +| `departureDate` | `LocalDate` | 出发日期(私人定制的固定出发日期,格式yyyy-MM-dd) | +| `mchId` | `String` | 商户ID(微信支付商户号,多商户时指定) | +| `safetyItems` | `List` | 安全保障项(JSON数组 [{icon,title,description}]) | +| `photographyItems` | `List` | 摄影跟拍项(JSON数组 [{name,description}]) | +| `diningHighlights` | `List` | 餐饮亮点(JSON数组 [{name,description}]) | +| `tierSeq` | `Integer` | 档位序号(1=标准,2=舒适,3=豪华) | +| `tierName` | `String` | 档位名称 | + +**响应** `Result` + +### 📌 `PUT` /admin/product/item/{id}/itinerary — Step2 行程整体保存 + +> 整体保存行程天+节点+住宿+路线点。支持新增/更新/删除(EntityDiffUtil对比)。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `ProductItinerarySaveReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `version` | `Integer` | 乐观锁版本号 | +| `days` | `List` | 行程天列表 | +| `dayId` | `Long` | 行程天ID(已有则传,新增不传) | +| `dayNumber` | `Integer` | 天序号 | +| `dayTitle` | `String` | 当日标题 | +| `quoteText` | `String` | 当日金句 | +| `description` | `String` | 行程描述 | +| `coverImageUrl` | `String` | 当日封面图 | +| `breakfast` | `String` | 早餐(HOTEL/CAMP/SPECIAL/SELF) | +| `lunch` | `String` | 午餐 | +| `dinner` | `String` | 晚餐 | +| `diningRemark` | `String` | 餐饮备注 | +| `dailyMileage` | `BigDecimal` | 当日里程(km) | +| `mileageManualOverride` | `Boolean` | 里程是否手动覆盖 | +| `photoUrls` | `List` | 照片集 | +| `customFields` | `Object` | 自定义概况 | +| `nodes` | `List` | 活动节点列表 | +| `hotels` | `List` | 住宿列表 | +| `routePoints` | `List` | 途经路线点列表 | +| `nodeId` | `Long` | 节点ID(已有则传,新增不传) | +| `sortOrder` | `Integer` | 排序 | +| `nodeType` | `String` | 节点类型(SCENIC/ACTIVITY/RESTAURANT/SERVICE/CUSTOM等) | +| `nodeName` | `String` | 节点名称 | +| `resourceType` | `String` | 资源类型 | +| `resourceId` | `Long` | 资源ID | +| `startTime` | `String` | 开始时间(如10:00) | +| `durationMinutes` | `Integer` | 时长(分钟) | +| `description` | `String` | 描述 | +| `images` | `List` | 图片列表 | +| `emojiIcon` | `String` | 图标 | +| `extraData` | `Object` | 扩展数据 | +| `id` | `Long` | 住宿ID(已有则传,新增不传) | +| `tierSeq` | `Integer` | 档位序号(无档位=1) | +| `hotelId` | `Long` | 酒店ID | +| `roomTypeId` | `Long` | 房型ID(私人定制用) | +| `roomCount` | `Integer` | 房间数(私人定制用) | +| `isDefault` | `Boolean` | 是否默认推荐 | +| `sortOrder` | `Integer` | 排序 | +| `resourceId` | `Long` | 资源ID(可空=自定义地点) | +| `name` | `String` | 地点名称 | +| `longitude` | `BigDecimal` | 经度 | +| `latitude` | `BigDecimal` | 纬度 | +| `sortOrder` | `Integer` | 排序 | + +**响应** `Result` + +### 📌 `POST` /admin/product/item/{id}/itinerary/day — Step2 增加一天 + +> 在行程末尾追加一天,自动编号。返回新天的dayId。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result` + +### 📌 `DELETE` /admin/product/item/{id}/itinerary/day/{dayNumber} — Step2 删除一天 + +> 删除指定天序号的行程天,级联删除节点+住宿+路线点,后续天序号自动-1。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + - `{dayNumber}` (`Integer`) — 路径参数 + +**响应** `Result` + +### 📌 `PUT` /admin/product/item/{id}/route — Step3 路线与备品保存 + +> 保存路线总览(1:1)+备品清单(1:N)+额外成本项(1:N)。备品和成本项支持增删改。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `ProductRouteSaveReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `version` | `Integer` | 乐观锁版本号 | +| `routeName` | `String` | 路线名称 | +| `routeMapUrl` | `String` | 路线图URL(手动上传) | +| `totalMileage` | `BigDecimal` | 总里程(km) | +| `routeDescription` | `String` | 线路描述 | +| `vehicleModelId` | `Long` | 实际用车车型ID | +| `staffCostIds` | `List` | 服务人员成本ID列表 | +| `supplies` | `List` | 备品列表 | +| `extraCosts` | `List` | 额外成本项列表 | +| `id` | `Long` | 备品ID(已有则传,新增不传) | +| `suppliesResourceId` | `Long` | 备品资源ID(可空=自定义) | +| `suppliesName` | `String` | 备品名称 | +| `category` | `String` | 分类 | +| `hasCost` | `Boolean` | 是否涉及成本 | +| `billingType` | `String` | 计费方式(PER_PERSON/PER_QUANTITY) | +| `unitPrice` | `BigDecimal` | 单价 | +| `quantity` | `Integer` | 数量 | +| `sortOrder` | `Integer` | 排序 | +| `id` | `Long` | 费用项ID(已有则传,新增不传) | +| `resourceType` | `String` | 资源类型 | +| `resourceId` | `Long` | 资源ID | +| `costItemId` | `Long` | 费用项ID | +| `name` | `String` | 名称 | +| `description` | `String` | 说明 | +| `unitPrice` | `BigDecimal` | 单价 | +| `quantity` | `Integer` | 数量 | +| `daily` | `Boolean` | 是否按天计费 | +| `sortOrder` | `Integer` | 排序 | + +**响应** `Result` + +### 📌 `GET` /admin/product/item/{id}/price-calendar — Step4 查询价格日历 + +> 按月份查询价格日历。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | `Long` | | +| `productId` | `Long` | | +| `tierSeq` | `Integer` | | +| `rangeId` | `Long` | | +| `date` | `LocalDate` | | +| `priceType` | `String` | | +| `adultSellPrice` | `BigDecimal` | | +| `childSellPrice` | `BigDecimal` | | +| `toddlerDiscount` | `BigDecimal` | | +| `infantPrice` | `BigDecimal` | | +| `dailyStock` | `Integer` | | +| `sold` | `Integer` | | + +### 📌 `GET` /admin/product/item/{id}/schedule/list — Step4 班期列表 + +> 查询产品下所有班期。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `batchId` | `Long` | | +| `productId` | `Long` | | +| `batchNo` | `String` | | +| `batchLabel` | `String` | | +| `batchName` | `String` | | +| `productStockLimit` | `Integer` | | +| `adultPrice` | `BigDecimal` | | +| `childPrice` | `BigDecimal` | | +| `toddlerDiscount` | `BigDecimal` | | +| `infantPrice` | `BigDecimal` | | +| `departureDate` | `LocalDate` | | +| `endDate` | `LocalDate` | | +| `enrollmentDeadline` | `LocalDate` | | +| `maxParticipants` | `Integer` | | +| `enrolledCount` | `Integer` | | +| `maxRooms` | `Integer` | | +| `bookedRooms` | `Integer` | | +| `batchStatus` | `String` | | +| `sortOrder` | `Integer` | | +| `remark` | `String` | | +| `version` | `Integer` | | +| `createBy` | `Long` | | + +### 📌 `POST` /admin/product/item/{id}/price-calendar/batch — Step4 价格日历批量设置 + +> 按日期范围+档位批量设置价格(UPSERT)。已上架产品用priceVersion,草稿用version。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `PriceCalendarBatchReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `version` | `Integer` | 版本号(草稿用version/已上架用priceVersion) | +| `startDate` | `LocalDate` | 开始日期 | +| `endDate` | `LocalDate` | 结束日期 | +| `tierSeq` | `Integer` | 档位序号(默认1) | +| `priceType` | `String` | 价格类型(NORMAL/PEAK/HOLIDAY/SPECIAL) | +| `adultSellPrice` | `BigDecimal` | 成人售价 | +| `childSellPrice` | `BigDecimal` | 儿童售价 | +| `toddlerDiscount` | `BigDecimal` | 小童优惠额(负数) | +| `infantPrice` | `BigDecimal` | 幼童售价(0=免费) | +| `dailyStock` | `Integer` | 每日库存上限(NULL=不限量) | + +**响应** `Result` + +### 📌 `DELETE` /admin/product/item/{id}/price-calendar — Step4 价格日历删除 + +> 按日期范围+档位删除价格日历(物理删除)。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `PriceCalendarBatchReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `version` | `Integer` | 版本号(草稿用version/已上架用priceVersion) | +| `startDate` | `LocalDate` | 开始日期 | +| `endDate` | `LocalDate` | 结束日期 | +| `tierSeq` | `Integer` | 档位序号(默认1) | +| `priceType` | `String` | 价格类型(NORMAL/PEAK/HOLIDAY/SPECIAL) | +| `adultSellPrice` | `BigDecimal` | 成人售价 | +| `childSellPrice` | `BigDecimal` | 儿童售价 | +| `toddlerDiscount` | `BigDecimal` | 小童优惠额(负数) | +| `infantPrice` | `BigDecimal` | 幼童售价(0=免费) | +| `dailyStock` | `Integer` | 每日库存上限(NULL=不限量) | + +**响应** `Result` + +### 📌 `POST` /admin/product/item/{id}/schedule — Step4 创建班期 + +> 创建新班期,batchId不传。自动生成班期编号、计算结束日期和报名截止日。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `ScheduleSaveReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `batchId` | `Long` | 班期ID(修改时传) | +| `batchName` | `String` | 班期名称 | +| `departureDate` | `LocalDate` | 出发日期 | +| `enrollmentDeadline` | `LocalDate` | 报名截止日(默认出发前1天) | +| `adultPrice` | `BigDecimal` | 成人价 | +| `childPrice` | `BigDecimal` | 儿童价 | +| `toddlerDiscount` | `BigDecimal` | 小童优惠额(负数) | +| `infantPrice` | `BigDecimal` | 幼童价 | +| `maxParticipants` | `Integer` | 最大参与人数 | +| `maxRooms` | `Integer` | 总房间数(0=不限) | +| `productStockLimit` | `Integer` | 产品库存上限 | +| `remark` | `String` | 备注 | + +**响应** `Result` + +### 📌 `DELETE` /admin/product/item/{id}/schedule/{scheduleId} — Step4 删除班期 + +> 删除班期(已有报名订单不允许删除)。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + - `{scheduleId}` (`Long`) — 路径参数 + +**响应** `Result` + +### 📌 `PUT` /admin/product/item/{id}/schedule — Step4 修改班期 + +> 修改已有班期,batchId在body中传。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `ScheduleSaveReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `batchId` | `Long` | 班期ID(修改时传) | +| `batchName` | `String` | 班期名称 | +| `departureDate` | `LocalDate` | 出发日期 | +| `enrollmentDeadline` | `LocalDate` | 报名截止日(默认出发前1天) | +| `adultPrice` | `BigDecimal` | 成人价 | +| `childPrice` | `BigDecimal` | 儿童价 | +| `toddlerDiscount` | `BigDecimal` | 小童优惠额(负数) | +| `infantPrice` | `BigDecimal` | 幼童价 | +| `maxParticipants` | `Integer` | 最大参与人数 | +| `maxRooms` | `Integer` | 总房间数(0=不限) | +| `productStockLimit` | `Integer` | 产品库存上限 | +| `remark` | `String` | 备注 | + +**响应** `Result` + +### 📌 `POST` /admin/product/item/{id}/schedule/batch-create — Step4 班期批量创建 + +> 按重复模式批量创建班期(最多52个,跨度≤12个月)。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `ScheduleBatchCreateReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `startDate` | `LocalDate` | 开始日期 | +| `endDate` | `LocalDate` | 结束日期 | +| `repeatMode` | `String` | 重复模式(WEEKLY/BIWEEKLY/MONTHLY) | +| `dayOfWeek` | `Integer` | 星期几(1=周一...7=周日) | +| `adultPrice` | `BigDecimal` | 成人价 | +| `childPrice` | `BigDecimal` | 儿童价 | +| `maxParticipants` | `Integer` | 最大参与人数 | +| `maxRooms` | `Integer` | 总房间数 | + +**响应** `Result` + +### 📌 `POST` /admin/product/item/{id}/schedule/{scheduleId}/cancel — Step4 取消班期 + +> 取消班期(有报名订单则标记为CANCELLING触发退款,无订单直接CANCELLED)。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + - `{scheduleId}` (`Long`) — 路径参数 + +**响应** `Result` + +### 📌 `GET` /admin/product/item/{id}/schedule/team — 班期团队查询 + +> 查询指定班期的团队成员列表。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | `Long` | | +| `batchId` | `Long` | | +| `productId` | `Long` | | +| `staffId` | `Long` | | +| `staffRole` | `String` | | +| `staffName` | `String` | | +| `staffPhone` | `String` | | +| `remark` | `String` | | +| `sortOrder` | `Integer` | | + +### 📌 `PUT` /admin/product/item/{id}/schedule/team — 班期团队保存 + +> 全量保存班期团队成员(全删全插策略,每次传完整列表)。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `ScheduleTeamSaveReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `staffList` | `List` | 团队成员列表 | +| `staffId` | `Long` | 员工ID | +| `staffRole` | `String` | 员工角色 | +| `staffName` | `String` | 员工名称 | +| `staffPhone` | `String` | 员工电话 | +| `remark` | `String` | 备注 | +| `sortOrder` | `Integer` | 排序 | + +**响应** `Result` + +### 📌 `POST` /admin/product/item/{id}/quote — 报价计算 + +> 根据出发日期+人数+档位计算总价,返回每日明细。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `QuoteReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `departureDate` | `LocalDate` | 出发日期 | +| `adultCount` | `Integer` | 成人数 | +| `batchId` | `Long` | 班期ID(小蒙马用) | + +**响应** `Result` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `totalAdultPrice` | `BigDecimal` | 成人总价 | +| `totalChildPrice` | `BigDecimal` | 儿童总价 | +| `grandTotal` | `BigDecimal` | 总价合计 | +| `dailyDetails` | `List` | 每日价格明细 | +| `date` | `String` | 日期 | +| `adultPrice` | `BigDecimal` | 成人价 | +| `childPrice` | `BigDecimal` | 儿童价 | + +### 📌 `PUT` /admin/product/item/{id}/supplement — Step5 补充信息保存 + +> 保存费用包含/不含/自定义项+人群优惠+补充配置(退改/车辆/保险/卖点等)。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `ProductSupplementSaveReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `version` | `Integer` | 乐观锁版本号 | +| `includedFees` | `List` | 费用包含列表 | +| `excludedFees` | `List` | 费用不含列表 | +| `customFees` | `List` | 自定义费用列表 | +| `feeIgnoredTypes` | `List` | 费用推导已忽略类型 | +| `childTicket` | `String` | 儿童门票优惠(FREE/HALF_PRICE/FULL_PRICE) | +| `childAccommodation` | `String` | 儿童住宿(BED/NO_BED) | +| `childMeal` | `String` | 儿童餐饮(FULL/HALF) | +| `elderTicket` | `String` | 老人门票优惠 | +| `elderAccommodation` | `String` | 老人住宿 | +| `crowdBenefitTips` | `String` | 人群优惠温馨提示 | +| `vehicleModelIds` | `List` | 展示车型ID列表 | +| `vehicleRuleText` | `String` | 用车规则说明 | +| `refundPolicyIds` | `Object` | 退改政策映射 | +| `bookingTermsId` | `Long` | 预订条款ID | +| `warmTipsId` | `Long` | 温馨提示ID | +| `equipmentAdvice` | `String` | 装备建议(富文本) | +| `insuranceNotice` | `String` | 保险告知(INCLUDED/EXCLUDED/OPTIONAL) | +| `insuranceSchemeId` | `Long` | 保险方案ID | +| `contractSchemeId` | `Long` | 合同方案ID | +| `quickUnderstand` | `Object` | 快速理解 | +| `childExperience` | `Object` | 孩子经历 | +| `growthGains` | `List` | 成长收获 | +| `id` | `Long` | 费用项ID(已有则传) | +| `feeType` | `String` | 费用类型 | +| `source` | `String` | 来源(AUTO/MANUAL) | +| `name` | `String` | 名称 | +| `description` | `String` | 说明 | +| `unitPrice` | `BigDecimal` | 单价(自定义费用) | +| `unit` | `String` | 单位(自定义费用) | +| `sortOrder` | `Integer` | 排序 | + +**响应** `Result` + +### 📌 `GET` /admin/product/item/{id} — 产品详情 + +> 聚合全部5步数据(主表+基础+行程+路线+价格+补充),用于编辑页回显。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `productNo` | `String` | 产品编号 | +| `productType` | `String` | 产品类型 | +| `status` | `String` | 产品状态 | +| `version` | `Integer` | 乐观锁版本号 | +| `priceVersion` | `Integer` | 调价版本号 | +| `createBy` | `Long` | 创建人ID | +| `publishedAt` | `LocalDateTime` | 上架时间 | +| `createTime` | `LocalDateTime` | 创建时间 | +| `name` | `String` | 产品名称 | +| `subtitle` | `String` | 副标题 | +| `introduction` | `String` | 产品简介 | +| `lineId` | `Long` | 产品线ID | +| `tripDays` | `Integer` | 行程天数 | +| `tripNights` | `Integer` | 行程晚数 | +| `coverImageUrl` | `String` | 封面图 | +| `tiers` | `Object` | 档位列表 | +| `seasons` | `Object` | 产品季节 | +| `tags` | `Object` | 产品标签 | +| `carouselImages` | `Object` | 轮播图 | +| `infantAgeMax` | `Integer` | 幼童年龄上限 | +| `toddlerAgeMax` | `Integer` | 小童年龄上限 | +| `childAgeMin` | `Integer` | 儿童年龄下限 | +| `childAgeMax` | `Integer` | 儿童年龄上限 | +| `isBooking` | `Boolean` | 是否预约产品 | +| `paymentType` | `String` | 支付方式 | +| `depositRatio` | `Integer` | 订金比例 | +| `depositAmount` | `BigDecimal` | 订金固定额 | +| `showReview` | `Boolean` | 展示评价 | +| `creatorAvatarUrl` | `String` | 创建者头像 | +| `creatorIntro` | `String` | 创建者简介 | +| `customizerId` | `Long` | 定制师ID | +| `customerName` | `String` | 客户姓名 | +| `departureDate` | `LocalDate` | 出发日期(定制) | +| `mchId` | `String` | 商户ID | +| `itinerary` | `List` | 行程天列表 | +| `routeInfo` | `ProductRouteInfoDO` | 路线信息 | +| `supplies` | `List` | 备品列表 | +| `extraCosts` | `List` | 额外成本项 | +| `schedules` | `List` | 班期列表 | +| `includedFees` | `List` | 费用包含 | +| `excludedFees` | `List` | 费用不含 | +| `customFees` | `List` | 自定义费用 | +| `crowdBenefit` | `ProductCrowdBenefitDO` | 人群优惠 | +| `supplement` | `ProductSupplementDO` | 补充配置 | + +### 📌 `GET` /admin/product/item/list — 产品分页列表 + +> 支持按名称关键词/产品类型/产品线/状态筛选。 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `productNo` | `String` | 产品编号 | +| `productType` | `String` | 产品类型 | +| `name` | `String` | 产品名称 | +| `coverImageUrl` | `String` | 封面图 | +| `tripDays` | `Integer` | 行程天数 | +| `status` | `String` | 产品状态 | +| `version` | `Integer` | 版本号 | +| `createBy` | `Long` | 创建人ID | +| `publishedAt` | `LocalDateTime` | 上架时间 | +| `createTime` | `LocalDateTime` | 创建时间 | +| `updateTime` | `LocalDateTime` | 更新时间 | + +### 📌 `DELETE` /admin/product/item/{id} — 删除产品 + +> 级联删除产品+全部子表。仅草稿/已驳回/已下架/已完成状态可删,已上架/待审核需先下架。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result` + +### 📌 `GET` /admin/product/item/{id}/operation-logs — 产品操作记录 + +> 查询产品的全部操作记录(按时间倒序),审计保留不随产品删除。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | `Long` | 记录ID | +| `action` | `String` | 操作类型 | +| `detail` | `String` | 详情/驳回原因 | +| `step` | `String` | 步骤号 | +| `operatorId` | `Long` | 操作人ID | +| `operatorName` | `String` | 操作人名称 | +| `createTime` | `LocalDateTime` | 操作时间 | + +### 📌 `POST` /admin/product/item/{id}/action — 产品状态变更 + +> 通过COLA状态机驱动状态流转。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `ProductActionReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `action` | `String` | 操作动作(SUBMIT_PUBLISH/WITHDRAW/DIRECT_PUBLISH/UNPUBLISH/FORCE_UNPUBLISH/COMPLETE) | +| `remark` | `String` | 备注/驳回原因(可选) | + +**响应** `Result` + +### 📌 `POST` /admin/product/item/{id}/copy — 复制产品 + +> 深拷贝产品+全部子表(主表+6张1:1/1:N子表+行程数据),新产品为草稿状态。返回新产品ID。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result` + +### 📌 `PUT` /admin/product/item/{id}/transfer — 转让产品 + +> 更换产品创建人(仅超管可操作)。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `ProductTransferReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `newCreatorId` | `Long` | 新创建人ID | + +**响应** `Result` + +### 📌 `GET` /admin/product/item/{id}/validate-publish — 上架预检 + +> 5级校验:L1基础字段→L2内容完整→L3定价→L4条款→L5资源。返回问题列表,空=全部通过。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result>` + +### 📌 `GET` /admin/product/item/{id}/fee-deduction-preview — 费用推导预览 + +> 根据行程节点类型自动推导费用包含项(景区→门票,酒店→住宿等),排除已忽略类型。 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `feeType` | `String` | 费用类型标签 | +| `name` | `String` | 费用名称 | +| `source` | `String` | 来源(AUTO=自动推导) | +| `sourceNodeName` | `String` | 来源节点名称 | + +### 🆕 新增: AdminProductLine + +### 📌 `POST` /admin/product/line — 创建/修改产品线 + +**请求体** `ProductLineSaveReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `lineId` | `Long` | 产品线ID(修改时传) | +| `name` | `String` | 名称 | +| `productType` | `String` | 绑定产品类型 | +| `description` | `String` | 描述 | +| `seasons` | `List` | 适用季节 | +| `tags` | `List` | 标签 | +| `coverImageUrl` | `String` | 封面图 | +| `sortOrder` | `Integer` | 排序 | +| `status` | `String` | 状态(ACTIVE/INACTIVE) | + +**响应** `Result` + +### 📌 `GET` /admin/product/line/{id} — 产品线详情 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `lineId` | `Long` | 产品线ID | +| `name` | `String` | 产品线名称 | +| `productType` | `String` | 绑定产品类型 | +| `description` | `String` | 描述 | +| `seasons` | `Object` | 适用季节 | +| `tags` | `Object` | 产品线标签 | +| `coverImageUrl` | `String` | 封面图 | +| `sortOrder` | `Integer` | 排序 | +| `status` | `String` | 状态 | +| `createBy` | `Long` | 创建人ID | +| `createTime` | `LocalDateTime` | 创建时间 | + +### 📌 `GET` /admin/product/line/list — 产品线分页列表 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `lineId` | `Long` | 产品线ID | +| `name` | `String` | 产品线名称 | +| `productType` | `String` | 绑定产品类型 | +| `description` | `String` | 描述 | +| `seasons` | `Object` | 适用季节 | +| `tags` | `Object` | 产品线标签 | +| `coverImageUrl` | `String` | 封面图 | +| `sortOrder` | `Integer` | 排序 | +| `status` | `String` | 状态 | +| `createBy` | `Long` | 创建人ID | +| `createTime` | `LocalDateTime` | 创建时间 | + +### 📌 `DELETE` /admin/product/line/{id} — 删除产品线 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result` + +### 📌 `GET` /admin/product/line/simple-list — 产品线下拉选项 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `lineId` | `Long` | 产品线ID | +| `name` | `String` | 产品线名称 | + +### 🆕 新增: AdminProductTemplate + +### 📌 `GET` /admin/product/booking-terms/enabled — 预订条款下拉 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `termsId` | `Long` | 条款ID | +| `termsName` | `String` | 条款名称 | + +### 📌 `GET` /admin/product/warm-tips/enabled — 温馨提示下拉 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `tipsId` | `Long` | 提示ID | +| `tipsName` | `String` | 提示名称 | + +### 📌 `GET` /admin/product/safety-template/enabled — 安全保障模板下拉 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `templateId` | `Long` | 模板ID | +| `name` | `String` | 模板名称 | +| `items` | `String` | 具体条目(JSON) | + +### 🆕 新增: MpProduct + +### 📌 `GET` /mp/product/{id} — 产品详情 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `productType` | `String` | 产品类型 | +| `name` | `String` | 产品名称 | +| `subtitle` | `String` | 副标题 | +| `introduction` | `String` | 产品简介 | +| `coverImageUrl` | `String` | 封面图 | +| `carouselImages` | `List` | 轮播图 | +| `tripDays` | `Integer` | 行程天数 | +| `tripNights` | `Integer` | 行程晚数 | +| `itinerary` | `List` | 行程天列表 | +| `includedFees` | `List` | 费用包含 | +| `excludedFees` | `List` | 费用不含 | +| `crowdBenefit` | `CrowdBenefitInfo` | 人群优惠 | +| `routeMapUrl` | `String` | 路线图URL | +| `creatorAvatarUrl` | `String` | 创建者头像 | +| `creatorIntro` | `String` | 创建者简介 | +| `dayNumber` | `Integer` | 天序号 | +| `dayTitle` | `String` | 标题 | +| `description` | `String` | 描述 | +| `coverImageUrl` | `String` | 封面图 | +| `breakfast` | `String` | 早餐 | +| `lunch` | `String` | 午餐 | +| `dinner` | `String` | 晚餐 | +| `nodes` | `List` | 节点列表 | +| `nodeType` | `String` | 节点类型 | +| `nodeName` | `String` | 节点名称 | +| `startTime` | `String` | 开始时间 | +| `durationMinutes` | `Integer` | 时长(分钟) | +| `description` | `String` | 描述 | +| `childTicket` | `String` | 儿童门票 | +| `childAccommodation` | `String` | 儿童住宿 | +| `childMeal` | `String` | 儿童餐饮 | +| `elderTicket` | `String` | 老人门票 | +| `tips` | `String` | 温馨提示 | +| `name` | `String` | 名称 | +| `description` | `String` | 说明 | + +### 📌 `GET` /mp/product/{id}/price-calendar — 价格日历 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `days` | `List` | 日历列表 | +| `date` | `String` | 日期(yyyy-MM-dd) | +| `priceType` | `String` | 价格类型 | +| `adultPrice` | `BigDecimal` | 成人售价 | +| `childPrice` | `BigDecimal` | 儿童售价 | +| `isSelectable` | `Boolean` | 是否可选(有库存且未过期) | +| `remainStock` | `Integer` | 剩余库存(NULL=不限量) | + +### 📌 `GET` /mp/product/{id}/schedules — 班期列表 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `batchId` | `Long` | | +| `productId` | `Long` | | +| `batchNo` | `String` | | +| `batchLabel` | `String` | | +| `batchName` | `String` | | +| `productStockLimit` | `Integer` | | +| `adultPrice` | `BigDecimal` | | +| `childPrice` | `BigDecimal` | | +| `toddlerDiscount` | `BigDecimal` | | +| `infantPrice` | `BigDecimal` | | +| `departureDate` | `LocalDate` | | +| `endDate` | `LocalDate` | | +| `enrollmentDeadline` | `LocalDate` | | +| `maxParticipants` | `Integer` | | +| `enrolledCount` | `Integer` | | +| `maxRooms` | `Integer` | | +| `bookedRooms` | `Integer` | | +| `batchStatus` | `String` | | +| `sortOrder` | `Integer` | | +| `remark` | `String` | | +| `version` | `Integer` | | +| `createBy` | `Long` | | + +### 📌 `POST` /mp/product/{id}/quote — C端报价 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**请求体** `QuoteReqVO` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `departureDate` | `LocalDate` | 出发日期 | +| `adultCount` | `Integer` | 成人数 | +| `batchId` | `Long` | 班期ID(小蒙马用) | + +**响应** `Result` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `totalAdultPrice` | `BigDecimal` | 成人总价 | +| `totalChildPrice` | `BigDecimal` | 儿童总价 | +| `grandTotal` | `BigDecimal` | 总价合计 | +| `dailyDetails` | `List` | 每日价格明细 | +| `date` | `String` | 日期 | +| `adultPrice` | `BigDecimal` | 成人价 | +| `childPrice` | `BigDecimal` | 儿童价 | + +### 📌 `GET` /mp/product/compare — 产品对比 + +**参数** + + - `ids` (`String`) — 查询参数 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `productType` | `String` | 产品类型 | +| `name` | `String` | 产品名称 | +| `subtitle` | `String` | 副标题 | +| `introduction` | `String` | 产品简介 | +| `coverImageUrl` | `String` | 封面图 | +| `carouselImages` | `List` | 轮播图 | +| `tripDays` | `Integer` | 行程天数 | +| `tripNights` | `Integer` | 行程晚数 | +| `itinerary` | `List` | 行程天列表 | +| `includedFees` | `List` | 费用包含 | +| `excludedFees` | `List` | 费用不含 | +| `crowdBenefit` | `CrowdBenefitInfo` | 人群优惠 | +| `routeMapUrl` | `String` | 路线图URL | +| `creatorAvatarUrl` | `String` | 创建者头像 | +| `creatorIntro` | `String` | 创建者简介 | +| `dayNumber` | `Integer` | 天序号 | +| `dayTitle` | `String` | 标题 | +| `description` | `String` | 描述 | +| `coverImageUrl` | `String` | 封面图 | +| `breakfast` | `String` | 早餐 | +| `lunch` | `String` | 午餐 | +| `dinner` | `String` | 晚餐 | +| `nodes` | `List` | 节点列表 | +| `nodeType` | `String` | 节点类型 | +| `nodeName` | `String` | 节点名称 | +| `startTime` | `String` | 开始时间 | +| `durationMinutes` | `Integer` | 时长(分钟) | +| `description` | `String` | 描述 | +| `childTicket` | `String` | 儿童门票 | +| `childAccommodation` | `String` | 儿童住宿 | +| `childMeal` | `String` | 儿童餐饮 | +| `elderTicket` | `String` | 老人门票 | +| `tips` | `String` | 温馨提示 | +| `name` | `String` | 名称 | +| `description` | `String` | 说明 | + +### 📌 `GET` /mp/product/{id}/tier-compare — 档位对比 + +**参数** + + - `{id}` (`Long`) — 路径参数 + +**响应** `Result>>` + +### 🆕 新增: MpProductLine + +### 📌 `GET` /mp/product-line/list — 产品线列表 + +**参数** + + - `season` (`String`) — 查询参数 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `lineId` | `Long` | 产品线ID | +| `name` | `String` | 产品线名称 | + +### 📌 `GET` /mp/product-line/{lineId}/products — 产品线下产品列表 + +**参数** + + - `{lineId}` (`Long`) — 路径参数 + +**响应** `Result>` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `productId` | `Long` | 产品ID | +| `name` | `String` | 产品名称 | +| `subtitle` | `String` | 副标题 | +| `coverImageUrl` | `String` | 封面图 | +| `tripDays` | `Integer` | 行程天数 | +| `productType` | `String` | 产品类型 | + +--- + +
+📁 全部变更文件 (点击展开) + +``` +A hl-product-service-v2/src/main/java/com/hulalv/product/controller/admin/AdminProductController.java +A hl-product-service-v2/src/main/java/com/hulalv/product/controller/admin/AdminProductLineController.java +A hl-product-service-v2/src/main/java/com/hulalv/product/controller/admin/AdminProductTemplateController.java +A hl-product-service-v2/src/main/java/com/hulalv/product/controller/mp/MpProductController.java +A hl-product-service-v2/src/main/java/com/hulalv/product/controller/mp/MpProductLineController.java +A hl-product-service-v2/src/main/java/com/hulalv/product/interceptor/AdminAuthInterceptor.java +``` +
\ No newline at end of file