docs(changelog): 12_fix_product_v2_toddler_discount_db_clear_on_update
PR #2111 / Issue #2110 — 修补 #2107 漏 UPDATE 路径; MP NOT_NULL 默认跳过 null 导致 child=0 时 toddler_discount DB 列残留. @TableField(updateStrategy=IGNORED) 强制 UPDATE 写入, 测试服 4 场景 DB 真测通过. 前端无需改动. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
这个提交包含在:
父节点
424ae19438
当前提交
5442777489
@ -0,0 +1,78 @@
|
||||
---
|
||||
date: 2026-05-12
|
||||
type: backend-fix
|
||||
service: hl-product-service-v2
|
||||
priority: high
|
||||
notify: ["@mmg"]
|
||||
status: verified
|
||||
---
|
||||
|
||||
# 价格日历: 小童优惠儿童=0 时 DB 列**真正清空**(补 #2107 漏网回归)
|
||||
|
||||
> **PR**: [#2111](https://git.1814.love:8443/wx/HL/pulls/2111) — 已合并 dev + 部署测试服
|
||||
> **Issue**: [#2110](https://git.1814.love:8443/wx/HL/issues/2110)
|
||||
> **接口**: `POST /admin/product/item/{id}/price-calendar/batch` (无 API 变化)
|
||||
> **状态**: ✅ **测试服已验证** — 4 场景 DB 落库 SQL 真测通过
|
||||
|
||||
---
|
||||
|
||||
## 背景
|
||||
|
||||
#2107 (changelog `12_fix_product_v2_toddler_discount_normalize_and_validate.md`) 修了"儿童=0 时强制 toddlerDiscount=null"逻辑, 单测 + INSERT 场景验证通过, **遗漏了 UPDATE 场景**:
|
||||
|
||||
- 原 DB 已有记录 `child=80, toddler=20`
|
||||
- 用户改 child=0 保存 (前端把 toddler 输入框 disabled)
|
||||
- 后端 step 0 把 `req.toddlerDiscount` 归一化为 null ✓
|
||||
- `expandToDailyRecords` 兜底 0→null ✓
|
||||
- **`batchUpsert` 走 `updateById(item)`, MP 默认 `FieldStrategy.NOT_NULL` 跳过 null 字段**, UPDATE SQL 不下发 `set toddler_discount=NULL`, **DB 列保留原值 20**
|
||||
|
||||
## 修复
|
||||
|
||||
```java
|
||||
public class ProductPriceCalendarDO extends BaseDO {
|
||||
@TableField(updateStrategy = FieldStrategy.IGNORED) // ← 加这一行
|
||||
private BigDecimal toddlerDiscount;
|
||||
}
|
||||
```
|
||||
|
||||
`FieldStrategy.IGNORED` 强制 UPDATE 写入此字段, 即使 null 也下发 `set toddler_discount=NULL`, 让 step 0 归一化真正落库。
|
||||
|
||||
## 测试服真测 (硬性凭证)
|
||||
|
||||
接口: `POST /admin/product/item/{id}/price-calendar/batch`
|
||||
产品: 2053668473820622849 (test_admin 自有产品)
|
||||
|
||||
```
|
||||
Scenario A: 已有 child=80,toddler=20 + 提交 child=0
|
||||
→ DB seed: toddler_discount=20.00 (insert 阶段)
|
||||
→ DB after: toddler_discount=NULL ✓ (update 阶段强制写入)
|
||||
|
||||
Scenario B: 已有 child=80,toddler=20 + 提交 child=60,toddler=0
|
||||
→ DB seed: 20.00
|
||||
→ DB after: NULL ✓ (expandToDailyRecords 0→null 归一化 + UPDATE 真写)
|
||||
|
||||
Scenario C: 已有 child=80,toddler=20 + 提交 child=60,toddler=15
|
||||
→ DB seed: 20.00
|
||||
→ DB after: 15.00 ✓ (正常 UPDATE)
|
||||
|
||||
Scenario D: 全新 child=0
|
||||
→ DB after: NULL ✓ (INSERT 阶段 null 正常写入)
|
||||
```
|
||||
|
||||
## 影响面
|
||||
|
||||
- **API 不变, 仅 DB 写入行为修正**, 前端无需改动
|
||||
- `infantPrice/singleRoomDiff` 等其他金额字段**不需要**同样调整 (0=合法值, 无 0→null 归一化语义)
|
||||
- `deductStock/restoreStock` 走 load → setSold → updateById, toddler_discount 字段从 DB 加载原值原样写回, IGNORED 策略下等价无变化, 安全
|
||||
|
||||
## 风险
|
||||
|
||||
- 极小. `@TableField(updateStrategy=IGNORED)` 是 MP 官方文档行为, 仅影响**该单字段**的 UPDATE 写入策略, 不影响 INSERT/SELECT/DELETE。
|
||||
|
||||
## 历史脏数据
|
||||
|
||||
DB 里可能存在 `childSellPrice=0 && toddler_discount IS NOT NULL` 的脏数据(#2107 之前的残留)。**下次该区间编辑保存**会被本 PR 真正清掉, 用户无感知, 无需单独迁移脚本。
|
||||
|
||||
## 前端 @mmg 无需改动
|
||||
|
||||
仅是后端 UPDATE 写入策略修正, 接口/字段/行为全无变化。本 changelog 仅备案。
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户