From f2c0dcd460dce70b6535ec43c55bba7c4ca1e3b3 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Sat, 18 Apr 2026 14:50:19 +0800 Subject: [PATCH] =?UTF-8?q?docs(product-v2):=20=E8=AE=A2=E9=87=91=E5=9B=BA?= =?UTF-8?q?=E5=AE=9A=E9=87=91=E9=A2=9D/=E6=AF=94=E4=BE=8B=20=E4=B8=A5?= =?UTF-8?q?=E6=A0=BC=E4=BA=92=E6=96=A5=E6=A0=A1=E9=AA=8C=20(PR=20#819)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 通知前端:切换 tab 时必须清空对方字段再提交,两字段同时有值后端 400 拒绝 --- ...-18_product-v2_deposit-mutex-validation.md | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 changelogs/2026-04/2026-04-18_product-v2_deposit-mutex-validation.md diff --git a/changelogs/2026-04/2026-04-18_product-v2_deposit-mutex-validation.md b/changelogs/2026-04/2026-04-18_product-v2_deposit-mutex-validation.md new file mode 100644 index 0000000..12889c5 --- /dev/null +++ b/changelogs/2026-04/2026-04-18_product-v2_deposit-mutex-validation.md @@ -0,0 +1,112 @@ +# 产品编辑 订金设置 固定金额/比例 严格互斥 + +> **服务**: hl-product-service-v2 (端口 8083) +> **PR**: #819 +> **Issue**: #816 +> **日期**: 2026-04-18 +> **影响范围**: 产品编辑 Step1 基础信息 → 订金设置 + +--- + +## 一、变更说明 + +此前后端未对订金"固定金额"和"比例"做互斥校验,允许两字段同时有值写入数据库;下单报价时按「固定额 > 0 优先」的逻辑处理,导致比例字段在同时有值时永远不生效,产生脏数据。 + +现在后端强制两字段**任意时刻最多只有一个有值**: + +1. 同时提交两个字段(都 > 0)→ 后端 **400 拒绝**,返回提示"订金固定金额与比例互斥,只能填写其中一个" +2. 保存时若 `depositAmount` 有值 → 数据库 `deposit_ratio` 显式置 NULL +3. 保存时若 `depositRatio` 有值 → 数据库 `deposit_amount` 显式置 NULL +4. 两者都为 null(或 0)→ 合法(视为全款模式 PaymentType=FULL) + +--- + +## 二、前端必须配合 + +**切换 tab 时清空对方字段再提交**,否则后端直接拒绝: + +| 前端场景 | 必须提交的 payload | +|---------|-------------------| +| 用户选"固定金额"填 500 | `{ "depositAmount": 500, "depositRatio": null }` | +| 用户从"固定金额"切到"比例"填 10% | `{ "depositAmount": null, "depositRatio": 10 }` | +| 用户选"全款支付"(无订金) | `{ "paymentType": "FULL", "depositAmount": null, "depositRatio": null }` | +| ❌ 错误示例(会被 400) | `{ "depositAmount": 500, "depositRatio": 10 }` | + +**关键点**:切换 tab 时**必须把对方字段在提交前 reset 成 null**,不要只是隐藏输入框。前端 state 里残留另一方的历史值会让后端拒绝。 + +--- + +## 三、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|---------|------| +| 1 | 保存产品基础信息 | POST | `/admin/product-basic/save` | 请求体新增校验 | `depositAmount`/`depositRatio` 不得同时 > 0 | + +--- + +## 四、接口详情 + +### 1. 保存产品基础信息 `POST /admin/product-basic/save` + +**VO**: `ProductBasicSaveReqVO` + +**相关字段**(字段本身没改,**校验行为改了**): + +| 字段 | 类型 | 必填 | 说明 | 变化 | +|------|------|------|------|------| +| `paymentType` | String | 是 | 支付方式:`FULL`=全款, `DEPOSIT`=订金+尾款 | 无 | +| `depositAmount` | BigDecimal | 否 | 订金固定金额(元/人),与 `depositRatio` **互斥** | 互斥校验 | +| `depositRatio` | Integer | 否 | 订金比例(%),与 `depositAmount` **互斥** | 互斥校验 | + +**互斥规则**: +- `depositAmount` 和 `depositRatio` 任意时刻最多只有一个 > 0 +- 两者都 null / 都 0 → 合法 +- 其中一个有值,另一个必须为 null 或 0 + +**违反互斥的错误响应**: + +```json +{ + "code": 400, + "message": "depositMutex: 订金固定金额与比例互斥,只能填写其中一个", + "success": false, + "data": null +} +``` + +--- + +## 五、数据库行为 + +保存成功后数据库 `product_basic` 表: + +| 前端提交 | `deposit_amount` 列 | `deposit_ratio` 列 | +|---------|--------------------|--------------------| +| `depositAmount=500, depositRatio=null` | `500.00` | `NULL` | +| `depositAmount=null, depositRatio=10` | `NULL` | `10` | +| 都为 null | `NULL` | `NULL` | + +即使前端不传"对方字段",后端也会**显式 SET NULL**,不会保留数据库里的历史残留值。 + +--- + +## 六、下单/报价逻辑未变 + +下单报价(`InternalProductService.fillPaymentInfo`)规则保持不变: + +``` +paymentType = 'DEPOSIT' 时: + if depositAmount > 0: 使用固定金额 + else if depositRatio > 0: depositAmount = totalPrice × depositRatio / 100 + else: depositAmount = 0 +``` + +因为现在产品保存阶段已经保证了两字段互斥,下单阶段永远走其中一条分支,不会再出现"两个都有值但比例被忽略"的脏数据场景。 + +--- + +## 七、影响范围 + +- 仅影响**管理后台产品编辑**的订金设置表单 +- 小程序端 C 端算价、订单创建、订单详情等接口**零影响** +- 历史数据(已经同时有两字段值的产品):后端读取逻辑不变,仍按固定额优先,不会异常。下次编辑保存时才会触发互斥校验