diff --git a/changelogs/2026-04/2026-04-17_order-v2_mp-order-detail-refactor.md b/changelogs/2026-04/2026-04-17_order-v2_mp-order-detail-refactor.md index 1966484..16b4a40 100644 --- a/changelogs/2026-04/2026-04-17_order-v2_mp-order-detail-refactor.md +++ b/changelogs/2026-04/2026-04-17_order-v2_mp-order-detail-refactor.md @@ -1,94 +1,241 @@ -# 接口变更记录 — 2026-04-17 +# C端订单详情页接口变更 — 2026-04-17 -> **服务** hl-order-service-v2 · **类型** feat · **说明** C端订单详情页重构(详情接口返回值变更 + 新增6个弹窗接口) - -## 变更总览 - -- **hl-order-service-v2** - - MpOrder(C端): 详情接口返回值从 `OrderDetailVO` 改为 `MpOrderDetailVO` - - MpOrder(C端): 新增6个服务详情弹窗接口 +> **服务** hl-order-service-v2(端口8094)· **类型** feat +> **前端调用路径**: 网关(8080) → hl-mp-service(8085) → hl-order-service-v2(8094) +> **前端实际请求前缀**: `GET /mp/order/`(mp-service 需同步新增 BFF 转发层) --- -## hl-order-service-v2 +## 一、已改接口(1个) -### MpOrder(C端/内部) +### ✏️ `GET /mp/order/{orderId}` — 订单详情 + +**请求参数**:不变(路径参数 orderId) + +**返回值变更**:整体结构从 `OrderDetailVO` 改为 `MpOrderDetailVO` + +#### 新增字段 + +| 字段 | 类型 | 必有 | 说明 | 示例 | +| --- | --- | --- | --- | --- | +| `serviceItems` | `List` | ✅ | 服务包含摘要列表,每项含 type/title/summary/clickable | 见下方 | +| `notIncluded` | `String` | 否 | 不含说明 | `"往返大交通、个人消费"` | +| `priceBreakdown` | `Object` | 否 | 价格明细(新订单有值,历史订单null) | 见下方 | +| `balancePayMethodLabel` | `String` | 否 | 尾款方式标签 | `"落地后由领队收取"` | +| `refundProgress` | `Object` | 否 | 退款进度(退款中/已取消时有值) | 见下方 | +| `tripProgress` | `Object` | 否 | 行程进度(仅行程中状态有值) | 见下方 | +| `invoiceStatus` | `String` | ✅ | 发票状态 | `"AFTER_TRIP"` | +| `productTypeLabel` | `String` | ✅ | 产品类型中文 | `"核心产品"` | + +#### 删除的字段(前端不再使用) + +`totalCost`, `surchargeAmount`, `pendingUpgradeAmount`, `depositRatio`, `balanceProofUrl`, `balancePayMethod`, `remark`, `checklistConfirmed`, `unlockRequestedAt`, `confirmedAt`, `readyAt`, `creatorAdminId`, `creatorName`, `mchId`, `mchName`, `sharerOpenid`, `productSnapshot`, `expiryMinutes`, `timeline`, `processStatus`, `processStatusLabel`, `discountReason`, `discounts`, `surcharges`, `transportSegments`, `roomInfo`, `hotelAssignments`, `hotelAssignmentDetails`, `vehicleInfo`, `refundPolicy`, `refundPolicyId`, `contactName`, `contactPhone` + +#### 保留不变的字段 + +`orderId`, `orderNo`, `groupCode`, `productId`, `productName`, `productCoverUrl`, `productType`, `departureDate`, `returnDate`, `tripDays`, `tripNights`, `adultCount`, `childCount`, `youngChildCount`, `babyCount`, `createTime`, `status`, `statusLabel`, `displayStatus`, `displayStatusLabel`, `cancelReason`, `cancelledAt`, `completedAt`, `expiryTime`, `totalPrice`, `depositAmount`, `balanceAmount`, `paidAmount`, `paymentType`, `paymentTypeLabel`, `paidAt`, `payMethodLabel`, `discountAmount`, `refundAmount`, `contracts`, `insurances`, `travelers`, `todos`, `reviewed`, `customizerId`, `customizerName`, `customizerAvatarUrl`, `customizerQrUrl` --- -### ✏️ `GET` /internal/mp/order/{orderId} — 订单详情(**返回值变更**) +## 二、新增接口(6个弹窗) -**变更前**返回 `OrderDetailVO`(管理端通用),**变更后**返回 `MpOrderDetailVO`(C端专用)。 +> 用户点击 `serviceItems` 列表中某项时按需调用,不需要在详情页一次性请求。 +> 每个接口都需要传 userId(mp-service 从 token 中获取后透传)。 -**删除的字段**(管理端专属,C端不再返回): +### 🆕 `GET /mp/order/{orderId}/tickets` — 门票清单 -| 删除字段 | 说明 | -| --- | --- | -| `totalCost` | 总成本(仅管理员可见) | -| `surchargeAmount` | 费用增加总额 | -| `pendingUpgradeAmount` | 待确认差价 | -| `depositRatio` | 订金比例 | -| `balanceProofUrl` | 尾款凭证URL | -| `balancePayMethod` | 尾款支付方式代码 | -| `remark` | 订单备注 | -| `checklistConfirmed` | 清单确认状态 | -| `unlockRequestedAt` | 解锁请求时间 | -| `confirmedAt` | 确认时间 | -| `readyAt` | 就绪时间 | -| `creatorAdminId` | 创建人管理员ID | -| `creatorName` | 创建人姓名 | -| `mchId` / `mchName` | 商户号/名称 | -| `sharerOpenid` | 分享人openid | -| `productSnapshot` | 产品快照JSON | -| `expiryMinutes` | 支付时限分钟数 | -| `timeline` | 时间线列表 | -| `processStatus` / `processStatusLabel` | 内部流程状态 | -| `discountReason` | 优惠原因 | -| `discounts` / `surcharges` | 优惠/附加费明细列表 | -| `transportSegments` | 交通信息列表 | -| `roomInfo` / `hotelAssignments` / `hotelAssignmentDetails` | 房间/酒店分配 | -| `vehicleInfo` | 车辆信息JSON | -| `refundPolicy` / `refundPolicyId` | 退款政策 | -| `contactName` / `contactPhone` | 联系人(出行人列表中已有) | +**请求参数**:路径参数 `orderId` -**新增的字段**: +**返回值** `MpTicketListVO`: | 字段 | 类型 | 说明 | | --- | --- | --- | -| 🆕 `serviceItems` | `List` | 服务包含摘要列表,见下方结构 | -| 🆕 `notIncluded` | `String` | 不含说明(如"往返大交通、个人消费") | -| 🆕 `priceBreakdown` | `PriceBreakdownVO` | 价格明细(按人头+优惠+总额),见下方结构 | -| 🆕 `balancePayMethodLabel` | `String` | 尾款方式标签(如"落地后由领队收取") | -| 🆕 `refundProgress` | `RefundProgressVO` | 退款进度(退款中/已取消时有值),见下方结构 | -| 🆕 `tripProgress` | `TripProgressVO` | 行程进度(仅行程中状态有值),见下方结构 | -| 🆕 `invoiceStatus` | `String` | 发票状态:`ISSUED`=已开/`PENDING`=待开/`NOT_ISSUED`=未开/`AFTER_TRIP`=出行后开 | -| 🆕 `productTypeLabel` | `String` | 产品类型中文标签 | - -**保留不变的字段**: `orderId`, `orderNo`, `groupCode`, `productId`, `productName`, `productCoverUrl`, `productType`, `departureDate`, `returnDate`, `tripDays`, `tripNights`, `adultCount`, `childCount`, `youngChildCount`, `babyCount`, `createTime`, `status`, `statusLabel`, `displayStatus`, `displayStatusLabel`, `cancelReason`, `cancelledAt`, `completedAt`, `expiryTime`, `totalPrice`, `depositAmount`, `balanceAmount`, `paidAmount`, `paymentType`, `paymentTypeLabel`, `paidAt`, `payMethodLabel`, `discountAmount`, `refundAmount`, `contracts`, `insurances`, `travelers`, `todos`, `reviewed`, `customizerId`, `customizerName`, `customizerAvatarUrl`, `customizerQrUrl` - ---- - -### 新增结构体说明 - -#### ServiceItemVO(服务包含摘要项) +| `hint` | `String` | 顶部提示语 | +| `items` | `List` | 门票列表 | +| `items[].seq` | `Integer` | 序号(1开始) | +| `items[].scenicName` | `String` | 景点名称 | +| `items[].coverUrl` | `String` | 景点封面图(可能为空) | +| `items[].dayNumber` | `Integer` | 第几天 | +| `items[].originalPrice` | `BigDecimal` | 门票原价/人(可能为null) | +| `items[].included` | `Boolean` | 是否已含 | +| `totalValue` | `BigDecimal` | 门票总价值/人(可能为null) | +| `footerText` | `String` | 底部文案 | +**响应示例**: ```json { - "type": "HOTEL", - "title": "住宿", - "summary": "5晚精选当地酒店", - "clickable": true + "code": 200, + "data": { + "hint": "以下景点门票已包含在套餐内,无需另购", + "items": [ + { "seq": 1, "scenicName": "莫日格勒河", "coverUrl": null, "dayNumber": 2, "originalPrice": 80.00, "included": true }, + { "seq": 2, "scenicName": "根河湿地", "coverUrl": null, "dayNumber": 3, "originalPrice": 120.00, "included": true } + ], + "totalValue": 200.00, + "footerText": "全部已含·无需再付" + } } ``` +--- + +### 🆕 `GET /mp/order/{orderId}/hotels` — 住宿清单 + +**请求参数**:路径参数 `orderId` + +**返回值** `MpHotelListVO`: + | 字段 | 类型 | 说明 | | --- | --- | --- | -| `type` | `String` | `HOTEL`/`MEAL`/`VEHICLE`/`GUIDE`/`PHOTOGRAPHER`/`TICKET`/`INSURANCE` | -| `title` | `String` | 服务标题 | -| `summary` | `String` | 摘要描述 | -| `clickable` | `Boolean` | 是否可点击查看弹窗详情 | +| `hint` | `String` | 顶部提示语(如"5晚精选当地酒店") | +| `items` | `List` | 酒店列表 | +| `items[].hotelName` | `String` | 酒店名称 | +| `items[].coverUrl` | `String` | 酒店封面图(可能为空) | +| `items[].dayNumber` | `Integer` | 第几天入住 | +| `items[].hotelType` | `String` | 酒店类型(如"5星"/"特色民宿",可能为空) | +| `items[].roomType` | `String` | 房型(如"大床房",可能为空) | +| `footerNote` | `String` | 底部备注 | -#### PriceBreakdownVO(价格明细) +**响应示例**: +```json +{ + "code": 200, + "data": { + "hint": "5晚精选当地酒店", + "items": [ + { "hotelName": "海拉尔铂尔曼大酒店", "coverUrl": null, "dayNumber": 1, "hotelType": "", "roomType": "大床房" } + ], + "footerNote": "实际入住酒店以出行前确认为准" + } +} +``` + +--- + +### 🆕 `GET /mp/order/{orderId}/meals` — 餐饮清单 + +**请求参数**:路径参数 `orderId` + +**返回值** `MpMealListVO`: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `hint` | `String` | 顶部提示语(如"已含5早8正餐") | +| `specialMeals` | `List` | 特色正餐列表 | +| `specialMeals[].dayNumber` | `Integer` | 第几天 | +| `specialMeals[].mealName` | `String` | 餐名 | +| `specialMeals[].mealType` | `String` | 餐型(午餐/晚餐) | +| `specialMeals[].description` | `String` | 菜品描述 | +| `stats` | `Object` | 餐饮统计 | +| `stats.breakfast` | `String` | 早餐统计(如"5餐·酒店自助") | +| `stats.specialDinner` | `String` | 特色正餐统计(如"3餐") | +| `stats.regularDinner` | `String` | 日常正餐统计(如"5餐·当地特色餐厅") | +| `footerNote` | `String` | 底部备注 | + +**响应示例**: +```json +{ + "code": 200, + "data": { + "hint": "已含1早2正餐", + "specialMeals": [ + { "dayNumber": 2, "mealName": "手把肉宴", "mealType": "午餐", "description": "含奶茶/手把肉" } + ], + "stats": { "breakfast": "1餐·酒店自助", "specialDinner": "1餐", "regularDinner": "1餐·当地特色餐厅" }, + "footerNote": "对食物有过敏请提前告知领队" + } +} +``` + +--- + +### 🆕 `GET /mp/order/{orderId}/vehicle` — 用车详情 + +**请求参数**:路径参数 `orderId` + +**返回值** `MpVehicleDetailVO`: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `hint` | `String` | 顶部提示语 | +| `vehicleName` | `String` | 车型名称 | +| `coverUrl` | `String` | 车型图片(可能为空) | +| `description` | `String` | 车型描述/特点(可能为空) | +| `matchRule` | `String` | 匹配规则说明 | + +**响应示例**: +```json +{ + "code": 200, + "data": { + "hint": "一家一车不拼团·全程同一辆车", + "vehicleName": "商务车", + "coverUrl": null, + "description": "", + "matchRule": "匹配规则:根据出行人数自动配车" + } +} +``` + +--- + +### 🆕 `GET /mp/order/{orderId}/guide` — 领队详情 + +**请求参数**:路径参数 `orderId` + +**返回值** `MpGuideInfoVO`: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `name` | `String` | 姓名(可能为null) | +| `avatarUrl` | `String` | 头像(可能为null) | +| `role` | `String` | 角色标签(固定"主领队"或"领队") | +| `phone` | `String` | 电话(可能为null) | +| `remark` | `String` | 简介(可能为null) | + +> ⚠️ 产品服务快照扩展完成前,除 `role` 外所有字段可能为 null + +--- + +### 🆕 `GET /mp/order/{orderId}/photographer` — 摄影详情 + +**请求参数**:路径参数 `orderId` + +**返回值** `MpPhotographerInfoVO`: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `name` | `String` | 姓名(可能为null) | +| `avatarUrl` | `String` | 头像(可能为null) | +| `role` | `String` | 角色标签(固定"摄影师") | +| `phone` | `String` | 电话(可能为null) | +| `remark` | `String` | 简介(可能为null) | + +> ⚠️ 同领队,产品服务快照扩展完成前字段可能为 null + +--- + +## 三、新增结构体详细说明 + +### serviceItems(服务包含摘要) + +详情接口返回的 `serviceItems` 是一个数组,每项代表一个服务类别: + +```json +[ + { "type": "HOTEL", "title": "住宿", "summary": "5晚精选当地酒店", "clickable": true }, + { "type": "MEAL", "title": "餐饮", "summary": "5早 8正餐", "clickable": true }, + { "type": "VEHICLE", "title": "用车", "summary": "专属车辆 + 司机", "clickable": true }, + { "type": "GUIDE", "title": "领队", "summary": "全程陪同", "clickable": true }, + { "type": "PHOTOGRAPHER", "title": "摄影", "summary": "全程跟拍 + 精修照片", "clickable": true }, + { "type": "TICKET", "title": "门票", "summary": "4处景点门票", "clickable": true }, + { "type": "INSURANCE", "title": "保险", "summary": "旅行意外险", "clickable": false } +] +``` + +`clickable=true` 时点击应调用对应弹窗接口:`type=HOTEL` → `GET /mp/order/{orderId}/hotels`,以此类推。 + +### priceBreakdown(价格明细) ```json { @@ -105,7 +252,9 @@ } ``` -#### RefundProgressVO(退款进度) +> ⚠️ 仅新创建的订单有值,历史订单为 null,前端需做空判断 + +### refundProgress(退款进度) ```json { @@ -122,13 +271,11 @@ } ``` -| step.status | 说明 | -| --- | --- | -| `COMPLETED` | 已完成 | -| `ACTIVE` | 进行中(当前步骤) | -| `PENDING` | 待处理 | +step.status 取值:`COMPLETED`=已完成 / `ACTIVE`=进行中 / `PENDING`=待处理 -#### TripProgressVO(行程进度) +> ⚠️ 仅退款中/已取消且有退款时有值,其他状态为 null + +### tripProgress(行程进度) ```json { @@ -139,104 +286,31 @@ } ``` ---- +> ⚠️ 仅行程中(displayStatus=TRAVELLING)状态有值,其他状态为 null -### 🆕 `GET` /internal/mp/order/{orderId}/tickets — 门票清单弹窗 +### invoiceStatus 取值 -**返回** `MpTicketListVO`: - -```json -{ - "hint": "以下景点门票已包含在套餐内,无需另购", - "items": [ - { "seq": 1, "scenicName": "莫日格勒河", "coverUrl": "...", "dayNumber": 2, "originalPrice": 80.00, "included": true } - ], - "totalValue": 340.00, - "footerText": "全部已含·无需再付" -} -``` - -### 🆕 `GET` /internal/mp/order/{orderId}/hotels — 住宿清单弹窗 - -**返回** `MpHotelListVO`: - -```json -{ - "hint": "5晚精选当地酒店", - "items": [ - { "hotelName": "海拉尔铂尔曼大酒店", "coverUrl": "...", "dayNumber": 1, "hotelType": "5星", "roomType": "大床房" } - ], - "footerNote": "实际入住酒店以出行前确认为准" -} -``` - -### 🆕 `GET` /internal/mp/order/{orderId}/meals — 餐饮清单弹窗 - -**返回** `MpMealListVO`: - -```json -{ - "hint": "已含5早8正餐", - "specialMeals": [ - { "dayNumber": 2, "mealName": "蒙古包午餐·手把肉宴", "mealType": "午餐", "description": "含奶茶/手把肉/烤包子" } - ], - "stats": { "breakfast": "5餐·酒店自助", "specialDinner": "3餐", "regularDinner": "5餐·当地特色餐厅" }, - "footerNote": "对食物有过敏请提前告知领队" -} -``` - -### 🆕 `GET` /internal/mp/order/{orderId}/vehicle — 用车详情弹窗 - -**返回** `MpVehicleDetailVO`: - -```json -{ - "hint": "一家一车不拼团·全程同一辆车", - "vehicleName": "别克GL8·商务MPV", - "coverUrl": "...", - "description": "独立航空座椅·后排空调·USB充电", - "matchRule": "匹配规则:根据出行人数自动配车" -} -``` - -### 🆕 `GET` /internal/mp/order/{orderId}/guide — 领队详情弹窗 - -**返回** `MpGuideInfoVO`: - -```json -{ - "name": "巴图", - "avatarUrl": "...", - "role": "主领队", - "phone": "13800001111", - "remark": "10年呼伦贝尔带队经验" -} -``` - -> ⚠️ 产品服务快照扩展完成前,`name`/`avatarUrl`/`phone`/`remark` 可能为空 - -### 🆕 `GET` /internal/mp/order/{orderId}/photographer — 摄影详情弹窗 - -**返回** `MpPhotographerInfoVO`: - -```json -{ - "name": "李维", - "avatarUrl": "...", - "role": "摄影师", - "phone": "13900002222", - "remark": "自然人像专长" -} -``` - -> ⚠️ 同领队,产品服务快照扩展完成前字段可能为空 +| 值 | 说明 | +| --- | --- | +| `ISSUED` | 已开票 | +| `PENDING` | 开票中 | +| `NOT_ISSUED` | 未开票(已完成订单可申请) | +| `AFTER_TRIP` | 出行后可开 | --- -> ⚠️ **前端注意**: -> 1. 详情接口返回值结构已变更,需按新的 `MpOrderDetailVO` 字段对接 -> 2. 6个弹窗接口按需调用(用户点击 serviceItems 某项时才调) -> 3. `coverUrl`/`description`/`originalPrice` 等字段在产品服务快照扩展完成前可能为 `null` 或空字符串,请做空判断 -> 4. `refundProgress` 仅退款中/已取消且有退款时有值,其他状态为 `null` -> 5. `tripProgress` 仅行程中状态有值,其他状态为 `null` -> 6. `priceBreakdown` 仅新创建的订单有值(历史订单为 `null`),前端做空判断 +## 四、mp-service BFF 层需同步新增 + +以上6个弹窗接口目前在 order-service-v2 的 `/internal/mp/order/` 路径下已就绪。 +mp-service(hl-mp-service)需要新增对应的 BFF 转发层,路径映射如下: + +| 前端调用 | mp-service 转发到 | +| --- | --- | +| `GET /mp/order/{orderId}/tickets` | `GET /internal/mp/order/{orderId}/tickets?userId=xxx` | +| `GET /mp/order/{orderId}/hotels` | `GET /internal/mp/order/{orderId}/hotels?userId=xxx` | +| `GET /mp/order/{orderId}/meals` | `GET /internal/mp/order/{orderId}/meals?userId=xxx` | +| `GET /mp/order/{orderId}/vehicle` | `GET /internal/mp/order/{orderId}/vehicle?userId=xxx` | +| `GET /mp/order/{orderId}/guide` | `GET /internal/mp/order/{orderId}/guide?userId=xxx` | +| `GET /mp/order/{orderId}/photographer` | `GET /internal/mp/order/{orderId}/photographer?userId=xxx` | + +> mp-service 从用户 token 中提取 userId,作为查询参数透传给 order-service。 diff --git a/changelogs/2026-04/2026-04-20_order-v2_admin-assignment-get.md b/changelogs/2026-04/2026-04-20_order-v2_admin-assignment-get.md new file mode 100644 index 0000000..6a61072 --- /dev/null +++ b/changelogs/2026-04/2026-04-20_order-v2_admin-assignment-get.md @@ -0,0 +1,137 @@ +# 管理端订单车辆/酒店分配独立 GET 接口 — 2026-04-20 + +> **服务** hl-order-service-v2(端口 8094)· **类型** feat · **关联** PR #1009 / #1018 +> **使用场景**:管理端订单详情 · 行程安排 Tab · 懒加载车辆/酒店分配数据 + +--- + +## 一、新增接口(2 个) + +### 🆕 `GET /admin/order/{orderId}/vehicle-assignment` — 查询车辆分配列表 + +**功能**:行程安排 Tab 懒加载。司机电话默认脱敏返回(如 `138****1234`)。订单不存在返回业务异常。 + +**请求参数**: + +| 参数 | 位置 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | --- | +| `orderId` | Path | `Long` | ✅ | 订单ID | + +**返回值** `Result>`: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `assignmentId` | `Long` | 分配ID | +| `orderId` | `Long` | 订单ID | +| `vehicleType` | `String` | 车型名称(如"丰田普拉多") | +| `vehicleCount` | `Integer` | 车辆数 | +| `plateNumber` | `String` | 车牌号(如"蒙A·A1234") | +| `driverName` | `String` | 司机姓名 | +| `driverPhone` | `String` | 司机电话(脱敏,如 `138****1234`) | +| `remark` | `String` | 备注 | +| `createTime` | `LocalDateTime` | 创建时间 | + +**响应示例**: + +```json +{ + "code": 200, + "data": [ + { + "assignmentId": 900001, + "orderId": 700001, + "vehicleType": "丰田普拉多", + "vehicleCount": 1, + "plateNumber": "蒙A·A1234", + "driverName": "李师傅", + "driverPhone": "138****1234", + "remark": null, + "createTime": "2026-04-20 10:00:00" + } + ] +} +``` + +--- + +### 🆕 `GET /admin/order/{orderId}/hotel-assignment` — 查询酒店分配列表 + +**功能**:行程安排 Tab 懒加载。按家庭/日期返回结构化酒店分配。订单不存在返回业务异常。 + +**请求参数**: + +| 参数 | 位置 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | --- | +| `orderId` | Path | `Long` | ✅ | 订单ID | + +**返回值** `Result>`: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `assignmentId` | `Long` | 分配ID | +| `orderId` | `Long` | 订单ID | +| `familyIndex` | `Integer` | 家庭序号(从1开始) | +| `hotelId` | `Long` | 酒店ID(资源服务) | +| `hotelName` | `String` | 酒店名称 | +| `roomTypeId` | `Long` | 房型ID(资源服务) | +| `roomType` | `String` | 房型名称 | +| `assignmentDate` | `LocalDate` | 分配日期 | +| `upgradePrice` | `BigDecimal` | 手动覆盖升级差价(null=使用系统计算) | +| `remark` | `String` | 备注 | + +**响应示例**: + +```json +{ + "code": 200, + "data": [ + { + "assignmentId": 800001, + "orderId": 700001, + "familyIndex": 1, + "hotelId": 500001, + "hotelName": "海拉尔铂尔曼大酒店", + "roomTypeId": 500101, + "roomType": "豪华大床房", + "assignmentDate": "2026-07-01", + "upgradePrice": null, + "remark": null + } + ] +} +``` + +--- + +## 二、与已有接口关系 + +| 接口 | 作用 | 状态 | +| --- | --- | --- | +| `PUT /admin/order/{orderId}/hotel-assignment` | 按天按家庭分配酒店 | 已有(未变) | +| `POST /admin/order/{orderId}/hotel-assignment/preview` | 升级差价预览 | 已有(未变) | +| 新增的两个 `GET` | 独立查询,供列表懒加载 | 本期新增 | + +订单详情接口 `GET /admin/order/{orderId}` 原有 `hotelAssignmentDetails` / `vehicleInfo` 等字段保持不变;新增的独立 GET 用于避免一次性加载全量订单详情时拉取重量级分配数据。 + +--- + +## 三、数据库变更 + +**无**。底层 `order_hotel_assignment` / `order_vehicle_assignment` 表未改。 + +--- + +## 四、错误码 + +HTTP 始终返回 200,错误码在 `Result.code` 中。 + +| code | 触发 | 说明 | +| --- | --- | --- | +| 200 | 正常 | 成功(无分配数据返回空列表) | +| 400 | 订单不存在 | `BusinessException` | + +--- + +## 五、关联 + +- PR: [wx/HL#1018](https://git.1814.love:8443/wx/HL/pulls/1018) diff --git a/changelogs/2026-04/2026-04-20_order-v2_mp-order-traveler.md b/changelogs/2026-04/2026-04-20_order-v2_mp-order-traveler.md new file mode 100644 index 0000000..580a24a --- /dev/null +++ b/changelogs/2026-04/2026-04-20_order-v2_mp-order-traveler.md @@ -0,0 +1,132 @@ +# 订单出行人 MP 端 CRUD 接口新增 — 2026-04-20 + +> **服务** hl-order-service-v2(端口 8094)· **类型** feat · **关联** Issue #1020 / PR #1022 +> **前端调用路径**: 网关(8080) → hl-mp-service(8085) → hl-order-service-v2(8094) +> **前端实际请求前缀**: `/mp/order/{orderId}/traveler`(4 个接口) + +--- + +## 一、能力概述 + +给小程序端补齐**订单出行人** CRUD 能力,对应原型 **L4xYN 乘坐人员勾选列表**、**Bf9jC 添加人员弹窗**。 + +| 原型节点 | 功能 | 主要接口 | +| --- | --- | --- | +| L4xYN pplList | 乘坐人员勾选列表 | `GET /mp/order/{orderId}/traveler` | +| Bf9jC | 添加人员弹窗 | `POST /mp/order/{orderId}/traveler` | +| Bf9jC(编辑) | 修改出行人 | `PUT /mp/order/{orderId}/traveler/{travelerId}` | +| Bf9jC(删除) | 删除出行人 | `DELETE /mp/order/{orderId}/traveler/{travelerId}` | + +**注意区分两套"出行人"模型**: + +| 维度 | 路径 | 说明 | +| --- | --- | --- | +| 用户常用出行人 | `/mp/user/traveler/*` | user-service 管,已有 | +| **订单出行人**(本次新增) | `/mp/order/{orderId}/traveler/*` | order-v2 的 `order_traveler` 表 | + +--- + +## 二、接口清单 + +鉴权:user token(登录态) + +| 方法 | 路径 | 用途 | +| --- | --- | --- | +| GET | `/mp/order/{orderId}/traveler` | 查某订单的出行人列表 | +| POST | `/mp/order/{orderId}/traveler` | 新增出行人到某订单 | +| PUT | `/mp/order/{orderId}/traveler/{travelerId}` | 修改出行人(字段为 null 保持原值不变) | +| DELETE | `/mp/order/{orderId}/traveler/{travelerId}` | 删除出行人 | + +--- + +## 三、请求体(`MpOrderTravelerSaveReqVO`) + +```jsonc +{ + "name": "张三", // 必填,≤50 字 + "idCardType": "ID_CARD", // 字典 id_card_type + "idCardNo": "110101199001011234", // 必填 + "phone": "13800000000", + "gender": "1", // 1=男 2=女 + "birthday": "1990-01-01", + "travelerType": "ADULT", // 默认 ADULT,字典 traveler_type + "nationality": "中国", + "emergencyContact": "李四", + "emergencyPhone": "13900000000", + "email": "zhangsan@example.com" +} +``` + +**字典取值**: +- `idCardType`: `ID_CARD` 身份证 / `PASSPORT` 护照 / `HK_MACAO_PASS` 港澳通行证 / `TAIWAN_PASS` 台湾通行证 / `OTHER` 其他 +- `travelerType`: `ADULT` 成人 / `CHILD` 儿童 / `YOUNG_CHILD` 幼儿 / `BABY` 婴儿 + +**后端自动处理**: +- 身份证(18 位)自动解析 `birthday` + `gender`(前端可不传) +- 同订单证件号重复检查(重复抛业务错误) + +--- + +## 四、响应体 + +### 列表 `GET` 响应 + +```jsonc +{ + "code": 200, + "data": [ + { + "travelerId": 10001, // Long,注:未强制 String(现有约定) + "name": "张三", + "travelerType": "ADULT", + "travelerTypeLabel":"成人", + "idCardType": "ID_CARD", + "idCardTypeLabel": "身份证", + "idCardNo": "110101199001011234", + "gender": "1", + "birthday": "1990-01-01", + "phone": "13800000000", + "nationality": "中国", + "emergencyContact": "李四", + "emergencyPhone": "13900000000", + "email": "zhangsan@example.com" + } + ] +} +``` + +### 新增/修改返回 + +返回单个对象(同列表元素结构)。 + +### 删除返回 + +```jsonc +{ "code": 200, "data": null } +``` + +### 约定 + +- 所有枚举字段都配 `xxxLabel` 中文标签 +- 空数据返回 `[]`,不是 `null` +- 时间格式统一 `yyyy-MM-dd`(出生日期)/ `yyyy-MM-dd HH:mm:ss`(含时间字段) + +--- + +## 五、前端接入建议 + +**L4xYN 乘坐人员勾选列表**: +1. 打开页面 → `GET /mp/order/{orderId}/traveler` 拉取订单已有出行人 +2. 用户勾选 → 把 travelerId 传给**到达信息**接口的 `travelerIds` 字段 + +**Bf9jC 添加人员弹窗**: +1. 可选:先调 `GET /mp/user/traveler`(已有)让用户从常用库选一个 → 填到表单里 +2. 用户补全/修改字段(含 OCR 扫描身份证填入,OCR 用小程序原生插件前端做) +3. 提交 `POST /mp/order/{orderId}/traveler` + +--- + +## 六、关联 + +- Issue: [wx/HL#1020](https://git.1814.love:8443/wx/HL/issues/1020) +- PR: [wx/HL#1022](https://git.1814.love:8443/wx/HL/pulls/1022)(已合并到 dev) diff --git a/changelogs/2026-04/2026-04-20_order-v2_mp-supplies-checklist.md b/changelogs/2026-04/2026-04-20_order-v2_mp-supplies-checklist.md new file mode 100644 index 0000000..741a722 --- /dev/null +++ b/changelogs/2026-04/2026-04-20_order-v2_mp-supplies-checklist.md @@ -0,0 +1,128 @@ +# C端「行李推荐弹窗」独立接口 — 2026-04-20 + +> **服务** hl-order-service-v2(端口 8094) + hl-mp-service(端口 8085)· **类型** feat · **关联** PR #1041 / #1042 +> **前端调用路径**: 网关(8080) → hl-mp-service(8085) → hl-order-service-v2(8094) + +--- + +## 一、新增接口(1 个) + +### 🆕 `GET /mp/order/{orderId}/supplies-checklist` — 订单备品清单(行李推荐弹窗) + +**功能**:对应小程序订单详情原型 `zgNiD` 行李推荐弹窗。从 `order_info.product_snapshot` 解析 `supplies`(按 `category` 分组,排除 `hasCost=true` 的成本项)+ `supplement.equipmentAdvice`(装备建议富文本)。 + +**请求参数**: + +| 参数 | 位置 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | --- | +| `orderId` | Path | `Long` | ✅ | 订单ID | + +> `userId` 由 mp-service 从 token 中获取后透传给 order-service,前端不传。 + +**返回值** `Result`: + +| 字段 | 类型 | 必有 | 说明 | +| --- | --- | --- | --- | +| `orderId` | `Long` | ✅ | 订单 ID | +| `categories` | `List` | ✅ | 备品清单按分类分组(无推荐时为空列表) | +| `equipmentAdvice` | `String` | 否 | 装备建议富文本(HTML,快照无值时为 null) | + +**`MpSuppliesCategoryVO`**: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `category` | `String` | 分类名(如"衣物"、"配件") | +| `items` | `List` | 该分类下的备品列表 | + +**`MpSuppliesItemVO`**: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `suppliesName` | `String` | 备品名称(如"防晒霜") | +| `category` | `String` | 分类(与父级同) | +| `quantity` | `Integer` | 数量 | +| `billingType` | `String` | 计费方式(`PER_PERSON`=按人 / `PER_QUANTITY`=按件) | +| `sortOrder` | `Integer` | 排序 | + +> ⚠️ 相比管理端 `OrderSuppliesVO`,C 端 VO 已去除 `hasCost`、`unitPrice` 等成本字段。 + +**请求示例**: + +``` +GET /mp/order/2043935542092980226/supplies-checklist +``` + +**响应示例**: + +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "orderId": 2043935542092980226, + "categories": [ + { + "category": "衣物", + "items": [ + { "suppliesName": "冲锋衣", "category": "衣物", "quantity": 1, "billingType": "PER_PERSON", "sortOrder": 1 }, + { "suppliesName": "速干裤", "category": "衣物", "quantity": 2, "billingType": "PER_PERSON", "sortOrder": 2 } + ] + }, + { + "category": "配件", + "items": [ + { "suppliesName": "防晒霜", "category": "配件", "quantity": 1, "billingType": "PER_QUANTITY", "sortOrder": 1 } + ] + } + ], + "equipmentAdvice": "

