package cn.iocoder.yudao.module.mes.service.pro.task;
|
|
import cn.hutool.core.collection.CollUtil;
|
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.task.vo.MesProTaskAutoGeneratePreviewRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskAutoGenerateReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPageReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskSaveReqVO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper;
|
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
|
import cn.iocoder.yudao.module.mes.enums.pro.MesProTaskStatusEnum;
|
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
|
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
|
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
|
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
|
import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
|
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
|
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
|
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteService;
|
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
|
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService;
|
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.math.BigDecimal;
|
import java.time.LocalDateTime;
|
import java.util.Collection;
|
import java.util.Collections;
|
import java.util.List;
|
|
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.PRO_TASK_ALREADY_FINISHED;
|
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_HAS_FEEDBACK;
|
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_NOT_EXISTS;
|
|
/**
|
* MES 生产任务 Service 实现类
|
*
|
* @author 芋道源码
|
*/
|
@Service
|
@Validated
|
public class MesProTaskServiceImpl implements MesProTaskService {
|
|
@Resource
|
private MesProTaskMapper taskMapper;
|
|
@Resource
|
private MesProFeedbackMapper feedbackMapper;
|
|
@Resource
|
@Lazy
|
private MesProWorkOrderService workOrderService;
|
@Resource
|
private MesMdWorkstationService workstationService;
|
@Resource
|
private MesProProcessService processService;
|
@Resource
|
private MesMdItemService itemService;
|
@Resource
|
private MesMdUnitMeasureService unitMeasureService;
|
@Resource
|
private MesProRouteService routeService;
|
@Resource
|
@Lazy
|
private MesProRouteProcessService routeProcessService;
|
@Resource
|
@Lazy
|
private MesProRouteProductService routeProductService;
|
@Resource
|
@Lazy
|
private MesProWorkOrderProcessService workOrderProcessService;
|
@Resource
|
private MesMdAutoCodeRecordService autoCodeRecordService;
|
|
@Override
|
public Long createTask(MesProTaskSaveReqVO createReqVO) {
|
// 1. 校验关联数据存在
|
MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderExists(createReqVO.getWorkOrderId());
|
workstationService.validateWorkstationExists(createReqVO.getWorkstationId());
|
routeService.validateRouteExists(createReqVO.getRouteId());
|
processService.validateProcessExistsAndEnable(createReqVO.getProcessId());
|
|
// 2. 自动确定 itemId:优先前端传入 → 工艺路线工序的产出产品 → 工单成品
|
Long itemId = createReqVO.getItemId();
|
if (itemId == null) {
|
MesProRouteProcessDO routeProcess = routeProcessService
|
.getRouteProcessByRouteIdAndProcessId(createReqVO.getRouteId(), createReqVO.getProcessId());
|
if (routeProcess != null && routeProcess.getOutputItemId() != null) {
|
itemId = routeProcess.getOutputItemId();
|
} else {
|
itemId = workOrder.getProductId();
|
}
|
}
|
MesMdItemDO item = itemService.validateItemExists(itemId);
|
|
// 3.1 构建任务 DO:自动填充客户信息
|
MesProTaskDO task = BeanUtils.toBean(createReqVO, MesProTaskDO.class)
|
.setItemId(itemId) // 确保使用计算后的 itemId
|
.setName(buildTaskName(item, createReqVO.getQuantity()))
|
.setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRO_TASK_CODE.getCode()))
|
.setStatus(MesProTaskStatusEnum.PREPARE.getStatus());
|
if (task.getClientId() == null && workOrder.getClientId() != null) {
|
task.setClientId(workOrder.getClientId());
|
}
|
// 2.2 插入
|
taskMapper.insert(task);
|
return task.getId();
|
}
|
|
@Override
|
public void updateTask(MesProTaskSaveReqVO updateReqVO) {
|
// 1.1 校验存在
|
MesProTaskDO task = validateTaskExists(updateReqVO.getId());
|
// 1.2 校验关联数据存在
|
if (updateReqVO.getWorkOrderId() != null) {
|
workOrderService.validateWorkOrderExists(updateReqVO.getWorkOrderId());
|
}
|
if (updateReqVO.getWorkstationId() != null) {
|
workstationService.validateWorkstationExists(updateReqVO.getWorkstationId());
|
}
|
if (updateReqVO.getRouteId() != null) {
|
routeService.validateRouteExists(updateReqVO.getRouteId());
|
}
|
if (updateReqVO.getProcessId() != null) {
|
processService.validateProcessExistsAndEnable(updateReqVO.getProcessId());
|
}
|
MesMdItemDO item;
|
if (updateReqVO.getItemId() != null) {
|
item = itemService.validateItemExists(updateReqVO.getItemId());
|
} else {
|
item = itemService.validateItemExists(task.getItemId());
|
}
|
BigDecimal quantity = updateReqVO.getQuantity() != null ? updateReqVO.getQuantity() : task.getQuantity();
|
|
// 2. 更新
|
MesProTaskDO updateObj = BeanUtils.toBean(updateReqVO, MesProTaskDO.class)
|
.setName(buildTaskName(item, quantity));
|
taskMapper.updateById(updateObj);
|
}
|
|
/**
|
* 构建任务名称,格式:"产品名【数量】单位"
|
*/
|
private String buildTaskName(MesMdItemDO item, BigDecimal quantity) {
|
String unitName = "";
|
if (item.getUnitMeasureId() != null) {
|
MesMdUnitMeasureDO unit = unitMeasureService.getUnitMeasure(item.getUnitMeasureId());
|
if (unit != null) {
|
unitName = unit.getName();
|
}
|
}
|
return item.getName() + "【" + quantity + "】" + unitName;
|
}
|
|
/**
|
* 确定排产使用的工艺路线 ID:优先请求指定 → 工单关联 → 产品关联
|
*/
|
private Long resolveRouteId(Long reqRouteId, MesProWorkOrderDO workOrder) {
|
if (reqRouteId != null) {
|
return reqRouteId;
|
}
|
if (workOrder.getRouteId() != null) {
|
return workOrder.getRouteId();
|
}
|
// 从产品关联的工艺路线获取
|
MesProRouteProductDO routeProduct = routeProductService.getRouteProductByItemId(workOrder.getProductId());
|
if (routeProduct == null) {
|
throw exception(cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_ROUTE_NOT_EXISTS);
|
}
|
return routeProduct.getRouteId();
|
}
|
|
/**
|
* 构建工序列表:优先使用工单已保存的工序 → 回退到工艺路线模板工序
|
*/
|
private List<MesProRouteProcessDO> buildRouteProcessList(Long routeId, Long workOrderId) {
|
// 1. 优先使用工单已保存的工序
|
List<MesProWorkOrderProcessDO> woProcesses = workOrderProcessService
|
.getWorkOrderProcessListByWorkOrderId(workOrderId);
|
if (CollUtil.isNotEmpty(woProcesses)) {
|
List<MesProRouteProcessDO> result = new java.util.ArrayList<>();
|
for (MesProWorkOrderProcessDO wp : woProcesses) {
|
result.add(new MesProRouteProcessDO()
|
.setRouteId(routeId)
|
.setProcessId(wp.getProcessId())
|
.setSort(wp.getSort())
|
.setKeyFlag(wp.getKeyFlag())
|
.setCheckFlag(wp.getCheckFlag())
|
.setBackflushFlag(wp.getBackflushFlag())
|
.setOutputItemId(wp.getOutputItemId())
|
.setColorCode(wp.getColorCode()));
|
}
|
return result;
|
}
|
// 2. 回退到工艺路线模板工序
|
List<MesProRouteProcessDO> routeProcesses = routeProcessService.getRouteProcessListByRouteId(routeId);
|
if (CollUtil.isEmpty(routeProcesses)) {
|
throw exception(cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_ROUTE_ENABLE_NO_PROCESS);
|
}
|
return routeProcesses;
|
}
|
|
@Override
|
public void deleteTask(Long id) {
|
// 1. 校验存在且非终态
|
validateTaskNotFinished(id);
|
// 2. 校验任务下无报工记录,避免删除后报工追溯断链
|
if (feedbackMapper.selectCountByTaskId(id) > 0) {
|
throw exception(PRO_TASK_HAS_FEEDBACK);
|
}
|
// 3. 删除
|
taskMapper.deleteById(id);
|
}
|
|
@Override
|
public MesProTaskDO getTask(Long id) {
|
return taskMapper.selectById(id);
|
}
|
|
@Override
|
public PageResult<MesProTaskDO> getTaskPage(MesProTaskPageReqVO pageReqVO) {
|
return taskMapper.selectPage(pageReqVO);
|
}
|
|
@Override
|
public PageResult<MesProTaskDO> getPendingFeedbackPage(MesProTaskPageReqVO pageReqVO) {
|
return taskMapper.selectPendingFeedbackPage(pageReqVO);
|
}
|
|
@Override
|
public List<MesProTaskDO> getTaskListByWorkOrderIds(Collection<Long> workOrderIds) {
|
if (CollUtil.isEmpty(workOrderIds)) {
|
return Collections.emptyList();
|
}
|
return taskMapper.selectListByWorkOrderIds(workOrderIds);
|
}
|
|
@Override
|
public MesProTaskDO validateTaskExists(Long id) {
|
MesProTaskDO task = taskMapper.selectById(id);
|
if (task == null) {
|
throw exception(PRO_TASK_NOT_EXISTS);
|
}
|
return task;
|
}
|
|
@Override
|
public MesProTaskDO validateTaskNotFinished(Long id) {
|
MesProTaskDO task = validateTaskExists(id);
|
if (MesProTaskStatusEnum.isEndStatus(task.getStatus())) {
|
throw exception(PRO_TASK_ALREADY_FINISHED);
|
}
|
return task;
|
}
|
|
@Override
|
public List<MesProTaskDO> getTaskList(Collection<Long> ids) {
|
if (CollUtil.isEmpty(ids)) {
|
return Collections.emptyList();
|
}
|
return taskMapper.selectByIds(ids);
|
}
|
|
@Override
|
public void finishTaskByOrderId(Long workOrderId) {
|
// 1. 读取工单下所有任务,过滤掉已完成/已取消的
|
List<MesProTaskDO> tasks = taskMapper.selectListByWorkOrderId(workOrderId);
|
LocalDateTime now = LocalDateTime.now();
|
// 2. 构建待更新列表,批量更新
|
List<MesProTaskDO> updateList = convertList(tasks,
|
task -> new MesProTaskDO().setId(task.getId())
|
.setStatus(MesProTaskStatusEnum.FINISHED.getStatus()).setFinishDate(now),
|
task -> !MesProTaskStatusEnum.isEndStatus(task.getStatus()));
|
if (CollUtil.isNotEmpty(updateList)) {
|
taskMapper.updateBatch(updateList);
|
}
|
}
|
|
@Override
|
public void cancelTaskByOrderId(Long workOrderId) {
|
// 1. 读取工单下所有任务,过滤掉已完成/已取消的
|
List<MesProTaskDO> tasks = taskMapper.selectListByWorkOrderId(workOrderId);
|
LocalDateTime now = LocalDateTime.now();
|
// 2. 构建待更新列表,批量更新
|
List<MesProTaskDO> updateList = convertList(tasks,
|
task -> new MesProTaskDO().setId(task.getId())
|
.setStatus(MesProTaskStatusEnum.CANCELED.getStatus()).setCancelDate(now),
|
task -> !MesProTaskStatusEnum.isEndStatus(task.getStatus()));
|
if (CollUtil.isNotEmpty(updateList)) {
|
taskMapper.updateBatch(updateList);
|
}
|
}
|
|
@Override
|
public void updateProducedQuantity(Long id,
|
BigDecimal incrProducedQuantity,
|
BigDecimal incrQualifyQuantity,
|
BigDecimal incrUnqualifyQuantity) {
|
// 校验任务存在
|
validateTaskExists(id);
|
// 更新数量
|
taskMapper.updateProducedQuantity(id, incrProducedQuantity, incrQualifyQuantity, incrUnqualifyQuantity);
|
}
|
|
@Override
|
public MesProTaskAutoGeneratePreviewRespVO previewAutoGenerate(MesProTaskAutoGenerateReqVO reqVO) {
|
// 1. 校验工单已确认
|
MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
|
// 2. 确定工艺路线:优先请求指定 → 工单关联 → 产品关联
|
Long routeId = resolveRouteId(reqVO.getRouteId(), workOrder);
|
MesProRouteDO route = routeService.validateRouteExists(routeId);
|
// 3. 获取工序列表:优先工单工序 → 工艺路线工序
|
List<MesProRouteProcessDO> routeProcesses = buildRouteProcessList(routeId, reqVO.getWorkOrderId());
|
// 按序号排序
|
routeProcesses.sort(java.util.Comparator.comparing(MesProRouteProcessDO::getSort, java.util.Comparator.nullsLast(java.util.Comparator.naturalOrder())));
|
// 4. 查询工序名称
|
java.util.Set<Long> processIds = routeProcesses.stream().map(MesProRouteProcessDO::getProcessId).collect(java.util.stream.Collectors.toSet());
|
java.util.Map<Long, cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO> procMap = processService.getProcessMap(processIds);
|
// 5. 组装预览
|
MesProTaskAutoGeneratePreviewRespVO vo = new MesProTaskAutoGeneratePreviewRespVO()
|
.setWorkOrderId(workOrder.getId())
|
.setRouteId(routeId)
|
.setRouteName(route.getName());
|
java.util.List<MesProTaskAutoGeneratePreviewRespVO.TaskPreview> taskList = new java.util.ArrayList<>();
|
for (MesProRouteProcessDO rp : routeProcesses) {
|
cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO proc = procMap.get(rp.getProcessId());
|
taskList.add(new MesProTaskAutoGeneratePreviewRespVO.TaskPreview()
|
.setProcessId(rp.getProcessId())
|
.setProcessName(proc != null ? proc.getName() : null)
|
.setSort(rp.getSort())
|
.setQuantity(workOrder.getQuantity())
|
.setKeyFlag(rp.getKeyFlag()));
|
}
|
vo.setTaskList(taskList);
|
return vo;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public List<Long> autoGenerateTasks(MesProTaskAutoGenerateReqVO reqVO) {
|
// 1. 校验工单已确认
|
MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
|
// 2. 确定工艺路线:优先请求指定 → 工单关联 → 产品关联
|
Long routeId = resolveRouteId(reqVO.getRouteId(), workOrder);
|
routeService.validateRouteExists(routeId);
|
// 3. 校验排产数量
|
workOrderService.validateScheduleQuantity(reqVO.getWorkOrderId(), workOrder.getQuantity());
|
// 4. 获取工序列表:优先工单工序 → 工艺路线工序
|
List<MesProRouteProcessDO> routeProcesses = buildRouteProcessList(routeId, reqVO.getWorkOrderId());
|
routeProcesses.sort(java.util.Comparator.comparing(MesProRouteProcessDO::getSort, java.util.Comparator.nullsLast(java.util.Comparator.naturalOrder())));
|
// 5. 查询工序信息
|
java.util.Set<Long> processIds = routeProcesses.stream().map(MesProRouteProcessDO::getProcessId).collect(java.util.stream.Collectors.toSet());
|
java.util.Map<Long, cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO> procMap = processService.getProcessMap(processIds);
|
// 6. 为每个工序创建任务
|
List<Long> taskIds = new java.util.ArrayList<>();
|
for (MesProRouteProcessDO rp : routeProcesses) {
|
cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO proc = procMap.get(rp.getProcessId());
|
Long itemId = rp.getOutputItemId() != null ? rp.getOutputItemId() : workOrder.getProductId();
|
cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO item = itemService.validateItemExists(itemId);
|
MesProTaskDO task = new MesProTaskDO()
|
.setWorkOrderId(workOrder.getId())
|
.setRouteId(routeId)
|
.setProcessId(rp.getProcessId())
|
.setItemId(itemId)
|
.setQuantity(workOrder.getQuantity())
|
.setName(buildTaskName(item, workOrder.getQuantity()))
|
.setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRO_TASK_CODE.getCode()))
|
.setStatus(MesProTaskStatusEnum.PREPARE.getStatus())
|
.setClientId(workOrder.getClientId())
|
.setColorCode(rp.getColorCode());
|
taskMapper.insert(task);
|
taskIds.add(task.getId());
|
}
|
// 7. 更新工单已排产数量
|
workOrderService.updateScheduledQuantity(workOrder.getId(), workOrder.getQuantity());
|
return taskIds;
|
}
|
|
}
|