package com.ruoyi.approve.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.approve.bean.dto.ApprovalInstanceDto; import com.ruoyi.approve.bean.vo.ApprovalInstanceVo; import com.ruoyi.approve.mapper.ApprovalInstanceMapper; import com.ruoyi.approve.mapper.ApprovalTemplateNodeApproverMapper; import com.ruoyi.approve.mapper.FinReimbursementMapper; import com.ruoyi.approve.pojo.*; import com.ruoyi.approve.service.*; import com.ruoyi.approve.utils.ApproveProcessConfigNodeUtils; import com.ruoyi.basic.enums.ApplicationTypeEnum; import com.ruoyi.basic.enums.RecordTypeEnum; import com.ruoyi.basic.utils.FileUtil; import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper; import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeDeptMapper; import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeUserMapper; import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews; import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept; import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser; import com.ruoyi.common.enums.*; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.OrderUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.framework.security.LoginUser; import com.ruoyi.framework.web.domain.R; import com.ruoyi.procurementrecord.utils.StockUtils; import com.ruoyi.project.system.domain.SysDept; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysDeptMapper; import com.ruoyi.project.system.mapper.SysUserDeptMapper; import com.ruoyi.project.system.mapper.SysUserMapper; import com.ruoyi.project.system.service.ISysNoticeService; import com.ruoyi.purchase.mapper.PurchaseLedgerMapper; import com.ruoyi.purchase.pojo.PurchaseLedger; import com.ruoyi.quality.utils.QualityInspectHelper; import com.ruoyi.sales.mapper.SalesLedgerProductMapper; import com.ruoyi.sales.mapper.SalesQuotationMapper; import com.ruoyi.sales.mapper.ShippingInfoMapper; import com.ruoyi.sales.pojo.SalesLedgerProduct; import com.ruoyi.sales.pojo.SalesQuotation; import com.ruoyi.sales.pojo.ShippingInfo; import com.ruoyi.staff.mapper.HolidayApplicationMapper; import com.ruoyi.staff.pojo.HolidayApplication; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; /** *

* 审批实例服务实现类 *

* * @since 2026-05-18 03:27:46 */ @Service @RequiredArgsConstructor public class ApprovalInstanceServiceImpl extends ServiceImpl implements ApprovalInstanceService { private static final String ENTERPRISE_NEWS_STATUS_PUBLISHED = "PUBLISHED"; private static final String ENTERPRISE_NEWS_STATUS_REJECTED = "REJECTED"; private final ApprovalInstanceMapper approvalInstanceMapper; private final ApproveProcessConfigNodeUtils approveProcessConfigNodeUtils; private final ApprovalInstanceNodeService approvalInstanceNodeService; private final ApprovalTaskService approvalTaskService; private final ApprovalRecordService approvalRecordService; private final ApprovalTemplateNodeService approvalTemplateNodeService; private final FinReimbursementMapper finReimbursementMapper; private final FileUtil fileUtil; private final ISysNoticeService sysNoticeService; private final PurchaseLedgerMapper purchaseLedgerMapper; private final SalesLedgerProductMapper salesLedgerProductMapper; private final StockUtils stockUtils; private final SalesQuotationMapper salesQuotationMapper; private final ShippingInfoMapper shippingInfoMapper; private final QualityInspectHelper qualityInspectHelper; private final EnterpriseNewsScopeUserMapper enterpriseNewsScopeUserMapper; private final SysUserMapper sysUserMapper; private final SysUserDeptMapper sysUserDeptMapper; private final SysDeptMapper sysDeptMapper; private final HolidayApplicationMapper holidayApplicationMapper; private final EnterpriseNewsMapper enterpriseNewsMapper; private final EnterpriseNewsScopeDeptMapper enterpriseNewsScopeDeptMapper; private final ApprovalTemplateNodeApproverMapper approvalTemplateNodeApproverMapper; @Override public R listPage(Page page, ApprovalInstanceDto approvalInstanceDto) { IPage approvalInstanceVoIPage = approvalInstanceMapper.listPage(page, approvalInstanceDto); List records = approvalInstanceVoIPage.getRecords(); if (records == null || records.isEmpty()) { return R.ok(approvalInstanceVoIPage); } records.forEach(vo -> { vo.setBusinessName(TypeEnums.getLabelByValue(vo.getBusinessType())); }); Long currentUserId = SecurityUtils.getUserId(); List instanceIds = records.stream() .map(ApprovalInstanceVo::getId) .filter(id -> id != null) .distinct() .collect(Collectors.toList()); if (!instanceIds.isEmpty()) { Map> recordMap = approvalRecordService.list( Wrappers.lambdaQuery() .in(ApprovalRecord::getInstanceId, instanceIds) .eq(ApprovalRecord::getDeleted, 0) ).stream().collect(Collectors.groupingBy(ApprovalRecord::getInstanceId)); Map> taskMap = approvalTaskService.list( Wrappers.lambdaQuery() .in(ApprovalTask::getInstanceId, instanceIds) .eq(ApprovalTask::getDeleted, 0) ).stream().collect(Collectors.groupingBy(ApprovalTask::getInstanceId)); for (ApprovalInstanceVo vo : records) { vo.setIsApprove(approveProcessConfigNodeUtils.isCurrentApprover(vo.getId(), currentUserId)); vo.setRecords(recordMap.getOrDefault(vo.getId(), new ArrayList<>())); vo.setTasks(taskMap.getOrDefault(vo.getId(), new ArrayList<>())); vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.APPROVAL_INSTANCE, vo.getId())); } } return R.ok(approvalInstanceVoIPage); } @Override @Transactional(rollbackFor = Exception.class) public Boolean add(ApprovalInstanceDto approvalInstanceDto) { String instanceNo = OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no"); approvalInstanceDto.setInstanceNo(instanceNo); approvalInstanceDto.setStatus("PENDING"); approvalInstanceDto.setCurrentLevel(1); boolean saved = this.save(approvalInstanceDto); if (!saved) { return false; } approveProcessConfigNodeUtils.createCurrentNodeAndTasks(approvalInstanceDto); fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, approvalInstanceDto.getId(), approvalInstanceDto.getStorageBlobDTOs()); sendApproveNotice(approvalInstanceDto, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId())); return true; } @Override @Transactional(rollbackFor = Exception.class) public Boolean update(ApprovalInstanceDto approvalInstanceDto) { if (approvalInstanceDto == null || approvalInstanceDto.getId() == null) { return false; } // 判断是否有正在进行的审批任务,有则不允许修改 long pendingTaskCount = approvalTaskService.count( Wrappers.lambdaQuery() .eq(ApprovalTask::getInstanceId, approvalInstanceDto.getId()) .eq(ApprovalTask::getTaskStatus, "PENDING") .eq(ApprovalTask::getDeleted, 0) ); if (pendingTaskCount > 0) { throw new ServiceException("该审批单有正在进行的审批任务,不允许修改"); } boolean updated = this.updateById(approvalInstanceDto); if (!updated) { return false; } fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, approvalInstanceDto.getId(), approvalInstanceDto.getStorageBlobDTOs()); return true; } @Override @Transactional(rollbackFor = Exception.class) public Boolean delete(List ids) { if (ids == null || ids.isEmpty()) { return false; } fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, ids); int instanceRows = approvalInstanceMapper.update( null, Wrappers.lambdaUpdate() .in(ApprovalInstance::getId, ids) .eq(ApprovalInstance::getDeleted, 0) .set(ApprovalInstance::getDeleted, (byte) 1) ); LambdaUpdateWrapper nodeUpdateWrapper = Wrappers.lambdaUpdate(); nodeUpdateWrapper.in(ApprovalInstanceNode::getInstanceId, ids) .eq(ApprovalInstanceNode::getDeleted, 0) .set(ApprovalInstanceNode::getDeleted, (byte) 1); approvalInstanceNodeService.update(nodeUpdateWrapper); LambdaUpdateWrapper taskUpdateWrapper = Wrappers.lambdaUpdate(); taskUpdateWrapper.in(ApprovalTask::getInstanceId, ids) .eq(ApprovalTask::getDeleted, 0) .set(ApprovalTask::getDeleted, (byte) 1); approvalTaskService.update(taskUpdateWrapper); LambdaUpdateWrapper recordUpdateWrapper = Wrappers.lambdaUpdate(); recordUpdateWrapper.in(ApprovalRecord::getInstanceId, ids) .eq(ApprovalRecord::getDeleted, 0) .set(ApprovalRecord::getDeleted, (byte) 1); approvalRecordService.update(recordUpdateWrapper); return instanceRows > 0; } @Override @Transactional(rollbackFor = Exception.class) public R approve(ApprovalInstanceDto approvalInstanceDto) { if (approvalInstanceDto == null || approvalInstanceDto.getId() == null) { return R.fail("审批实例 ID 不能为空"); } String approveAction = normalizeApproveAction(approvalInstanceDto.getApproveAction()); if (approveAction == null) { return R.fail("审批动作只支持 APPROVED 或 REJECTED"); } ApprovalInstance instance = getPendingApprovalInstance(approvalInstanceDto.getId()); if (instance == null) { return R.fail("审批实例不存在"); } ApprovalInstanceNode currentNode = approveProcessConfigNodeUtils.getCurrentNode(instance.getId()); if (currentNode == null) { return R.fail("当前没有待处理的审批节点"); } Long currentUserId = SecurityUtils.getUserId(); ApprovalTask currentTask = approveProcessConfigNodeUtils.getCurrentUserTask(instance.getId(), currentUserId); if (currentTask == null) { return R.fail("当前用户没有可审批任务"); } LoginUser loginUser = SecurityUtils.getLoginUser(); String operatorName = loginUser.getUser() != null ? loginUser.getUser().getNickName() : SecurityUtils.getUsername(); LocalDateTime now = LocalDateTime.now(); if (!updateCurrentTask(approvalInstanceDto, approveAction, currentTask, now)) { return R.fail("当前任务已被处理,请刷新后重试"); } saveApprovalRecord( instance.getId(), currentNode.getId(), currentTask.getId(), currentUserId, operatorName, approveAction, approvalInstanceDto.getApproveComment() ); //审批拒绝的处理 if ("REJECTED".equals(approveAction)) { return rejectCurrentNode(instance, currentNode, now); } if (!approveProcessConfigNodeUtils.canProceedToNextLevel(instance.getId(), currentNode.getApproveType())) { return R.ok("审批成功,等待其他审批人处理"); } return approveAndMoveNext(instance, currentNode, approvalInstanceDto, now); } private String normalizeApproveAction(String approveAction) { if (!StringUtils.hasText(approveAction)) { return null; } String normalizedAction = approveAction.trim().toUpperCase(Locale.ROOT); return "APPROVED".equals(normalizedAction) || "REJECTED".equals(normalizedAction) ? normalizedAction : null; } private ApprovalInstance getPendingApprovalInstance(Long instanceId) { return this.getOne( new LambdaQueryWrapper() .eq(ApprovalInstance::getId, instanceId) .eq(ApprovalInstance::getDeleted, 0) .last("LIMIT 1") ); } private boolean updateCurrentTask(ApprovalInstanceDto approvalInstanceDto, String approveAction, ApprovalTask currentTask, LocalDateTime now) { // 仅允许待审批任务被成功处理一次,避免并发下重复审批成功。 return approvalTaskService.update( Wrappers.lambdaUpdate() .eq(ApprovalTask::getId, currentTask.getId()) .eq(ApprovalTask::getTaskStatus, "PENDING") .eq(ApprovalTask::getDeleted, 0) .set(ApprovalTask::getTaskStatus, approveAction) .set(ApprovalTask::getComment, approvalInstanceDto.getApproveComment()) .set(ApprovalTask::getApproveTime, now) .set(ApprovalTask::getIsRead, (byte) 1) ); } private R rejectCurrentNode(ApprovalInstance instance, ApprovalInstanceNode currentNode, LocalDateTime now) { if (!updateCurrentNodeStatus(currentNode.getId(), "REJECTED", now)) { return R.ok("当前节点已处理完成"); } closePendingTasks(instance.getId(), currentNode.getId()); instance.setStatus("REJECTED"); instance.setFinishTime(now); this.updateById(instance); // 驳回对应的企业新闻, 差旅报销 if (instance.getBusinessType().equals(TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode())) { enterpriseNewsMapper.update( new LambdaUpdateWrapper() .eq(EnterpriseNews::getId, instance.getBusinessId()) .set(EnterpriseNews::getStatus, "REJECTED") ); }else if (instance.getBusinessType().equals(TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode())||instance.getBusinessType().equals(TypeEnums.EXPENSE_APPROVAL.getCode())) { finReimbursementMapper.update( new LambdaUpdateWrapper() .eq(FinReimbursement::getId, instance.getBusinessId()) .set(FinReimbursement::getBillStatus, "REJECTED") ); } return R.ok("审批已驳回"); } private R approveAndMoveNext(ApprovalInstance instance, ApprovalInstanceNode currentNode, ApprovalInstanceDto approvalInstanceDto, LocalDateTime now) { if (!updateCurrentNodeStatus(currentNode.getId(), "APPROVED", now)) { return R.ok("当前节点已处理完成"); } closePendingTasks(instance.getId(), currentNode.getId()); int nextLevel = currentNode.getLevelNo() + 1; ApprovalInstanceNode nextInstanceNode = approvalInstanceNodeService.getOne( new LambdaQueryWrapper() .eq(ApprovalInstanceNode::getInstanceId, instance.getId()) .eq(ApprovalInstanceNode::getLevelNo, nextLevel) .eq(ApprovalInstanceNode::getDeleted, 0) .orderByAsc(ApprovalInstanceNode::getId) .last("LIMIT 1") ); if (nextInstanceNode != null) { if (!activateNextInstanceNode(nextInstanceNode.getId(), now)) { return R.ok("下一审批节点已被激活,请刷新后重试"); } instance.setCurrentLevel(nextLevel); instance.setStatus("PENDING"); this.updateById(instance); List nextTasks = approvalTaskService.list( Wrappers.lambdaQuery() .eq(ApprovalTask::getInstanceId, instance.getId()) .eq(ApprovalTask::getNodeId, nextInstanceNode.getId()) .eq(ApprovalTask::getTaskStatus, "PENDING") .eq(ApprovalTask::getDeleted, 0) ); sendApproveNotice(instance, nextTasks); return R.ok("审批成功,已流转到下一节点"); } ApprovalTemplateNode nextTemplateNode = approvalTemplateNodeService.getOne( new LambdaQueryWrapper() .eq(ApprovalTemplateNode::getTemplateId, instance.getTemplateId()) .eq(ApprovalTemplateNode::getLevelNo, nextLevel) .orderByAsc(ApprovalTemplateNode::getId) .last("LIMIT 1") ); if (nextTemplateNode == null) { instance.setStatus("APPROVED"); instance.setFinishTime(now); this.updateById(instance); handleBusinessAfterApprovalFinished(instance); return R.ok("审批已完成"); } instance.setCurrentLevel(nextLevel); instance.setStatus("PENDING"); this.updateById(instance); approveProcessConfigNodeUtils.createCurrentNodeAndTasks(instance, false); sendApproveNotice(instance, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId())); return R.ok("审批成功,已流转到下一节点"); } private boolean activateNextInstanceNode(Long nodeId, LocalDateTime now) { return approvalInstanceNodeService.update( Wrappers.lambdaUpdate() .eq(ApprovalInstanceNode::getId, nodeId) .eq(ApprovalInstanceNode::getStatus, "WAITING") .eq(ApprovalInstanceNode::getDeleted, 0) .set(ApprovalInstanceNode::getStatus, "PENDING") .set(ApprovalInstanceNode::getStartTime, now) ); } private boolean updateCurrentNodeStatus(Long nodeId, String targetStatus, LocalDateTime now) { // 仅允许一个请求将当前节点从待处理推进到目标状态,避免重复流转。 return approvalInstanceNodeService.update( Wrappers.lambdaUpdate() .eq(ApprovalInstanceNode::getId, nodeId) .eq(ApprovalInstanceNode::getStatus, "PENDING") .eq(ApprovalInstanceNode::getDeleted, 0) .set(ApprovalInstanceNode::getStatus, targetStatus) .set(ApprovalInstanceNode::getFinishTime, now) ); } private void handleBusinessAfterApprovalFinished(ApprovalInstance instance) { String status = instance.getStatus(); Long businessType = instance.getBusinessType(); if (TypeEnums.PURCHASE_APPROVAL.getCode().equals(businessType)) { handlePurchaseApprovalFinished(instance, status); return; } if (TypeEnums.QUOTATION_APPROVAL.getCode().equals(businessType)) { handleSalesQuotationApprovalFinished(instance, status); return; } if (TypeEnums.SHIPPING_APPROVAL.getCode().equals(businessType)) { handleShippingApprovalFinished(instance, status); return; } if (TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode().equals(businessType) || TypeEnums.EXPENSE_APPROVAL.getCode().equals(businessType)) { handleReimbursementApprovalFinished(instance, status); return; } // 驳回对应的企业新闻、加班申请、请假申请可以重新再提交 if (TypeEnums.LEAVE_APPROVAL.getCode().equals(businessType) || TypeEnums.OVERTIME_APPROVAL.getCode().equals(businessType)) { handleHolidayApplicationApprovalFinished(instance, status); return; } if (TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode().equals(businessType)) { handleNewsApprovalFinished(instance, status); } } private void handleReimbursementApprovalFinished(ApprovalInstance instance, String status) { if (instance == null || instance.getBusinessId() == null) { return; } FinReimbursement reimbursement = new FinReimbursement(); reimbursement.setId(instance.getBusinessId()); if ("APPROVED".equals(status)) { reimbursement.setBillStatus("APPROVED"); reimbursement.setApprovedTime(instance.getFinishTime()); } else if ("REJECTED".equals(status)) { reimbursement.setBillStatus("REJECTED"); } else if ("PENDING".equals(status)) { reimbursement.setBillStatus("IN_APPROVAL"); } else { return; } finReimbursementMapper.updateById(reimbursement); } private void handleNewsApprovalFinished(ApprovalInstance instance, String status) { if (instance == null || instance.getBusinessId() == null) { return; } EnterpriseNews enterpriseNews = new EnterpriseNews(); enterpriseNews.setId(instance.getBusinessId()); if ("APPROVED".equals(status)) { enterpriseNews.setStatus(ENTERPRISE_NEWS_STATUS_PUBLISHED); enterpriseNewsMapper.updateById(enterpriseNews); sendEnterpriseNewsNotice(instance.getBusinessId()); return; } if ("REJECTED".equals(status)) { enterpriseNews.setStatus(ENTERPRISE_NEWS_STATUS_REJECTED); enterpriseNewsMapper.updateById(enterpriseNews); } } private void handleHolidayApplicationApprovalFinished(ApprovalInstance instance, String status) { if (instance == null || instance.getBusinessId() == null) { return; } HolidayApplication holidayApplication = new HolidayApplication(); holidayApplication.setId(instance.getBusinessId()); if ("APPROVED".equals(status)) { holidayApplication.setStatus("APPROVED"); } else if ("REJECTED".equals(status)) { holidayApplication.setStatus("REJECTED"); } else if ("PENDING".equals(status)) { holidayApplication.setStatus("PENDING"); } else { return; } holidayApplicationMapper.updateById(holidayApplication); } private void handlePurchaseApprovalFinished(ApprovalInstance instance, String status) { PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne( new LambdaQueryWrapper() .eq(PurchaseLedger::getId, instance.getBusinessId()) .last("limit 1") ); if (purchaseLedger == null) { return; } if ("APPROVED".equals(status)) { purchaseLedger.setApprovalStatus(ApprovalStatusEnum.APPROVED.getCode()); List salesLedgerProducts = salesLedgerProductMapper.selectList( new QueryWrapper().lambda() .eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId()) .eq(SalesLedgerProduct::getType, 2) ); for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) { if (salesLedgerProduct.getIsChecked()) { qualityInspectHelper.addQualityInspect(purchaseLedger, salesLedgerProduct); } else { stockUtils.addStockWithBatchNo( salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber() + "-" + salesLedgerProduct.getId() ); } } } else if ("REJECTED".equals(status)) { purchaseLedger.setApprovalStatus(ApprovalStatusEnum.REJECTED.getCode()); } else if ("PENDING".equals(status)) { purchaseLedger.setApprovalStatus(ApprovalStatusEnum.IN_PROGRESS.getCode()); } purchaseLedgerMapper.updateById(purchaseLedger); } private void handleSalesQuotationApprovalFinished(ApprovalInstance instance, String status) { SalesQuotation salesQuote = salesQuotationMapper.selectOne( new LambdaQueryWrapper() .eq(SalesQuotation::getId, instance.getBusinessId()) .last("limit 1") ); if (salesQuote == null) { return; } if ("APPROVED".equals(status)) { salesQuote.setStatus(SalesQuotationStatusEnum.APPROVED.getCode()); } else if ("REJECTED".equals(status)) { salesQuote.setStatus(SalesQuotationStatusEnum.REJECTED.getCode()); } else if ("PENDING".equals(status)) { salesQuote.setStatus(SalesQuotationStatusEnum.IN_PROGRESS.getCode()); } salesQuotationMapper.updateById(salesQuote); } private void handleShippingApprovalFinished(ApprovalInstance instance, String status) { ShippingInfo shippingInfo = shippingInfoMapper.selectOne( new LambdaQueryWrapper() .eq(ShippingInfo::getId, instance.getTitle()) .orderByDesc(ShippingInfo::getCreateTime) .last("limit 1") ); if (shippingInfo == null) { return; } if ("APPROVED".equals(status)) { shippingInfo.setStatus(ShippingStatusEnum.APPROVED.getCode()); shippingInfo.setShippingDate(new Date()); stockUtils.shipmentStatus(StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shippingInfo.getId()); } else if ("REJECTED".equals(status)) { stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode()); shippingInfo.setStatus(ShippingStatusEnum.REJECTED.getCode()); } else if ("PENDING".equals(status)) { shippingInfo.setStatus(ShippingStatusEnum.IN_PROGRESS.getCode()); } shippingInfoMapper.updateById(shippingInfo); } private List createNodeAndTasks(ApprovalInstance instance, ApprovalTemplateNode templateNode) { List approvers = approvalTemplateNodeApproverMapper.selectList( new LambdaQueryWrapper() .eq(ApprovalTemplateNodeApprover::getTemplateId, instance.getTemplateId()) .eq(ApprovalTemplateNodeApprover::getNodeId, templateNode.getId()) .eq(ApprovalTemplateNodeApprover::getDeleted, 0L) .orderByAsc(ApprovalTemplateNodeApprover::getSortNo) ); if (approvers == null || approvers.isEmpty()) { throw new RuntimeException("下一审批节点未配置审批人"); } ApprovalInstanceNode instanceNode = new ApprovalInstanceNode(); instanceNode.setInstanceId(instance.getId()); instanceNode.setLevelNo(templateNode.getLevelNo()); instanceNode.setApproveType(templateNode.getApproveType()); instanceNode.setStatus("PENDING"); instanceNode.setStartTime(LocalDateTime.now()); instanceNode.setDeleted((byte) 0); approvalInstanceNodeService.save(instanceNode); List taskList = new ArrayList<>(approvers.size()); for (ApprovalTemplateNodeApprover approver : approvers) { ApprovalTask task = new ApprovalTask(); task.setInstanceId(instance.getId()); task.setNodeId(instanceNode.getId()); task.setLevelNo(instanceNode.getLevelNo()); task.setApproverId(approver.getApproverId()); task.setApproverName(approver.getApproverName()); task.setTaskStatus("PENDING"); task.setIsRead((byte) 0); task.setDeleted((byte) 0); taskList.add(task); } approvalTaskService.saveBatch(taskList); return taskList; } private void sendApproveNotice(ApprovalInstance instance, List tasks) { if (instance == null || tasks == null || tasks.isEmpty()) { return; } List approverIds = tasks.stream() .map(ApprovalTask::getApproverId) .filter(id -> id != null && id > 0) .distinct() .collect(Collectors.toList()); if (approverIds.isEmpty()) { return; } String title = StringUtils.hasText(instance.getTemplateName()) ? instance.getTemplateName() : "审批提醒"; String message = "审批单号 " + instance.getInstanceNo() + " 需要您审批"; String jumpPath = "/officeProcessAutomation/ApproveManage/approve-list?id=" + instance.getId(); sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath); } private void sendEnterpriseNewsNotice(Long newsId) { EnterpriseNews enterpriseNews = enterpriseNewsMapper.selectById(newsId); if (enterpriseNews == null) { return; } List userIds = getEnterpriseNewsNoticeUserIds(enterpriseNews); if (userIds == null || userIds.isEmpty()) { return; } String title = "企业新闻"; String message = "您有新的企业新闻《" + enterpriseNews.getTitle() + "》请及时查阅"; String jumpPath = "/officeProcessAutomation/EnterpriseNews?id=" + newsId; sysNoticeService.simpleNoticeByUser(title, message, userIds, jumpPath); } private List getEnterpriseNewsNoticeUserIds(EnterpriseNews enterpriseNews) { if (enterpriseNews == null || !org.springframework.util.StringUtils.hasText(enterpriseNews.getReadScope())) { return Collections.emptyList(); } String readScope = enterpriseNews.getReadScope().trim(); if ("all".equals(readScope)) { return sysUserMapper.selectList(new LambdaQueryWrapper() .select(SysUser::getUserId) .eq(SysUser::getDelFlag, "0")) .stream() .map(SysUser::getUserId) .filter(id -> id != null && id > 0) .distinct() .collect(Collectors.toList()); } if ("dept".equals(readScope)) { List deptIds = enterpriseNewsScopeDeptMapper.selectList( new LambdaQueryWrapper() .eq(EnterpriseNewsScopeDept::getNewsId, enterpriseNews.getId())) .stream() .map(EnterpriseNewsScopeDept::getDeptId) .filter(id -> id != null && id > 0) .distinct() .collect(Collectors.toList()); if (deptIds.isEmpty()) { return Collections.emptyList(); } return sysUserDeptMapper.selectDistinctUserIdsByDeptIds(collectDeptIdsWithChildren(deptIds)); } if ("custom".equals(readScope)) { return enterpriseNewsScopeUserMapper.selectList( new LambdaQueryWrapper() .eq(EnterpriseNewsScopeUser::getNewsId, enterpriseNews.getId())) .stream() .map(EnterpriseNewsScopeUser::getUserId) .filter(id -> id != null && id > 0) .distinct() .collect(Collectors.toList()); } return Collections.emptyList(); } private List collectDeptIdsWithChildren(List deptIds) { Set allDeptIds = new LinkedHashSet<>(); for (Long deptId : deptIds) { if (deptId == null) { continue; } allDeptIds.add(deptId); List children = sysDeptMapper.selectChildrenDeptById(deptId); if (children != null && !children.isEmpty()) { for (SysDept child : children) { if (child != null && child.getDeptId() != null) { allDeptIds.add(child.getDeptId()); } } } } return new ArrayList<>(allDeptIds); } private void closePendingTasks(Long instanceId, Long nodeId) { LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); updateWrapper.eq(ApprovalTask::getInstanceId, instanceId) .eq(ApprovalTask::getNodeId, nodeId) .eq(ApprovalTask::getTaskStatus, "PENDING") .eq(ApprovalTask::getDeleted, 0) .set(ApprovalTask::getDeleted, (byte) 1); approvalTaskService.update(updateWrapper); } private void saveApprovalRecord(Long instanceId, Long nodeId, Long taskId, Long operatorId, String operatorName, String action, String comment) { ApprovalRecord record = new ApprovalRecord(); record.setInstanceId(instanceId); record.setNodeId(nodeId); record.setTaskId(taskId); record.setOperatorId(operatorId); record.setOperatorName(operatorName); record.setAction(action); record.setComment(comment); record.setDeleted((byte) 0); approvalRecordService.save(record); } }