# 产品编辑 订金设置 固定金额/比例 严格互斥 > **服务**: 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 端算价、订单创建、订单详情等接口**零影响** - 历史数据(已经同时有两字段值的产品):后端读取逻辑不变,仍按固定额优先,不会异常。下次编辑保存时才会触发互斥校验