diff --git a/klp-wms/src/main/java/com/klp/controller/WmsAttendanceScheduleController.java b/klp-wms/src/main/java/com/klp/controller/WmsAttendanceScheduleController.java index 397258f3..a40b16e5 100644 --- a/klp-wms/src/main/java/com/klp/controller/WmsAttendanceScheduleController.java +++ b/klp-wms/src/main/java/com/klp/controller/WmsAttendanceScheduleController.java @@ -19,6 +19,7 @@ import com.klp.common.enums.BusinessType; import com.klp.common.utils.poi.ExcelUtil; import com.klp.domain.vo.WmsAttendanceScheduleVo; import com.klp.domain.bo.WmsAttendanceScheduleBo; +import com.klp.domain.bo.GenerateScheduleBo; import com.klp.service.IWmsAttendanceScheduleService; import com.klp.common.core.page.TableDataInfo; @@ -96,4 +97,14 @@ public class WmsAttendanceScheduleController extends BaseController { @PathVariable Long[] scheduleIds) { return toAjax(iWmsAttendanceScheduleService.deleteWithValidByIds(Arrays.asList(scheduleIds), true)); } + + /** + * 生成排班 + */ + @Log(title = "生成排班", businessType = BusinessType.INSERT) + @PostMapping("/generate") + public R generateSchedule(@Validated @RequestBody GenerateScheduleBo bo) { + iWmsAttendanceScheduleService.generateSchedule(bo); + return R.ok(); + } } diff --git a/klp-wms/src/main/java/com/klp/domain/WmsAttendanceShiftRule.java b/klp-wms/src/main/java/com/klp/domain/WmsAttendanceShiftRule.java index be87fa76..f0c8f197 100644 --- a/klp-wms/src/main/java/com/klp/domain/WmsAttendanceShiftRule.java +++ b/klp-wms/src/main/java/com/klp/domain/WmsAttendanceShiftRule.java @@ -44,6 +44,14 @@ public class WmsAttendanceShiftRule extends BaseEntity { * 班次B(通常夜班ID) */ private Long shiftB; + /** + * 倒班日(白班转夜班)使用的班次ID + */ + private Long changeShiftBId; + /** + * 倒班日(夜班转白班)使用的班次ID + */ + private Long changeShiftAId; /** * 备注 */ diff --git a/klp-wms/src/main/java/com/klp/domain/bo/GenerateScheduleBo.java b/klp-wms/src/main/java/com/klp/domain/bo/GenerateScheduleBo.java new file mode 100644 index 00000000..b68ba399 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/domain/bo/GenerateScheduleBo.java @@ -0,0 +1,49 @@ +package com.klp.domain.bo; + +import lombok.Data; +import javax.validation.constraints.*; +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.util.Date; + +/** + * 生成排班请求对象 + * + * @author klp + * @date 2026-05-09 + */ +@Data +public class GenerateScheduleBo { + + /** + * 员工ID + */ + @NotNull(message = "员工ID不能为空") + private Long userId; + + /** + * 班次ID + */ + @NotNull(message = "班次ID不能为空") + private Long shiftId; + + /** + * 倒班规则ID(可为空,为空则不倒班) + */ + private Long ruleId; + + /** + * 开始时间 + */ + @NotNull(message = "开始时间不能为空") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date startDate; + + /** + * 结束时间 + */ + @NotNull(message = "结束时间不能为空") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date endDate; + +} diff --git a/klp-wms/src/main/java/com/klp/domain/bo/WmsAttendanceShiftRuleBo.java b/klp-wms/src/main/java/com/klp/domain/bo/WmsAttendanceShiftRuleBo.java index bf789f57..9e587d31 100644 --- a/klp-wms/src/main/java/com/klp/domain/bo/WmsAttendanceShiftRuleBo.java +++ b/klp-wms/src/main/java/com/klp/domain/bo/WmsAttendanceShiftRuleBo.java @@ -47,6 +47,16 @@ public class WmsAttendanceShiftRuleBo extends BaseEntity { */ private Long shiftB; + /** + * 倒班日(白班转夜班)使用的班次ID + */ + private Long changeShiftBId; + + /** + * 倒班日(夜班转白班)使用的班次ID + */ + private Long changeShiftAId; + /** * 备注 */ diff --git a/klp-wms/src/main/java/com/klp/domain/vo/WmsAttendanceShiftRuleVo.java b/klp-wms/src/main/java/com/klp/domain/vo/WmsAttendanceShiftRuleVo.java index cc6e79d1..c24a9481 100644 --- a/klp-wms/src/main/java/com/klp/domain/vo/WmsAttendanceShiftRuleVo.java +++ b/klp-wms/src/main/java/com/klp/domain/vo/WmsAttendanceShiftRuleVo.java @@ -60,6 +60,18 @@ public class WmsAttendanceShiftRuleVo { @ExcelDictFormat(readConverterExp = "通=常夜班ID") private Long shiftB; + /** + * 倒班日(白班转夜班)使用的班次ID + */ + @ExcelProperty(value = "倒班日白转夜班次ID") + private Long changeShiftBId; + + /** + * 倒班日(夜班转白班)使用的班次ID + */ + @ExcelProperty(value = "倒班日夜转白班次ID") + private Long changeShiftAId; + /** * 备注 */ diff --git a/klp-wms/src/main/java/com/klp/service/IWmsAttendanceScheduleService.java b/klp-wms/src/main/java/com/klp/service/IWmsAttendanceScheduleService.java index 5d3303d9..b7a383fe 100644 --- a/klp-wms/src/main/java/com/klp/service/IWmsAttendanceScheduleService.java +++ b/klp-wms/src/main/java/com/klp/service/IWmsAttendanceScheduleService.java @@ -3,6 +3,7 @@ package com.klp.service; import com.klp.domain.WmsAttendanceSchedule; import com.klp.domain.vo.WmsAttendanceScheduleVo; import com.klp.domain.bo.WmsAttendanceScheduleBo; +import com.klp.domain.bo.GenerateScheduleBo; import com.klp.common.core.page.TableDataInfo; import com.klp.common.core.domain.PageQuery; @@ -46,4 +47,9 @@ public interface IWmsAttendanceScheduleService { * 校验并批量删除排班(谁在哪天上班)信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 生成排班 + */ + void generateSchedule(GenerateScheduleBo bo); } diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceScheduleServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceScheduleServiceImpl.java index 7b4a0034..02cda810 100644 --- a/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceScheduleServiceImpl.java +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceScheduleServiceImpl.java @@ -7,17 +7,30 @@ 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.ArrayList; +import java.util.Date; import java.util.List; -import java.util.Map; + import java.util.Collection; +import java.util.Map; /** * 排班(谁在哪天上班)Service业务层处理 @@ -30,6 +43,8 @@ import java.util.Collection; public class WmsAttendanceScheduleServiceImpl implements IWmsAttendanceScheduleService { private final WmsAttendanceScheduleMapper baseMapper; + private final IWmsAttendanceShiftRuleService shiftRuleService; + private final IWmsAttendanceShiftService shiftService; /** * 查询排班(谁在哪天上班) @@ -110,4 +125,180 @@ public class WmsAttendanceScheduleServiceImpl implements IWmsAttendanceScheduleS } return baseMapper.deleteBatchIds(ids) > 0; } + + /** + * 生成排班 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void generateSchedule(GenerateScheduleBo bo) { + 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) { + List schedules = new ArrayList<>(); + + // 获取班次信息 + WmsAttendanceShiftVo shift = shiftService.queryById(bo.getShiftId()); + if (shift == null) { + throw new RuntimeException("班次不存在"); + } + + LocalDate currentDate = startDate; + while (!currentDate.isAfter(endDate)) { + // 检查是否已存在排班 + if (!isScheduleExists(bo.getUserId(), 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("倒班规则中的班次不存在"); + } + + List schedules = new ArrayList<>(); + LocalDate currentDate = startDate; + long daysFromStart = 0; + + // 确定初始班次 + boolean isCurrentShiftA = bo.getShiftId().equals(rule.getShiftA()); + + while (!currentDate.isAfter(endDate)) { + if (!isScheduleExists(bo.getUserId(), currentDate)) { + WmsAttendanceSchedule schedule = new WmsAttendanceSchedule(); + schedule.setUserId(bo.getUserId()); + schedule.setWorkDate(java.sql.Date.valueOf(currentDate)); + + // 判断是否为倒班日 + long cycleDays = rule.getCycleDays() != null ? rule.getCycleDays() : 10; + boolean isChangeDay = (daysFromStart + 1) % 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()); + } + // 下一天也使用倒班班次(工作18小时) + LocalDate nextDay = currentDate.plusDays(1); + if (!nextDay.isAfter(endDate) && !isScheduleExists(bo.getUserId(), nextDay)) { + WmsAttendanceSchedule nextSchedule = new WmsAttendanceSchedule(); + nextSchedule.setUserId(bo.getUserId()); + nextSchedule.setWorkDate(java.sql.Date.valueOf(nextDay)); + if (changeShiftB != null) { + nextSchedule.setShiftId(rule.getChangeShiftBId()); + nextSchedule.setShiftName(changeShiftB.getShiftName()); + } else { + nextSchedule.setShiftId(rule.getShiftB()); + nextSchedule.setShiftName(shiftB.getShiftName()); + } + schedules.add(nextSchedule); + } + isCurrentShiftA = false; + currentDate = currentDate.plusDays(1); // 跳过下一天,因为已经处理了 + daysFromStart++; + } 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()) { + for (WmsAttendanceSchedule schedule : schedules) { + baseMapper.insert(schedule); + } + } + } + + /** + * 检查排班是否已存在 + */ + private boolean isScheduleExists(Long userId, LocalDate workDate) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(WmsAttendanceSchedule::getUserId, userId) + .eq(WmsAttendanceSchedule::getWorkDate, java.sql.Date.valueOf(workDate)); + return baseMapper.selectCount(wrapper) > 0; + } } diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceShiftRuleServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceShiftRuleServiceImpl.java index ed70ccaa..c859663d 100644 --- a/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceShiftRuleServiceImpl.java +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceShiftRuleServiceImpl.java @@ -66,6 +66,8 @@ public class WmsAttendanceShiftRuleServiceImpl implements IWmsAttendanceShiftRul lqw.eq(bo.getCycleDays() != null, WmsAttendanceShiftRule::getCycleDays, bo.getCycleDays()); lqw.eq(bo.getShiftA() != null, WmsAttendanceShiftRule::getShiftA, bo.getShiftA()); lqw.eq(bo.getShiftB() != null, WmsAttendanceShiftRule::getShiftB, bo.getShiftB()); + lqw.eq(bo.getChangeShiftBId() != null, WmsAttendanceShiftRule::getChangeShiftBId, bo.getChangeShiftBId()); + lqw.eq(bo.getChangeShiftAId() != null, WmsAttendanceShiftRule::getChangeShiftAId, bo.getChangeShiftAId()); return lqw; } diff --git a/klp-wms/src/main/resources/mapper/klp/WmsAttendanceShiftRuleMapper.xml b/klp-wms/src/main/resources/mapper/klp/WmsAttendanceShiftRuleMapper.xml index 3299330f..67805219 100644 --- a/klp-wms/src/main/resources/mapper/klp/WmsAttendanceShiftRuleMapper.xml +++ b/klp-wms/src/main/resources/mapper/klp/WmsAttendanceShiftRuleMapper.xml @@ -11,6 +11,8 @@ + +