From 3ff6604c75c887f4056ee03545d204bc9323286c Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 15 十月 2026 17:47:19 +0800
Subject: [PATCH] feat(mes): 新增报工设备快照功能并优化状态校验

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java |  226 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 220 insertions(+), 6 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 b210c28..88956ea 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
@@ -12,6 +12,7 @@
 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.process.MesProProcessParamDO;
 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;
@@ -24,6 +25,7 @@
 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.service.pro.process.MesProProcessParamService;
 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;
@@ -33,6 +35,7 @@
 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 cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderParamRecordService;
 import jakarta.annotation.Resource;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -50,23 +53,48 @@
 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
 public class MesProTaskServiceImpl implements MesProTaskService {
 
     @Resource
+    private cn.iocoder.yudao.module.mes.service.pro.feedback.MesProFeedbackEquipmentService equipmentService;
+
+    @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 MesProFeedbackMapper feedbackMapper;
+
+    @Resource
+    private MesProTaskPauseRecordMapper taskPauseRecordMapper;
+
+    @Resource
+    private MesProTaskAssigneeMapper taskAssigneeMapper;
 
     @Resource
     @Lazy
@@ -91,6 +119,12 @@
     @Lazy
     private MesProWorkOrderProcessService workOrderProcessService;
     @Resource
+    @Lazy
+    private MesProWorkOrderParamRecordService workOrderParamRecordService;
+    @Resource
+    @Lazy
+    private MesProProcessParamService processParamService;
+    @Resource
     private MesMdAutoCodeRecordService autoCodeRecordService;
     @Resource
     private MesMdWorkstationMachineService workstationMachineService;
@@ -99,10 +133,15 @@
     private MesDvMachineryService machineryService;
 
     @Override
+    @Transactional(rollbackFor = Exception.class, isolation = org.springframework.transaction.annotation.Isolation.SERIALIZABLE)
     public Long createTask(MesProTaskSaveReqVO createReqVO) {
+        scheduleGuard.lock();
         // 1. 鏍¢獙鍏宠仈鏁版嵁瀛樺湪
-        MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderExists(createReqVO.getWorkOrderId());
-        workstationService.validateWorkstationExists(createReqVO.getWorkstationId());
+        MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(createReqVO.getWorkOrderId());
+        var station = workstationService.validateWorkstationExistsAndEnable(createReqVO.getWorkstationId());
+        if (!java.util.Objects.equals(station.getProcessId(), createReqVO.getProcessId())) {
+            throw exception(cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_SCHEDULE_INVALID, "宸ヤ綔绔欎笌宸ュ簭涓嶅尮閰�");
+        }
         routeService.validateRouteExists(createReqVO.getRouteId());
         processService.validateProcessExistsAndEnable(createReqVO.getProcessId());
 
@@ -128,15 +167,40 @@
         if (task.getClientId() == null && workOrder.getClientId() != null) {
             task.setClientId(workOrder.getClientId());
         }
+        scheduleGuard.validate(workOrder, task, null);
         // 2.2 鎻掑叆
         taskMapper.insert(task);
+        // 2.2.1 鍙傛暟蹇収锛氫紭鍏堜娇鐢ㄦā鏉匡紱鏃犳ā鏉挎椂鍥為��鍒板伐搴忎富鏁版嵁缁戝畾鐨勫伐鑹哄弬鏁�
+        createTaskParamRecordWithFallback(task);
+
+        // 2.3 淇濆瓨鎸囧畾鎶ュ伐浜哄憳
+        updateTaskAssignees(task.getId(), createReqVO.getAssigneeUserIds());
+        synchronizeScheduledQuantity(workOrder, task.getRouteId());
+
         return task.getId();
     }
 
+    /**
+     * 涓轰换鍔″浐鍖栧弬鏁板揩鐓э細浼樺厛浣跨敤璺嚎/宸ュ崟宸ュ簭缁戝畾鐨勫弬鏁版ā鏉匡紱鏃犳ā鏉挎椂鍥為��鍒板伐搴忎富鏁版嵁缁戝畾鐨勫伐鑹哄弬鏁�
+     */
+    private void createTaskParamRecordWithFallback(MesProTaskDO task) {
+        if (task.getParamTemplateId() != null) {
+            workOrderParamRecordService.createTaskParamRecord(task.getId(), task.getParamTemplateId());
+            return;
+        }
+        // 鏃犳ā鏉匡細浠庡伐搴忎富鏁版嵁瑙f瀽缁戝畾鐨勫伐鑹哄弬鏁�
+        List<MesProProcessParamDO> processParams = processParamService.getProcessParamListByProcessId(task.getProcessId());
+        if (CollUtil.isNotEmpty(processParams)) {
+            workOrderParamRecordService.createTaskParamRecordFromProcessParams(task.getId(), processParams);
+        }
+    }
+
     @Override
+    @Transactional(rollbackFor = Exception.class, isolation = org.springframework.transaction.annotation.Isolation.SERIALIZABLE)
     public void updateTask(MesProTaskSaveReqVO updateReqVO) {
+        scheduleGuard.lock();
         // 1.1 鏍¢獙瀛樺湪
-        MesProTaskDO task = validateTaskExists(updateReqVO.getId());
+        MesProTaskDO task = validateTaskNotFinished(updateReqVO.getId());
         // 1.2 鏍¢獙鍏宠仈鏁版嵁瀛樺湪
         if (updateReqVO.getWorkOrderId() != null) {
             workOrderService.validateWorkOrderExists(updateReqVO.getWorkOrderId());
@@ -158,10 +222,71 @@
         }
         BigDecimal quantity = updateReqVO.getQuantity() != null ? updateReqVO.getQuantity() : task.getQuantity();
 
+        MesProTaskDO candidate = BeanUtils.toBean(task, MesProTaskDO.class)
+                .setWorkOrderId(updateReqVO.getWorkOrderId() != null ? updateReqVO.getWorkOrderId() : task.getWorkOrderId())
+                .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.validateWorkstationExistsAndEnable(candidate.getWorkstationId());
+        if (!java.util.Objects.equals(station.getProcessId(), candidate.getProcessId())) {
+            throw exception(cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_SCHEDULE_INVALID, "宸ヤ綔绔欎笌宸ュ簭涓嶅尮閰�");
+        }
+        scheduleGuard.validate(workOrderService.validateWorkOrderConfirmed(candidate.getWorkOrderId()), candidate, task.getId());
+
         // 2. 鏇存柊
         MesProTaskDO updateObj = BeanUtils.toBean(updateReqVO, MesProTaskDO.class)
                 .setName(buildTaskName(item, quantity));
         taskMapper.updateById(updateObj);
+        
+        // 3. 鏇存柊鎸囧畾鎶ュ伐浜哄憳锛堟瘮瀵规洿鏂帮級
+        updateTaskAssignees(task.getId(), updateReqVO.getAssigneeUserIds());
+        synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(candidate.getWorkOrderId()),
+                updateReqVO.getRouteId() != null ? updateReqVO.getRouteId() : task.getRouteId());
+        if (!java.util.Objects.equals(task.getWorkOrderId(), candidate.getWorkOrderId())) {
+            synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(task.getWorkOrderId()), task.getRouteId());
+        }
+    }
+
+    /** 宸ュ崟鎺掍骇閲忔寜鍚勫伐搴忕疮璁℃帓浜ч噺鐨勬渶灏忓�艰绠楋紝浠诲姟淇敼/鍒犻櫎鍚庡悓姝ワ紝閬垮厤鏃犳硶琛ユ帓銆� */
+    private void synchronizeScheduledQuantity(MesProWorkOrderDO order, Long routeId) {
+        List<MesProRouteProcessDO> required = buildRouteProcessList(routeId, order.getId());
+        List<MesProTaskDO> tasks = taskMapper.selectListByWorkOrderId(order.getId());
+        BigDecimal scheduled = 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 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);
+        }
     }
 
     /**
@@ -209,6 +334,7 @@
                 result.add(new MesProRouteProcessDO()
                         .setRouteId(routeId)
                         .setProcessId(wp.getProcessId())
+                        .setParamTemplateId(wp.getParamTemplateId())
                         .setSort(wp.getSort())
                         .setKeyFlag(wp.getKeyFlag())
                         .setCheckFlag(wp.getCheckFlag())
@@ -227,15 +353,20 @@
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void deleteTask(Long id) {
+        scheduleGuard.lock();
         // 1. 鏍¢獙瀛樺湪涓旈潪缁堟��
-        validateTaskNotFinished(id);
+        MesProTaskDO task = validateTaskNotFinished(id);
         // 2. 鏍¢獙浠诲姟涓嬫棤鎶ュ伐璁板綍锛岄伩鍏嶅垹闄ゅ悗鎶ュ伐杩芥函鏂摼
         if (feedbackMapper.selectCountByTaskId(id) > 0) {
             throw exception(PRO_TASK_HAS_FEEDBACK);
         }
-        // 3. 鍒犻櫎
+        // 3. 鍒犻櫎浠诲姟涓昏〃
         taskMapper.deleteById(id);
+        // 4. 鍒犻櫎鍏宠仈鐨勪汉鍛�
+        taskAssigneeMapper.deleteByTaskId(id);
+        synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(task.getWorkOrderId()), task.getRouteId());
     }
 
     @Override
@@ -259,6 +390,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
@@ -288,7 +427,9 @@
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void finishTaskByOrderId(Long workOrderId) {
+        equipmentService.lock();
         // 1. 璇诲彇宸ュ崟涓嬫墍鏈変换鍔★紝杩囨护鎺夊凡瀹屾垚/宸插彇娑堢殑
         List<MesProTaskDO> tasks = taskMapper.selectListByWorkOrderId(workOrderId);
         LocalDateTime now = LocalDateTime.now();
@@ -300,10 +441,13 @@
         if (CollUtil.isNotEmpty(updateList)) {
             taskMapper.updateBatch(updateList);
         }
+        equipmentService.afterTaskStateChanged(tasks);
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void cancelTaskByOrderId(Long workOrderId) {
+        equipmentService.lock();
         // 1. 璇诲彇宸ュ崟涓嬫墍鏈変换鍔★紝杩囨护鎺夊凡瀹屾垚/宸插彇娑堢殑
         List<MesProTaskDO> tasks = taskMapper.selectListByWorkOrderId(workOrderId);
         LocalDateTime now = LocalDateTime.now();
@@ -315,6 +459,7 @@
         if (CollUtil.isNotEmpty(updateList)) {
             taskMapper.updateBatch(updateList);
         }
+        equipmentService.afterTaskStateChanged(tasks);
     }
 
     @Override
@@ -326,6 +471,15 @@
         validateTaskExists(id);
         // 鏇存柊鏁伴噺
         taskMapper.updateProducedQuantity(id, incrProducedQuantity, incrQualifyQuantity, incrUnqualifyQuantity);
+        MesProTaskDO task = taskMapper.selectById(id);
+        if (task != null) {
+            LocalDateTime now = LocalDateTime.now();
+            MesProTaskDO timeUpdate = new MesProTaskDO().setId(id).setLastFeedbackTime(now);
+            if (task.getFirstFeedbackTime() == null) {
+                timeUpdate.setFirstFeedbackTime(now);
+            }
+            taskMapper.updateById(timeUpdate);
+        }
     }
 
     @Override
@@ -391,6 +545,7 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public List<Long> autoGenerateTasks(MesProTaskAutoGenerateReqVO reqVO) {
+        scheduleGuard.lock();
         // 1. 鏍¢獙宸ュ崟宸茬‘璁�
         MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
         // 2. 纭畾宸ヨ壓璺嚎锛氫紭鍏堣姹傛寚瀹� 鈫� 宸ュ崟鍏宠仈 鈫� 浜у搧鍏宠仈
@@ -419,9 +574,13 @@
                     .setName(buildTaskName(item, workOrder.getQuantity()))
                     .setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRO_TASK_CODE.getCode()))
                     .setStatus(MesProTaskStatusEnum.PREPARE.getStatus())
+                    .setParamTemplateId(rp.getParamTemplateId())
                     .setClientId(workOrder.getClientId())
                     .setColorCode(rp.getColorCode());
+            scheduleGuard.validate(workOrder, task, null);
             taskMapper.insert(task);
+            // 鍙傛暟蹇収锛氫紭鍏堜娇鐢ㄦā鏉匡紱鏃犳ā鏉挎椂鍥為��鍒板伐搴忎富鏁版嵁缁戝畾鐨勫伐鑹哄弬鏁�
+            createTaskParamRecordWithFallback(task);
             taskIds.add(task.getId());
         }
         // 7. 鏇存柊宸ュ崟宸叉帓浜ф暟閲�
@@ -429,4 +588,59 @@
         return taskIds;
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void pauseTask(MesProTaskPauseReqVO pauseReqVO) {
+        equipmentService.lock();
+        // 1. 鏍¢獙浠诲姟瀛樺湪
+        MesProTaskDO task = validateTaskExists(pauseReqVO.getId());
+        // 2. 鏍¢獙鐘舵�佹槸鍚﹀厑璁稿仠宸ワ紙濡傚繀椤绘槸 鐢熶骇涓� 鎴� 閮ㄥ垎瀹屾垚锛�
+        if (!ObjectUtils.equalsAny(task.getStatus(), MesProTaskStatusEnum.PREPARE.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);
+        equipmentService.afterTaskStateChanged(List.of(task));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void resumeTask(MesProTaskResumeReqVO resumeReqVO) {
+        equipmentService.lock();
+        // 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.PREPARE.getStatus();
+        taskMapper.updateById(new MesProTaskDO().setId(task.getId()).setStatus(resumeStatus));
+        equipmentService.validateResume(task);
+        equipmentService.afterTaskStateChanged(List.of(task));
+
+        // 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