From ac2e2b5af3726ee64dc37c3ebf26989a0346f22a Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期六, 07 十一月 2026 19:16:05 +0800
Subject: [PATCH] feat(feedback): 优化报工审批流程并增加报工人校验
---
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java | 153 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 146 insertions(+), 7 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 018b633..c2bba44 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
@@ -2,6 +2,10 @@
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;
@@ -11,6 +15,7 @@
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;
@@ -25,6 +30,7 @@
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;
@@ -51,6 +57,7 @@
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;
@@ -64,6 +71,8 @@
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 瀹炵幇绫�
@@ -73,9 +82,15 @@
@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;
@@ -114,12 +129,21 @@
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());
- workstationService.validateWorkstationExists(createReqVO.getWorkstationId());
+ 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);
@@ -134,23 +158,35 @@
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) {
- // 1.1 鏍¢獙瀛樺湪
- MesProTaskDO task = validateTaskExists(updateReqVO.getId());
+ 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());
@@ -173,10 +209,76 @@
}
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));
}
/**
@@ -190,6 +292,38 @@
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(","));
}
/**
@@ -279,15 +413,17 @@
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public void deleteTask(Long id) {
- // 1. 鏍¢獙瀛樺湪涓旈潪缁堟��
- validateTaskNotFinished(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
@@ -311,8 +447,8 @@
}
@Override
- public Map<Long, Long> getPendingFeedbackCountByWorkstation() {
- List<Map<String, Object>> rows = taskMapper.selectPendingFeedbackCountByWorkstation();
+ 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");
@@ -462,6 +598,7 @@
@Override
@Transactional(rollbackFor = Exception.class)
public List<Long> autoGenerateTasks(MesProTaskAutoGenerateReqVO reqVO) {
+ scheduleGuard.lock();
// 1. 鏍¢獙宸ュ崟宸茬‘璁�
MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
// 2. 纭畾宸ヨ壓璺嚎锛氫紭鍏堣姹傛寚瀹� 鈫� 宸ュ崟鍏宠仈 鈫� 浜у搧鍏宠仈
@@ -492,6 +629,8 @@
.setStatus(MesProTaskStatusEnum.PREPARE.getStatus())
.setClientId(workOrder.getClientId())
.setColorCode(rp.getColorCode());
+ scheduleGuard.validate(workOrder, task, null);
+ freezeTaskParamConfig(task);
taskMapper.insert(task);
taskIds.add(task.getId());
}
--
Gitblit v1.9.3