gaoluyang
5 小时以前 578f84ece732a710278e7914396e127d8a27ed8a
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmEmployeeScheduleServiceImpl.java
@@ -6,14 +6,19 @@
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleBatchSaveReqVO;
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleBatchSaveRespVO;
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleLeaveRespVO;
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleListReqVO;
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleRespVO;
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleSaveReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.approval.HrmLeaveApplicationDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceHolidayDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmEmployeeScheduleDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmShiftDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO;
import cn.iocoder.yudao.module.hrm.dal.mysql.approval.HrmLeaveApplicationMapper;
import cn.iocoder.yudao.module.hrm.enums.HrmAttendanceHolidayTypeEnum;
import cn.iocoder.yudao.module.hrm.enums.HrmLeaveTypeEnum;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmEmployeeScheduleMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmShiftMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeMapper;
@@ -26,6 +31,7 @@
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.YearMonth;
import java.util.*;
import java.util.function.Function;
@@ -53,6 +59,8 @@
    private DeptApi deptApi;
    @Resource
    private HrmAttendanceHolidayService attendanceHolidayService;
    @Resource
    private HrmLeaveApplicationMapper leaveApplicationMapper;
    @Override
    public List<HrmEmployeeScheduleRespVO> getScheduleList(HrmEmployeeScheduleListReqVO reqVO) {
@@ -191,12 +199,20 @@
                .collect(Collectors.toMap(HrmAttendanceHolidayDO::getDate, HrmAttendanceHolidayDO::getType,
                        (a, b) -> a));
        // 5. 按日期逐天生成排班:休息日删除原班次,工作日 upsert
        // 5. 预加载目标员工的已审批请假:请假当天跳过,不排班也不清除已有排班
        Map<Long, Set<LocalDate>> leaveDateMap = buildLeaveDateMap(employees,
                reqVO.getStartDate(), reqVO.getEndDate());
        // 6. 按日期逐天生成排班:休息日删除原班次,工作日 upsert
        int itemCount = 0;
        for (LocalDate date = reqVO.getStartDate(); !date.isAfter(reqVO.getEndDate()); date = date.plusDays(1)) {
            boolean rest = isRestDay(date, reqVO.getRestMode(), holidayTypeMap.get(date))
                    || reqVO.getShiftId() == null;
            for (HrmEmployeeDO employee : employees) {
                // 员工当天在请假期内,保持原排班不动
                if (leaveDateMap.getOrDefault(employee.getId(), Collections.emptySet()).contains(date)) {
                    continue;
                }
                HrmEmployeeScheduleDO existing = existingMap.get(scheduleKey(employee.getId(), date));
                if (rest) {
                    if (existing != null) {
@@ -226,6 +242,75 @@
        return respVO;
    }
    @Override
    public List<HrmEmployeeScheduleLeaveRespVO> getApprovedLeaveList(LocalDate startDate, LocalDate endDate) {
        if (startDate == null || endDate == null || endDate.isBefore(startDate)) {
            return Collections.emptyList();
        }
        List<HrmLeaveApplicationDO> leaves = leaveApplicationMapper.selectApprovedListByTimeRange(
                startDate.atStartOfDay(), endDate.atTime(LocalTime.MAX), null);
        if (CollUtil.isEmpty(leaves)) {
            return Collections.emptyList();
        }
        Set<Long> userIds = leaves.stream().map(HrmLeaveApplicationDO::getUserId)
                .filter(Objects::nonNull).collect(Collectors.toSet());
        Map<Long, HrmEmployeeDO> userEmployeeMap = CollUtil.isEmpty(userIds) ? Collections.emptyMap()
                : employeeMapper.selectList(new LambdaQueryWrapperX<HrmEmployeeDO>()
                        .in(HrmEmployeeDO::getUserId, userIds)).stream()
                .filter(employee -> employee.getUserId() != null)
                .collect(Collectors.toMap(HrmEmployeeDO::getUserId, Function.identity(), (a, b) -> a));
        return leaves.stream().map(leave -> {
            HrmEmployeeDO employee = userEmployeeMap.get(leave.getUserId());
            if (employee == null) {
                return null;
            }
            HrmEmployeeScheduleLeaveRespVO respVO = new HrmEmployeeScheduleLeaveRespVO();
            respVO.setId(leave.getId());
            respVO.setEmployeeId(employee.getId());
            respVO.setEmployeeName(employee.getName());
            respVO.setLeaveType(leave.getLeaveType());
            respVO.setLeaveTypeName(HrmLeaveTypeEnum.getNameByStatus(leave.getLeaveType()));
            respVO.setStartTime(leave.getStartTime());
            respVO.setEndTime(leave.getEndTime());
            respVO.setDuration(leave.getDuration());
            return respVO;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }
    /**
     * 构建「员工ID -> 请假日集合」映射(只取审批通过的请假,且裁剪到排班日期范围内)
     */
    private Map<Long, Set<LocalDate>> buildLeaveDateMap(List<HrmEmployeeDO> employees,
                                                        LocalDate startDate, LocalDate endDate) {
        Map<Long, HrmEmployeeDO> userEmployeeMap = employees.stream()
                .filter(employee -> employee.getUserId() != null)
                .collect(Collectors.toMap(HrmEmployeeDO::getUserId, Function.identity(), (a, b) -> a));
        if (userEmployeeMap.isEmpty()) {
            return Collections.emptyMap();
        }
        List<HrmLeaveApplicationDO> leaves = leaveApplicationMapper.selectApprovedListByTimeRange(
                startDate.atStartOfDay(), endDate.atTime(LocalTime.MAX), userEmployeeMap.keySet());
        if (CollUtil.isEmpty(leaves)) {
            return Collections.emptyMap();
        }
        Map<Long, Set<LocalDate>> result = new HashMap<>();
        for (HrmLeaveApplicationDO leave : leaves) {
            HrmEmployeeDO employee = userEmployeeMap.get(leave.getUserId());
            if (employee == null || leave.getStartTime() == null || leave.getEndTime() == null) {
                continue;
            }
            LocalDate from = leave.getStartTime().toLocalDate().isBefore(startDate)
                    ? startDate : leave.getStartTime().toLocalDate();
            LocalDate to = leave.getEndTime().toLocalDate().isAfter(endDate)
                    ? endDate : leave.getEndTime().toLocalDate();
            Set<LocalDate> dates = result.computeIfAbsent(employee.getId(), key -> new HashSet<>());
            for (LocalDate date = from; !date.isAfter(to); date = date.plusDays(1)) {
                dates.add(date);
            }
        }
        return result;
    }
    private Set<Long> getSelfAndChildDeptIds(Long deptId) {
        Set<Long> deptIds = new LinkedHashSet<>();
        deptIds.add(deptId);