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.mybatis.core.query.LambdaQueryWrapperX;
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
import cn.iocoder.yudao.module.mes.service.pd.processparamtemplate.MesPdProcessParamTemplateService;
|
import cn.iocoder.yudao.module.mes.service.pro.feedback.dto.MesTaskParamSnapshot;
|
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.dv.machinery.MesDvMachineryDO;
|
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.md.workstation.MesMdWorkstationMachineDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationWorkerDO;
|
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.dv.machinery.MesDvMachineryService;
|
import cn.iocoder.yudao.module.mes.service.cal.team.MesCalTeamService;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamDO;
|
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.MesMdWorkstationMachineService;
|
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
|
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationWorkerService;
|
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.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_SCHEDULE_INVALID;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_ALREADY_FINISHED;
|
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_CANNOT_PAUSE;
|
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;
|
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_NOT_PAUSED;
|
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskPauseRecordDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskPauseRecordMapper;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPauseReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskResumeReqVO;
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
|
/**
|
* MES 生产任务 Service 实现类
|
*
|
* @author 超级管理员
|
*/
|
@Service
|
@Validated
|
public class MesProTaskServiceImpl implements MesProTaskService {
|
@Resource
|
private cn.iocoder.yudao.module.mes.service.pro.task.schedule.MesProScheduleGuardService scheduleGuard;
|
@Resource
|
private cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderMapper scheduleWorkOrderMapper;
|
|
@Resource
|
private MesProTaskMapper taskMapper;
|
@Resource
|
private MesPdProcessParamTemplateService paramTemplateService;
|
|
@Resource
|
private MesProFeedbackMapper feedbackMapper;
|
|
@Resource
|
private MesProTaskPauseRecordMapper taskPauseRecordMapper;
|
|
@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;
|
@Resource
|
private MesMdWorkstationMachineService workstationMachineService;
|
@Resource
|
@Lazy
|
private MesDvMachineryService machineryService;
|
@Resource
|
private MesCalTeamService teamService;
|
@Resource
|
private MesMdWorkstationWorkerService workstationWorkerService;
|
@Resource
|
private AdminUserApi adminUserApi;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public Long createTask(MesProTaskSaveReqVO createReqVO) {
|
scheduleGuard.lock();
|
// 1. 校验关联数据存在
|
MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderExists(createReqVO.getWorkOrderId());
|
var station = workstationService.validateWorkstationExists(createReqVO.getWorkstationId());
|
if (java.util.Objects.equals(workOrder.getType(), cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderTypeEnum.SELF.getType()) && !java.util.Objects.equals(station.getProcessId(), createReqVO.getProcessId())) {
|
throw exception(PRO_TASK_SCHEDULE_INVALID, "工作站与工序不匹配");
|
}
|
routeService.validateRouteExists(createReqVO.getRouteId());
|
processService.validateProcessExistsAndEnable(createReqVO.getProcessId());
|
fillTeamInfo(createReqVO);
|
|
// 2. 自动确定 itemId:优先前端传入 → 工艺路线工序的产出产品 → 工单成品
|
Long itemId = createReqVO.getItemId();
|
if (itemId == null) {
|
MesProRouteProcessDO routeProcess = routeProcessService
|
.getRouteProcessByRouteIdAndProcessId(createReqVO.getRouteId(), createReqVO.getProcessId());
|
itemId = resolveTaskItemId(routeProcess, workOrder.getProductId());
|
}
|
MesMdItemDO item = itemService.validateItemExists(itemId);
|
|
// 3.1 构建任务 DO:自动填充客户信息
|
List<Long> assigneeUserIds = createReqVO.getAssigneeUserIds();
|
if (CollUtil.isEmpty(assigneeUserIds)) {
|
// 未指定报工人时默认取工作站配置的人员,避免一键排产生成的任务无人可报
|
assigneeUserIds = getWorkstationWorkerUserIds(createReqVO.getWorkstationId());
|
}
|
MesProTaskDO task = BeanUtils.toBean(createReqVO, MesProTaskDO.class)
|
.setItemId(itemId) // 确保使用计算后的 itemId
|
.setName(buildTaskName(item, createReqVO.getQuantity()))
|
.setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRO_TASK_CODE.getCode()))
|
.setAssigneeUserIds(joinUserIds(assigneeUserIds))
|
.setStatus(MesProTaskStatusEnum.PREPARE.getStatus());
|
if (task.getClientId() == null && workOrder.getClientId() != null) {
|
task.setClientId(workOrder.getClientId());
|
}
|
// 2.2 插入
|
scheduleGuard.validate(workOrder, task, null);
|
freezeTaskParamConfig(task);
|
taskMapper.insert(task);
|
synchronizeScheduledQuantity(workOrder, task.getRouteId());
|
return task.getId();
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void updateTask(MesProTaskSaveReqVO updateReqVO) {
|
scheduleGuard.lock();
|
MesProTaskDO task = taskMapper.selectOne(new LambdaQueryWrapperX<MesProTaskDO>()
|
.eq(MesProTaskDO::getId, updateReqVO.getId()).last("FOR UPDATE"));
|
if (task == null) throw exception(PRO_TASK_NOT_EXISTS);
|
// 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());
|
}
|
fillTeamInfo(updateReqVO);
|
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();
|
|
MesProTaskDO candidate = BeanUtils.toBean(task, MesProTaskDO.class)
|
.setWorkOrderId(updateReqVO.getWorkOrderId() != null ? updateReqVO.getWorkOrderId() : task.getWorkOrderId())
|
.setRouteId(updateReqVO.getRouteId() != null ? updateReqVO.getRouteId() : task.getRouteId())
|
.setProcessId(updateReqVO.getProcessId() != null ? updateReqVO.getProcessId() : task.getProcessId())
|
.setWorkstationId(updateReqVO.getWorkstationId() != null ? updateReqVO.getWorkstationId() : task.getWorkstationId())
|
.setQuantity(quantity)
|
.setStartTime(updateReqVO.getStartTime() != null ? updateReqVO.getStartTime() : task.getStartTime())
|
.setEndTime(updateReqVO.getEndTime() != null ? updateReqVO.getEndTime() : task.getEndTime());
|
var station = workstationService.validateWorkstationExists(candidate.getWorkstationId());
|
if (java.util.Objects.equals(workOrderService.validateWorkOrderExists(candidate.getWorkOrderId()).getType(), cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderTypeEnum.SELF.getType()) && !java.util.Objects.equals(station.getProcessId(), candidate.getProcessId())) {
|
throw exception(PRO_TASK_SCHEDULE_INVALID, "工作站与工序不匹配");
|
}
|
scheduleGuard.validate(workOrderService.validateWorkOrderExists(candidate.getWorkOrderId()), candidate, task.getId());
|
// 2. 更新
|
MesProTaskDO updateObj = BeanUtils.toBean(updateReqVO, MesProTaskDO.class)
|
.setName(buildTaskName(item, quantity));
|
// 报工人为 null 表示不修改;空集合表示清空限制(所有人可报工)
|
if (updateReqVO.getAssigneeUserIds() != null) {
|
updateObj.setAssigneeUserIds(joinUserIds(updateReqVO.getAssigneeUserIds()));
|
}
|
if (updateReqVO.getProcessId() != null && !updateReqVO.getProcessId().equals(task.getProcessId())) {
|
if (feedbackMapper.selectCountByTaskId(task.getId()) > 0) throw exception(PRO_TASK_HAS_FEEDBACK);
|
freezeTaskParamConfig(updateObj);
|
}
|
taskMapper.updateById(updateObj);
|
synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(candidate.getWorkOrderId()), candidate.getRouteId());
|
if (!java.util.Objects.equals(task.getWorkOrderId(), candidate.getWorkOrderId())) synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(task.getWorkOrderId()), task.getRouteId());
|
}
|
|
private void synchronizeScheduledQuantity(MesProWorkOrderDO order, Long routeId) {
|
if (!java.util.Objects.equals(order.getType(), cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderTypeEnum.SELF.getType()) || order.getQuantity() == null || routeId == null) return;
|
List<MesProRouteProcessDO> required = buildRouteProcessList(routeId, order.getId());
|
List<MesProTaskDO> tasks = taskMapper.selectListByWorkOrderId(order.getId());
|
BigDecimal scheduled = required.isEmpty() ? BigDecimal.ZERO : order.getQuantity();
|
for (var process : required) {
|
BigDecimal amount = tasks.stream().filter(task -> java.util.Objects.equals(task.getProcessId(), process.getProcessId())
|
&& !java.util.Objects.equals(task.getStatus(), MesProTaskStatusEnum.CANCELED.getStatus()))
|
.map(task -> task.getQuantity() == null ? BigDecimal.ZERO : task.getQuantity()).reduce(BigDecimal.ZERO, BigDecimal::add);
|
scheduled = scheduled.min(amount);
|
}
|
scheduleWorkOrderMapper.updateById(new MesProWorkOrderDO().setId(order.getId()).setQuantityScheduled(scheduled));
|
}
|
|
/** 从工序和模板复制定义,后续模板维护不改变已排产任务。 */
|
private void freezeTaskParamConfig(MesProTaskDO task) {
|
var process = processService.getProcess(task.getProcessId());
|
if (process == null) throw exception(cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_PROCESS_NOT_EXISTS);
|
int mode = process.getParamCollectMode() == null ? 0 : process.getParamCollectMode();
|
task.setParamCollectMode(mode);
|
task.setParamTemplateSnapshot("");
|
task.setParamSharedSnapshot("");
|
if (mode == 0) return;
|
if ((mode != 1 && mode != 2) || process.getParamTemplateId() == null)
|
throw exception(cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_PROCESS_PARAM_CONFIG_INVALID);
|
var template = paramTemplateService.validateProcessParamTemplateExists(process.getParamTemplateId());
|
var details = paramTemplateService.getProcessParamTemplateDetailListByTemplateId(template.getId());
|
if (CollUtil.isEmpty(details)) throw exception(cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_WORK_ORDER_PARAM_RECORD_TEMPLATE_NO_DETAIL);
|
MesTaskParamSnapshot snapshot = new MesTaskParamSnapshot();
|
snapshot.setTemplateId(template.getId());
|
snapshot.setTemplateCode(template.getTemplateCode());
|
snapshot.setTemplateName(template.getTemplateName());
|
List<MesTaskParamSnapshot.Detail> frozenDetails = BeanUtils.toBean(details, MesTaskParamSnapshot.Detail.class);
|
for (MesTaskParamSnapshot.Detail detail : frozenDetails) {
|
if (detail.getUnitMeasureId() != null) {
|
var unit = unitMeasureService.getUnitMeasure(detail.getUnitMeasureId());
|
if (unit != null) detail.setUnitMeasureName(unit.getName());
|
}
|
}
|
snapshot.setDetails(frozenDetails);
|
task.setParamTemplateSnapshot(JsonUtils.toJsonString(snapshot));
|
}
|
|
/**
|
* 校验班组存在并回填班组名称
|
*/
|
private void fillTeamInfo(MesProTaskSaveReqVO reqVO) {
|
if (reqVO.getTeamId() == null) {
|
return;
|
}
|
MesCalTeamDO team = teamService.validateTeamExists(reqVO.getTeamId());
|
if (team != null) {
|
reqVO.setTeamName(team.getName());
|
}
|
}
|
|
/**
|
* 获得工作站配置岗位对应的员工编号
|
*/
|
private List<Long> getWorkstationWorkerUserIds(Long workstationId) {
|
if (workstationId == null) {
|
return Collections.emptyList();
|
}
|
List<MesMdWorkstationWorkerDO> workers = workstationWorkerService
|
.getWorkstationWorkerListByWorkstationId(workstationId);
|
if (CollUtil.isEmpty(workers)) {
|
return Collections.emptyList();
|
}
|
Set<Long> postIds = convertSet(workers, MesMdWorkstationWorkerDO::getPostId);
|
if (CollUtil.isEmpty(postIds)) {
|
return Collections.emptyList();
|
}
|
return convertList(adminUserApi.getUserListByPostIds(postIds), AdminUserRespDTO::getId);
|
}
|
|
/**
|
* 将报工人用户编号列表拼接为逗号分隔字符串
|
*
|
* @return null 表示不修改/不限制;空字符串表示清空限制
|
*/
|
private String joinUserIds(List<Long> userIds) {
|
if (userIds == null) {
|
return null;
|
}
|
return userIds.stream().filter(java.util.Objects::nonNull).distinct()
|
.map(String::valueOf).collect(java.util.stream.Collectors.joining(","));
|
}
|
|
/**
|
* 构建任务名称,格式:"产品名【数量】单位"
|
*/
|
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;
|
}
|
|
/**
|
* 任务产出物料:优先取工序在当前产品下的产出(按归属过滤),没有产出时取工单成品
|
*/
|
private Long resolveTaskItemId(MesProRouteProcessDO routeProcess, Long productId) {
|
if (routeProcess == null) {
|
return productId;
|
}
|
if (routeProcess.getId() != null) {
|
// 工艺路线模板:按产出归属过滤出当前产品的产出
|
List<Long> outputIds = routeProcessService
|
.getOutputItemIdsByRouteProcessId(routeProcess.getId(), productId);
|
if (outputIds != null) {
|
// 有归属配置:该产品无产出时取工单成品;null 表示存量无归属数据,继续走聚合兜底
|
return CollUtil.isNotEmpty(outputIds) ? outputIds.get(0) : productId;
|
}
|
}
|
// 工单工序快照 / 存量无归属数据:产出已按产品过滤,取第一个
|
String outputItemId = routeProcess.getOutputItemId();
|
if (outputItemId != null && !outputItemId.isBlank()) {
|
return Long.valueOf(outputItemId.split(",")[0].trim());
|
}
|
return productId;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void deleteTask(Long id) {
|
scheduleGuard.lock();
|
MesProTaskDO task = validateTaskNotFinished(id);
|
// 2. 校验任务下无报工记录,避免删除后报工追溯断链
|
if (feedbackMapper.selectCountByTaskId(id) > 0) {
|
throw exception(PRO_TASK_HAS_FEEDBACK);
|
}
|
// 3. 删除
|
taskMapper.deleteById(id);
|
synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(task.getWorkOrderId()), task.getRouteId());
|
}
|
|
@Override
|
public MesProTaskDO getTask(Long id) {
|
return taskMapper.selectById(id);
|
}
|
|
@Override
|
public MesProTaskDO getTaskByCode(String code) {
|
return taskMapper.selectByCode(code);
|
}
|
|
@Override
|
public PageResult<MesProTaskDO> getTaskPage(MesProTaskPageReqVO pageReqVO) {
|
return taskMapper.selectPage(pageReqVO);
|
}
|
|
@Override
|
public PageResult<MesProTaskDO> getPendingFeedbackPage(MesProTaskPageReqVO pageReqVO) {
|
return taskMapper.selectPendingFeedbackPage(pageReqVO);
|
}
|
|
@Override
|
public Map<Long, Long> getPendingFeedbackCountByWorkstation(Long assigneeUserId) {
|
List<Map<String, Object>> rows = taskMapper.selectPendingFeedbackCountByWorkstation(assigneeUserId);
|
Map<Long, Long> result = new HashMap<>(rows.size());
|
for (Map<String, Object> row : rows) {
|
Object workstationId = row.get("workstation_id");
|
Object count = row.get("pending_count");
|
if (workstationId instanceof Number && count instanceof Number) {
|
result.put(((Number) workstationId).longValue(), ((Number) count).longValue());
|
}
|
}
|
return result;
|
}
|
|
@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 Integer calculateDuration(Long workstationId, BigDecimal quantity) {
|
if (quantity == null || quantity.compareTo(BigDecimal.ZERO) <= 0) {
|
return 0;
|
}
|
// 1. 查询工作站绑定的设备
|
List<MesMdWorkstationMachineDO> machines = workstationMachineService
|
.getWorkstationMachineListByWorkstationId(workstationId);
|
if (CollUtil.isEmpty(machines)) {
|
return 0;
|
}
|
// 2. 获取设备详情,计算总效率
|
Set<Long> machineryIds = convertSet(machines, MesMdWorkstationMachineDO::getMachineryId);
|
List<MesDvMachineryDO> machineryList = machineryService.getMachineryList(machineryIds);
|
BigDecimal totalEfficiency = BigDecimal.ZERO;
|
for (MesDvMachineryDO m : machineryList) {
|
BigDecimal efficiency = m.getProductionEfficiency() != null ? m.getProductionEfficiency() : BigDecimal.ZERO;
|
Integer belt = m.getConveyorBelt() != null && m.getConveyorBelt() > 0 ? m.getConveyorBelt() : 1;
|
totalEfficiency = totalEfficiency.add(efficiency.multiply(BigDecimal.valueOf(belt)));
|
}
|
if (totalEfficiency.compareTo(BigDecimal.ZERO) <= 0) {
|
return 0;
|
}
|
// 3. 生产时长(小时) = ceil(排产数量 / 总效率),效率单位为每小时产量
|
return quantity.divide(totalEfficiency, 0, java.math.RoundingMode.CEILING).intValue();
|
}
|
|
@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) {
|
scheduleGuard.lock();
|
// 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 = resolveTaskItemId(rp, 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());
|
scheduleGuard.validate(workOrder, task, null);
|
freezeTaskParamConfig(task);
|
taskMapper.insert(task);
|
taskIds.add(task.getId());
|
}
|
// 7. 更新工单已排产数量
|
workOrderService.updateScheduledQuantity(workOrder.getId(), workOrder.getQuantity());
|
return taskIds;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void pauseTask(MesProTaskPauseReqVO pauseReqVO) {
|
// 1. 校验任务存在
|
MesProTaskDO task = validateTaskExists(pauseReqVO.getId());
|
// 2. 校验状态是否允许停工(如必须是 生产中 或 部分完成)
|
if (!ObjectUtils.equalsAny(task.getStatus(), MesProTaskStatusEnum.PRODUCING.getStatus(),
|
MesProTaskStatusEnum.PARTIAL_FINISHED.getStatus())) {
|
throw exception(PRO_TASK_CANNOT_PAUSE);
|
}
|
|
// 3. 修改任务状态为已停工
|
taskMapper.updateById(new MesProTaskDO().setId(task.getId()).setStatus(MesProTaskStatusEnum.PAUSED.getStatus()));
|
|
// 4. 创建停工记录
|
MesProTaskPauseRecordDO pauseRecord = new MesProTaskPauseRecordDO()
|
.setTaskId(task.getId())
|
.setWorkOrderId(task.getWorkOrderId())
|
.setWorkstationId(task.getWorkstationId())
|
.setReason(pauseReqVO.getReason())
|
.setRemark(pauseReqVO.getRemark())
|
.setPauseTime(LocalDateTime.now())
|
.setPauseUserId(SecurityFrameworkUtils.getLoginUserId());
|
taskPauseRecordMapper.insert(pauseRecord);
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void resumeTask(MesProTaskResumeReqVO resumeReqVO) {
|
// 1. 校验任务存在
|
MesProTaskDO task = validateTaskExists(resumeReqVO.getId());
|
// 2. 校验状态必须是已停工
|
if (!ObjectUtils.equalsAny(task.getStatus(), MesProTaskStatusEnum.PAUSED.getStatus())) {
|
throw exception(PRO_TASK_NOT_PAUSED);
|
}
|
|
// 3. 恢复任务状态
|
Integer resumeStatus = task.getProducedQuantity() != null
|
&& task.getProducedQuantity().signum() > 0
|
? MesProTaskStatusEnum.PARTIAL_FINISHED.getStatus()
|
: MesProTaskStatusEnum.PRODUCING.getStatus();
|
taskMapper.updateById(new MesProTaskDO().setId(task.getId()).setStatus(resumeStatus));
|
|
// 4. 更新最新的一条停工记录
|
MesProTaskPauseRecordDO pauseRecord = taskPauseRecordMapper.selectLastUnresumedByTaskId(task.getId());
|
if (pauseRecord != null) {
|
pauseRecord.setResumeTime(LocalDateTime.now());
|
pauseRecord.setResumeUserId(SecurityFrameworkUtils.getLoginUserId());
|
taskPauseRecordMapper.updateById(pauseRecord);
|
}
|
}
|
|
}
|