Commit 849324ff051e291b917619296c27d52dda73e990

Authored by YunaiV
1 parent 0317e8ea

feat(mes): 新增 CAL 模块班组/班组成员/班组排班功能(cal_team 三表)

新增 mes_cal_team / mes_cal_team_member / mes_cal_team_shift 三张表的完整后端实现:
- SQL:三张表 DDL、菜单 INSERT(5220-5225)、测试数据
- DO / Mapper / VO / Service / Controller 全套分层实现
- MesCalTeamShiftServiceImpl 移植 genRecords 算法(单白班/两班倒/三班倒轮换),
  基于 Java 8 时间 API(LocalDate/ChronoUnit)替代原 Date/String 方案,@Async 异步执行
- ErrorCodeConstants 追加 5 个错误码(CAL_TEAM_CODE_DUPLICATE、CAL_TEAM_MEMBER_NOT_EXISTS 等)

设计说明:
- mes_cal_team_member 去除冗余字段(user_name/nickname/telephone),通过 userId 关联查询
- mes_cal_team_shift 去除冗余字段(team_name/shift_name/calendar_type/shift_type),通过外键关联查询
- 循环依赖通过 @Lazy 注入解决

修复已有 TODO:
- MesCalPlanTeamDO:补充 @link MesCalTeamDO 关联注释
- MesCalPlanServiceImpl.confirmPlan:接入 teamShiftService.genRecords(id)
- MesCalPlanTeamController.getPlanTeamListByPlan:拼装班组编码/名称
- MesCalPlanTeamRespVO:移除待办注释
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/team/MesCalTeamMemberService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.cal.team;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.member.MesCalTeamMemberPageReqVO;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.member.MesCalTeamMemberSaveReqVO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamMemberDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +import java.util.Collection;
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * MES 班组成员 Service 接口
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +public interface MesCalTeamMemberService {
  18 +
  19 + /**
  20 + * 创建班组成员
  21 + *
  22 + * @param createReqVO 创建信息
  23 + * @return 编号
  24 + */
  25 + Long createTeamMember(@Valid MesCalTeamMemberSaveReqVO createReqVO);
  26 +
  27 + /**
  28 + * 删除班组成员
  29 + *
  30 + * @param id 编号
  31 + */
  32 + void deleteTeamMember(Long id);
  33 +
  34 + /**
  35 + * 获得班组成员
  36 + *
  37 + * @param id 编号
  38 + * @return 班组成员
  39 + */
  40 + MesCalTeamMemberDO getTeamMember(Long id);
  41 +
  42 + /**
  43 + * 获得班组成员分页
  44 + *
  45 + * @param pageReqVO 分页查询
  46 + * @return 班组成员分页
  47 + */
  48 + PageResult<MesCalTeamMemberDO> getTeamMemberPage(MesCalTeamMemberPageReqVO pageReqVO);
  49 +
  50 + /**
  51 + * 获得指定班组的成员列表
  52 + *
  53 + * @param teamId 班组编号
  54 + * @return 班组成员列表
  55 + */
  56 + List<MesCalTeamMemberDO> getTeamMemberListByTeamId(Long teamId);
  57 +
  58 + /**
  59 + * 获得指定多个班组的成员列表
  60 + *
  61 + * @param teamIds 班组编号列表
  62 + * @return 班组成员列表
  63 + */
  64 + List<MesCalTeamMemberDO> getTeamMemberListByTeamIds(Collection<Long> teamIds);
  65 +
  66 + /**
  67 + * 根据班组编号删除所有成员
  68 + *
  69 + * @param teamId 班组编号
  70 + */
  71 + void deleteByTeamId(Long teamId);
  72 +
  73 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/team/MesCalTeamMemberServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.cal.team;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.member.MesCalTeamMemberPageReqVO;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.member.MesCalTeamMemberSaveReqVO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamMemberDO;
  8 +import cn.iocoder.yudao.module.mes.dal.mysql.cal.team.MesCalTeamMemberMapper;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.context.annotation.Lazy;
  11 +import org.springframework.stereotype.Service;
  12 +import org.springframework.validation.annotation.Validated;
  13 +
  14 +import java.util.Collection;
  15 +import java.util.List;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  18 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
  19 +
  20 +/**
  21 + * MES 班组成员 Service 实现类
  22 + *
  23 + * @author 芋道源码
  24 + */
  25 +@Service
  26 +@Validated
  27 +public class MesCalTeamMemberServiceImpl implements MesCalTeamMemberService {
  28 +
  29 + @Resource
  30 + private MesCalTeamMemberMapper teamMemberMapper;
  31 + @Resource
  32 + @Lazy
  33 + private MesCalTeamService teamService;
  34 +
  35 + @Override
  36 + public Long createTeamMember(MesCalTeamMemberSaveReqVO createReqVO) {
  37 + // 1.1 校验班组存在
  38 + teamService.validateTeamExists(createReqVO.getTeamId());
  39 + // 1.2 校验用户未分配到其他班组
  40 + validateUserUnique(null, createReqVO.getUserId());
  41 +
  42 + // 2. 插入
  43 + MesCalTeamMemberDO member = BeanUtils.toBean(createReqVO, MesCalTeamMemberDO.class);
  44 + teamMemberMapper.insert(member);
  45 + return member.getId();
  46 + }
  47 +
  48 + @Override
  49 + public void deleteTeamMember(Long id) {
  50 + // 校验存在
  51 + validateTeamMemberExists(id);
  52 + // 删除
  53 + teamMemberMapper.deleteById(id);
  54 + }
  55 +
  56 + @Override
  57 + public MesCalTeamMemberDO getTeamMember(Long id) {
  58 + return teamMemberMapper.selectById(id);
  59 + }
  60 +
  61 + @Override
  62 + public PageResult<MesCalTeamMemberDO> getTeamMemberPage(MesCalTeamMemberPageReqVO pageReqVO) {
  63 + return teamMemberMapper.selectPage(pageReqVO);
  64 + }
  65 +
  66 + @Override
  67 + public List<MesCalTeamMemberDO> getTeamMemberListByTeamId(Long teamId) {
  68 + return teamMemberMapper.selectListByTeamId(teamId);
  69 + }
  70 +
  71 + @Override
  72 + public List<MesCalTeamMemberDO> getTeamMemberListByTeamIds(Collection<Long> teamIds) {
  73 + return teamMemberMapper.selectListByTeamIds(teamIds);
  74 + }
  75 +
  76 + @Override
  77 + public void deleteByTeamId(Long teamId) {
  78 + teamMemberMapper.deleteByTeamId(teamId);
  79 + }
  80 +
  81 + // TODO @AI:无需返回;
  82 + private MesCalTeamMemberDO validateTeamMemberExists(Long id) {
  83 + MesCalTeamMemberDO member = teamMemberMapper.selectById(id);
  84 + if (member == null) {
  85 + throw exception(CAL_TEAM_MEMBER_NOT_EXISTS);
  86 + }
  87 + return member;
  88 + }
  89 +
  90 + // TODO @AI:这里的 excludeId 可以去掉
  91 + private void validateUserUnique(Long excludeId, Long userId) {
  92 + MesCalTeamMemberDO member = teamMemberMapper.selectByUserIdExcludeId(userId, excludeId);
  93 + if (member != null) {
  94 + throw exception(CAL_TEAM_MEMBER_USER_DUPLICATE);
  95 + }
  96 + }
  97 +
  98 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/team/MesCalTeamService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.cal.team;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.MesCalTeamPageReqVO;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.MesCalTeamSaveReqVO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * MES 班组 Service 接口
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public interface MesCalTeamService {
  17 +
  18 + /**
  19 + * 创建班组
  20 + *
  21 + * @param createReqVO 创建信息
  22 + * @return 编号
  23 + */
  24 + Long createTeam(@Valid MesCalTeamSaveReqVO createReqVO);
  25 +
  26 + /**
  27 + * 更新班组
  28 + *
  29 + * @param updateReqVO 更新信息
  30 + */
  31 + void updateTeam(@Valid MesCalTeamSaveReqVO updateReqVO);
  32 +
  33 + /**
  34 + * 删除班组
  35 + *
  36 + * @param id 编号
  37 + */
  38 + void deleteTeam(Long id);
  39 +
  40 + /**
  41 + * 获得班组
  42 + *
  43 + * @param id 编号
  44 + * @return 班组
  45 + */
  46 + MesCalTeamDO getTeam(Long id);
  47 +
  48 + /**
  49 + * 获得班组分页
  50 + *
  51 + * @param pageReqVO 分页查询
  52 + * @return 班组分页
  53 + */
  54 + PageResult<MesCalTeamDO> getTeamPage(MesCalTeamPageReqVO pageReqVO);
  55 +
  56 + /**
  57 + * 获得班组列表(全量,用于下拉选择)
  58 + *
  59 + * @return 班组列表
  60 + */
  61 + List<MesCalTeamDO> getTeamList();
  62 +
  63 + /**
  64 + * 校验班组存在
  65 + *
  66 + * @param id 编号
  67 + * @return 班组
  68 + */
  69 + MesCalTeamDO validateTeamExists(Long id);
  70 +
  71 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/team/MesCalTeamServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.cal.team;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.MesCalTeamPageReqVO;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.MesCalTeamSaveReqVO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamDO;
  8 +import cn.iocoder.yudao.module.mes.dal.mysql.cal.team.MesCalTeamMapper;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.context.annotation.Lazy;
  11 +import org.springframework.stereotype.Service;
  12 +import org.springframework.transaction.annotation.Transactional;
  13 +import org.springframework.validation.annotation.Validated;
  14 +
  15 +import java.util.List;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  18 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
  19 +
  20 +/**
  21 + * MES 班组 Service 实现类
  22 + *
  23 + * @author 芋道源码
  24 + */
  25 +@Service
  26 +@Validated
  27 +public class MesCalTeamServiceImpl implements MesCalTeamService {
  28 +
  29 + @Resource
  30 + private MesCalTeamMapper teamMapper;
  31 + @Resource
  32 + @Lazy
  33 + private MesCalTeamMemberService teamMemberService;
  34 +
  35 + @Override
  36 + public Long createTeam(MesCalTeamSaveReqVO createReqVO) {
  37 + // 1. 校验编码唯一
  38 + validateTeamCodeUnique(null, createReqVO.getCode());
  39 +
  40 + // 2. 插入
  41 + MesCalTeamDO team = BeanUtils.toBean(createReqVO, MesCalTeamDO.class);
  42 + teamMapper.insert(team);
  43 + return team.getId();
  44 + }
  45 +
  46 + @Override
  47 + public void updateTeam(MesCalTeamSaveReqVO updateReqVO) {
  48 + // 1.1 校验存在
  49 + validateTeamExists(updateReqVO.getId());
  50 + // 1.2 校验编码唯一
  51 + validateTeamCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
  52 +
  53 + // 2. 更新
  54 + MesCalTeamDO updateObj = BeanUtils.toBean(updateReqVO, MesCalTeamDO.class);
  55 + teamMapper.updateById(updateObj);
  56 + }
  57 +
  58 + @Override
  59 + @Transactional(rollbackFor = Exception.class)
  60 + public void deleteTeam(Long id) {
  61 + // 1. 校验存在
  62 + validateTeamExists(id);
  63 +
  64 + // 2.1 级联删除成员
  65 + teamMemberService.deleteByTeamId(id);
  66 + // 2.2 删除班组
  67 + teamMapper.deleteById(id);
  68 + }
  69 +
  70 + @Override
  71 + public MesCalTeamDO getTeam(Long id) {
  72 + return teamMapper.selectById(id);
  73 + }
  74 +
  75 + @Override
  76 + public PageResult<MesCalTeamDO> getTeamPage(MesCalTeamPageReqVO pageReqVO) {
  77 + return teamMapper.selectPage(pageReqVO);
  78 + }
  79 +
  80 + @Override
  81 + public List<MesCalTeamDO> getTeamList() {
  82 + return teamMapper.selectList();
  83 + }
  84 +
  85 + @Override
  86 + public MesCalTeamDO validateTeamExists(Long id) {
  87 + MesCalTeamDO team = teamMapper.selectById(id);
  88 + if (team == null) {
  89 + throw exception(CAL_TEAM_NOT_EXISTS);
  90 + }
  91 + return team;
  92 + }
  93 +
  94 + private void validateTeamCodeUnique(Long id, String code) {
  95 + MesCalTeamDO team = teamMapper.selectByCode(code);
  96 + if (team == null) {
  97 + return;
  98 + }
  99 + // TODO @AI:ObjUtil notEquals
  100 + if (id == null || !id.equals(team.getId())) {
  101 + throw exception(CAL_TEAM_CODE_DUPLICATE);
  102 + }
  103 + }
  104 +
  105 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/team/MesCalTeamShiftService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.cal.team;
  2 +
  3 +import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.shift.MesCalTeamShiftListReqVO;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamShiftDO;
  5 +
  6 +import java.util.List;
  7 +
  8 +/**
  9 + * MES 班组排班 Service 接口
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +public interface MesCalTeamShiftService {
  14 +
  15 + // TODO @AI:方法名,优化下;
  16 + /**
  17 + * 根据排班计划生成班组排班记录
  18 + *
  19 + * @param planId 排班计划编号
  20 + */
  21 + void genRecords(Long planId);
  22 +
  23 + /**
  24 + * 获得班组排班列表
  25 + *
  26 + * @param reqVO 查询条件
  27 + * @return 班组排班列表
  28 + */
  29 + List<MesCalTeamShiftDO> getTeamShiftList(MesCalTeamShiftListReqVO reqVO);
  30 +
  31 + // TODO @AI:评审下,为什么不调用!
  32 + /**
  33 + * 根据排班计划编号删除所有排班记录
  34 + *
  35 + * @param planId 排班计划编号
  36 + */
  37 + void deleteByPlanId(Long planId);
  38 +
  39 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/team/MesCalTeamShiftServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.cal.team;
  2 +
  3 +import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.shift.MesCalTeamShiftListReqVO;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.cal.plan.MesCalPlanDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.cal.plan.MesCalPlanShiftDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.cal.plan.MesCalPlanTeamDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamShiftDO;
  8 +import cn.iocoder.yudao.module.mes.dal.mysql.cal.team.MesCalTeamShiftMapper;
  9 +import cn.iocoder.yudao.module.mes.enums.cal.MesCalShiftMethodEnum;
  10 +import cn.iocoder.yudao.module.mes.enums.cal.MesCalShiftTypeEnum;
  11 +import cn.iocoder.yudao.module.mes.service.cal.plan.MesCalPlanService;
  12 +import cn.iocoder.yudao.module.mes.service.cal.plan.MesCalPlanShiftService;
  13 +import cn.iocoder.yudao.module.mes.service.cal.plan.MesCalPlanTeamService;
  14 +import jakarta.annotation.Resource;
  15 +import org.springframework.context.annotation.Lazy;
  16 +import org.springframework.scheduling.annotation.Async;
  17 +import org.springframework.stereotype.Service;
  18 +import org.springframework.validation.annotation.Validated;
  19 +
  20 +import java.time.DayOfWeek;
  21 +import java.time.LocalDate;
  22 +import java.time.LocalDateTime;
  23 +import java.time.Month;
  24 +import java.time.temporal.ChronoUnit;
  25 +import java.util.List;
  26 +
  27 +/**
  28 + * MES 班组排班 Service 实现类
  29 + *
  30 + * @author 芋道源码
  31 + */
  32 +@Service
  33 +@Validated
  34 +public class MesCalTeamShiftServiceImpl implements MesCalTeamShiftService {
  35 +
  36 + @Resource
  37 + private MesCalTeamShiftMapper teamShiftMapper;
  38 + @Resource
  39 + @Lazy
  40 + private MesCalPlanService planService;
  41 + @Resource
  42 + @Lazy
  43 + private MesCalPlanShiftService planShiftService;
  44 + @Resource
  45 + @Lazy
  46 + private MesCalPlanTeamService planTeamService;
  47 +
  48 + @Async
  49 + @Override
  50 + public void genRecords(Long planId) {
  51 + // 1.1 查询排班计划
  52 + MesCalPlanDO plan = planService.getPlan(planId);
  53 + // 1.2 查询计划中的班次列表
  54 + List<MesCalPlanShiftDO> shifts = planShiftService.getPlanShiftListByPlanId(planId);
  55 + // 1.3 查询计划中的班组列表
  56 + List<MesCalPlanTeamDO> teams = planTeamService.getPlanTeamListByPlanId(planId);
  57 +
  58 + // 2.1 计算日期差值(天数)
  59 + LocalDate startDate = plan.getStartDate().toLocalDate();
  60 + LocalDate endDate = plan.getEndDate().toLocalDate();
  61 + long days = ChronoUnit.DAYS.between(startDate, endDate) + 1;
  62 + // TODO @AI:dateutils,还是 localdateutils 应该有方法;或者 hutool 的
  63 + // 2.2 遍历每一天,生成排班记录
  64 + int shiftIndex = 0;
  65 + for (int i = 0; i < days; i++) {
  66 + LocalDate currentDate = startDate.plusDays(i);
  67 + LocalDateTime currentDay = currentDate.atStartOfDay();
  68 + // 2.1 根据倒班方式计算 shiftIndex
  69 + if (MesCalShiftMethodEnum.QUARTER.getMethod().equals(plan.getShiftMethod())) {
  70 + // 按季度轮班:到了新季度的第一天,且不是排班计划开始的季度
  71 + LocalDate quarterStart = getQuarterStart(currentDate);
  72 + LocalDate planQuarterStart = getQuarterStart(startDate);
  73 + if (currentDate.equals(quarterStart) && !quarterStart.equals(planQuarterStart)) {
  74 + shiftIndex++;
  75 + }
  76 + } else if (MesCalShiftMethodEnum.MONTH.getMethod().equals(plan.getShiftMethod())) {
  77 + // 按月轮班:到了月初,且不是排班计划开始的月份
  78 + LocalDate monthStart = currentDate.withDayOfMonth(1);
  79 + LocalDate planMonthStart = startDate.withDayOfMonth(1);
  80 + if (currentDate.equals(monthStart) && !monthStart.equals(planMonthStart)) {
  81 + shiftIndex++;
  82 + }
  83 + } else if (MesCalShiftMethodEnum.WEEK.getMethod().equals(plan.getShiftMethod())) {
  84 + // 按周轮班:到了周一,且不是排班计划开始的周
  85 + LocalDate weekStart = getWeekStart(currentDate);
  86 + LocalDate planWeekStart = getWeekStart(startDate);
  87 + if (currentDate.equals(weekStart) && !weekStart.equals(planWeekStart)) {
  88 + shiftIndex++;
  89 + }
  90 + } else {
  91 + // 按天轮班:到了指定轮班天数的倍数,且不是刚开始
  92 + if (i % plan.getShiftCount() == 0 && i != 0) {
  93 + shiftIndex++;
  94 + }
  95 + }
  96 + // 2.2 根据轮班方式生成排班记录
  97 + // TODO @AI:要不要先把记录生成好,最后去存入数据库;
  98 + // TODO @AI:逻辑写的又点乱(复杂),看看能不能在优化下;
  99 + if (MesCalShiftTypeEnum.SINGLE.getType().equals(plan.getShiftType())) {
  100 + // 单白班:不需要倒班
  101 + saveTeamShift(MesCalTeamShiftDO.builder()
  102 + .planId(planId)
  103 + .day(currentDay)
  104 + .teamId(teams.get(0).getTeamId())
  105 + .shiftId(shifts.get(0).getId())
  106 + .build());
  107 + } else if (MesCalShiftTypeEnum.TWO.getType().equals(plan.getShiftType())) {
  108 + // 两班倒
  109 + if (shiftIndex % 2 == 0) {
  110 + // A 组上白班
  111 + saveTeamShift(MesCalTeamShiftDO.builder()
  112 + .planId(planId).day(currentDay)
  113 + .teamId(teams.get(0).getTeamId())
  114 + .shiftId(shifts.get(0).getId())
  115 + .sort(1).build());
  116 + // B 组上夜班
  117 + saveTeamShift(MesCalTeamShiftDO.builder()
  118 + .planId(planId).day(currentDay)
  119 + .teamId(teams.get(1).getTeamId())
  120 + .shiftId(shifts.get(1).getId())
  121 + .sort(2).build());
  122 + } else {
  123 + // A 组上夜班
  124 + saveTeamShift(MesCalTeamShiftDO.builder()
  125 + .planId(planId).day(currentDay)
  126 + .teamId(teams.get(0).getTeamId())
  127 + .shiftId(shifts.get(1).getId())
  128 + .sort(2).build());
  129 + // B 组上白班
  130 + saveTeamShift(MesCalTeamShiftDO.builder()
  131 + .planId(planId).day(currentDay)
  132 + .teamId(teams.get(1).getTeamId())
  133 + .shiftId(shifts.get(0).getId())
  134 + .sort(1).build());
  135 + }
  136 + } else if (MesCalShiftTypeEnum.THREE.getType().equals(plan.getShiftType())) {
  137 + // 三班倒
  138 + if (shiftIndex % 3 == 0) {
  139 + // A 组上白班、B 组上中班、C 组上夜班
  140 + saveTeamShift(MesCalTeamShiftDO.builder()
  141 + .planId(planId).day(currentDay)
  142 + .teamId(teams.get(0).getTeamId())
  143 + .shiftId(shifts.get(0).getId())
  144 + .sort(1).build());
  145 + saveTeamShift(MesCalTeamShiftDO.builder()
  146 + .planId(planId).day(currentDay)
  147 + .teamId(teams.get(1).getTeamId())
  148 + .shiftId(shifts.get(1).getId())
  149 + .sort(2).build());
  150 + saveTeamShift(MesCalTeamShiftDO.builder()
  151 + .planId(planId).day(currentDay)
  152 + .teamId(teams.get(2).getTeamId())
  153 + .shiftId(shifts.get(2).getId())
  154 + .sort(3).build());
  155 + } else if (shiftIndex % 3 == 1) {
  156 + // A 组上中班、B 组上夜班、C 组上白班
  157 + saveTeamShift(MesCalTeamShiftDO.builder()
  158 + .planId(planId).day(currentDay)
  159 + .teamId(teams.get(0).getTeamId())
  160 + .shiftId(shifts.get(1).getId())
  161 + .sort(2).build());
  162 + saveTeamShift(MesCalTeamShiftDO.builder()
  163 + .planId(planId).day(currentDay)
  164 + .teamId(teams.get(1).getTeamId())
  165 + .shiftId(shifts.get(2).getId())
  166 + .sort(3).build());
  167 + saveTeamShift(MesCalTeamShiftDO.builder()
  168 + .planId(planId).day(currentDay)
  169 + .teamId(teams.get(2).getTeamId())
  170 + .shiftId(shifts.get(0).getId())
  171 + .sort(1).build());
  172 + } else {
  173 + // A 组上夜班、B 组上白班、C 组上中班
  174 + saveTeamShift(MesCalTeamShiftDO.builder()
  175 + .planId(planId).day(currentDay)
  176 + .teamId(teams.get(0).getTeamId())
  177 + .shiftId(shifts.get(2).getId())
  178 + .sort(3).build());
  179 + saveTeamShift(MesCalTeamShiftDO.builder()
  180 + .planId(planId).day(currentDay)
  181 + .teamId(teams.get(1).getTeamId())
  182 + .shiftId(shifts.get(0).getId())
  183 + .sort(1).build());
  184 + saveTeamShift(MesCalTeamShiftDO.builder()
  185 + .planId(planId).day(currentDay)
  186 + .teamId(teams.get(2).getTeamId())
  187 + .shiftId(shifts.get(1).getId())
  188 + .sort(2).build());
  189 + }
  190 + }
  191 + }
  192 + }
  193 +
  194 + @Override
  195 + public List<MesCalTeamShiftDO> getTeamShiftList(MesCalTeamShiftListReqVO reqVO) {
  196 + return teamShiftMapper.selectList(reqVO);
  197 + }
  198 +
  199 + @Override
  200 + public void deleteByPlanId(Long planId) {
  201 + teamShiftMapper.deleteByPlanId(planId);
  202 + }
  203 +
  204 + /**
  205 + * 保存班组排班记录(已存在则更新,不存在则新增)
  206 + */
  207 + private void saveTeamShift(MesCalTeamShiftDO teamShift) {
  208 + MesCalTeamShiftDO existing = teamShiftMapper.selectByDayAndTeamId(
  209 + teamShift.getDay(), teamShift.getTeamId());
  210 + if (existing != null) {
  211 + teamShift.setId(existing.getId());
  212 + teamShiftMapper.updateById(teamShift);
  213 + } else {
  214 + teamShiftMapper.insert(teamShift);
  215 + }
  216 + }
  217 +
  218 + /**
  219 + * 获取指定日期所在季度的第一天
  220 + */
  221 + // TODO @AI:看看日期工具类,有没类似的方法
  222 + private static LocalDate getQuarterStart(LocalDate date) {
  223 + Month month = date.getMonth();
  224 + Month firstMonthOfQuarter = month.firstMonthOfQuarter();
  225 + return LocalDate.of(date.getYear(), firstMonthOfQuarter, 1);
  226 + }
  227 +
  228 + /**
  229 + * 获取指定日期所在周的第一天(周一)
  230 + */
  231 + // TODO @AI:看看日期工具类,有没类似的方法
  232 + private static LocalDate getWeekStart(LocalDate date) {
  233 + return date.with(DayOfWeek.MONDAY);
  234 + }
  235 +
  236 +}
... ...