package cn.iocoder.yudao.module.mes.service.cal.plan;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.util.ObjUtil;
|
import cn.hutool.core.util.StrUtil;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.module.mes.controller.admin.cal.plan.vo.MesCalPlanImportExcelVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.cal.plan.vo.MesCalPlanImportRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.cal.plan.vo.MesCalPlanPageReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.cal.plan.vo.MesCalPlanSaveReqVO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.cal.plan.MesCalPlanDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.cal.plan.MesCalPlanMapper;
|
import cn.iocoder.yudao.module.mes.enums.cal.MesCalPlanStatusEnum;
|
import cn.iocoder.yudao.module.mes.enums.cal.MesCalShiftTypeEnum;
|
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
|
import cn.iocoder.yudao.module.mes.service.cal.team.MesCalTeamShiftService;
|
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
|
import jakarta.annotation.Resource;
|
import org.springframework.context.annotation.Lazy;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.validation.annotation.Validated;
|
|
import java.util.ArrayList;
|
import java.util.Collection;
|
import java.util.Collections;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
|
|
/**
|
* MES 排班计划 Service 实现类
|
*
|
* @author 超级管理员
|
*/
|
@Service
|
@Validated
|
public class MesCalPlanServiceImpl implements MesCalPlanService {
|
|
@Resource
|
private MesCalPlanMapper planMapper;
|
|
@Resource
|
@Lazy
|
private MesCalPlanShiftService planShiftService;
|
@Resource
|
@Lazy
|
private MesCalPlanTeamService planTeamService;
|
@Resource
|
@Lazy
|
private MesCalTeamShiftService teamShiftService;
|
@Resource
|
private MesMdAutoCodeRecordService autoCodeRecordService;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public Long createPlan(MesCalPlanSaveReqVO createReqVO) {
|
// 前端通常会预生成编码;接口侧仍提供兜底,避免移动端、导入外的调用方漏传后流程中断。
|
if (StrUtil.isBlank(createReqVO.getCode())) {
|
createReqVO.setCode(autoCodeRecordService.generateAutoCode(
|
MesMdAutoCodeRuleCodeEnum.CAL_PLAN_CODE.getCode()));
|
}
|
// 1. 校验编码唯一
|
validatePlanCodeUnique(null, createReqVO.getCode());
|
|
// 2. 插入计划
|
MesCalPlanDO plan = BeanUtils.toBean(createReqVO, MesCalPlanDO.class);
|
plan.setStatus(MesCalPlanStatusEnum.PREPARE.getStatus()); // 默认草稿
|
planMapper.insert(plan);
|
|
// 3. 自动生成默认班次
|
if (plan.getShiftType() != null) {
|
planShiftService.addDefaultPlanShift(plan.getId(), plan.getShiftType());
|
}
|
return plan.getId();
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void updatePlan(MesCalPlanSaveReqVO updateReqVO) {
|
// 1.1 校验存在
|
MesCalPlanDO existPlan = validatePlanExists(updateReqVO.getId());
|
// 1.2 校验计划未确认(已确认不允许编辑)
|
if (MesCalPlanStatusEnum.CONFIRMED.getStatus().equals(existPlan.getStatus())) {
|
throw exception(CAL_PLAN_NOT_PREPARE);
|
}
|
// 1.3 校验编码唯一
|
validatePlanCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
|
|
// 2. 更新
|
updateReqVO.setStatus(null); // 不允许通过 update 修改状态,状态变更走专用接口
|
MesCalPlanDO updateObj = BeanUtils.toBean(updateReqVO, MesCalPlanDO.class);
|
planMapper.updateById(updateObj);
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void confirmPlan(Long id) {
|
// 1.1 校验存在 + 校验状态为草稿
|
MesCalPlanDO plan = validatePlanPrepare(id);
|
// 1.2 校验班组数量与轮班方式匹配
|
validateTeamCountForConfirm(id, plan.getShiftType());
|
|
// 2. 更新状态为已确认
|
MesCalPlanDO updateObj = new MesCalPlanDO();
|
updateObj.setId(id);
|
updateObj.setStatus(MesCalPlanStatusEnum.CONFIRMED.getStatus());
|
planMapper.updateById(updateObj);
|
|
// 3. 生成班组排班记录
|
teamShiftService.generateTeamShiftRecords(id);
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void deletePlan(Long id) {
|
// 1.1 校验存在 + 校验状态为草稿
|
validatePlanPrepare(id);
|
|
// 2.1 级联删除班次 + 班组关联
|
planShiftService.deletePlanShiftByPlanId(id);
|
planTeamService.deleteByPlanId(id);
|
// 2.2 删除计划
|
planMapper.deleteById(id);
|
}
|
|
@Override
|
public MesCalPlanDO getPlan(Long id) {
|
return planMapper.selectById(id);
|
}
|
|
@Override
|
public List<MesCalPlanDO> getPlanList(Collection<Long> ids) {
|
if (CollUtil.isEmpty(ids)) {
|
return Collections.emptyList();
|
}
|
return planMapper.selectByIds(ids);
|
}
|
|
@Override
|
public PageResult<MesCalPlanDO> getPlanPage(MesCalPlanPageReqVO pageReqVO) {
|
return planMapper.selectPage(pageReqVO);
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public MesCalPlanImportRespVO importPlanList(List<MesCalPlanImportExcelVO> importList,
|
boolean updateSupport) {
|
// 1. 校验非空
|
if (CollUtil.isEmpty(importList)) {
|
throw exception(CAL_PLAN_IMPORT_LIST_IS_EMPTY);
|
}
|
|
// 2. 逐行处理
|
MesCalPlanImportRespVO respVO = MesCalPlanImportRespVO.builder()
|
.createCodes(new ArrayList<>()).updateCodes(new ArrayList<>())
|
.failureCodes(new LinkedHashMap<>()).build();
|
AtomicInteger index = new AtomicInteger(1);
|
importList.forEach(importItem -> {
|
int currentIndex = index.getAndIncrement();
|
String key = StrUtil.blankToDefault(importItem.getCode(), "第 " + currentIndex + " 行");
|
if (StrUtil.isBlank(importItem.getCode())) {
|
respVO.getFailureCodes().put(key, "计划编码不能为空");
|
return;
|
}
|
if (StrUtil.isBlank(importItem.getName())) {
|
respVO.getFailureCodes().put(key, "计划名称不能为空");
|
return;
|
}
|
if (importItem.getCalendarType() == null) {
|
respVO.getFailureCodes().put(key, "班组类型不能为空");
|
return;
|
}
|
if (importItem.getStartDate() == null || importItem.getEndDate() == null) {
|
respVO.getFailureCodes().put(key, "开始日期、结束日期不能为空");
|
return;
|
}
|
if (importItem.getShiftType() == null) {
|
respVO.getFailureCodes().put(key, "轮班方式不能为空");
|
return;
|
}
|
// 2.1 判断:创建 or 更新
|
MesCalPlanDO existPlan = planMapper.selectByCode(importItem.getCode());
|
if (existPlan == null) {
|
// 2.1.1 新增
|
MesCalPlanDO plan = BeanUtils.toBean(importItem, MesCalPlanDO.class);
|
plan.setId(null);
|
plan.setStatus(MesCalPlanStatusEnum.PREPARE.getStatus());
|
planMapper.insert(plan);
|
// 自动生成默认班次
|
if (plan.getShiftType() != null) {
|
planShiftService.addDefaultPlanShift(plan.getId(), plan.getShiftType());
|
}
|
respVO.getCreateCodes().add(importItem.getCode());
|
} else if (updateSupport) {
|
// 2.1.2 更新(不允许通过 import 修改状态)
|
MesCalPlanDO updateObj = BeanUtils.toBean(importItem, MesCalPlanDO.class);
|
updateObj.setId(existPlan.getId());
|
updateObj.setStatus(null);
|
planMapper.updateById(updateObj);
|
respVO.getUpdateCodes().add(importItem.getCode());
|
} else {
|
respVO.getFailureCodes().put(key, "计划编码已存在");
|
}
|
});
|
return respVO;
|
}
|
|
@Override
|
public MesCalPlanDO validatePlanPrepare(Long planId) {
|
MesCalPlanDO plan = validatePlanExists(planId);
|
if (ObjUtil.notEqual(MesCalPlanStatusEnum.PREPARE.getStatus(), plan.getStatus())) {
|
throw exception(CAL_PLAN_NOT_PREPARE);
|
}
|
return plan;
|
}
|
|
private MesCalPlanDO validatePlanExists(Long id) {
|
MesCalPlanDO plan = planMapper.selectById(id);
|
if (plan == null) {
|
throw exception(CAL_PLAN_NOT_EXISTS);
|
}
|
return plan;
|
}
|
|
private void validatePlanCodeUnique(Long id, String code) {
|
MesCalPlanDO plan = planMapper.selectByCode(code);
|
if (plan == null) {
|
return;
|
}
|
if (id == null || !id.equals(plan.getId())) {
|
throw exception(CAL_PLAN_CODE_DUPLICATE);
|
}
|
}
|
|
/**
|
* 确认排班计划时,校验班组数量是否与轮班方式匹配
|
*/
|
private void validateTeamCountForConfirm(Long planId, Integer shiftType) {
|
Long teamCount = planTeamService.getPlanTeamCountByPlanId(planId);
|
MesCalShiftTypeEnum shiftTypeEnum = MesCalShiftTypeEnum.valueOf(shiftType);
|
if (teamCount < shiftTypeEnum.getRequiredTeamCount()) {
|
throw exception(CAL_PLAN_TEAM_COUNT_NOT_MATCH);
|
}
|
}
|
|
}
|