gaoluyang
93 分钟以前 f9d83ae75f8c911c1c05304f8131fc222579768a
fix(hrm): 已清除的排班日期重新排班时报唯一键冲突

hrm_employee_schedule 的唯一索引 uk_employee_date(employee_id,date) 不含 deleted,
排班清除走逻辑删除后,再给同员工同日期排班会插入重复键,整批保存事务回滚,
表现为「排了班但保存后查不到」。

改为插入前先复活同员工同日期的已删除记录(save / batch-save 两条路径)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
已修改2个文件
25 ■■■■■ 文件已修改
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/attendance/HrmEmployeeScheduleMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmEmployeeScheduleServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/attendance/HrmEmployeeScheduleMapper.java
@@ -4,6 +4,8 @@
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmEmployeeScheduleDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import java.time.LocalDate;
import java.util.Collection;
@@ -48,4 +50,15 @@
                .eq(HrmEmployeeScheduleDO::getShiftId, shiftId));
    }
    /**
     * 复活一条已被逻辑删除的排班(唯一索引 uk_employee_date 不含 deleted,
     * 逻辑删除后再插入同员工同日期会撞唯一键,这里改为把旧行改回有效)
     *
     * @return 更新行数,0 表示不存在已删除的同员工同日期记录
     */
    @Update("UPDATE hrm_employee_schedule SET deleted = 0, shift_id = #{shiftId}, remark = #{remark}, "
            + "update_time = NOW() WHERE employee_id = #{employeeId} AND date = #{date} AND deleted = 1")
    int reviveDeletedSchedule(@Param("employeeId") Long employeeId, @Param("date") LocalDate date,
                              @Param("shiftId") Long shiftId, @Param("remark") String remark);
}
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmEmployeeScheduleServiceImpl.java
@@ -153,7 +153,11 @@
                }
            } else {
                if (existing == null) {
                    employeeScheduleMapper.insert(BeanUtils.toBean(reqVO, HrmEmployeeScheduleDO.class));
                    // 该员工该日期可能是之前被清除(逻辑删除)的排班,唯一索引不含 deleted,直接 insert 会撞唯一键
                    if (employeeScheduleMapper.reviveDeletedSchedule(reqVO.getEmployeeId(), reqVO.getDate(),
                            reqVO.getShiftId(), reqVO.getRemark()) == 0) {
                        employeeScheduleMapper.insert(BeanUtils.toBean(reqVO, HrmEmployeeScheduleDO.class));
                    }
                } else {
                    existing.setShiftId(reqVO.getShiftId());
                    existing.setRemark(reqVO.getRemark());
@@ -222,6 +226,12 @@
                    continue;
                }
                if (existing == null) {
                    // 已被清除(逻辑删除)的日期直接 insert 会撞唯一索引,优先复活旧行
                    if (employeeScheduleMapper.reviveDeletedSchedule(employee.getId(), date,
                            reqVO.getShiftId(), null) > 0) {
                        itemCount++;
                        continue;
                    }
                    HrmEmployeeScheduleDO schedule = new HrmEmployeeScheduleDO();
                    schedule.setEmployeeId(employee.getId());
                    schedule.setDate(date);