feat: 2026-04-19 小蒙马班期 maxParticipants 必填 + 早鸟一产品一计划约束
- 2026-04-19_schedule-max-participants-required.md (PR #927 / Issue #859) - 2026-04-19_early-bird-plan-product-unique.md (PR #929 / Issue #917) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
这个提交包含在:
父节点
9f8611d9c0
当前提交
553d14e034
@ -0,0 +1,80 @@
|
||||
# refactor: 早鸟计划保存强制"一产品一早鸟"唯一约束
|
||||
|
||||
> **服务**: hl-order-service-v2
|
||||
> **PR**: #929
|
||||
> **Issue**: #917
|
||||
> **日期**: 2026-04-19
|
||||
> **前端是否需要改动**: **建议改动错误展示**(新增约束冲突错误,需前端友好展示)
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
一个产品同时只能绑定一个早鸟优惠计划(业务规则)。之前 DB 无唯一约束,Service 也不校验,运营可重复绑定同一产品到多个计划,导致 `matchPlan` 命中不确定。
|
||||
|
||||
本次从 **Service 层 + DB 索引** 两道防线强制约束。
|
||||
|
||||
## 二、变更接口
|
||||
|
||||
| 方法 | 路径 | 变更类型 |
|
||||
|------|------|---------|
|
||||
| POST | `/admin/order/early-bird` | 新增业务约束校验 |
|
||||
| PUT | `/admin/order/early-bird/{id}` | 同上 |
|
||||
|
||||
## 三、新增约束行为
|
||||
|
||||
**保存前校验**:请求体 `productIds` 中任意产品已被其他早鸟计划绑定 → 直接返回 400 + 明细消息。
|
||||
|
||||
编辑时会自动排除当前计划 ID,即编辑同一计划仍使用原产品不算冲突。
|
||||
|
||||
## 四、错误响应示例
|
||||
|
||||
### 冲突场景
|
||||
|
||||
```json
|
||||
POST /admin/order/early-bird
|
||||
{
|
||||
"planName": "五一早鸟",
|
||||
"discountAmount": 100.00,
|
||||
"minPeople": 2,
|
||||
"startDate": "2026-05-01",
|
||||
"endDate": "2026-05-15",
|
||||
"productIds": [2045345825172639746, 2045345825172639747]
|
||||
}
|
||||
|
||||
// 响应(产品 2045345825172639747 已被其他计划绑定)
|
||||
{
|
||||
"code": 400,
|
||||
"message": "以下产品已绑定其他早鸟计划,不能重复绑定:阿尔山3日(产品ID=2045345825172639747, 已关联计划ID=2045586012523864065)",
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
- 多个冲突会用 `; ` 分隔列出
|
||||
- 消息含产品名(Feign 失败时退化为 "产品[ID]" 占位)+ 产品 ID + 占用的计划 ID
|
||||
|
||||
### 编辑同一计划的产品(不算冲突)
|
||||
|
||||
```json
|
||||
PUT /admin/order/early-bird/2045586012523864065
|
||||
{
|
||||
"planName": "默认早鸟优惠",
|
||||
"productIds": [2045345825172639746] // 同一计划原本就绑的产品
|
||||
}
|
||||
// 响应: 200 OK
|
||||
```
|
||||
|
||||
## 五、前端改动建议
|
||||
|
||||
1. **错误提示展示**:后端返回的错误消息已自带产品名/ID/冲突计划 ID,前端直接用 `message` 字段展示即可
|
||||
2. **可选优化**:前端在选择 "适用产品" 时,可先调 `GET /admin/order/early-bird` 列表接口收集已占用的产品 ID,在下拉框禁用或标灰提示"已被计划 XX 占用"
|
||||
3. **编辑时**:保持原绑定的产品不算冲突,前端无需特殊处理
|
||||
|
||||
## 六、兼容性
|
||||
|
||||
- 之前一个产品绑多个计划的脏数据在生产环境由运维按 `sql/migrations/20260419_917_early_bird_product_unique.sql` 清洗(默认保留最早一条)
|
||||
- 本地 / 测试环境已完成 DDL 迁移,本地 DB 若存在重复数据请运行脚本中【审计】+【清洗】SQL 后再拉代码
|
||||
|
||||
## 七、关联
|
||||
|
||||
- 后端 changelog: `2026-04-19_early-bird-product-unique-ddl.md`(含 DDL 迁移细节)
|
||||
@ -0,0 +1,75 @@
|
||||
# fix: 小蒙马班期保存 maxParticipants 改为必填字段
|
||||
|
||||
> **服务**: hl-product-service-v2
|
||||
> **PR**: #927
|
||||
> **Issue**: #859
|
||||
> **日期**: 2026-04-19
|
||||
> **前端是否需要改动**: **需要改动**(之前允许不传或传 null,现在必须显式传数字;0=不限)
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
测试环境反馈前端保存班期时缺传 `maxParticipants` 会收到模糊的"违反约束"报错,用户无法分辨是哪个字段出问题。
|
||||
|
||||
后端之前 `ScheduleSaveReqVO.maxParticipants` 只用 `@ApiModelProperty(required=true)` 做了文档标记,Bean Validation 层未强制校验,结果 null 一路打到 SQL 层才报错。
|
||||
|
||||
## 二、变更接口
|
||||
|
||||
| 方法 | 路径 | 变更类型 |
|
||||
|------|------|---------|
|
||||
| POST | `/admin/product/item/{id}/schedule` | 请求体字段校验收紧 |
|
||||
| POST | `/admin/product/item/{id}/schedule/batch-create` | 同上 |
|
||||
|
||||
## 三、字段约束变化
|
||||
|
||||
| 字段 | 之前 | 现在 | 允许值 |
|
||||
|------|------|------|--------|
|
||||
| `maxParticipants` | 可不传 / null(后端兜底 0) | **必传** | 0=不限;>0=上限人数 |
|
||||
|
||||
## 四、错误响应示例
|
||||
|
||||
### 缺传 maxParticipants
|
||||
|
||||
```json
|
||||
POST /admin/product/item/123/schedule
|
||||
{
|
||||
"batchName": "五一特别团",
|
||||
"departureDate": "2026-05-01",
|
||||
"adultPrice": 5160,
|
||||
"childPrice": 4580
|
||||
}
|
||||
|
||||
// 响应(之前是 "数据操作违反约束,请检查输入",歧义)
|
||||
{
|
||||
"code": 400,
|
||||
"message": "最大参与人数不能为空",
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
### 正常请求
|
||||
|
||||
```json
|
||||
POST /admin/product/item/123/schedule
|
||||
{
|
||||
"batchName": "五一特别团",
|
||||
"departureDate": "2026-05-01",
|
||||
"adultPrice": 5160,
|
||||
"childPrice": 4580,
|
||||
"maxParticipants": 30, // 必填
|
||||
"maxRooms": 15 // 可选,默认取行程最少房间数
|
||||
}
|
||||
```
|
||||
|
||||
## 五、前端改动建议
|
||||
|
||||
1. **班期创建/编辑表单**:`maxParticipants` 输入框加前端必填校验,避免到后端才被拦下
|
||||
2. **批量创建表单**:同上
|
||||
3. **提示文案**:如果字段空值,提示"最大参与人数不能为空,0 表示不限人数"
|
||||
|
||||
## 六、兼容性
|
||||
|
||||
- 前端原本填写 0 或具体数字的调用方不受影响
|
||||
- 前端如有"不填=不限"的历史 UX,需改成"显式填 0=不限"
|
||||
- 后端 Service 层 null→0 兜底代码保留作为双保险,但实际不会再走到
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户