呼伦贝尔 7 月早晚温差大,建议携带薄款冲锋衣。

" + } +} +``` + +**空推荐响应示例**(快照无 supplies 与 equipmentAdvice): + +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "orderId": 2043935542092980226, + "categories": [], + "equipmentAdvice": null + } +} +``` + +--- + +## 二、mp-service BFF 转发 + +| 前端调用 | mp-service 转发到 | +| --- | --- | +| `GET /mp/order/{orderId}/supplies-checklist` | `GET /internal/mp/order/{orderId}/supplies-checklist` | + +--- + +## 三、数据库变更 + +**无**。数据来自下单时固化的 `order_info.product_snapshot`。 + +--- + +## 四、错误码 + +HTTP 始终返回 200,错误码在 `Result.code` 中。 + +| code | 触发 | 说明 | +| --- | --- | --- | +| 200 | 正常 | 成功(快照缺失时 categories 为空列表,equipmentAdvice 为 null) | +| 400 | 订单不存在 | `BusinessException` | + +--- + +## 五、关联 + +- PR: [wx/HL#1042](https://git.1814.love:8443/wx/HL/pulls/1042) +- 原型节点: `zgNiD` — 行李推荐弹窗 +- 相关字段: `OrderDetailVO` 已同步新增 `supplies` / `equipmentAdvice` 字段,见 `2026-04-20_order-v2_order-detail-supplies-fields.md` diff --git a/changelogs/2026-04/2026-04-20_order-v2_order-detail-supplies-fields.md b/changelogs/2026-04/2026-04-20_order-v2_order-detail-supplies-fields.md new file mode 100644 index 0000000..56efb36 --- /dev/null +++ b/changelogs/2026-04/2026-04-20_order-v2_order-detail-supplies-fields.md @@ -0,0 +1,86 @@ +# 订单详情新增 supplies / equipmentAdvice 强类型字段 — 2026-04-20 + +> **服务** hl-order-service-v2(端口 8094)· **类型** feat · **关联** PR #1026 / #1031 +> **前端调用路径**: 网关(8080) → hl-mp-service(8085) → hl-order-service-v2(8094) + +--- + +## 一、现有接口字段新增(无路径/参数变更) + +### ✏️ 订单详情接口 — 新增 2 字段 + +影响接口: +- `GET /mp/order/{orderId}` — C 端订单详情(`MpOrderDetailVO`) +- `GET /admin/order/{orderId}` — 管理端订单详情(`OrderDetailVO`) + +两个接口共享底层 `OrderDetailQueryService`,均同步增加以下字段: + +| 字段 | 类型 | 必有 | 说明 | +| --- | --- | --- | --- | +| `supplies` | `List` | 否 | 订单备品列表(来自下单时固化的产品快照) | +| `equipmentAdvice` | `String` | 否 | 装备建议富文本(HTML,来自 `supplement.equipmentAdvice`) | + +**`OrderSuppliesVO` 字段**: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | `Long` | 备品ID(快照中的产品备品ID) | +| `suppliesName` | `String` | 备品名称(如"帐篷") | +| `category` | `String` | 分类(如"户外装备") | +| `hasCost` | `Boolean` | 是否涉及成本 | +| `billingType` | `String` | 计费方式(`PER_PERSON`=按人 / `PER_QUANTITY`=按件) | +| `unitPrice` | `BigDecimal` | 单价 | +| `quantity` | `Integer` | 数量 | +| `sortOrder` | `Integer` | 排序 | + +> ⚠️ `OrderSuppliesVO` 含**完整成本字段**(`hasCost` / `unitPrice`),与 C 端"行李推荐弹窗"使用的 `MpSuppliesItemVO`(去掉了成本字段)不同。 + +**数据来源**:后端装配时从 `order_info.product_snapshot` JSON 解析 `supplies` 数组与 `supplement.equipmentAdvice` 字符串,填充为强类型字段。`productSnapshot` 原字段保持不变,**兼容现有前端**。 + +**降级行为**:快照为 `null`/非法/缺失子节点时,`supplies` 置为空列表,`equipmentAdvice` 置为 `null`,接口正常返回。 + +--- + +## 二、响应示例(新增字段部分) + +```json +{ + "code": 200, + "data": { + "orderId": 2043935542092980226, + "...": "(其余字段未变)", + "supplies": [ + { + "id": 600001, + "suppliesName": "帐篷", + "category": "户外装备", + "hasCost": true, + "billingType": "PER_QUANTITY", + "unitPrice": 80.00, + "quantity": 2, + "sortOrder": 1 + } + ], + "equipmentAdvice": "

