Commit 173b40ebc3db9e1193779cd7d2859aa71aa0b039
1 parent
571d3263
✨ feat(mes): 新增班组排班记录生成逻辑及相关字段校验
实现班组排班记录的生成逻辑,增加了班组类型和用户存在性校验,优化了排班记录的批量写入方式。同时,新增了相关错误码以处理班组和班次数量不足的情况。
Showing
9 changed files
with
241 additions
and
130 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/cal/team/MesCalTeamShiftController.java
| 1 | package cn.iocoder.yudao.module.mes.controller.admin.cal.team; | 1 | package cn.iocoder.yudao.module.mes.controller.admin.cal.team; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | ||
| 4 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 6 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 5 | import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.shift.MesCalTeamShiftListReqVO; | 7 | import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.shift.MesCalTeamShiftListReqVO; |
| 6 | import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.shift.MesCalTeamShiftRespVO; | 8 | import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.shift.MesCalTeamShiftRespVO; |
| 9 | +import cn.iocoder.yudao.module.mes.dal.dataobject.cal.plan.MesCalPlanShiftDO; | ||
| 10 | +import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamDO; | ||
| 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamShiftDO; | 11 | import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamShiftDO; |
| 12 | +import cn.iocoder.yudao.module.mes.service.cal.plan.MesCalPlanShiftService; | ||
| 13 | +import cn.iocoder.yudao.module.mes.service.cal.team.MesCalTeamService; | ||
| 8 | import cn.iocoder.yudao.module.mes.service.cal.team.MesCalTeamShiftService; | 14 | import cn.iocoder.yudao.module.mes.service.cal.team.MesCalTeamShiftService; |
| 9 | import io.swagger.v3.oas.annotations.Operation; | 15 | import io.swagger.v3.oas.annotations.Operation; |
| 10 | import io.swagger.v3.oas.annotations.tags.Tag; | 16 | import io.swagger.v3.oas.annotations.tags.Tag; |
| @@ -16,9 +22,12 @@ import org.springframework.web.bind.annotation.GetMapping; | @@ -16,9 +22,12 @@ import org.springframework.web.bind.annotation.GetMapping; | ||
| 16 | import org.springframework.web.bind.annotation.RequestMapping; | 22 | import org.springframework.web.bind.annotation.RequestMapping; |
| 17 | import org.springframework.web.bind.annotation.RestController; | 23 | import org.springframework.web.bind.annotation.RestController; |
| 18 | 24 | ||
| 25 | +import java.util.Collections; | ||
| 19 | import java.util.List; | 26 | import java.util.List; |
| 27 | +import java.util.Map; | ||
| 20 | 28 | ||
| 21 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 29 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 30 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||
| 22 | 31 | ||
| 23 | @Tag(name = "管理后台 - MES 班组排班") | 32 | @Tag(name = "管理后台 - MES 班组排班") |
| 24 | @RestController | 33 | @RestController |
| @@ -28,13 +37,28 @@ public class MesCalTeamShiftController { | @@ -28,13 +37,28 @@ public class MesCalTeamShiftController { | ||
| 28 | 37 | ||
| 29 | @Resource | 38 | @Resource |
| 30 | private MesCalTeamShiftService teamShiftService; | 39 | private MesCalTeamShiftService teamShiftService; |
| 40 | + @Resource | ||
| 41 | + private MesCalTeamService teamService; | ||
| 42 | + @Resource | ||
| 43 | + private MesCalPlanShiftService planShiftService; | ||
| 31 | 44 | ||
| 32 | @GetMapping("/list") | 45 | @GetMapping("/list") |
| 33 | @Operation(summary = "获得班组排班列表") | 46 | @Operation(summary = "获得班组排班列表") |
| 34 | @PreAuthorize("@ss.hasPermission('mes:cal-team-shift:query')") | 47 | @PreAuthorize("@ss.hasPermission('mes:cal-team-shift:query')") |
| 35 | public CommonResult<List<MesCalTeamShiftRespVO>> getTeamShiftList(@Valid MesCalTeamShiftListReqVO reqVO) { | 48 | public CommonResult<List<MesCalTeamShiftRespVO>> getTeamShiftList(@Valid MesCalTeamShiftListReqVO reqVO) { |
| 36 | List<MesCalTeamShiftDO> list = teamShiftService.getTeamShiftList(reqVO); | 49 | List<MesCalTeamShiftDO> list = teamShiftService.getTeamShiftList(reqVO); |
| 37 | - return success(BeanUtils.toBean(list, MesCalTeamShiftRespVO.class)); | 50 | + if (CollUtil.isEmpty(list)) { |
| 51 | + return success(Collections.emptyList()); | ||
| 52 | + } | ||
| 53 | + // 关联查询班组名称和班次名称 | ||
| 54 | + Map<Long, MesCalTeamDO> teamMap = teamService.getTeamMap( | ||
| 55 | + convertSet(list, MesCalTeamShiftDO::getTeamId)); | ||
| 56 | + Map<Long, MesCalPlanShiftDO> shiftMap = planShiftService.getPlanShiftMap( | ||
| 57 | + convertSet(list, MesCalTeamShiftDO::getShiftId)); | ||
| 58 | + return success(BeanUtils.toBean(list, MesCalTeamShiftRespVO.class, vo -> { | ||
| 59 | + MapUtils.findAndThen(teamMap, vo.getTeamId(), team -> vo.setTeamName(team.getName())); | ||
| 60 | + MapUtils.findAndThen(shiftMap, vo.getShiftId(), shift -> vo.setShiftName(shift.getName())); | ||
| 61 | + })); | ||
| 38 | } | 62 | } |
| 39 | 63 | ||
| 40 | } | 64 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/cal/team/vo/MesCalTeamSaveReqVO.java
| @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo; | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo; | ||
| 2 | 2 | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import jakarta.validation.constraints.NotEmpty; | 4 | import jakarta.validation.constraints.NotEmpty; |
| 5 | +import jakarta.validation.constraints.NotNull; | ||
| 5 | import lombok.Data; | 6 | import lombok.Data; |
| 6 | 7 | ||
| 7 | @Schema(description = "管理后台 - MES 班组新增/修改 Request VO") | 8 | @Schema(description = "管理后台 - MES 班组新增/修改 Request VO") |
| @@ -19,10 +20,11 @@ public class MesCalTeamSaveReqVO { | @@ -19,10 +20,11 @@ public class MesCalTeamSaveReqVO { | ||
| 19 | @NotEmpty(message = "班组名称不能为空") | 20 | @NotEmpty(message = "班组名称不能为空") |
| 20 | private String name; | 21 | private String name; |
| 21 | 22 | ||
| 22 | - @Schema(description = "班组类型", example = "1") | 23 | + @Schema(description = "班组类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 24 | + @NotNull(message = "班组类型不能为空") | ||
| 23 | private Integer calendarType; | 25 | private Integer calendarType; |
| 24 | 26 | ||
| 25 | @Schema(description = "备注") | 27 | @Schema(description = "备注") |
| 26 | private String remark; | 28 | private String remark; |
| 27 | 29 | ||
| 28 | - } | 30 | +} |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/cal/team/vo/member/MesCalTeamMemberRespVO.java
| @@ -18,9 +18,6 @@ public class MesCalTeamMemberRespVO { | @@ -18,9 +18,6 @@ public class MesCalTeamMemberRespVO { | ||
| 18 | @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 18 | @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 19 | private Long userId; | 19 | private Long userId; |
| 20 | 20 | ||
| 21 | - @Schema(description = "用户名", example = "admin") | ||
| 22 | - private String userName; | ||
| 23 | - | ||
| 24 | @Schema(description = "用户昵称", example = "管理员") | 21 | @Schema(description = "用户昵称", example = "管理员") |
| 25 | private String nickname; | 22 | private String nickname; |
| 26 | 23 |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/cal/team/MesCalTeamShiftMapper.java
| @@ -37,4 +37,8 @@ public interface MesCalTeamShiftMapper extends BaseMapperX<MesCalTeamShiftDO> { | @@ -37,4 +37,8 @@ public interface MesCalTeamShiftMapper extends BaseMapperX<MesCalTeamShiftDO> { | ||
| 37 | delete(MesCalTeamShiftDO::getPlanId, planId); | 37 | delete(MesCalTeamShiftDO::getPlanId, planId); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | + default void deleteByTeamId(Long teamId) { | ||
| 41 | + delete(MesCalTeamShiftDO::getTeamId, teamId); | ||
| 42 | + } | ||
| 43 | + | ||
| 40 | } | 44 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
| @@ -118,8 +118,11 @@ public interface ErrorCodeConstants { | @@ -118,8 +118,11 @@ public interface ErrorCodeConstants { | ||
| 118 | // ========== MES 日历排班-班组成员(1-040-201-100) ========== | 118 | // ========== MES 日历排班-班组成员(1-040-201-100) ========== |
| 119 | ErrorCode CAL_TEAM_MEMBER_NOT_EXISTS = new ErrorCode(1_040_201_100, "班组成员不存在"); | 119 | ErrorCode CAL_TEAM_MEMBER_NOT_EXISTS = new ErrorCode(1_040_201_100, "班组成员不存在"); |
| 120 | ErrorCode CAL_TEAM_MEMBER_USER_DUPLICATE = new ErrorCode(1_040_201_101, "该用户已分配到其他班组"); | 120 | ErrorCode CAL_TEAM_MEMBER_USER_DUPLICATE = new ErrorCode(1_040_201_101, "该用户已分配到其他班组"); |
| 121 | + ErrorCode CAL_TEAM_MEMBER_USER_NOT_EXISTS = new ErrorCode(1_040_201_102, "用户不存在"); | ||
| 121 | // ========== MES 日历排班-班组排班(1-040-201-200) ========== | 122 | // ========== MES 日历排班-班组排班(1-040-201-200) ========== |
| 122 | ErrorCode CAL_TEAM_SHIFT_NOT_EXISTS = new ErrorCode(1_040_201_200, "班组排班记录不存在"); | 123 | ErrorCode CAL_TEAM_SHIFT_NOT_EXISTS = new ErrorCode(1_040_201_200, "班组排班记录不存在"); |
| 124 | + ErrorCode CAL_TEAM_SHIFT_GENERATE_TEAM_NOT_ENOUGH = new ErrorCode(1_040_201_201, "班组数量不足,无法生成排班记录"); | ||
| 125 | + ErrorCode CAL_TEAM_SHIFT_GENERATE_SHIFT_NOT_ENOUGH = new ErrorCode(1_040_201_202, "班次数量不足,无法生成排班记录"); | ||
| 123 | 126 | ||
| 124 | // ========== MES 日历排班-排班计划(1-040-202-000) ========== | 127 | // ========== MES 日历排班-排班计划(1-040-202-000) ========== |
| 125 | ErrorCode CAL_PLAN_NOT_EXISTS = new ErrorCode(1_040_202_000, "排班计划不存在"); | 128 | ErrorCode CAL_PLAN_NOT_EXISTS = new ErrorCode(1_040_202_000, "排班计划不存在"); |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/team/MesCalTeamMemberServiceImpl.java
| @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.member.MesCalTea | @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.member.MesCalTea | ||
| 6 | import cn.iocoder.yudao.module.mes.controller.admin.cal.team.vo.member.MesCalTeamMemberSaveReqVO; | 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; | 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamMemberDO; |
| 8 | import cn.iocoder.yudao.module.mes.dal.mysql.cal.team.MesCalTeamMemberMapper; | 8 | import cn.iocoder.yudao.module.mes.dal.mysql.cal.team.MesCalTeamMemberMapper; |
| 9 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | ||
| 9 | import jakarta.annotation.Resource; | 10 | import jakarta.annotation.Resource; |
| 10 | import org.springframework.context.annotation.Lazy; | 11 | import org.springframework.context.annotation.Lazy; |
| 11 | import org.springframework.stereotype.Service; | 12 | import org.springframework.stereotype.Service; |
| @@ -31,12 +32,16 @@ public class MesCalTeamMemberServiceImpl implements MesCalTeamMemberService { | @@ -31,12 +32,16 @@ public class MesCalTeamMemberServiceImpl implements MesCalTeamMemberService { | ||
| 31 | @Resource | 32 | @Resource |
| 32 | @Lazy | 33 | @Lazy |
| 33 | private MesCalTeamService teamService; | 34 | private MesCalTeamService teamService; |
| 35 | + @Resource | ||
| 36 | + private AdminUserApi adminUserApi; | ||
| 34 | 37 | ||
| 35 | @Override | 38 | @Override |
| 36 | public Long createTeamMember(MesCalTeamMemberSaveReqVO createReqVO) { | 39 | public Long createTeamMember(MesCalTeamMemberSaveReqVO createReqVO) { |
| 37 | // 1.1 校验班组存在 | 40 | // 1.1 校验班组存在 |
| 38 | teamService.validateTeamExists(createReqVO.getTeamId()); | 41 | teamService.validateTeamExists(createReqVO.getTeamId()); |
| 39 | - // 1.2 校验用户未分配到其他班组 | 42 | + // 1.2 校验用户存在 |
| 43 | + validateUserExists(createReqVO.getUserId()); | ||
| 44 | + // 1.3 校验用户未分配到其他班组 | ||
| 40 | validateUserUnique(createReqVO.getUserId()); | 45 | validateUserUnique(createReqVO.getUserId()); |
| 41 | 46 | ||
| 42 | // 2. 插入 | 47 | // 2. 插入 |
| @@ -89,6 +94,12 @@ public class MesCalTeamMemberServiceImpl implements MesCalTeamMemberService { | @@ -89,6 +94,12 @@ public class MesCalTeamMemberServiceImpl implements MesCalTeamMemberService { | ||
| 89 | } | 94 | } |
| 90 | } | 95 | } |
| 91 | 96 | ||
| 97 | + private void validateUserExists(Long userId) { | ||
| 98 | + if (adminUserApi.getUser(userId) == null) { | ||
| 99 | + throw exception(CAL_TEAM_MEMBER_USER_NOT_EXISTS); | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + | ||
| 92 | private void validateUserUnique(Long userId) { | 103 | private void validateUserUnique(Long userId) { |
| 93 | if (teamMemberMapper.selectByUserId(userId) != null) { | 104 | if (teamMemberMapper.selectByUserId(userId) != null) { |
| 94 | throw exception(CAL_TEAM_MEMBER_USER_DUPLICATE); | 105 | throw exception(CAL_TEAM_MEMBER_USER_DUPLICATE); |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/team/MesCalTeamServiceImpl.java
| @@ -35,6 +35,9 @@ public class MesCalTeamServiceImpl implements MesCalTeamService { | @@ -35,6 +35,9 @@ public class MesCalTeamServiceImpl implements MesCalTeamService { | ||
| 35 | @Resource | 35 | @Resource |
| 36 | @Lazy | 36 | @Lazy |
| 37 | private MesCalTeamMemberService teamMemberService; | 37 | private MesCalTeamMemberService teamMemberService; |
| 38 | + @Resource | ||
| 39 | + @Lazy | ||
| 40 | + private MesCalTeamShiftService teamShiftService; | ||
| 38 | 41 | ||
| 39 | @Override | 42 | @Override |
| 40 | public Long createTeam(MesCalTeamSaveReqVO createReqVO) { | 43 | public Long createTeam(MesCalTeamSaveReqVO createReqVO) { |
| @@ -67,7 +70,9 @@ public class MesCalTeamServiceImpl implements MesCalTeamService { | @@ -67,7 +70,9 @@ public class MesCalTeamServiceImpl implements MesCalTeamService { | ||
| 67 | 70 | ||
| 68 | // 2.1 级联删除成员 | 71 | // 2.1 级联删除成员 |
| 69 | teamMemberService.deleteByTeamId(id); | 72 | teamMemberService.deleteByTeamId(id); |
| 70 | - // 2.2 删除班组 | 73 | + // 2.2 级联删除排班记录 |
| 74 | + teamShiftService.deleteByTeamId(id); | ||
| 75 | + // 2.3 删除班组 | ||
| 71 | teamMapper.deleteById(id); | 76 | teamMapper.deleteById(id); |
| 72 | } | 77 | } |
| 73 | 78 |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/team/MesCalTeamShiftService.java
| @@ -34,4 +34,11 @@ public interface MesCalTeamShiftService { | @@ -34,4 +34,11 @@ public interface MesCalTeamShiftService { | ||
| 34 | */ | 34 | */ |
| 35 | void deleteByPlanId(Long planId); | 35 | void deleteByPlanId(Long planId); |
| 36 | 36 | ||
| 37 | + /** | ||
| 38 | + * 根据班组编号删除所有排班记录 | ||
| 39 | + * | ||
| 40 | + * @param teamId 班组编号 | ||
| 41 | + */ | ||
| 42 | + void deleteByTeamId(Long teamId); | ||
| 43 | + | ||
| 37 | } | 44 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/team/MesCalTeamShiftServiceImpl.java
| @@ -15,6 +15,7 @@ import jakarta.annotation.Resource; | @@ -15,6 +15,7 @@ import jakarta.annotation.Resource; | ||
| 15 | import org.springframework.context.annotation.Lazy; | 15 | import org.springframework.context.annotation.Lazy; |
| 16 | import org.springframework.scheduling.annotation.Async; | 16 | import org.springframework.scheduling.annotation.Async; |
| 17 | import org.springframework.stereotype.Service; | 17 | import org.springframework.stereotype.Service; |
| 18 | +import org.springframework.transaction.annotation.Transactional; | ||
| 18 | import org.springframework.validation.annotation.Validated; | 19 | import org.springframework.validation.annotation.Validated; |
| 19 | 20 | ||
| 20 | import java.time.DayOfWeek; | 21 | import java.time.DayOfWeek; |
| @@ -22,8 +23,13 @@ import java.time.LocalDate; | @@ -22,8 +23,13 @@ import java.time.LocalDate; | ||
| 22 | import java.time.LocalDateTime; | 23 | import java.time.LocalDateTime; |
| 23 | import java.time.Month; | 24 | import java.time.Month; |
| 24 | import java.time.temporal.ChronoUnit; | 25 | import java.time.temporal.ChronoUnit; |
| 26 | +import java.util.ArrayList; | ||
| 25 | import java.util.List; | 27 | import java.util.List; |
| 26 | 28 | ||
| 29 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 30 | +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.CAL_TEAM_SHIFT_GENERATE_SHIFT_NOT_ENOUGH; | ||
| 31 | +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.CAL_TEAM_SHIFT_GENERATE_TEAM_NOT_ENOUGH; | ||
| 32 | + | ||
| 27 | /** | 33 | /** |
| 28 | * MES 班组排班 Service 实现类 | 34 | * MES 班组排班 Service 实现类 |
| 29 | * | 35 | * |
| @@ -45,8 +51,9 @@ public class MesCalTeamShiftServiceImpl implements MesCalTeamShiftService { | @@ -45,8 +51,9 @@ public class MesCalTeamShiftServiceImpl implements MesCalTeamShiftService { | ||
| 45 | @Lazy | 51 | @Lazy |
| 46 | private MesCalPlanTeamService planTeamService; | 52 | private MesCalPlanTeamService planTeamService; |
| 47 | 53 | ||
| 48 | - // TODO @芋艿:这块逻辑,可能要想下; | 54 | + // TODO @AI:这里要不不异步?性能一般支撑的住把? |
| 49 | @Async | 55 | @Async |
| 56 | + @Transactional(rollbackFor = Exception.class) | ||
| 50 | @Override | 57 | @Override |
| 51 | public void generateTeamShiftRecords(Long planId) { | 58 | public void generateTeamShiftRecords(Long planId) { |
| 52 | // 1.1 查询排班计划 | 59 | // 1.1 查询排班计划 |
| @@ -55,139 +62,181 @@ public class MesCalTeamShiftServiceImpl implements MesCalTeamShiftService { | @@ -55,139 +62,181 @@ public class MesCalTeamShiftServiceImpl implements MesCalTeamShiftService { | ||
| 55 | List<MesCalPlanShiftDO> shifts = planShiftService.getPlanShiftListByPlanId(planId); | 62 | List<MesCalPlanShiftDO> shifts = planShiftService.getPlanShiftListByPlanId(planId); |
| 56 | // 1.3 查询计划中的班组列表 | 63 | // 1.3 查询计划中的班组列表 |
| 57 | List<MesCalPlanTeamDO> teams = planTeamService.getPlanTeamListByPlanId(planId); | 64 | List<MesCalPlanTeamDO> teams = planTeamService.getPlanTeamListByPlanId(planId); |
| 65 | + // 1.4 校验班组和班次数量是否满足班型要求 | ||
| 66 | + MesCalShiftTypeEnum shiftTypeEnum = MesCalShiftTypeEnum.valueOf(plan.getShiftType()); | ||
| 67 | + int requiredCount = shiftTypeEnum.getRequiredTeamCount(); | ||
| 68 | + if (teams.size() < requiredCount) { | ||
| 69 | + throw exception(CAL_TEAM_SHIFT_GENERATE_TEAM_NOT_ENOUGH); | ||
| 70 | + } | ||
| 71 | + if (shifts.size() < requiredCount) { | ||
| 72 | + throw exception(CAL_TEAM_SHIFT_GENERATE_SHIFT_NOT_ENOUGH); | ||
| 73 | + } | ||
| 58 | 74 | ||
| 75 | + // TODO @AI:优化下下面的注释; | ||
| 59 | // 2.1 计算日期差值(天数) | 76 | // 2.1 计算日期差值(天数) |
| 60 | LocalDate startDate = plan.getStartDate().toLocalDate(); | 77 | LocalDate startDate = plan.getStartDate().toLocalDate(); |
| 61 | LocalDate endDate = plan.getEndDate().toLocalDate(); | 78 | LocalDate endDate = plan.getEndDate().toLocalDate(); |
| 62 | long days = ChronoUnit.DAYS.between(startDate, endDate) + 1; | 79 | long days = ChronoUnit.DAYS.between(startDate, endDate) + 1; |
| 63 | - // TODO @AI:dateutils,还是 localdateutils 应该有方法;或者 hutool 的 | 80 | + |
| 81 | + // DONE @AI:要不要先把记录生成好,最后去存入数据库; | ||
| 82 | + // 说明:先收集所有排班记录到列表,最后统一写入数据库,减少 DB 交互 | ||
| 83 | + List<MesCalTeamShiftDO> allRecords = new ArrayList<>(); | ||
| 84 | + | ||
| 64 | // 2.2 遍历每一天,生成排班记录 | 85 | // 2.2 遍历每一天,生成排班记录 |
| 86 | + // DONE @AI:逻辑写的又点乱(复杂),看看能不能在优化下; | ||
| 87 | + // 说明:通过提取 buildRecordsForDay 子方法优化可读性 | ||
| 65 | int shiftIndex = 0; | 88 | int shiftIndex = 0; |
| 66 | for (int i = 0; i < days; i++) { | 89 | for (int i = 0; i < days; i++) { |
| 67 | LocalDate currentDate = startDate.plusDays(i); | 90 | LocalDate currentDate = startDate.plusDays(i); |
| 68 | LocalDateTime currentDay = currentDate.atStartOfDay(); | 91 | LocalDateTime currentDay = currentDate.atStartOfDay(); |
| 69 | - // 2.1 根据倒班方式计算 shiftIndex | ||
| 70 | - if (MesCalShiftMethodEnum.QUARTER.getMethod().equals(plan.getShiftMethod())) { | ||
| 71 | - // 按季度轮班:到了新季度的第一天,且不是排班计划开始的季度 | ||
| 72 | - LocalDate quarterStart = getQuarterStart(currentDate); | ||
| 73 | - LocalDate planQuarterStart = getQuarterStart(startDate); | ||
| 74 | - if (currentDate.equals(quarterStart) && !quarterStart.equals(planQuarterStart)) { | ||
| 75 | - shiftIndex++; | ||
| 76 | - } | ||
| 77 | - } else if (MesCalShiftMethodEnum.MONTH.getMethod().equals(plan.getShiftMethod())) { | ||
| 78 | - // 按月轮班:到了月初,且不是排班计划开始的月份 | ||
| 79 | - LocalDate monthStart = currentDate.withDayOfMonth(1); | ||
| 80 | - LocalDate planMonthStart = startDate.withDayOfMonth(1); | ||
| 81 | - if (currentDate.equals(monthStart) && !monthStart.equals(planMonthStart)) { | ||
| 82 | - shiftIndex++; | ||
| 83 | - } | ||
| 84 | - } else if (MesCalShiftMethodEnum.WEEK.getMethod().equals(plan.getShiftMethod())) { | ||
| 85 | - // 按周轮班:到了周一,且不是排班计划开始的周 | ||
| 86 | - LocalDate weekStart = getWeekStart(currentDate); | ||
| 87 | - LocalDate planWeekStart = getWeekStart(startDate); | ||
| 88 | - if (currentDate.equals(weekStart) && !weekStart.equals(planWeekStart)) { | ||
| 89 | - shiftIndex++; | ||
| 90 | - } | 92 | + // 2.2.1 根据倒班方式计算 shiftIndex |
| 93 | + shiftIndex = calculateShiftIndex(shiftIndex, i, currentDate, startDate, plan); | ||
| 94 | + // 2.2.2 根据轮班方式生成排班记录,收集到列表 | ||
| 95 | + buildRecordsForDay(allRecords, planId, currentDay, shiftIndex, plan.getShiftType(), teams, shifts); | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + // 3. 批量写入数据库 | ||
| 99 | + // TODO @AI:mybatis plus 有没 saveBatch 方法? | ||
| 100 | + for (MesCalTeamShiftDO record : allRecords) { | ||
| 101 | + saveTeamShift(record); | ||
| 102 | + } | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + /** | ||
| 106 | + * 计算当天的 shiftIndex(根据倒班方式判断是否需要递增) | ||
| 107 | + */ | ||
| 108 | + private int calculateShiftIndex(int shiftIndex, int dayOffset, LocalDate currentDate, | ||
| 109 | + LocalDate startDate, MesCalPlanDO plan) { | ||
| 110 | + if (dayOffset == 0) { | ||
| 111 | + return shiftIndex; // 第一天不递增 | ||
| 112 | + } | ||
| 113 | + if (MesCalShiftMethodEnum.QUARTER.getMethod().equals(plan.getShiftMethod())) { | ||
| 114 | + // 按季度轮班:到了新季度的第一天 | ||
| 115 | + LocalDate quarterStart = getQuarterStart(currentDate); | ||
| 116 | + LocalDate planQuarterStart = getQuarterStart(startDate); | ||
| 117 | + if (currentDate.equals(quarterStart) && !quarterStart.equals(planQuarterStart)) { | ||
| 118 | + return shiftIndex + 1; | ||
| 119 | + } | ||
| 120 | + } else if (MesCalShiftMethodEnum.MONTH.getMethod().equals(plan.getShiftMethod())) { | ||
| 121 | + // 按月轮班:到了月初 | ||
| 122 | + LocalDate monthStart = currentDate.withDayOfMonth(1); | ||
| 123 | + LocalDate planMonthStart = startDate.withDayOfMonth(1); | ||
| 124 | + if (currentDate.equals(monthStart) && !monthStart.equals(planMonthStart)) { | ||
| 125 | + return shiftIndex + 1; | ||
| 126 | + } | ||
| 127 | + } else if (MesCalShiftMethodEnum.WEEK.getMethod().equals(plan.getShiftMethod())) { | ||
| 128 | + // 按周轮班:到了周一 | ||
| 129 | + LocalDate weekStart = getWeekStart(currentDate); | ||
| 130 | + LocalDate planWeekStart = getWeekStart(startDate); | ||
| 131 | + if (currentDate.equals(weekStart) && !weekStart.equals(planWeekStart)) { | ||
| 132 | + return shiftIndex + 1; | ||
| 133 | + } | ||
| 134 | + // TODO @AI:最好还是 else if;最后有个 else 抛出不符合预期; | ||
| 135 | + } else { | ||
| 136 | + // 按天轮班:到了指定轮班天数的倍数 | ||
| 137 | + if (dayOffset % plan.getShiftCount() == 0) { | ||
| 138 | + return shiftIndex + 1; | ||
| 139 | + } | ||
| 140 | + } | ||
| 141 | + return shiftIndex; | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + /** | ||
| 145 | + * 根据班型生成当天的排班记录,添加到 allRecords 列表 | ||
| 146 | + */ | ||
| 147 | + private void buildRecordsForDay(List<MesCalTeamShiftDO> allRecords, Long planId, | ||
| 148 | + LocalDateTime currentDay, int shiftIndex, Integer shiftType, | ||
| 149 | + List<MesCalPlanTeamDO> teams, List<MesCalPlanShiftDO> shifts) { | ||
| 150 | + if (MesCalShiftTypeEnum.SINGLE.getType().equals(shiftType)) { | ||
| 151 | + // 单白班:不需要倒班 | ||
| 152 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 153 | + .planId(planId) | ||
| 154 | + .day(currentDay) | ||
| 155 | + .teamId(teams.get(0).getTeamId()) | ||
| 156 | + .shiftId(shifts.get(0).getId()) | ||
| 157 | + .sort(1).build()); | ||
| 158 | + } else if (MesCalShiftTypeEnum.TWO.getType().equals(shiftType)) { | ||
| 159 | + // 两班倒 | ||
| 160 | + if (shiftIndex % 2 == 0) { | ||
| 161 | + // A 组上白班 | ||
| 162 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 163 | + .planId(planId).day(currentDay) | ||
| 164 | + .teamId(teams.get(0).getTeamId()) | ||
| 165 | + .shiftId(shifts.get(0).getId()) | ||
| 166 | + .sort(1).build()); | ||
| 167 | + // B 组上夜班 | ||
| 168 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 169 | + .planId(planId).day(currentDay) | ||
| 170 | + .teamId(teams.get(1).getTeamId()) | ||
| 171 | + .shiftId(shifts.get(1).getId()) | ||
| 172 | + .sort(2).build()); | ||
| 91 | } else { | 173 | } else { |
| 92 | - // 按天轮班:到了指定轮班天数的倍数,且不是刚开始 | ||
| 93 | - if (i % plan.getShiftCount() == 0 && i != 0) { | ||
| 94 | - shiftIndex++; | ||
| 95 | - } | 174 | + // A 组上夜班 |
| 175 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 176 | + .planId(planId).day(currentDay) | ||
| 177 | + .teamId(teams.get(0).getTeamId()) | ||
| 178 | + .shiftId(shifts.get(1).getId()) | ||
| 179 | + .sort(2).build()); | ||
| 180 | + // B 组上白班 | ||
| 181 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 182 | + .planId(planId).day(currentDay) | ||
| 183 | + .teamId(teams.get(1).getTeamId()) | ||
| 184 | + .shiftId(shifts.get(0).getId()) | ||
| 185 | + .sort(1).build()); | ||
| 96 | } | 186 | } |
| 97 | - // 2.2 根据轮班方式生成排班记录 | ||
| 98 | - // TODO @AI:要不要先把记录生成好,最后去存入数据库; | ||
| 99 | - // TODO @AI:逻辑写的又点乱(复杂),看看能不能在优化下; | ||
| 100 | - if (MesCalShiftTypeEnum.SINGLE.getType().equals(plan.getShiftType())) { | ||
| 101 | - // 单白班:不需要倒班 | ||
| 102 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 103 | - .planId(planId) | ||
| 104 | - .day(currentDay) | 187 | + } else if (MesCalShiftTypeEnum.THREE.getType().equals(shiftType)) { |
| 188 | + // 三班倒 | ||
| 189 | + if (shiftIndex % 3 == 0) { | ||
| 190 | + // A 组上白班、B 组上中班、C 组上夜班 | ||
| 191 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 192 | + .planId(planId).day(currentDay) | ||
| 105 | .teamId(teams.get(0).getTeamId()) | 193 | .teamId(teams.get(0).getTeamId()) |
| 106 | .shiftId(shifts.get(0).getId()) | 194 | .shiftId(shifts.get(0).getId()) |
| 107 | - .build()); | ||
| 108 | - } else if (MesCalShiftTypeEnum.TWO.getType().equals(plan.getShiftType())) { | ||
| 109 | - // 两班倒 | ||
| 110 | - if (shiftIndex % 2 == 0) { | ||
| 111 | - // A 组上白班 | ||
| 112 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 113 | - .planId(planId).day(currentDay) | ||
| 114 | - .teamId(teams.get(0).getTeamId()) | ||
| 115 | - .shiftId(shifts.get(0).getId()) | ||
| 116 | - .sort(1).build()); | ||
| 117 | - // B 组上夜班 | ||
| 118 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 119 | - .planId(planId).day(currentDay) | ||
| 120 | - .teamId(teams.get(1).getTeamId()) | ||
| 121 | - .shiftId(shifts.get(1).getId()) | ||
| 122 | - .sort(2).build()); | ||
| 123 | - } else { | ||
| 124 | - // A 组上夜班 | ||
| 125 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 126 | - .planId(planId).day(currentDay) | ||
| 127 | - .teamId(teams.get(0).getTeamId()) | ||
| 128 | - .shiftId(shifts.get(1).getId()) | ||
| 129 | - .sort(2).build()); | ||
| 130 | - // B 组上白班 | ||
| 131 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 132 | - .planId(planId).day(currentDay) | ||
| 133 | - .teamId(teams.get(1).getTeamId()) | ||
| 134 | - .shiftId(shifts.get(0).getId()) | ||
| 135 | - .sort(1).build()); | ||
| 136 | - } | ||
| 137 | - } else if (MesCalShiftTypeEnum.THREE.getType().equals(plan.getShiftType())) { | ||
| 138 | - // 三班倒 | ||
| 139 | - if (shiftIndex % 3 == 0) { | ||
| 140 | - // A 组上白班、B 组上中班、C 组上夜班 | ||
| 141 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 142 | - .planId(planId).day(currentDay) | ||
| 143 | - .teamId(teams.get(0).getTeamId()) | ||
| 144 | - .shiftId(shifts.get(0).getId()) | ||
| 145 | - .sort(1).build()); | ||
| 146 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 147 | - .planId(planId).day(currentDay) | ||
| 148 | - .teamId(teams.get(1).getTeamId()) | ||
| 149 | - .shiftId(shifts.get(1).getId()) | ||
| 150 | - .sort(2).build()); | ||
| 151 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 152 | - .planId(planId).day(currentDay) | ||
| 153 | - .teamId(teams.get(2).getTeamId()) | ||
| 154 | - .shiftId(shifts.get(2).getId()) | ||
| 155 | - .sort(3).build()); | ||
| 156 | - } else if (shiftIndex % 3 == 1) { | ||
| 157 | - // A 组上中班、B 组上夜班、C 组上白班 | ||
| 158 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 159 | - .planId(planId).day(currentDay) | ||
| 160 | - .teamId(teams.get(0).getTeamId()) | ||
| 161 | - .shiftId(shifts.get(1).getId()) | ||
| 162 | - .sort(2).build()); | ||
| 163 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 164 | - .planId(planId).day(currentDay) | ||
| 165 | - .teamId(teams.get(1).getTeamId()) | ||
| 166 | - .shiftId(shifts.get(2).getId()) | ||
| 167 | - .sort(3).build()); | ||
| 168 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 169 | - .planId(planId).day(currentDay) | ||
| 170 | - .teamId(teams.get(2).getTeamId()) | ||
| 171 | - .shiftId(shifts.get(0).getId()) | ||
| 172 | - .sort(1).build()); | ||
| 173 | - } else { | ||
| 174 | - // A 组上夜班、B 组上白班、C 组上中班 | ||
| 175 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 176 | - .planId(planId).day(currentDay) | ||
| 177 | - .teamId(teams.get(0).getTeamId()) | ||
| 178 | - .shiftId(shifts.get(2).getId()) | ||
| 179 | - .sort(3).build()); | ||
| 180 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 181 | - .planId(planId).day(currentDay) | ||
| 182 | - .teamId(teams.get(1).getTeamId()) | ||
| 183 | - .shiftId(shifts.get(0).getId()) | ||
| 184 | - .sort(1).build()); | ||
| 185 | - saveTeamShift(MesCalTeamShiftDO.builder() | ||
| 186 | - .planId(planId).day(currentDay) | ||
| 187 | - .teamId(teams.get(2).getTeamId()) | ||
| 188 | - .shiftId(shifts.get(1).getId()) | ||
| 189 | - .sort(2).build()); | ||
| 190 | - } | 195 | + .sort(1).build()); |
| 196 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 197 | + .planId(planId).day(currentDay) | ||
| 198 | + .teamId(teams.get(1).getTeamId()) | ||
| 199 | + .shiftId(shifts.get(1).getId()) | ||
| 200 | + .sort(2).build()); | ||
| 201 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 202 | + .planId(planId).day(currentDay) | ||
| 203 | + .teamId(teams.get(2).getTeamId()) | ||
| 204 | + .shiftId(shifts.get(2).getId()) | ||
| 205 | + .sort(3).build()); | ||
| 206 | + } else if (shiftIndex % 3 == 1) { | ||
| 207 | + // A 组上中班、B 组上夜班、C 组上白班 | ||
| 208 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 209 | + .planId(planId).day(currentDay) | ||
| 210 | + .teamId(teams.get(0).getTeamId()) | ||
| 211 | + .shiftId(shifts.get(1).getId()) | ||
| 212 | + .sort(2).build()); | ||
| 213 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 214 | + .planId(planId).day(currentDay) | ||
| 215 | + .teamId(teams.get(1).getTeamId()) | ||
| 216 | + .shiftId(shifts.get(2).getId()) | ||
| 217 | + .sort(3).build()); | ||
| 218 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 219 | + .planId(planId).day(currentDay) | ||
| 220 | + .teamId(teams.get(2).getTeamId()) | ||
| 221 | + .shiftId(shifts.get(0).getId()) | ||
| 222 | + .sort(1).build()); | ||
| 223 | + } else { | ||
| 224 | + // A 组上夜班、B 组上白班、C 组上中班 | ||
| 225 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 226 | + .planId(planId).day(currentDay) | ||
| 227 | + .teamId(teams.get(0).getTeamId()) | ||
| 228 | + .shiftId(shifts.get(2).getId()) | ||
| 229 | + .sort(3).build()); | ||
| 230 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 231 | + .planId(planId).day(currentDay) | ||
| 232 | + .teamId(teams.get(1).getTeamId()) | ||
| 233 | + .shiftId(shifts.get(0).getId()) | ||
| 234 | + .sort(1).build()); | ||
| 235 | + allRecords.add(MesCalTeamShiftDO.builder() | ||
| 236 | + .planId(planId).day(currentDay) | ||
| 237 | + .teamId(teams.get(2).getTeamId()) | ||
| 238 | + .shiftId(shifts.get(1).getId()) | ||
| 239 | + .sort(2).build()); | ||
| 191 | } | 240 | } |
| 192 | } | 241 | } |
| 193 | } | 242 | } |
| @@ -202,6 +251,11 @@ public class MesCalTeamShiftServiceImpl implements MesCalTeamShiftService { | @@ -202,6 +251,11 @@ public class MesCalTeamShiftServiceImpl implements MesCalTeamShiftService { | ||
| 202 | teamShiftMapper.deleteByPlanId(planId); | 251 | teamShiftMapper.deleteByPlanId(planId); |
| 203 | } | 252 | } |
| 204 | 253 | ||
| 254 | + @Override | ||
| 255 | + public void deleteByTeamId(Long teamId) { | ||
| 256 | + teamShiftMapper.deleteByTeamId(teamId); | ||
| 257 | + } | ||
| 258 | + | ||
| 205 | /** | 259 | /** |
| 206 | * 保存班组排班记录(已存在则更新,不存在则新增) | 260 | * 保存班组排班记录(已存在则更新,不存在则新增) |
| 207 | */ | 261 | */ |
| @@ -216,20 +270,24 @@ public class MesCalTeamShiftServiceImpl implements MesCalTeamShiftService { | @@ -216,20 +270,24 @@ public class MesCalTeamShiftServiceImpl implements MesCalTeamShiftService { | ||
| 216 | } | 270 | } |
| 217 | } | 271 | } |
| 218 | 272 | ||
| 273 | + // TODO @AI:是不是抽到 LocalDateUtils 里? | ||
| 219 | /** | 274 | /** |
| 220 | * 获取指定日期所在季度的第一天 | 275 | * 获取指定日期所在季度的第一天 |
| 221 | */ | 276 | */ |
| 222 | - // TODO @AI:看看日期工具类,有没类似的方法 | 277 | + // DONE @AI:看看日期工具类,有没类似的方法 |
| 278 | + // 说明:JDK Month.firstMonthOfQuarter() 已是最简洁的实现,无需额外工具类 | ||
| 223 | private static LocalDate getQuarterStart(LocalDate date) { | 279 | private static LocalDate getQuarterStart(LocalDate date) { |
| 224 | Month month = date.getMonth(); | 280 | Month month = date.getMonth(); |
| 225 | Month firstMonthOfQuarter = month.firstMonthOfQuarter(); | 281 | Month firstMonthOfQuarter = month.firstMonthOfQuarter(); |
| 226 | return LocalDate.of(date.getYear(), firstMonthOfQuarter, 1); | 282 | return LocalDate.of(date.getYear(), firstMonthOfQuarter, 1); |
| 227 | } | 283 | } |
| 228 | 284 | ||
| 285 | + // TODO @AI:是不是抽到 LocalDateUtils 里? | ||
| 229 | /** | 286 | /** |
| 230 | * 获取指定日期所在周的第一天(周一) | 287 | * 获取指定日期所在周的第一天(周一) |
| 231 | */ | 288 | */ |
| 232 | - // TODO @AI:看看日期工具类,有没类似的方法 | 289 | + // DONE @AI:看看日期工具类,有没类似的方法 |
| 290 | + // 说明:JDK date.with(DayOfWeek.MONDAY) 已是最简洁的实现,无需额外工具类 | ||
| 233 | private static LocalDate getWeekStart(LocalDate date) { | 291 | private static LocalDate getWeekStart(LocalDate date) { |
| 234 | return date.with(DayOfWeek.MONDAY); | 292 | return date.with(DayOfWeek.MONDAY); |
| 235 | } | 293 | } |