Files
mgm-daylight-mcrm-api-mobile/docs/merge_daylight_dl_reservation_dedup.md
2026-09-08 15:12:29 +08:00

5.0 KiB
Raw Permalink Blame History

Daylight 合并与 dl_reservation_id 去重说明

本文档说明 BookingService 中为减少「同一 Daylight 预订在 bookings 表出现多行」所做的修改及调用关系。


1. 涉及文件与入口

位置 说明
BookingService.java mergeDaylightRecordToDatabaseInfo、insertDaylightBooking、consolidateDuplicateDlReservations 及相关私有辅助方法
BookingController(recordSearch 等) 在拉取 Daylight 列表后调用 mergeDaylightRecordToDatabaseInfo

2. mergeDaylightRecordToDatabaseInfo 的改动要点

2.1 预查询:按 dlReservationId 收集(不再依赖确认号)

原问题:仅当 confirmationNo 非空时才把 dlReservationId 放进 findByDlReservationIdIn 的入参。若接口有 dl_reservation_id 但尚未返回确认号,则查不到库里已有行,会误判为「不存在」而走插入,产生重复。

现逻辑:对本次 result(Daylight 返回列表)中每条记录,只要 dlReservationId 非空白即加入 LinkedHashSet,再转为 List 做 findByDlReservationIdIn。

2.2 合并后物理去重:consolidateDuplicateDlReservations

在 updateBookingList 执行 bookingDao.saveAll 之后,对本次涉及到的每个 dl_reservation_id 再查库:

  • 若同一 dl_reservation_id 仍对应 ≥2 行(例如一条前台 PN、一条同步误插入的 N),则:
    • 按评分规则选出 keeper(主行),其余为 duplicate(次行);
    • 将次行上可对齐的字段并入主行后 delete 次行。

主行评分 keeperScoreForDlDedup(分数高者优先):

  • 有 player_id:+1000
  • approve_status == "PN":+100
  • 有 confirmation_no:+10
  • 有 member:+5

同分按 较小 id 靠前(作为 keeper)。

不合并、不删除 shouldSkipConsolidatingDuplicateRow:

  • 次行 approve_status 以 W 开头(候补等)
  • 次行 revision_of 指向 keeper(待审批修订子单)
  • keeper revision_of 指向次行

字段合并 mergeDuplicateBookingIntoKeeper:

  • 确认号、resv_id、Opera 状态、酒店/客源/房价码、player_id、member 与姓名字段、房/房型、booking_type_id、chain_code、block_id 等在主行为空或可从次行安全补齐时写入主行;last_opera_status 若次行有值则写入主行(与 Opera 同步一致)。

日志前缀:[consolidateDuplicateDlReservations]。


3. insertDaylightBooking 的改动要点

3.1 按 dl_reservation_id 兜底防插

在组好 Bookings book 并准备 save 之前,若 operaBooking.getDlReservationId() 非空,则先 findByDlReservationIdIn:

  • 若已存在任一行:不再 save 新单,打日志 [insertDaylightBooking] skip insert, existing dl_reservation_id=...,并设置返回视图上的 id 等为已有记录,直接 return operaBooking。

避免「合并阶段漏匹配」时再次插入第二条同 dl_reservation_id。

3.2 按确认号防重(空列表才插入)

  • 仅当 confirmationNo 非空时执行 findByConfirmationNo;
  • 仅当查询结果为空 时才 bookingDao.save(book);
  • 若已有同确认号记录,则复用其 id,不重复插入。

4. 根因归纳(与旧版对比)

原因 说明
预查漏掉无确认号的 dl_reservation_id 已用 LinkedHashSet + 非空 dlReservationId 修复
仅合并不删重复行 已用 consolidateDuplicateDlReservations 在当次同步末尾收敛
插入路径无 dl_reservation_id 防重 / 确认号逻辑不当 已在 insertDaylightBooking 中补齐
数据库无唯一约束 仍建议在库上对非空 dl_reservation_id 做过滤唯一索引(上线前清理历史重复)
并发双写 当前以逻辑合并 + 完成后去重为主;多实例强一致可再加分布式锁或 DB 唯一约束

5. 运维与验证

  1. 验证:部署后对曾出现双行的会员再触发一次会走 mergeDaylightRecordToDatabaseInfo 的查询(如 recordSearch);观察日志中 consolidateDuplicateDlReservations 是否合并并删除次行。
  2. 历史数据:长期未再走合并流程的重复行,需一次性脚本或手工按业务规则保留主行、删除/归档次行。
  3. 外键:若 delete 次行失败(子表引用),需先迁移或删除子表记录后再删。

6. 可选后续增强(未列入当前分支必选)

  • 合并匹配改为 Strings.CI.equals(忽略 dl_reservation_id 大小写),与接口/库实际存储一致。
  • 对 mergeDaylightRecordToDatabaseInfo 增加 @Transactional(rollbackFor = Exception.class),使 saveAll 与 consolidateDuplicateDlReservations 同事务回滚。
  • 高并发下对同一 dl_reservation_id 的插入加应用层锁或依赖唯一索引 + 捕获冲突后改走更新。

文档与 daylight_1.0.0 分支中 BookingService 当前实现对应;若行号随重构变化请以方法名为准。