package cn.iocoder.yudao.module.mes.service.pro.batch;
|
|
import cn.hutool.core.collection.CollUtil;
|
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.pro.batch.vo.MesProBatchPageReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.batch.vo.MesProBatchSaveReqVO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchWorkerDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.line.MesProLineDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchWorkerMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper;
|
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
|
import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchStatusEnum;
|
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
|
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
|
import cn.iocoder.yudao.module.mes.service.pro.line.MesProLineService;
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import jakarta.annotation.Resource;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.validation.annotation.Validated;
|
|
import java.util.Collection;
|
import java.util.HashSet;
|
import java.util.List;
|
import java.util.Objects;
|
import java.util.Set;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
|
|
/**
|
* MES 生产批次 Service 实现类
|
*
|
* @author 超级管理员
|
*/
|
@Service
|
@Validated
|
@Slf4j
|
public class MesProBatchServiceImpl implements MesProBatchService {
|
|
@Resource
|
private MesProBatchMapper batchMapper;
|
@Resource
|
private MesProBatchWorkerMapper batchWorkerMapper;
|
@Resource
|
private MesProBagCodeMapper bagCodeMapper;
|
@Resource
|
private MesProPalletMapper palletMapper;
|
|
@Resource
|
private MesMdAutoCodeRecordService autoCodeRecordService;
|
@Resource
|
private AdminUserApi adminUserApi;
|
@Resource
|
private MesMdmItemService mdmItemService;
|
@Resource
|
private MesProLineService lineService;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public Long createBatch(MesProBatchSaveReqVO createReqVO) {
|
// 1. 校验关联数据(品种、产线、生产人员)
|
MesMdmItemDO item = mdmItemService.validateItemExists(createReqVO.getItemId());
|
MesProLineDO line = lineService.validateLineExistsAndEnable(createReqVO.getLineId());
|
validateRelations(createReqVO);
|
|
// 2. 生成批次号(编码规则:前缀 + 日期 + 产线编码 + 品种编码 + 流水号)
|
String code = generateBatchCode(line.getCode(), item.getCode());
|
|
// 3. 插入批次(产线编码/名称冗余回填)
|
MesProBatchDO batch = BeanUtils.toBean(createReqVO, MesProBatchDO.class)
|
.setCode(code)
|
.setLineCode(line.getCode())
|
.setLineName(line.getName())
|
.setBagQuantity(0)
|
.setPalletQuantity(0)
|
.setStatus(MesProBatchStatusEnum.DRAFT.getStatus());
|
batchMapper.insert(batch);
|
|
// 4. 插入生产人员配置
|
insertBatchWorkers(batch.getId(), createReqVO.getWorkers());
|
return batch.getId();
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void updateBatch(MesProBatchSaveReqVO updateReqVO) {
|
// 1. 校验存在 + 状态(草稿/生产中才允许修改)
|
MesProBatchDO oldBatch = validateBatchExists(updateReqVO.getId());
|
validateBatchEditable(oldBatch);
|
// 2. 校验关联数据(生产人员)
|
validateRelations(updateReqVO);
|
|
// 3. 品种/产线变化时重新生成批次号(批次号包含产线编码 + 品种编码要素)
|
boolean itemChanged = !Objects.equals(oldBatch.getItemId(), updateReqVO.getItemId());
|
boolean lineChanged = !Objects.equals(oldBatch.getLineId(), updateReqVO.getLineId());
|
String code = oldBatch.getCode();
|
MesProBatchDO updateObj = BeanUtils.toBean(updateReqVO, MesProBatchDO.class);
|
if (itemChanged || lineChanged) {
|
MesMdmItemDO item = mdmItemService.validateItemExists(updateReqVO.getItemId());
|
// 变更产线时,目标产线必须启用;未变更则仅校验存在(避免停用产线的历史批次无法编辑其他字段)
|
MesProLineDO line = lineChanged
|
? lineService.validateLineExistsAndEnable(updateReqVO.getLineId())
|
: lineService.validateLineExists(updateReqVO.getLineId());
|
code = generateBatchCode(line.getCode(), item.getCode());
|
updateObj.setLineCode(line.getCode()).setLineName(line.getName());
|
}
|
// 4. 更新批次
|
updateObj.setCode(code);
|
batchMapper.updateById(updateObj);
|
|
// 5. 重建生产人员配置
|
batchWorkerMapper.deleteByBatchId(updateReqVO.getId());
|
insertBatchWorkers(updateReqVO.getId(), updateReqVO.getWorkers());
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void deleteBatch(Long id) {
|
// 1. 校验存在
|
MesProBatchDO batch = validateBatchExists(id);
|
// 2. 校验状态:仅草稿、已作废可删除
|
if (!MesProBatchStatusEnum.DRAFT.getStatus().equals(batch.getStatus())
|
&& !MesProBatchStatusEnum.CANCELED.getStatus().equals(batch.getStatus())) {
|
throw exception(PRO_BATCH_STATUS_NOT_ALLOW_DELETE);
|
}
|
// 3. 校验未生成袋码、托盘码
|
if (bagCodeMapper.selectCountByBatchId(id) > 0 || palletMapper.selectListByBatchId(id).size() > 0) {
|
throw exception(PRO_BATCH_HAS_CODE_NOT_DELETE);
|
}
|
// 4. 删除批次与人员配置
|
batchMapper.deleteById(id);
|
batchWorkerMapper.deleteByBatchId(id);
|
}
|
|
@Override
|
public void updateBatchStatus(Long id, Integer status) {
|
// 1. 校验存在
|
MesProBatchDO batch = validateBatchExists(id);
|
// 2. 校验状态流转合法性
|
Integer oldStatus = batch.getStatus();
|
boolean valid = false;
|
if (MesProBatchStatusEnum.DRAFT.getStatus().equals(oldStatus)) {
|
valid = MesProBatchStatusEnum.PRODUCING.getStatus().equals(status)
|
|| MesProBatchStatusEnum.CANCELED.getStatus().equals(status);
|
} else if (MesProBatchStatusEnum.PRODUCING.getStatus().equals(oldStatus)) {
|
valid = MesProBatchStatusEnum.FINISHED.getStatus().equals(status)
|
|| MesProBatchStatusEnum.CANCELED.getStatus().equals(status);
|
}
|
if (!valid) {
|
throw exception(PRO_BATCH_STATUS_INVALID);
|
}
|
// 3. 更新状态
|
batchMapper.updateById(new MesProBatchDO().setId(id).setStatus(status));
|
}
|
|
@Override
|
public MesProBatchDO getBatch(Long id) {
|
return batchMapper.selectById(id);
|
}
|
|
@Override
|
public MesProBatchDO getBatchByCode(String code) {
|
return batchMapper.selectByCode(code);
|
}
|
|
@Override
|
public MesProBatchDO validateBatchExists(Long id) {
|
MesProBatchDO batch = batchMapper.selectById(id);
|
if (batch == null) {
|
throw exception(PRO_BATCH_NOT_EXISTS);
|
}
|
return batch;
|
}
|
|
@Override
|
public PageResult<MesProBatchDO> getBatchPage(MesProBatchPageReqVO pageReqVO) {
|
return batchMapper.selectPage(pageReqVO);
|
}
|
|
@Override
|
public List<MesProBatchDO> getBatchList(Collection<Long> ids) {
|
if (CollUtil.isEmpty(ids)) {
|
return List.of();
|
}
|
return batchMapper.selectBatchIds(ids);
|
}
|
|
@Override
|
public List<MesProBatchWorkerDO> getBatchWorkerListByBatchId(Long batchId) {
|
return batchWorkerMapper.selectListByBatchId(batchId);
|
}
|
|
@Override
|
public List<MesProBatchWorkerDO> getBatchWorkerListByBatchIds(Collection<Long> batchIds) {
|
if (CollUtil.isEmpty(batchIds)) {
|
return List.of();
|
}
|
return batchWorkerMapper.selectListByBatchIds(batchIds);
|
}
|
|
// ==================== 私有方法 ====================
|
|
/**
|
* 校验关联数据(生产人员:责任人 + 生产人员配置)
|
* <p>
|
* 品种、产线的校验在创建/更新流程中内联执行(需取编码参与批次号生成)
|
*/
|
private void validateRelations(MesProBatchSaveReqVO reqVO) {
|
Set<Long> userIds = new HashSet<>();
|
userIds.add(reqVO.getOwnerUserId());
|
userIds.addAll(convertList(reqVO.getWorkers(), MesProBatchSaveReqVO.Worker::getUserId));
|
adminUserApi.validateUserList(userIds);
|
}
|
|
/**
|
* 生成批次号
|
* <p>
|
* 编码规则:固定前缀(PC) + 日期(yyyyMMdd) + 输入字符(产线编码 + 品种编码) + 流水号(按日循环)
|
*
|
* @param lineCode 产线编码
|
* @param itemCode 品种(物料)编码
|
* @return 批次号
|
*/
|
private String generateBatchCode(String lineCode, String itemCode) {
|
String inputChar = StrUtil.emptyToDefault(lineCode, "") + StrUtil.emptyToDefault(itemCode, "");
|
return autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRO_BATCH_CODE.getCode(), inputChar);
|
}
|
|
/**
|
* 校验批次可编辑(草稿、生产中)
|
*/
|
private void validateBatchEditable(MesProBatchDO batch) {
|
if (!MesProBatchStatusEnum.DRAFT.getStatus().equals(batch.getStatus())
|
&& !MesProBatchStatusEnum.PRODUCING.getStatus().equals(batch.getStatus())) {
|
throw exception(PRO_BATCH_STATUS_NOT_DRAFT_OR_PRODUCING);
|
}
|
}
|
|
/**
|
* 插入批次生产人员配置
|
*/
|
private void insertBatchWorkers(Long batchId, List<MesProBatchSaveReqVO.Worker> workers) {
|
if (CollUtil.isEmpty(workers)) {
|
return;
|
}
|
List<MesProBatchWorkerDO> workerDOs = convertList(workers,
|
worker -> new MesProBatchWorkerDO()
|
.setBatchId(batchId)
|
.setUserId(worker.getUserId())
|
.setWorkerType(worker.getWorkerType())
|
.setRemark(worker.getRemark()));
|
batchWorkerMapper.insertBatch(workerDOs);
|
}
|
|
}
|