liyong
4 天以前 d3e57f662fc00214c9d1b1caa9a4dd542a9d2e1e
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/team/MesCalTeamShiftServiceImpl.java
@@ -1,13 +1,16 @@
package cn.iocoder.yudao.module.mes.service.cal.team;
import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.shift.MesCalTeamShiftListReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.cal.holiday.MesCalHolidayDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.cal.plan.MesCalPlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.cal.plan.MesCalPlanShiftDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.cal.plan.MesCalPlanTeamDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamShiftDO;
import cn.iocoder.yudao.module.mes.dal.mysql.cal.team.MesCalTeamShiftMapper;
import cn.iocoder.yudao.module.mes.enums.cal.MesCalHolidayTypeEnum;
import cn.iocoder.yudao.module.mes.enums.cal.MesCalShiftMethodEnum;
import cn.iocoder.yudao.module.mes.enums.cal.MesCalShiftTypeEnum;
import cn.iocoder.yudao.module.mes.service.cal.holiday.MesCalHolidayService;
import cn.iocoder.yudao.module.mes.service.cal.plan.MesCalPlanService;
import cn.iocoder.yudao.module.mes.service.cal.plan.MesCalPlanShiftService;
import cn.iocoder.yudao.module.mes.service.cal.plan.MesCalPlanTeamService;
@@ -18,11 +21,14 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.CAL_TEAM_SHIFT_GENERATE_SHIFT_NOT_ENOUGH;
@@ -49,6 +55,9 @@
    @Resource
    @Lazy
    private MesCalPlanTeamService planTeamService;
    @Resource
    @Lazy
    private MesCalHolidayService holidayService;
    @Transactional(rollbackFor = Exception.class)
    @Override
@@ -69,23 +78,56 @@
            throw exception(CAL_TEAM_SHIFT_GENERATE_SHIFT_NOT_ENOUGH);
        }
        // 2. 计算日期跨度,遍历每天生成排班记录,最后统一写入数据库
        // 1.5 加载日期区间内的假日数据,构建 Map<日期, 类型>
        Map<LocalDate, Integer> holidayMap = holidayService.getHolidayList(plan.getStartDate(), plan.getEndDate())
                .stream().collect(Collectors.toMap(h -> h.getDay().toLocalDate(), MesCalHolidayDO::getType));
        // 2. 计算日期跨度,逐天判断工作日,生成排班记录
        LocalDate startDate = plan.getStartDate().toLocalDate();
        LocalDate endDate = plan.getEndDate().toLocalDate();
        long days = ChronoUnit.DAYS.between(startDate, endDate) + 1;
        boolean excludeWeekend = plan.getExcludeWeekend() != null && plan.getExcludeWeekend();
        List<MesCalTeamShiftDO> allRecords = new ArrayList<>();
        int shiftIndex = 0;
        int workdayOffset = 0; // 仅工作日递增,用于倒班轮换计数
        for (int i = 0; i < days; i++) {
            LocalDate currentDate = startDate.plusDays(i);
            // 2.1 跳过非工作日
            if (!isWorkday(currentDate, holidayMap, excludeWeekend)) {
                continue;
            }
            LocalDateTime currentDay = currentDate.atStartOfDay();
            // 2.2.1 根据倒班方式计算 shiftIndex
            shiftIndex = calculateShiftIndex(shiftIndex, i, currentDate, startDate, plan);
            // 2.2.1 根据倒班方式计算 shiftIndex(以工作日偏移为准)
            shiftIndex = calculateShiftIndex(shiftIndex, workdayOffset, currentDate, startDate, plan);
            // 2.2.2 根据轮班方式生成排班记录,收集到列表
            buildRecordsForDay(allRecords, planId, currentDay, shiftIndex, plan.getShiftType(), teams, shifts);
            workdayOffset++;
        }
        // 3. 批量插入排班记录
        teamShiftMapper.insertBatch(allRecords);
    }
    /**
     * 判断指定日期是否为工作日
     *
     * 优先级:holiday 表 > 周末排除开关 > 默认工作日
     */
    private boolean isWorkday(LocalDate date, Map<LocalDate, Integer> holidayMap, boolean excludeWeekend) {
        // 1. holiday 表优先:显式标记的日期以标记为准
        Integer holidayType = holidayMap.get(date);
        if (holidayType != null) {
            return MesCalHolidayTypeEnum.WORKDAY.getType().equals(holidayType);
        }
        // 2. 若开启了排除周末,周六日默认为休息日
        if (excludeWeekend) {
            DayOfWeek dow = date.getDayOfWeek();
            if (dow == DayOfWeek.SATURDAY || dow == DayOfWeek.SUNDAY) {
                return false;
            }
        }
        // 3. 默认工作日
        return true;
    }
    /**
@@ -96,6 +138,10 @@
        if (dayOffset == 0) {
            return shiftIndex; // 第一天不递增
        }
        // 单白班不需要倒班,shiftMethod 为 null,直接返回
        if (plan.getShiftMethod() == null) {
            return shiftIndex;
        }
        if (MesCalShiftMethodEnum.QUARTER.getMethod().equals(plan.getShiftMethod())) {
            // 按季度轮班:到了新季度的第一天
            LocalDate quarterStart = LocalDateTimeUtils.getQuarterStart(currentDate);