|
@@ -0,0 +1,109 @@
|
|
|
|
|
+# 应报工时重复与离职后数据清理设计
|
|
|
|
|
+
|
|
|
|
|
+> 日期:2026-07-15
|
|
|
|
|
+> 状态:设计已确认,等待 dry-run 精确统计与删除前二次确认
|
|
|
|
|
+
|
|
|
|
|
+## 目标
|
|
|
|
|
+
|
|
|
|
|
+清理「应填报工时」表中的两类无效数据:
|
|
|
|
|
+
|
|
|
|
|
+1. 同一员工同一应填报日期存在多条记录时,只保留一条。
|
|
|
|
|
+2. 员工已有离职日期时,删除应填报日期晚于离职日期的记录。
|
|
|
|
|
+
|
|
|
|
|
+离职当天及之前的数据保留。无法匹配人员档案、人员档案公司或属性为空等其他情况继续沿用现有保留逻辑,不在本次清理范围内。
|
|
|
|
|
+
|
|
|
|
|
+## 已确认现状
|
|
|
|
|
+
|
|
|
|
|
+- 每日增量同步在 03:30、12:45 执行,每次重新读取全量人员档案。
|
|
|
|
|
+- 人员档案的离职日期字段为 `dateField_mh8xhqc7`。
|
|
|
|
|
+- 写入前已有 `workDay > offlineDate` 过滤,离职日后不再新增应报工时。
|
|
|
|
|
+- 状态为离职但离职日期为空时,代码会整人防御跳过;当前数据不存在这种情况。
|
|
|
|
|
+- 线上离职清理 dry-run 扫描 43,851 条记录,识别 74 名有离职日期的员工、796 条离职日后记录。
|
|
|
|
|
+
|
|
|
|
|
+## 重复记录判定
|
|
|
|
|
+
|
|
|
|
|
+重复键为:
|
|
|
|
|
+
|
|
|
|
|
+```text
|
|
|
|
|
+employeeId + "|" + workDay
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- `employeeId` 从 `employeeField_mmd8onl4` 提取。
|
|
|
|
|
+- `workDay` 从 `dateField_mmd8onl5` 解析。
|
|
|
|
|
+- 员工或日期缺失时无法形成可靠重复键,该记录保留并计入 `skippedInvalidKey`。
|
|
|
|
|
+- 仅键完全相同且数量大于 1 的记录视为重复组。
|
|
|
|
|
+
|
|
|
|
|
+## 重复组保留规则
|
|
|
|
|
+
|
|
|
|
|
+每个重复组只保留一条,按以下顺序选择:
|
|
|
|
|
+
|
|
|
|
|
+1. 保留业务字段非空数量最多的记录。
|
|
|
|
|
+2. 完整度相同时,保留 `gmtModified` 最近的记录。
|
|
|
|
|
+3. 修改时间仍相同时,保留 `gmtCreate` 最近的记录。
|
|
|
|
|
+4. 仍相同时,按 `formInstanceId` 做稳定排序,保证重复执行结果一致。
|
|
|
|
|
+
|
|
|
|
|
+完整度仅统计已有应报工时业务字段:工时、Manager、员工编号、属性、部门、归属公司、是否 CF 员工。核心键字段不计分。
|
|
|
|
|
+
|
|
|
|
|
+不合并不同记录的字段,避免构造从未真实存在过的组合数据。此前公司和属性已完成统一回填,因此选择最完整记录即可。
|
|
|
|
|
+
|
|
|
|
|
+## 接口与执行流程
|
|
|
|
|
+
|
|
|
|
|
+新增一次性接口:
|
|
|
|
|
+
|
|
|
|
|
+```text
|
|
|
|
|
+GET /workhours/cleanup-duplicates?dryRun=true
|
|
|
|
|
+GET /workhours/cleanup-duplicates
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+执行分为四步:
|
|
|
|
|
+
|
|
|
|
|
+1. 按月份扫描从 2026-04 至当前月的应报工时,绕过宜搭搜索结果 30,000 条上限。
|
|
|
|
|
+2. 在内存中按重复键分组并选择保留记录,收集其余实例 ID。
|
|
|
|
|
+3. dry-run 返回统计与前 5 个重复组样本,不调用删除接口。
|
|
|
|
|
+4. 正式模式按每批最多 100 条调用 `delete_batch`,返回实际删除数与失败数。
|
|
|
|
|
+
|
|
|
|
|
+返回统计至少包含:
|
|
|
|
|
+
|
|
|
|
|
+- `total`
|
|
|
|
|
+- `uniqueKeys`
|
|
|
|
|
+- `duplicateGroups`
|
|
|
|
|
+- `duplicateRecords`
|
|
|
|
|
+- `toDelete`
|
|
|
|
|
+- `skippedInvalidKey`
|
|
|
|
|
+- `deleted`
|
|
|
|
|
+- `fail`
|
|
|
|
|
+- `samples`
|
|
|
|
|
+
|
|
|
|
|
+## 删除安全门槛
|
|
|
|
|
+
|
|
|
|
|
+正式删除前必须把以下范围复述给用户并取得二次确认:
|
|
|
|
|
+
|
|
|
|
|
+- 目标表单:应填报工时表。
|
|
|
|
|
+- 重复组数量、重复记录总数和待删除实例数。
|
|
|
|
|
+- 离职后待删除记录数,当前 dry-run 为 796。
|
|
|
|
|
+- 重复记录保留规则。
|
|
|
|
|
+
|
|
|
|
|
+正式删除后必须再次执行:
|
|
|
|
|
+
|
|
|
|
|
+1. `/workhours/cleanup-duplicates?dryRun=true`,期望 `toDelete=0`。
|
|
|
|
|
+2. `/workhours/cleanup-after-offline?dryRun=true`,期望 `toDelete=0`。
|
|
|
|
|
+3. `/workhours/cleanup-future?dryRun=true`,期望 `toDelete=0`。
|
|
|
|
|
+
|
|
|
|
|
+## 测试
|
|
|
|
|
+
|
|
|
|
|
+采用测试驱动实现,至少覆盖:
|
|
|
|
|
+
|
|
|
|
|
+1. 同组中保留业务字段最完整的记录。
|
|
|
|
|
+2. 完整度相同时保留最近修改的记录。
|
|
|
|
|
+3. 员工或日期缺失的记录不进入删除集合。
|
|
|
|
|
+4. dry-run 只返回待删除数量,不调用 `delete_batch`。
|
|
|
|
|
+5. 离职边界保持 `workDay > offlineDate`:离职当天保留,次日删除或跳过写入。
|
|
|
|
|
+
|
|
|
|
|
+最终执行模块测试、完整打包、差异检查,再提交和部署。
|
|
|
|
|
+
|
|
|
|
|
+## 不在本次范围
|
|
|
|
|
+
|
|
|
|
|
+- 不删除无法匹配人员档案的记录。
|
|
|
|
|
+- 不清空人员档案公司或属性为空的历史字段。
|
|
|
|
|
+- 不处理项目变更审批 Manager 回算。
|
|
|
|
|
+- 不启用 `projectChangeSyncEnabled`。
|