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 | 81 +++++++++++++++++++++++++++++++++++++---
1 files changed, 75 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 71194e3..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
@@ -76,6 +76,15 @@
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
@@ -124,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());
@@ -153,6 +167,7 @@
if (task.getClientId() == null && workOrder.getClientId() != null) {
task.setClientId(workOrder.getClientId());
}
+ scheduleGuard.validate(workOrder, task, null);
// 2.2 鎻掑叆
taskMapper.insert(task);
// 2.2.1 鍙傛暟蹇収锛氫紭鍏堜娇鐢ㄦā鏉匡紱鏃犳ā鏉挎椂鍥為��鍒板伐搴忎富鏁版嵁缁戝畾鐨勫伐鑹哄弬鏁�
@@ -160,6 +175,7 @@
// 2.3 淇濆瓨鎸囧畾鎶ュ伐浜哄憳
updateTaskAssignees(task.getId(), createReqVO.getAssigneeUserIds());
+ synchronizeScheduledQuantity(workOrder, task.getRouteId());
return task.getId();
}
@@ -180,9 +196,11 @@
}
@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());
@@ -204,6 +222,19 @@
}
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));
@@ -211,6 +242,26 @@
// 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) {
@@ -302,9 +353,11 @@
}
@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);
@@ -313,6 +366,7 @@
taskMapper.deleteById(id);
// 4. 鍒犻櫎鍏宠仈鐨勪汉鍛�
taskAssigneeMapper.deleteByTaskId(id);
+ synchronizeScheduledQuantity(workOrderService.validateWorkOrderExists(task.getWorkOrderId()), task.getRouteId());
}
@Override
@@ -373,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();
@@ -385,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();
@@ -400,6 +459,7 @@
if (CollUtil.isNotEmpty(updateList)) {
taskMapper.updateBatch(updateList);
}
+ equipmentService.afterTaskStateChanged(tasks);
}
@Override
@@ -485,6 +545,7 @@
@Override
@Transactional(rollbackFor = Exception.class)
public List<Long> autoGenerateTasks(MesProTaskAutoGenerateReqVO reqVO) {
+ scheduleGuard.lock();
// 1. 鏍¢獙宸ュ崟宸茬‘璁�
MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
// 2. 纭畾宸ヨ壓璺嚎锛氫紭鍏堣姹傛寚瀹� 鈫� 宸ュ崟鍏宠仈 鈫� 浜у搧鍏宠仈
@@ -516,6 +577,7 @@
.setParamTemplateId(rp.getParamTemplateId())
.setClientId(workOrder.getClientId())
.setColorCode(rp.getColorCode());
+ scheduleGuard.validate(workOrder, task, null);
taskMapper.insert(task);
// 鍙傛暟蹇収锛氫紭鍏堜娇鐢ㄦā鏉匡紱鏃犳ā鏉挎椂鍥為��鍒板伐搴忎富鏁版嵁缁戝畾鐨勫伐鑹哄弬鏁�
createTaskParamRecordWithFallback(task);
@@ -529,10 +591,12 @@
@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())) {
+ if (!ObjectUtils.equalsAny(task.getStatus(), MesProTaskStatusEnum.PREPARE.getStatus(),
+ MesProTaskStatusEnum.PRODUCING.getStatus(), MesProTaskStatusEnum.PARTIAL_FINISHED.getStatus())) {
throw exception(PRO_TASK_CANNOT_PAUSE);
}
@@ -549,11 +613,13 @@
.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. 鏍¢獙鐘舵�佸繀椤绘槸宸插仠宸�
@@ -562,8 +628,11 @@
}
// 3. 鎭㈠浠诲姟鐘舵��
- Integer resumeStatus = MesProTaskStatusEnum.PREPARE.getStatus();
+ 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());
--
Gitblit v1.9.3