hl-api-changelog/changelogs/2026-05/23_hotfix_itinerary_reorder_wipes_fields.md
API Changelog Bot 21ce4af85e hotfix(product-v2): 拖动行程字段抹平为 null 修复 (PR #2933)
P0 数据损坏 修复 + 真测通过, 前端无 API 变化无需改。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 10:11:47 +08:00

3.6 KiB

hotfix(product-v2): 拖动行程把所有天数据字段抹平为 null (P0 数据损坏)

仓库: HL (后端 hl-product-service-v2) 关联 PR/Issue: PR #2933, Closes #2931 日期: 2026-05-23 影响范围: 行程天数拖拽重排接口 /admin/product/item/{productId}/itinerary/reorder 接收方: mmg (前端) 前端: 无 API 变化, 无需改前端


🚨 紧急修复说明

wx 反馈 (2026-05-23 09:50): 拖动产品行程天数重排后,所有天的 dayTitle / description / coverImageUrl / 早晚餐 / 集合点 / 解散点 / 当日里程 等字段被抹空为 null

P0 数据损坏: 任何调用过 PR #2926 reorder 端点的产品,已经填好的行程内容全部被清空。day_id 关联的活动节点 (product_itinerary_node) 没丢, 只有 product_itinerary_day 主表的 IGNORED 策略字段被抹空

根因

ProductItineraryDayDO 大量字段标了 @TableField(updateStrategy = FieldStrategy.IGNORED) (Issue #2119 设计: 业务允许"清空字段")。PR #2926 reorderDays 实现里:

// 反例代码 (已修)
ProductItineraryDayDO up = new ProductItineraryDayDO();  // 空对象!
up.setDayId(d.getDayId());
up.setDayNumber(-d.getDayNumber());
negPhase.add(up);
dayService.updateBatch(negPhase);  // IGNORED 策略下 null 字段会被写入!

IGNORED 策略下,空对象的 null 字段会被 SQL UPDATE 写入 DB,把用户填写的字段全部抹空。

修复

  1. ProductItineraryDayMapper 新增 default updateDayNumberById(dayId, dayNumber),用 LambdaUpdateWrapperX.set 只 update day_number 单列,永不触碰其他字段
  2. ProductItineraryService.reorderDays Step1/Step2 改调 dayService.updateDayNumberById,不再走 updateBatch(空对象集合)
  3. 新增回归测试 reorderDays_doesNotWipeIgnoredFields 守护

API 行为变化

无! /admin/product/item/{productId}/itinerary/reorder 接口请求/响应字段完全一致,前端无需做任何改动

修复前: 拖动后所有 IGNORED 字段被抹空 修复后: 拖动后只改 day_number, 其他字段保留


🚨 前端需要做的事

无需任何改动

可选: 通知 wx 数据库已损坏的产品(包括截图中的 ID=2053668473820622849)需要重新填写行程内容,无法自动恢复(binlog 时长有限)。


测试服真测记录

部署 product-v2 (rolling-deploy 2026-05-23 10:05) 后真测:

Step 内容 结果
1 DB 写测试字段: day_id=2043611284271677442 day_number=1 → day_title='TEST', description='应保留', cover='https://...', mileage=88.8 写入
2 调 reorder API 把 6 天全部倒序 (D1→6, D2→5, ...) 200 重排成功
3 查 DB: day_id=2043611284271677442 跑到 day_number=6, dayTitle/description/cover/mileage 完整保留 字段不丢
4 清测试数据

单测: hl-product-service-v2 ProductItinerary*Test 50/50 全绿 (含新增的 reorderDays_doesNotWipeIgnoredFields 回归测试 + 既有 ProductItineraryDOFieldStrategyTest 守护 IGNORED 策略)


复盘 / 经验沉淀

为什么 PR #2926 真测没捕获: 当时真测脚本只验证 day_number 字段重排正确,没验证其他字段不被改。原 PR 真测产品的 dayTitle 本来就是 null, 无法暴露此 bug。

今后真测要求: 任何"写实体"的 PR 真测必须包含 BEFORE / AFTER 对照: 显式校验未修改字段不被改。已记入 MEMORY 经验。


联系人

后端: wx (呼籁旅行) 前端: mmg

如有疑问可在 #2931 评论区留言。