From 56e8a8e8f6c90faf8240a5a05d64a6da441888ab Mon Sep 17 00:00:00 2001
From: hsy <1029150275@qq.com>
Date: 星期二, 18 八月 2026 09:50:23 +0800
Subject: [PATCH] feat(mes): add task assignee implementation

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java |  152 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 148 insertions(+), 4 deletions(-)

diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
index d19426b..d13aa2c 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
@@ -7,17 +7,21 @@
 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.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.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.pro.process.MesProProcessService;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
@@ -40,17 +44,29 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+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.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.dal.dataobject.pro.task.MesProTaskAssigneeDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskAssigneeMapper;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPauseReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskResumeReqVO;
 
 /**
  * MES 鐢熶骇浠诲姟 Service 瀹炵幇绫�
  *
- * @author 鑺嬮亾婧愮爜
+ * @author 瓒呯骇绠$悊鍛�
  */
 @Service
 @Validated
@@ -61,6 +77,12 @@
 
     @Resource
     private MesProFeedbackMapper feedbackMapper;
+
+    @Resource
+    private MesProTaskPauseRecordMapper taskPauseRecordMapper;
+
+    @Resource
+    private MesProTaskAssigneeMapper taskAssigneeMapper;
 
     @Resource
     @Lazy
@@ -86,6 +108,11 @@
     private MesProWorkOrderProcessService workOrderProcessService;
     @Resource
     private MesMdAutoCodeRecordService autoCodeRecordService;
+    @Resource
+    private MesMdWorkstationMachineService workstationMachineService;
+    @Resource
+    @Lazy
+    private MesDvMachineryService machineryService;
 
     @Override
     public Long createTask(MesProTaskSaveReqVO createReqVO) {
@@ -101,7 +128,7 @@
             MesProRouteProcessDO routeProcess = routeProcessService
                     .getRouteProcessByRouteIdAndProcessId(createReqVO.getRouteId(), createReqVO.getProcessId());
             if (routeProcess != null && routeProcess.getOutputItemId() != null) {
-                itemId = routeProcess.getOutputItemId();
+                itemId = Long.valueOf(routeProcess.getOutputItemId().split(",")[0].trim());
             } else {
                 itemId = workOrder.getProductId();
             }
@@ -119,6 +146,10 @@
         }
         // 2.2 鎻掑叆
         taskMapper.insert(task);
+        
+        // 2.3 淇濆瓨鎸囧畾鎶ュ伐浜哄憳
+        updateTaskAssignees(task.getId(), createReqVO.getAssigneeUserIds());
+        
         return task.getId();
     }
 
@@ -151,6 +182,34 @@
         MesProTaskDO updateObj = BeanUtils.toBean(updateReqVO, MesProTaskDO.class)
                 .setName(buildTaskName(item, quantity));
         taskMapper.updateById(updateObj);
+        
+        // 3. 鏇存柊鎸囧畾鎶ュ伐浜哄憳锛堟瘮瀵规洿鏂帮級
+        updateTaskAssignees(task.getId(), updateReqVO.getAssigneeUserIds());
+    }
+
+    private void updateTaskAssignees(Long taskId, List<Long> assigneeUserIds) {
+        if (assigneeUserIds == null) {
+            assigneeUserIds = Collections.emptyList();
+        }
+        // 1. 鏌ヨ鏁版嵁搴撲腑鍘熸湁鐨�
+        List<MesProTaskAssigneeDO> oldAssignees = taskAssigneeMapper.selectListByTaskId(taskId);
+        Set<Long> oldUserIds = convertSet(oldAssignees, MesProTaskAssigneeDO::getUserId);
+        Set<Long> newUserIds = new java.util.HashSet<>(assigneeUserIds);
+
+        // 2. 璁$畻闇�瑕佹柊澧炵殑鍜岄渶瑕佸垹闄ょ殑
+        Set<Long> toAddUserIds = newUserIds.stream().filter(id -> !oldUserIds.contains(id)).collect(java.util.stream.Collectors.toSet());
+        Set<Long> toRemoveUserIds = oldUserIds.stream().filter(id -> !newUserIds.contains(id)).collect(java.util.stream.Collectors.toSet());
+
+        // 3. 鏂板
+        if (CollUtil.isNotEmpty(toAddUserIds)) {
+            List<MesProTaskAssigneeDO> addList = convertList(toAddUserIds, userId -> new MesProTaskAssigneeDO().setTaskId(taskId).setUserId(userId));
+            taskAssigneeMapper.insertBatch(addList);
+        }
+
+        // 4. 鍒犻櫎
+        if (CollUtil.isNotEmpty(toRemoveUserIds)) {
+            taskAssigneeMapper.deleteByTaskIdAndUserIds(taskId, toRemoveUserIds);
+        }
     }
 
     /**
@@ -223,8 +282,10 @@
         if (feedbackMapper.selectCountByTaskId(id) > 0) {
             throw exception(PRO_TASK_HAS_FEEDBACK);
         }
-        // 3. 鍒犻櫎
+        // 3. 鍒犻櫎浠诲姟涓昏〃
         taskMapper.deleteById(id);
+        // 4. 鍒犻櫎鍏宠仈鐨勪汉鍛�
+        taskAssigneeMapper.deleteByTaskId(id);
     }
 
     @Override
@@ -248,6 +309,14 @@
             return Collections.emptyList();
         }
         return taskMapper.selectListByWorkOrderIds(workOrderIds);
+    }
+
+    @Override
+    public List<MesProTaskAssigneeDO> getTaskAssigneeListByTaskIds(Collection<Long> taskIds) {
+        if (CollUtil.isEmpty(taskIds)) {
+            return Collections.emptyList();
+        }
+        return taskAssigneeMapper.selectListByTaskIds(taskIds);
     }
 
     @Override
@@ -318,6 +387,33 @@
     }
 
     @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());
@@ -370,7 +466,7 @@
         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();
+            Long itemId = rp.getOutputItemId() != null ? Long.valueOf(rp.getOutputItemId().split(",")[0].trim()) : workOrder.getProductId();
             cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO item = itemService.validateItemExists(itemId);
             MesProTaskDO task = new MesProTaskDO()
                     .setWorkOrderId(workOrder.getId())
@@ -391,4 +487,52 @@
         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.PREPARE.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 = MesProTaskStatusEnum.PREPARE.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);
+        }
+    }
+
 }

--
Gitblit v1.9.3