# 产品 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 字符之间 ```