package com.klp.service.impl; import cn.hutool.core.bean.BeanUtil; import com.klp.common.core.page.TableDataInfo; import com.klp.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.klp.common.utils.StringUtils; import com.klp.domain.vo.WmsAttendanceShiftRuleVo; import com.klp.domain.vo.WmsAttendanceShiftVo; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.klp.domain.bo.WmsAttendanceScheduleBo; import com.klp.domain.bo.GenerateScheduleBo; import com.klp.domain.vo.WmsAttendanceScheduleVo; import com.klp.domain.WmsAttendanceSchedule; import com.klp.domain.WmsAttendanceShiftRule; import com.klp.domain.WmsAttendanceShift; import com.klp.mapper.WmsAttendanceScheduleMapper; import com.klp.service.IWmsAttendanceScheduleService; import com.klp.service.IWmsAttendanceShiftRuleService; import com.klp.service.IWmsAttendanceShiftService; import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.stream.Collectors; /** * 排班(谁在哪天上班)Service业务层处理 * * @author klp * @date 2026-05-08 */ @RequiredArgsConstructor @Service public class WmsAttendanceScheduleServiceImpl implements IWmsAttendanceScheduleService { private final WmsAttendanceScheduleMapper baseMapper; private final IWmsAttendanceShiftRuleService shiftRuleService; private final IWmsAttendanceShiftService shiftService; /** * 查询排班(谁在哪天上班) */ @Override public WmsAttendanceScheduleVo queryById(Long scheduleId){ WmsAttendanceSchedule schedule = baseMapper.selectById(scheduleId); if (schedule == null) { return null; } // 使用关联查询获取详细信息 List list = baseMapper.selectScheduleWithDetails( schedule.getUserId(), schedule.getWorkDate(), schedule.getShiftId(),null, null); return list.isEmpty() ? null : list.get(0); } /** * 查询排班(谁在哪天上班)列表 */ @Override public TableDataInfo queryPageList(WmsAttendanceScheduleBo bo, PageQuery pageQuery) { // 1. 构建 MP 标准分页对象 Page page = pageQuery.build(); // 2. 调用 Mapper baseMapper.selectScheduleWithDetailsPage(page, bo); // 3. 直接返回 return TableDataInfo.build(page); } /** * 查询排班(谁在哪天上班)列表 */ @Override public List queryList(WmsAttendanceScheduleBo bo) { return baseMapper.selectScheduleWithDetails(bo.getUserId(), bo.getWorkDate(), bo.getShiftId(), bo.getStartDate(), bo.getEndDate()); } /** * 新增排班(谁在哪天上班) */ @Override public Boolean insertByBo(WmsAttendanceScheduleBo bo) { WmsAttendanceSchedule add = BeanUtil.toBean(bo, WmsAttendanceSchedule.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setScheduleId(add.getScheduleId()); } return flag; } /** * 修改排班(谁在哪天上班) */ @Override public Boolean updateByBo(WmsAttendanceScheduleBo bo) { WmsAttendanceSchedule update = BeanUtil.toBean(bo, WmsAttendanceSchedule.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(WmsAttendanceSchedule entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除排班(谁在哪天上班) */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } /** * 生成排班 */ @Override @Transactional(rollbackFor = Exception.class) public void generateSchedule(List boList) { for (GenerateScheduleBo bo : boList) { LocalDate startDate = bo.getStartDate().toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate(); LocalDate endDate = bo.getEndDate().toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate(); if (startDate.isAfter(endDate)) { throw new RuntimeException("开始时间不能大于结束时间"); } if (bo.getRuleId() == null) { // 正常排班,不倒班 generateNormalSchedule(bo, startDate, endDate); } else { // 倒班排班 generateShiftSchedule(bo, startDate, endDate); } } } /** * 生成正常排班 */ private void generateNormalSchedule(GenerateScheduleBo bo, LocalDate startDate, LocalDate endDate) { WmsAttendanceShiftVo shift = shiftService.queryById(bo.getShiftId()); if (shift == null) { throw new RuntimeException("班次不存在"); } // 一次查询整个日期范围内已存在的排班 Set existingDates = getExistingScheduleDates(bo.getUserId(), startDate, endDate); List schedules = new ArrayList<>(); LocalDate currentDate = startDate; while (!currentDate.isAfter(endDate)) { if (!existingDates.contains(currentDate)) { WmsAttendanceSchedule schedule = new WmsAttendanceSchedule(); schedule.setUserId(bo.getUserId()); schedule.setWorkDate(java.sql.Date.valueOf(currentDate)); schedule.setShiftId(bo.getShiftId()); schedule.setShiftName(shift.getShiftName()); schedules.add(schedule); } currentDate = currentDate.plusDays(1); } // 批量插入 if (!schedules.isEmpty()) { boolean ok = baseMapper.insertBatch(schedules); if (!ok) { throw new RuntimeException("排班插入失败"); } } } /** * 生成倒班排班 */ private void generateShiftSchedule(GenerateScheduleBo bo, LocalDate startDate, LocalDate endDate) { // 获取倒班规则 WmsAttendanceShiftRuleVo rule = shiftRuleService.queryById(bo.getRuleId()); if (rule == null) { throw new RuntimeException("倒班规则不存在"); } // 验证班次匹配 if (!bo.getShiftId().equals(rule.getShiftA()) && !bo.getShiftId().equals(rule.getShiftB())) { throw new RuntimeException("班次ID与倒班规则不匹配"); } // 获取班次信息 WmsAttendanceShiftVo shiftA = shiftService.queryById(rule.getShiftA()); WmsAttendanceShiftVo shiftB = shiftService.queryById(rule.getShiftB()); WmsAttendanceShiftVo changeShiftB = rule.getChangeShiftBId() != null ? shiftService.queryById(rule.getChangeShiftBId()) : null; WmsAttendanceShiftVo changeShiftA = rule.getChangeShiftAId() != null ? shiftService.queryById(rule.getChangeShiftAId()) : null; if (shiftA == null || shiftB == null) { throw new RuntimeException("倒班规则中的班次不存在"); } // 一次查询整个日期范围内已存在的排班 Set existingDates = getExistingScheduleDates(bo.getUserId(), startDate, endDate); List schedules = new ArrayList<>(); LocalDate currentDate = startDate; long daysFromStart = 0; // 确定初始班次 boolean isCurrentShiftA = bo.getShiftId().equals(rule.getShiftA()); long cycleDays = rule.getCycleDays() != null ? rule.getCycleDays() : 10; while (!currentDate.isAfter(endDate)) { if (!existingDates.contains(currentDate)) { WmsAttendanceSchedule schedule = new WmsAttendanceSchedule(); schedule.setUserId(bo.getUserId()); schedule.setWorkDate(java.sql.Date.valueOf(currentDate)); boolean isChangeDay = daysFromStart > 0 && daysFromStart % cycleDays == 0; if (isChangeDay) { if (isCurrentShiftA) { if (changeShiftB != null) { schedule.setShiftId(rule.getChangeShiftBId()); schedule.setShiftName(changeShiftB.getShiftName()); } else { schedule.setShiftId(rule.getShiftB()); schedule.setShiftName(shiftB.getShiftName()); } isCurrentShiftA = false; } else { // 夜班转白班 if (changeShiftA != null) { schedule.setShiftId(rule.getChangeShiftAId()); schedule.setShiftName(changeShiftA.getShiftName()); } else { schedule.setShiftId(rule.getShiftA()); schedule.setShiftName(shiftA.getShiftName()); } isCurrentShiftA = true; } } else { // 普通日 if (isCurrentShiftA) { schedule.setShiftId(rule.getShiftA()); schedule.setShiftName(shiftA.getShiftName()); } else { schedule.setShiftId(rule.getShiftB()); schedule.setShiftName(shiftB.getShiftName()); } } schedules.add(schedule); } currentDate = currentDate.plusDays(1); daysFromStart++; } // 批量插入 if (!schedules.isEmpty()) { boolean i = baseMapper.insertBatch(schedules); if (!i) { throw new RuntimeException("批量插入倒班排班失败"); } } } /** * 批量查询指定用户日期范围内已存在的排班日期 */ private Set getExistingScheduleDates(Long userId, LocalDate startDate, LocalDate endDate) { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.eq(WmsAttendanceSchedule::getUserId, userId) .between(WmsAttendanceSchedule::getWorkDate, java.sql.Date.valueOf(startDate), java.sql.Date.valueOf(endDate)) .select(WmsAttendanceSchedule::getWorkDate); List list = baseMapper.selectList(wrapper); return list.stream() .map(s -> new java.sql.Date(s.getWorkDate().getTime()).toLocalDate()) .collect(Collectors.toSet()); } }