From c20c28d7582d8ffe953c932e53119a2d8cfa4348 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Thu, 30 Apr 2026 14:18:42 +0800 Subject: [PATCH] =?UTF-8?q?fix(product):=20=E8=A1=8C=E7=A8=8B=E5=BF=85?= =?UTF-8?q?=E5=A1=AB=E6=A0=A1=E9=AA=8C=E6=94=B9=E5=88=B0=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1/=E4=B8=8A=E6=9E=B6=E6=97=B6=20(PR=20#1560=20?= =?UTF-8?q?=E7=BF=BB=E7=9B=98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #1558 任务B 把 @NotBlank 加在 ReqVO 等于"保存即必填" 用户翻盘:草稿应允许空,只完成设计/提交上架时才必填 通知前端 yst/mmg:草稿保存按钮可取消必填校验 完成设计/上架按 code=400404 多行 message 展示批量错误清单 --- ...x_product_itinerary-validate-on-publish.md | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 changelogs/2026-04/30_fix_product_itinerary-validate-on-publish.md diff --git a/changelogs/2026-04/30_fix_product_itinerary-validate-on-publish.md b/changelogs/2026-04/30_fix_product_itinerary-validate-on-publish.md new file mode 100644 index 0000000..51c20fd --- /dev/null +++ b/changelogs/2026-04/30_fix_product_itinerary-validate-on-publish.md @@ -0,0 +1,115 @@ +# 修复:产品行程必填校验改到「完成设计」「上架审批」时,草稿允许空 + +**类型**: 后端 BUG 修复(翻盘 PR #1558 任务B 方向) +**关联**: 工单 #1557 / PR #1558(误) / PR #1560(修正) +**日期**: 2026-04-30 +**前端处理者**: yst / mmg +**影响范围**: 管理后台产品编辑页 Step 2 行程编排 + 完成设计按钮 + 提交上架审批按钮 + +--- + +## 翻盘说明 + +### PR #1558 任务B 错误方向(已 revert) +PR #1558 在 `ProductItinerarySaveReqVO` 三处加 `@NotBlank/@NotEmpty`,等于"**保存即必填**" — 把草稿态的中间保存也拦下了。 + +### 用户反馈(2026-04-30) +> "上架和完成实际时候得校验不能有空数据" + +### 正确业务规则 +| 操作 | 路径 | 校验 | +|---|---|---| +| **保存草稿** | `PUT /admin/product/item/{id}/itinerary` | **不校验**(允许空,方便分阶段保存) | +| **完成设计**(仅 CUSTOM) | `POST /admin/product/item/{id}/complete` | **必填校验** | +| **提交上架审批** | `POST /admin/product/item/{id}/toggle-publish`(SUBMIT_PUBLISH 方向) | **必填校验** | +| **超管直接上架** | 同上(DIRECT_PUBLISH 方向) | **必填校验** | +| **下架/撤回/驳回回退** | 同上(其他方向) | **不校验**(避免阻塞补救路径) | + +## 后端改动 + +### 1. 回滚 ReqVO 注解 +`ProductItinerarySaveReqVO.java` 撤销 PR #1558 三处: +- L53 `description` 移除 `@NotBlank` +- L84 `nodes` 移除 `@NotEmpty` +- L87 `hotels` 移除 `@NotEmpty` + +(`gatherPlace/dismissalPlace` 的 `@NotNull` 不动 — PR #1558 之前就有。) + +### 2. ProductValidationService 新增两 public 方法 + +```java +// 返回错误清单(空列表=可上架) +public List validateItineraryCompleteness(Long productId) + +// 硬校验,失败抛 BusinessException(批量错误一次抛) +public void assertItineraryCompleteForPublish(Long productId) +``` + +实现要点: +- DB 3 次读(行程天 + 全部活动节点 + 全部酒店),按 dayId 分组避免 N+1 +- 一次扫所有 day,缺哪些字段累加到 issues 列表,调用方一次抛出 +- 错误码 `ProductCoreErrorCode.PRODUCT_ITINERARY_INCOMPLETE` (400404) + +### 3. 挂载点 +- `ProductStatusService.completeDesign` 入口直接调 +- `ProductStatusService.togglePublish` 仅 `preAction == SUBMIT_PUBLISH || DIRECT_PUBLISH` 触发 + +## 错误响应示例 + +完成设计 / 提交上架时,行程不完整返回: + +```jsonc +HTTP 200 (走业务错误码,非协议错误) +{ + "code": 400404, + "message": "行程未完整填写,请补全后再操作:\nDay 1 行程描述未填写\nDay 1 活动安排为空\nDay 1 住宿安排为空\nDay 3 集合地未填写", + "data": null +} +``` + +## 测试服 round-trip 结果(用户截图原产品 2049468625294708737) + +| Case | 操作 | 实际结果 | +|------|------|---------| +| 草稿 PUT itinerary 全空 | PUT /itinerary body 含空 description+nodes=[]+hotels=[] | ✅ 200 code=200 msg="行程保存成功" | +| 完成设计 | POST /complete | ✅ code=400404 含批量错误清单 | +| 提交上架审批 | POST /toggle-publish | ✅ 同上批量错误 | + +## 前端要做的改动 ⚠️ + +### 1. 草稿保存路径 — 无须改动 +保存草稿现在允许 description / nodes / hotels 为空,前端可以**取消保存按钮的"必填校验提示"**,让用户中间状态自由保存。 + +### 2. 完成设计 / 提交上架 按钮 +点击后如果后端返回 `code=400404`: +- `message` 字段是多行文本,**保留 `\n` 换行直接展示**(如 `
` 或 white-space: pre-wrap)
+- 内容形如:
+  ```
+  行程未完整填写,请补全后再操作:
+  Day 1 行程描述未填写
+  Day 1 活动安排为空
+  Day 3 集合地未填写
+  ```
+- 用户根据清单逐条补齐再点完成设计/上架即可
+
+### 3. 错误码字典
+| 业务码 | 含义 | 触发 |
+|---|---|---|
+| 400404 | 行程未完整 | 完成设计/提交上架前预检 |
+
+## 部署状态
+
+- ✅ PR #1560 合并到 dev (sha 040d2ccb)
+- ✅ Deploy Panel 任务 success,hl-product-service-v2:8083+8183 双实例 1/1 Up
+- ✅ 测试服 3 笔 round-trip 全通过
+- ⏳ Release dev → main 暂缓(用户指示"不用立即合 main")
+
+## 单测
+
+`mvn test -pl hl-product-service-v2`: 1019 全绿 (-8 旧 +7 新)
+
+## 不动
+
+- 任务A 最低价接口完全不动
+- `gatherPlace/dismissalPlace @NotNull` 不动
+- NodeItem/HotelItem 子字段不动(原工单未要求,防回归)