diff --git a/changelogs/2026-04/2026-04-23_supplement-save-valid-effective.md b/changelogs/2026-04/2026-04-23_supplement-save-valid-effective.md new file mode 100644 index 0000000..34640e5 --- /dev/null +++ b/changelogs/2026-04/2026-04-23_supplement-save-valid-effective.md @@ -0,0 +1,135 @@ +# 产品 Step1-5 保存接口 JSR-303 校验真实生效 + +**日期**: 2026-04-23 +**PR**: #1270 (Closes #1269) — PR #1259 后续补丁 +**服务**: hl-product-service-v2 / hl-order-service-v2 +**类型**: fix(修复校验漏补) + +--- + +## 背景 + +PR #1259(装备建议字段改条目列表)里 SaveReqVO 加了 JSR-303 校验注解(`@NotBlank` / `@Size(max=20)` / `@Size(min=1, max=50)`),但 `AdminProductController` 的 Controller 方法未加 `@Valid` 修饰符,**校验注解实际未触发**。非法数据仍能 200 入库。 + +本次补丁把 Step1-5 全部保存接口及一个订单接口补齐 `@Valid`,校验现在真实生效。 + +--- + +## 影响接口(9 处) + +| 接口 | Method | +|------|--------| +| `POST /admin/product/item/basic` | saveBasic | +| `POST /admin/product/item/{id}/itinerary` | saveItinerary | +| `POST /admin/product/item/{id}/route` | saveRoute | +| `POST /admin/product/item/{id}/price-calendar/batch` | savePriceCalendar | +| `POST /admin/product/item/{id}/schedule` | saveSchedule(2 个)| +| `POST /admin/product/item/{id}/schedule/batch` | batchCreateSchedule | +| `PUT /admin/product/item/{id}/supplement` | saveSupplement | +| `PUT /admin/product/item/{id}/detail-blocks` | saveDetailBlocks | +| `DELETE /admin/order/{orderId}/itinerary/pending/{editId}` | withdrawPendingEdit | + +--- + +## 行为变化(前端必读) + +### 之前(PR #1270 前) +```http +PUT /admin/product/item/{id}/supplement +{ + "equipmentList": [ + {"text": ""} // 非法:空 text + ] +} +→ 200 OK(错误地通过!数据存入 DB) +``` + +### 之后(PR #1270 后) +```http +PUT /admin/product/item/{id}/supplement +{ + "equipmentList": [ + {"text": ""} + ] +} +→ 400 { + "code": 400, + "success": false, + "message": "equipmentList[0].text: 装备文案不能为空" + } +``` + +--- + +## 装备建议校验规则(生效中) + +| 规则 | 违反时错误消息 | +|------|---------------| +| `equipmentList` 条目数 ≤ 20 | `equipmentList: 装备建议条目数不能超过20条` | +| 单条 `text` 非空非空白 | `equipmentList[0].text: 装备文案不能为空` | +| 单条 `text` 长度 1-50 字 | `equipmentList[0].text: 装备文案长度需在 1-50 字符之间` | + +**注意错误消息格式**:`{字段路径}: {消息}`,含数组索引(`[0]`、`[1]` 等),前端可精确定位哪条条目出错。 + +--- + +## 前端建议 + +### 1. 前置校验(推荐) +admin 编辑器在提交前自行 validate 一次: +- 条目数 > 20 → 禁止提交 + toast +- 单条 text 空 → 禁止提交 + 红色高亮 +- 单条 text 长度 > 50 → 禁止提交 + 高亮 + +避免走到后端才发现,体验更好。 + +### 2. 后端报错展示(兜底) +如果绕过了前置校验,后端 400 返回的 `message` 字段可直接 toast: + +```javascript +const resp = await api.saveSupplement(req); +if (resp.code !== 200) { + ElMessage.error(resp.message || '保存失败'); + return; +} +``` + +**错误消息已中文化**,直接展示即可。 + +### 3. 数组索引提示 +如果要做精细错误提示,可解析 `equipmentList[N].text` 中的 N,滚动聚焦到对应条目输入框。 + +--- + +## 其他接口的校验 + +`basic / itinerary / route / price-calendar / schedule` 等其他 Step 接口的 SaveReqVO 本来就有 JSR-303 注解,但同样因为缺 `@Valid` 没有生效。**本次补丁一起修复**。 + +常见校验约束(以 `ProductBasicSaveReqVO` 为例): +- `productType` 非空 +- `name` 长度 ≤ 30 字 +- `tripDays` ≥ 1 +- 具体以 Knife4j `@ApiModelProperty` 中的 `required`/`allowableValues` 为准 + +如果前端之前传错数据能 200,现在可能 400,请以实际接口返回为准调整。 + +--- + +## 向后兼容 + +- **合法请求**:行为不变,仍 200 +- **非法请求**:从"假成功(200 入脏数据)"变为"正确失败(400 + 可读消息)" +- 属于**修复性变化**,非破坏性 + +--- + +## 验证 + +测试服网关已 curl 4 场景全过: + +```bash +# 合法 3 条 → 200 +# 21 条 → 400 equipmentList: 装备建议条目数不能超过20条 +# 空 text → 400 equipmentList[0].text: 装备文案不能为空 +# 51 字 text → 400 equipmentList[0].text: 装备文案长度需在 1-50 字符之间 +```