新增: 产品v2 上架校验补齐订单详情依赖字段 + 订单详情字段补齐 (PR #854)
这个提交包含在:
父节点
cefe2ae145
当前提交
aed14f4451
@ -0,0 +1,227 @@
|
||||
# fix(product-v2): 上架校验补齐订单详情依赖字段(7 类新校验) + 订单详情字段补齐
|
||||
|
||||
> **服务**: hl-product-service-v2 (端口 8083) + hl-order-service-v2 (端口 8084)
|
||||
> **PR**: #854 https://git.1814.love:8443/wx/HL/pulls/854
|
||||
> **Issue**: #846
|
||||
> **Merge commit**: `a77c4505`
|
||||
> **日期**: 2026-04-18
|
||||
> **影响范围**:
|
||||
> - 管理端【产品上架】预检 + 上架接口(校验规则收紧,可能 400)
|
||||
> - 小程序【订单详情】弹窗(住宿/用车/餐饮/领队 4 处补齐字段)
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
排查发现产品 v2 上架前校验 `ProductValidationService` 原本只做 11 项基础字段校验,对**订单详情 6 个弹窗(门票/住宿/餐饮/用车/领队/摄影)**所需要的关键字段覆盖不全,导致:
|
||||
- 产品能上架,但用户下单后订单详情字段空白、统计恒为 0、Feign 返回 404 降级
|
||||
- 订金配置不完整,下单时 `depositAmount` 落 null
|
||||
- GROUP 产品可能没班期或班期无领队,订单详情领队弹窗永远"未分配"
|
||||
|
||||
本 PR 同时修复:
|
||||
1. **P0x3 + P1x4 = 7 类新增上架校验**(收紧规则,老产品可能上不了架)
|
||||
2. **订单详情依赖的 Feign 快照字段补齐**(5 项 VO 字段补强 + 1 个 internal 接口新增)
|
||||
|
||||
---
|
||||
|
||||
## 二、上架校验收紧(P0 + P1 共 7 条)
|
||||
|
||||
### 变更接口
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 |
|
||||
|---|------|------|------|----------|
|
||||
| 1 | 上架预检 | GET | `/admin/product/{id}/validate-publish` | 返回 issues 列表新增 7 类错误提示 |
|
||||
| 2 | 上架/下架切换 | POST | `/admin/product/{id}/toggle-publish` | 上架动作先跑 validatePublish,有 issues 则 400 拒绝 |
|
||||
|
||||
### 新增 7 条校验规则
|
||||
|
||||
| # | 等级 | 触发条件 | 错误提示文案(示例) |
|
||||
|---|------|---------|---------------------|
|
||||
| 1 | P0 | `payment_type=DEPOSIT` 时 `depositAmount>0` 和 `depositRatio∈[1,99]` 都没填 | `订金支付方式(payment_type=DEPOSIT)必须配置订金金额(>0)或订金比例(1-99)` |
|
||||
| 2 | P0 | `payment_type=DEPOSIT` 时 `balance_due_days` 为 null | `订金支付方式(payment_type=DEPOSIT)必须配置尾款到期天数 balance_due_days` |
|
||||
| 3 | P0 | 行程节点缺少 `HOTEL` 类型 | `缺少酒店节点,订单详情住宿弹窗将为空` |
|
||||
| 3 | P0 | 行程节点缺少 `RESTAURANT` 类型 | `缺少餐饮节点,订单详情餐饮弹窗将为空` |
|
||||
| 3 | P0 | 行程节点 `SCENIC` + `ACTIVITY` 合计 = 0 | `缺少景点/活动节点,订单详情门票/活动弹窗将为空` |
|
||||
| 4 | P1 | SCENIC 节点 `resourceId` 为 null | `SCENIC 景点节点(nodeId=X 名称=Y)缺少 resourceId,订单门票弹窗将无法显示封面/原价` |
|
||||
| 5 | P1 | `tripNights ≥ 1` 且 非自理早餐(`breakfast ≠ SELF`)天数占比 < 70% | `餐饮配置不足:行程共 N 晚,至少 M 天早餐应该非自理(SELF),当前仅 K 天非自理,订单详情'已含早餐数'统计将不准确` |
|
||||
| 6 | P1 | `product_route_info.vehicle_model_id` 为 null | `路线总览未配置实际用车车型(product_route_info.vehicle_model_id),订单用车弹窗将无法展示车型信息` |
|
||||
| 7 | P1 | `product_type=GROUP` 且 `group_tour_batch` 为空 | `GROUP(小蒙马)产品至少需要 1 个班期(group_tour_batch),否则订单领队弹窗将永远为空` |
|
||||
| 7 | P1 | `product_type=GROUP` 某班期 `group_batch_staff` 中没有 `staff_role=LEADER` 成员 | `班期(batchNo=X batchId=Y)缺少 LEADER 角色成员,订单详情领队弹窗将显示'未分配'` |
|
||||
|
||||
### 校验接口响应示例
|
||||
|
||||
**成功(全部通过)**:
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"success": true,
|
||||
"message": "操作成功",
|
||||
"data": []
|
||||
}
|
||||
```
|
||||
|
||||
**失败(上架预检返回多条 issues)**:
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"success": true,
|
||||
"message": "操作成功",
|
||||
"data": [
|
||||
"订金支付方式(payment_type=DEPOSIT)必须配置订金金额(>0)或订金比例(1-99)",
|
||||
"缺少酒店节点,订单详情住宿弹窗将为空",
|
||||
"SCENIC 景点节点(nodeId=1234 名称=额尔古纳湿地)缺少 resourceId,订单门票弹窗将无法显示封面/原价",
|
||||
"GROUP(小蒙马)产品至少需要 1 个班期(group_tour_batch),否则订单领队弹窗将永远为空"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**toggle-publish 触发上架失败时**(HTTP 仍 200,`code` 为业务错误码):
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"success": false,
|
||||
"message": "上架前校验失败:缺少酒店节点...;路线总览未配置实际用车车型...",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### 前端必须配合的改动
|
||||
|
||||
#### 1. 产品编辑页「上架前」先调 `/validate-publish` 给出整改清单
|
||||
|
||||
原型建议:点"上架"按钮前先 GET `/admin/product/{id}/validate-publish`,拿到 `data: []` 则允许上架,否则展示 issues 清单供运营整改后再试。
|
||||
|
||||
```js
|
||||
// 推荐流程
|
||||
const res = await GET(`/admin/product/${id}/validate-publish`);
|
||||
if (res.data.length === 0) {
|
||||
await POST(`/admin/product/${id}/toggle-publish`, { remark: '...' });
|
||||
} else {
|
||||
// 弹出整改清单,每行是一条中文错误提示
|
||||
showIssuesModal(res.data);
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 展示多条错误
|
||||
|
||||
`toggle-publish` 失败时后端的 `message` 会把所有 issues 用 `;` 拼接,前端可以按 `;` 切分展示,或者优先用 `validate-publish` 拿结构化列表。
|
||||
|
||||
---
|
||||
|
||||
## 三、兼容性风险(前端 + 运营重点关注)
|
||||
|
||||
### 老产品可能上不了架
|
||||
|
||||
以下情况产品会被拒绝上架,需要运营**先补数据**:
|
||||
|
||||
| 场景 | 解决 |
|
||||
|------|------|
|
||||
| 产品只有 NOTE/CUSTOM/TRANSPORT/FREE 节点 | 至少补 1 个 SCENIC/ACTIVITY + 1 个 HOTEL + 1 个 RESTAURANT 节点 |
|
||||
| 老 DEPOSIT 产品只填了 `depositAmount`/`depositRatio` 没填 `balanceDueDays` | 编辑页补填尾款到期天数 |
|
||||
| 老 GROUP 产品没建班期 | 至少创建 1 个班期 |
|
||||
| 老 GROUP 产品班期没绑定领队 | 每个班期至少加 1 个 `staff_role=LEADER` 的成员 |
|
||||
| 所有行程早餐都是 SELF(自理) | 至少 70% 的天早餐改成非 SELF(如 HOTEL/CAMP/SPECIAL) |
|
||||
| SCENIC 景点节点只填了名称没关联景区资源 | 编辑节点绑定 `resourceId` |
|
||||
| 路线总览没选实际车型 | 路线总览页选车型 |
|
||||
|
||||
### 早餐 70% 校验严格度说明
|
||||
|
||||
- 5 天行程(`tripNights=4`):至少 `⌈4 × 0.7⌉ = 3` 天早餐非 SELF
|
||||
- 6 天行程(`tripNights=5`):至少 `⌈5 × 0.7⌉ = 4` 天早餐非 SELF
|
||||
- 7 天行程(`tripNights=6`):至少 `⌈6 × 0.7⌉ = 5` 天早餐非 SELF(只能有 1 天 SELF)
|
||||
|
||||
前端表单要能保存 `SELF/HOTEL/CAMP/SPECIAL` 四种早餐类型,上架校验才能放行。
|
||||
|
||||
### 已上架产品不会受影响
|
||||
|
||||
**只有在「上架」动作时(`toggle-publish` 切到 PUBLISHED)才校验**,已经处于上架状态的老产品没动它,下架再上架才会触发。
|
||||
|
||||
---
|
||||
|
||||
## 四、订单详情字段补齐(小程序 MP 端)
|
||||
|
||||
### 变更接口
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | 订单详情 | GET | `/mp/order/{orderId}/detail` | 响应字段扩展 | 4 个弹窗 VO 字段补全,数据从产品快照取 |
|
||||
|
||||
具体定位到订单详情 VO 层次:
|
||||
|
||||
#### 1) `MpHotelItemVO`(订单住宿弹窗项)新增字段
|
||||
|
||||
| 字段 | 类型 | 可空 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `hotelType` | String | 是 | 酒店类型(5星/特色民宿/蒙古包等),资源服务降级时可能为 null |
|
||||
|
||||
#### 2) `MpVehicleDetailVO`(订单用车弹窗)新增字段
|
||||
|
||||
| 字段 | 类型 | 可空 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `coverUrl` | String | 是 | 车型封面图 URL(资源服务暂未提供,预留 null) |
|
||||
| `seatCount` | Integer | 是 | 座位数(来自产品快照 `vehicleModels.seatCount`) |
|
||||
| `vehicleCategory` | String | 是 | 车辆分类,字典 `vehicle_type`:`CAR`=小轿车 / `SUV`=越野车 / `VAN`=商务车 / `BUS`=大巴 / `MINIBUS`=中巴 |
|
||||
| `driverYearsRequired` | String | 是 | 驾龄要求(资源服务暂未提供,预留 null) |
|
||||
| `features` | List\<String\> | 是 | 配置/亮点列表(资源服务暂未提供,预留空数组) |
|
||||
|
||||
#### 3) `MpMealStatsVO.breakfastCount`(订单餐饮统计)行为修复
|
||||
|
||||
**旧行为**:`breakfastCount` 永远返回 `0`,因为产品 v2 快照没下发 `mealType` 字段,订单 `OrderMealAssignmentService` 无法区分早午晚餐。
|
||||
|
||||
**新行为**:产品快照补齐 `nodes[].mealType`(`BREAKFAST`/`LUNCH`/`DINNER`) 后,订单端按餐别分桶计数,`breakfastCount` 真实反映"产品内置多少餐早餐"。前端展示文案"已含 N 早"不用改,数值正确即可。
|
||||
|
||||
#### 4) GROUP 产品订单详情【领队/摄影】弹窗不再永远"未分配"
|
||||
|
||||
**旧行为**:订单服务调 `productFeignClient.getBatchStaff(batchId)` 永远 404,`fallback` 返空 list → 所有 GROUP 订单领队/摄影弹窗显示"未分配"。
|
||||
|
||||
**新行为**:产品服务新增了 `GET /internal/product/schedule/{batchId}/staff` internal endpoint(详情见后端 changelog),订单服务能拿到真实的班期成员,按 `staffRole` 过滤展示领队/摄影/司机。
|
||||
|
||||
### 前端展示建议
|
||||
|
||||
#### 订单住宿弹窗
|
||||
原型上要求在酒店名下显示【5 星】【特色民宿】【蒙古包】标签 → 直接用 `hotelType`,为 null 则不展示标签。
|
||||
|
||||
#### 订单用车弹窗
|
||||
原型字段对照:
|
||||
- "车型名":`vehicleType`
|
||||
- "座位数":`seatCount`(null → "—")
|
||||
- "车辆类型":翻译 `vehicleCategory` 字典(null → "—")
|
||||
- "驾龄要求":`driverYearsRequired`(null → "—"或隐藏)
|
||||
- "车辆配置":`features`(空数组 → 隐藏)
|
||||
|
||||
---
|
||||
|
||||
## 五、下单/报价逻辑
|
||||
|
||||
本 PR **不改下单/报价核心逻辑**,只改:
|
||||
- 上架阶段收紧校验(卡在 `toggle-publish`)
|
||||
- 订单详情 `VO` 补字段(从已有产品快照多读几个字段)
|
||||
|
||||
C 端已下单的历史订单,订单详情读的是创建时序列化的 `product_snapshot`,老订单没有新字段,仍会是 null。**仅新下的订单会有完整字段**。
|
||||
|
||||
---
|
||||
|
||||
## 六、不受影响的接口
|
||||
|
||||
- 小程序首页/列表/产品详情:零影响
|
||||
- 产品创建/编辑/复制/下架:零影响
|
||||
- 产品报价 `/admin/product/item/{id}/quote`:零影响
|
||||
- 价格日历 `/admin/product/item/{id}/price-calendar/*`:零影响
|
||||
|
||||
---
|
||||
|
||||
## 七、受影响服务 + 重启提示
|
||||
|
||||
本次需要**同时重启** 2 个服务:
|
||||
- `hl-product-service-v2`(8083):新增校验 + 新增 internal endpoint
|
||||
- `hl-order-service-v2`(8084):VO 字段扩展 + Feign 快照反序列化新字段
|
||||
|
||||
测试环境重启顺序:先 product-service-v2,再 order-service-v2(order 依赖 product 的 internal 接口)。
|
||||
|
||||
---
|
||||
|
||||
## 八、相关链接
|
||||
|
||||
- Issue: https://git.1814.love:8443/wx/HL/issues/846
|
||||
- PR: https://git.1814.love:8443/wx/HL/pulls/854
|
||||
- Merge commit: `a77c4505`
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户