呼伦贝尔 7 月早晚温差大,建议携带薄款冲锋衣。

" + } +} +``` + +--- + +## 三、其他字段保持不变 + +所有现有字段(`orderId`、`productName`、`departureDate`、`travelers`、`insurances` 等)均未改动。 + +--- + +## 四、数据库变更 + +**无**。数据来自已有的 `order_info.product_snapshot` JSON 字段。 + +--- + +## 五、关联 + +- PR: [wx/HL#1031](https://git.1814.love:8443/wx/HL/pulls/1031) +- 相关接口:`GET /mp/order/{orderId}/supplies-checklist`(独立行李推荐弹窗),见 `2026-04-20_order-v2_mp-supplies-checklist.md` diff --git a/changelogs/2026-04/2026-04-20_order-v2_pre-trip-checklist.md b/changelogs/2026-04/2026-04-20_order-v2_pre-trip-checklist.md new file mode 100644 index 0000000..9101c39 --- /dev/null +++ b/changelogs/2026-04/2026-04-20_order-v2_pre-trip-checklist.md @@ -0,0 +1,154 @@ +# TripDetailVO 扩出发准备清单字段 — 2026-04-20 + +> **服务** hl-order-service-v2(端口 8094)· **类型** feat · **关联** Issue #1024 / PR #1025 +> **前端调用路径**: 网关(8080) → hl-mp-service(8085) → hl-order-service-v2(8094) +> **前端实际请求前缀**: `GET /mp/trip/{orderId}`(**无新接口**,扩字段) + +--- + +## 一、能力概述 + +补齐原型 **ew2Kk 出发准备页的 prepSec 清单区块**。其余数据(倒计时 / 带队团队 / 行程概览 / 合同保险状态)已经在 `TripDetailVO` 中。 + +**⚠️ 无新接口**:扩现有 `GET /mp/trip/{orderId}` 的返回体,在 `TripDetailVO` 加一个字段 `preTripChecklist`。 + +| 触发条件 | 行为 | +| --- | --- | +| `tripPhase == "BEFORE_START"` | `preTripChecklist` 填充 6 项清单 | +| `tripPhase == "IN_PROGRESS"` | `preTripChecklist` 为 `null` | +| `tripPhase == "ENDED"` | `preTripChecklist` 为 `null` | + +--- + +## 二、接口清单 + +**无新接口**。使用现有: + +| 方法 | 路径 | 变化 | +| --- | --- | --- | +| GET | `/mp/trip/{orderId}` | 返回体新增 `preTripChecklist` 字段 | + +--- + +## 三、新增字段结构(`PreTripChecklistVO`) + +```jsonc +{ + "preTripChecklist": { + "completedCount": 3, // 已完成项数 + "totalCount": 6, // 总项数 + "items": [ + { + "key": "CONTRACT_SIGN", // 项 key + "title": "签署旅行合同", // 标题 + "subtitle": "合同已准备,请尽快签署", // 副标题 + "status": "WARNING", // DONE / PENDING / WARNING + "statusLabel": "紧急", + "actionType": "NAVIGATE", // NAVIGATE / CONFIRM / CONTACT_CS / null + "actionPayload": { // 前端按 actionType 解析 + "pageKey": "contract_sign", + "orderId": "1000", + "contractId": "9001" + } + }, + { + "key": "DEPOSIT_PAID", + "title": "订金已支付", + "subtitle": "¥2000 已支付", + "status": "DONE", + "statusLabel": "已完成", + "actionType": null, + "actionPayload": null + } + // ...共 6 项 + ] + } +} +``` + +--- + +## 四、清单 6 项 + 状态规则 + +| Key | 标题 | DONE 条件 | WARNING 条件 | 其他→PENDING | +| --- | --- | --- | --- | --- | +| `DEPOSIT_PAID` | 订金/全款支付 | `paidAmount >= 应付`(FULL→totalPrice / DEPOSIT→depositAmount) | — | 金额不足 | +| `TRAVELER_INFO` | 出行人完整度 | `validateTravelers.isComplete=true` | 有出行人信息缺失 | 人数不够 | +| `ARRIVAL_INFO` | 接送机信息 | arrivals 和 departures 都有 | — | 缺任一方向 | +| `CONTRACT_SIGN` | 合同签署 | contract.status=`SIGNED` | `PREPARING` / `UNSIGNED` | 无合同 | +| `INSURANCE` | 保险承保 | insurance.status=`INSURED` | `FAILED` | `PENDING` / 无保险 | +| `CHECKLIST_CONFIRMED` | 出行清单确认 | `OrderInfo.checklistConfirmed=true` | — | 未确认 | + +### actionType 语义 + +| 值 | 前端应做 | +| --- | --- | +| `NAVIGATE` | 跳转小程序页面(按 `actionPayload.pageKey` 映射) | +| `CONFIRM` | 弹确认对话框(如"确认出行清单") | +| `CONTACT_CS` | 打开客服会话 | +| `null` | 不可操作(DONE 状态) | + +### pageKey 映射建议(前端维护) + +| pageKey | 小程序路径 | +| --- | --- | +| `order_pay` | 支付页 | +| `traveler_list` | 出行人列表 | +| `arrival_form` | 到达信息表单 | +| `contract_sign` | 合同签署 | +| `checklist_confirm` | 清单确认 | + +### 排序规则 + +后端已按 **WARNING > PENDING > DONE** 排序(紧急项靠前),前端直接渲染即可。 + +--- + +## 五、示例 + +### BEFORE_START 阶段完整响应(节选) + +```jsonc +{ + "code": 200, + "data": { + "orderId": "1000", + "tripPhase": "BEFORE_START", + "countdownDays": 12, + "departureDate": "2026-07-01", + "productName": "草原环线体验", + "guideName": "巴图", // 带队团队 + "guidePhone": "138****5678", + "itinerarySummary": [...], // 行程概览 + "contracts": [...], + "insurances": [...], + "preTripChecklist": { // ← 新增字段 + "completedCount": 3, + "totalCount": 6, + "items": [...] + } + } +} +``` + +### IN_PROGRESS 阶段响应 + +```jsonc +{ + "code": 200, + "data": { + "orderId": "1000", + "tripPhase": "IN_PROGRESS", + "todayDayNumber": 3, + "todayTitle": "莫日格勒河 → 额尔古纳", + "preTripChecklist": null // ← 非 BEFORE_START 阶段为 null + } +} +``` + +--- + +## 六、关联 + +- Issue: [wx/HL#1024](https://git.1814.love:8443/wx/HL/issues/1024) +- PR: [wx/HL#1025](https://git.1814.love:8443/wx/HL/pulls/1025)(已合并到 dev) diff --git a/changelogs/2026-04/2026-04-21_order-v2_hotel-assignment-day-number.md b/changelogs/2026-04/2026-04-21_order-v2_hotel-assignment-day-number.md new file mode 100644 index 0000000..f2bc015 --- /dev/null +++ b/changelogs/2026-04/2026-04-21_order-v2_hotel-assignment-day-number.md @@ -0,0 +1,113 @@ +# 酒店分配接口 date → dayNumber — 2026-04-21 + +> **服务** hl-order-service-v2(端口 8094)· **类型** refactor · **关联** Issue #1049 / PR #1051 +> **破坏性变更**:请求入参字段名由 `date` 改为 `dayNumber` + +--- + +## 一、影响接口 + +### ✏️ `PUT /admin/order/{orderId}/hotel-assignment` — 分配酒店(管理端) + +**请求参数变更**:每个 `assignments[]` 元素的「日期字段」从 `date`(`LocalDate`)改为 `dayNumber`(`Integer`,第几天,从 1 开始)。 + +**变更对比**: + +| 旧字段 | 新字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | --- | +| ~~`date`~~ | **`dayNumber`** | `Integer` | ✅ | 第几天(从 1 开始,≥1) | + +**其他字段保持不变**:`familyIndex` · `hotelId` · `hotelName` · `roomTypeId` · `roomType` · `upgradePrice` · `remark` + +**新请求示例**: + +```json +PUT /admin/order/700001/hotel-assignment +{ + "assignments": [ + { + "familyIndex": 1, + "hotelId": 2023714929877450753, + "hotelName": "呼伦贝尔香格里拉大酒店", + "roomTypeId": 3002000000000000013, + "roomType": "大床房", + "dayNumber": 1, + "remark": "" + }, + { + "familyIndex": 1, + "hotelId": 2023714929877450753, + "hotelName": "呼伦贝尔香格里拉大酒店", + "roomTypeId": 3002000000000000013, + "roomType": "大床房", + "dayNumber": 2 + } + ] +} +``` + +> ⚠️ 真实入住日期由后端按 `order.departureDate + dayNumber - 1` 现算,前端传递时**不再需要知道订单出发日期**。 + +--- + +### ✏️ `GET /admin/order/{orderId}/hotel-assignment` — 查询酒店分配列表(管理端) + +**返回值字段变更**:新增 `dayNumber` 字段;`assignmentDate` 字段**保留**(Service 层按 `order.departureDate + dayNumber - 1` 现算填充,前端展示逻辑无需改)。 + +**`HotelAssignmentDetailVO` 字段**: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `assignmentId` | `Long` | 分配ID | +| `orderId` | `Long` | 订单ID | +| `familyIndex` | `Integer` | 家庭序号(从1开始) | +| `hotelId` | `Long` | 酒店ID | +| `hotelName` | `String` | 酒店名称 | +| `roomTypeId` | `Long` | 房型ID | +| `roomType` | `String` | 房型名称 | +| **`dayNumber`** | `Integer` | **新增**:第几天(从1开始) | +| `assignmentDate` | `LocalDate` | 入住日期(后端现算,保留) | +| `upgradePrice` | `BigDecimal` | 手动覆盖升级差价 | +| `remark` | `String` | 备注 | + +--- + +### ✏️ `GET /mp/order/{orderId}/hotels` — C 端订单住宿弹窗 + +`MpHotelItemVO` 同步新增 `dayNumber` 字段;`assignmentDate` 保留(后端现算)。 + +--- + +## 二、数据库变更 + +**已在测试服执行**(本 changelog 生成时): + +```sql +ALTER TABLE order_hotel_assignment + ADD COLUMN day_number INT NOT NULL COMMENT '第几天(从1开始,与meal/scenic assignment一致)' AFTER room_type; +ALTER TABLE order_hotel_assignment DROP COLUMN assignment_date; +``` + +历史数据按 `DATEDIFF(assignment_date, order.departure_date) + 1` 回填。 + +> 生产部署顺序必须 **先跑迁移 SQL 再重启服务**,否则老代码写入会因 `assignment_date` 列缺失而失败。 + +--- + +## 三、错误码 + +HTTP 始终返回 200,错误码在 `Result.code` 中。 + +| code | 触发 | 说明 | +| --- | --- | --- | +| 200 | 正常 | 成功 | +| 400 | `dayNumber` 缺失 | `第几天不能为空` | +| 400 | `dayNumber < 1` | `第几天必须大于等于1` | + +--- + +## 四、关联 + +- Issue: [wx/HL#1049](https://git.1814.love:8443/wx/HL/issues/1049) +- PR: [wx/HL#1051](https://git.1814.love:8443/wx/HL/pulls/1051)(已合并到 dev) +- 前置原因:原 `assignment_date` 在订单出发日变更时需要级联更新,且与 `order_meal_assignment` / `order_scenic_assignment` 的 `day_number` 字段设计不一致