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.UpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.approve.bean.vo.ApproveProcessConfigNodeVo; import com.ruoyi.approve.mapper.ApproveNodeMapper; import com.ruoyi.approve.mapper.ApproveProcessMapper; import com.ruoyi.approve.pojo.ApproveNode; import com.ruoyi.approve.pojo.ApproveProcess; import com.ruoyi.approve.service.IApproveNodeService; import com.ruoyi.collaborativeApproval.mapper.SealApplicationManagementMapper; import com.ruoyi.collaborativeApproval.pojo.SealApplicationManagement; import com.ruoyi.common.enums.FileNameType; import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.device.mapper.DeviceRepairMapper; import com.ruoyi.device.pojo.DeviceRepair; import com.ruoyi.other.service.impl.TempFileServiceImpl; import com.ruoyi.procurementrecord.utils.StockUtils; import com.ruoyi.project.system.domain.SysUser; 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.mapper.QualityInspectMapper; import com.ruoyi.quality.mapper.QualityInspectParamMapper; import com.ruoyi.quality.mapper.QualityTestStandardMapper; import com.ruoyi.quality.mapper.QualityTestStandardParamMapper; import com.ruoyi.quality.pojo.QualityInspect; import com.ruoyi.quality.pojo.QualityInspectParam; import com.ruoyi.quality.pojo.QualityTestStandard; import com.ruoyi.quality.pojo.QualityTestStandardParam; import com.ruoyi.sales.mapper.CommonFileMapper; import com.ruoyi.sales.mapper.SalesLedgerProductMapper; import com.ruoyi.sales.mapper.SalesQuotationMapper; import com.ruoyi.sales.mapper.ShippingInfoMapper; import com.ruoyi.sales.pojo.CommonFile; import com.ruoyi.sales.pojo.SalesLedgerProduct; import com.ruoyi.sales.pojo.SalesQuotation; import com.ruoyi.sales.pojo.ShippingInfo; import com.ruoyi.sales.service.impl.CommonFileServiceImpl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.io.IOException; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class ApproveNodeServiceImpl extends ServiceImpl implements IApproveNodeService { private final ApproveNodeMapper approveNodeMapper; private final ApproveProcessMapper approveProcessMapper; private final SysUserMapper sysUserMapper; private final TempFileServiceImpl tempFileService; private final ISysNoticeService sysNoticeService; private final CommonFileMapper fileMapper; private final DeviceRepairMapper deviceRepairMapper; private final PurchaseLedgerMapper purchaseLedgerMapper; private final SalesQuotationMapper salesQuotationMapper; private final ShippingInfoMapper shippingInfoMapper; private final CommonFileServiceImpl commonFileService; private final StockUtils stockUtils; private final SalesLedgerProductMapper salesLedgerProductMapper; private final QualityInspectMapper qualityInspectMapper; private final QualityTestStandardMapper qualityTestStandardMapper; private final QualityTestStandardParamMapper qualityTestStandardParamMapper; private final QualityInspectParamMapper qualityInspectParamMapper; private final SealApplicationManagementMapper sealApplicationManagementMapper; public ApproveProcess getApproveById(String id) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(ApproveProcess::getApproveId, id); queryWrapper.eq(ApproveProcess::getApproveDelete, 0); // queryWrapper.eq(ApproveProcess::getTenantId, SecurityUtils.getLoginUser().getTenantId()); queryWrapper.last("limit 1"); ApproveProcess one = approveProcessMapper.selectOne(queryWrapper); return one; } @Override public void initApproveNodes(String approveUserIds, String approveID, Long tenantId) { if (approveUserIds == null || approveUserIds.isEmpty()) { return; } List config = new ArrayList<>(); String[] ids = approveUserIds.split(","); for (int i = 0; i < ids.length; i++) { if (ids[i] == null || ids[i].trim().isEmpty()) { continue; } ApproveProcessConfigNodeVo vo = new ApproveProcessConfigNodeVo(); vo.setNodeOrder(i + 1); vo.setApproverId(Long.parseLong(ids[i].trim())); vo.setApproveMode("or"); config.add(vo); } initApproveNodes(config, approveID, tenantId); } @Override public void initApproveNodes(List config, String approveID, Long tenantId) { Long userId = SecurityUtils.getLoginUser().getUser().getUserId(); // 按节点顺序分组,保证同一节点的多审批人写入相同 approveNodeOrder Map> groups = new LinkedHashMap<>(); for (ApproveProcessConfigNodeVo vo : config) { if (vo.getApproverId() == null) { continue; } int order = vo.getNodeOrder() == null ? groups.size() + 1 : vo.getNodeOrder(); groups.computeIfAbsent(order, k -> new ArrayList<>()).add(vo); } for (Map.Entry> entry : groups.entrySet()) { Integer order = entry.getKey(); String mode = entry.getValue().get(0).getApproveMode(); if (mode == null || mode.isEmpty()) { mode = "or"; } List seenIds = new ArrayList<>(); for (ApproveProcessConfigNodeVo vo : entry.getValue()) { Long approverId = vo.getApproverId(); if (seenIds.contains(approverId)) { continue; } seenIds.add(approverId); SysUser sysUser = sysUserMapper.selectUserById(approverId); if (sysUser == null) { continue; } ApproveNode approveNode = new ApproveNode(); approveNode.setApproveProcessId(approveID); approveNode.setApproveNodeOrder(order); approveNode.setApproveMode(mode); approveNode.setApproveNodeUser(sysUser.getNickName()); approveNode.setApproveNodeUserId(sysUser.getUserId()); approveNode.setApproveNodeTime(new Date()); approveNode.setApproveNodeStatus(0); approveNode.setTenantId(tenantId); approveNode.setDeleteFlag(0); approveNode.setCreateUser(userId); approveNode.setUpdateUser(userId); approveNode.setCreateTime(LocalDateTime.now()); approveNode.setUpdateTime(LocalDateTime.now()); approveNodeMapper.insert(approveNode); } } } @Override public List details(String id) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(ApproveNode::getApproveProcessId, id); queryWrapper.eq(ApproveNode::getDeleteFlag, 0); List list = list(queryWrapper); // 按 approveNodeOrder 升序、同节点按 id 升序,保证同节点多审批人顺序稳定 list.sort(Comparator.comparingInt(ApproveNode::getApproveNodeOrder).thenComparingLong(ApproveNode::getId)); bindNodeUrl(list); ApproveProcess approveProcess = getApproveById(id); if (approveProcess == null || approveProcess.getApproveStatus() == 2 || approveProcess.getApproveStatus() == 3 || approveProcess.getApproveStatus() == 4) { // 流程已完成/驳回/重新提交,全部只读 return list; } Integer currentOrder = pendingOrder(id); Long loginUserId = SecurityUtils.getLoginUser().getUser().getUserId(); boolean admin = SecurityUtils.isAdmin(loginUserId); boolean ishenSet = false; for (ApproveNode approveNode : list) { // 管理员不受审批人限制:代审当前节点首个待审记录(整节点代审由 updateApproveNode 完成) if (approveNode.getApproveNodeOrder().equals(currentOrder) && approveNode.getApproveNodeStatus() == 0 && (admin || approveNode.getApproveNodeUserId().equals(loginUserId)) && !ishenSet) { approveNode.setIsShen(true); ishenSet = true; } } return list; } /** * 业务回调:按审批类型更新对应业务单据状态 * status: 1=审核中(会签部分通过), 2=审核完成(同意), 3=审核未通过(拒绝) */ private void applyBusinessStatus(ApproveProcess approveProcess, Integer status) { // 采购审批 if (approveProcess.getApproveType().equals(5)) { PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper() .eq(PurchaseLedger::getPurchaseContractNumber, approveProcess.getApproveReason()) .last("limit 1")); if (purchaseLedger != null) { if (status.equals(2)) { // 同意:生成质检/入库 purchaseLedger.setApprovalStatus(3); List salesLedgerProducts = salesLedgerProductMapper.selectList(new QueryWrapper() .lambda().eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId()).eq(SalesLedgerProduct::getType, 2)); for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) { if (salesLedgerProduct.getIsChecked()) { addQualityInspect(purchaseLedger, salesLedgerProduct); } else { // 直接入库 stockUtils.addStock(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), purchaseLedger.getId(), "-", "-", "-"); } } } else if (status.equals(3)) { // 拒绝 purchaseLedger.setApprovalStatus(4); } else if (status.equals(1)) { // 审核中 purchaseLedger.setApprovalStatus(2); } purchaseLedgerMapper.updateById(purchaseLedger); } } // 销售报价状态修改 if (approveProcess.getApproveType().equals(6)) { SalesQuotation salesQuote = salesQuotationMapper.selectOne(new LambdaQueryWrapper() .eq(SalesQuotation::getQuotationNo, approveProcess.getApproveReason()) .last("limit 1")); if (salesQuote != null) { if (status.equals(2)) { salesQuote.setStatus("通过"); } else if (status.equals(3)) { salesQuote.setStatus("拒绝"); } else if (status.equals(1)) { salesQuote.setStatus("审核中"); } salesQuotationMapper.updateById(salesQuote); } } // 出库审批状态修改 if (approveProcess.getApproveType().equals(7)) { String[] split = approveProcess.getApproveReason().split(":"); if (split.length > 1) { ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper() .eq(ShippingInfo::getShippingNo, split[1]) .orderByDesc(ShippingInfo::getCreateTime) .last("limit 1")); if (shippingInfo != null) { if (status.equals(2)) { shippingInfo.setStatus("审核通过"); } else if (status.equals(3)) { shippingInfo.setStatus("审核拒绝"); } else if (status.equals(1)) { shippingInfo.setStatus("审核中"); } shippingInfoMapper.updateById(shippingInfo); } } } // 用印审批状态修改 if (approveProcess.getApproveType().equals(9)) { SealApplicationManagement seal = sealApplicationManagementMapper.selectOne(new LambdaQueryWrapper() .eq(SealApplicationManagement::getApproveProcessId, approveProcess.getId()) .last("limit 1")); if (seal != null) { if (status.equals(2)) { seal.setStatus("approved"); } else if (status.equals(3)) { seal.setStatus("rejected"); } else if (status.equals(1)) { seal.setStatus("pending"); } sealApplicationManagementMapper.updateById(seal); } } } /** * 当前待审批节点序号:所有未审批节点中最小的 approveNodeOrder */ private Integer pendingOrder(String approveId) { return list(new LambdaQueryWrapper() .eq(ApproveNode::getApproveProcessId, approveId) .eq(ApproveNode::getDeleteFlag, 0) .eq(ApproveNode::getApproveNodeStatus, 0)) .stream() .map(ApproveNode::getApproveNodeOrder) .min(Integer::compareTo) .orElse(null); } /** * 查询指定流程指定节点的全部审批节点(同节点多审批人,按 id 升序) */ private List nodesByOrder(String approveId, Integer order) { return list(new LambdaQueryWrapper() .eq(ApproveNode::getApproveProcessId, approveId) .eq(ApproveNode::getApproveNodeOrder, order) .eq(ApproveNode::getDeleteFlag, 0) .orderByAsc(ApproveNode::getId)); } /** * 设置流程当前节点待审人(多值逗号分隔,兼容旧的单值字段) */ private void setCurrentApprovers(ApproveProcess approveProcess, List nodes) { if (nodes == null || nodes.isEmpty()) { approveProcess.setApproveUserCurrentIds(null); approveProcess.setApproveUserCurrentNames(null); approveProcess.setApproveUserCurrentId(null); approveProcess.setApproveUserCurrentName(null); return; } approveProcess.setApproveUserCurrentIds(nodes.stream().map(ApproveNode::getApproveNodeUserId) .map(String::valueOf).collect(Collectors.joining(","))); approveProcess.setApproveUserCurrentNames(nodes.stream().map(ApproveNode::getApproveNodeUser) .collect(Collectors.joining(","))); approveProcess.setApproveUserCurrentId(nodes.get(0).getApproveNodeUserId()); approveProcess.setApproveUserCurrentName(nodes.get(0).getApproveNodeUser()); } /** * 通知当前节点全部待审人 */ private void notifyApproveUsers(List userIds, ApproveProcess approveProcess) { if (userIds == null || userIds.isEmpty()) { return; } List ids = userIds.stream().distinct().collect(Collectors.toList()); if (approveProcess.getApproveType() == 8) { sysNoticeService.simpleNoticeByUser(approveProcessType(approveProcess.getApproveType()), approveProcess.getApproveId() + "流程编号的审批需要您审核!!!!!", ids, "/safeProduction/safeWorkApproval?approveType=" + approveProcess.getApproveType() + "&approveId=" + approveProcess.getApproveId()); } else { sysNoticeService.simpleNoticeByUser(approveProcessType(approveProcess.getApproveType()), approveProcess.getApproveId() + "流程编号的审批需要您审核!!!!!", ids, "/collaborativeApproval/approvalProcess?approveType=" + approveProcess.getApproveType() + "&approveId=" + approveProcess.getApproveId()); } } /** * 填充审批节点附件 url */ private void bindNodeUrl(List nodes) { if (CollectionUtils.isEmpty(nodes)) { return; } for (ApproveNode approveNode : nodes) { List commonFiles = fileMapper.selectList(new LambdaQueryWrapper() .eq(CommonFile::getCommonId, approveNode.getId()) .eq(CommonFile::getType, FileNameType.ApproveNode.getValue())); if (!CollectionUtils.isEmpty(commonFiles)) { approveNode.setUrl(commonFiles.get(0).getUrl()); } } } /** * 临时附件迁移为正式附件 */ private void bindNodeFiles(ApproveNode approveNode, List tempFileIds) throws IOException { if (!CollectionUtils.isEmpty(tempFileIds) && approveNode.getApproveNodeStatus() != null && approveNode.getApproveNodeStatus() == 1) { tempFileService.migrateTempFilesToFormal(approveNode.getId(), tempFileIds, FileNameType.ApproveNode.getValue()); } } @Override public void updateApproveNode(ApproveNode approveNode) throws IOException { ApproveNode db = approveNodeMapper.selectById(approveNode.getId()); if (db == null || db.getDeleteFlag() != null && db.getDeleteFlag() == 1) { throw new RuntimeException("审批节点不存在"); } if (db.getApproveNodeStatus() != null && db.getApproveNodeStatus() != 0) { throw new RuntimeException("该节点已审批,请勿重复操作"); } Long loginUserId = SecurityUtils.getLoginUser().getUser().getUserId(); boolean admin = SecurityUtils.isAdmin(loginUserId); // 管理员不受审批人限制 if (!admin && !db.getApproveNodeUserId().equals(loginUserId)) { throw new RuntimeException("无权审批该节点"); } ApproveProcess approveProcess = getApproveById(db.getApproveProcessId()); if (approveProcess == null) { throw new RuntimeException("审批流程不存在"); } if (approveProcess.getApproveStatus() == null || (approveProcess.getApproveStatus() != 0 && approveProcess.getApproveStatus() != 1)) { throw new RuntimeException("当前流程状态不允许审批"); } Integer curOrder = pendingOrder(db.getApproveProcessId()); if (!db.getApproveNodeOrder().equals(curOrder)) { throw new RuntimeException("尚未轮到该节点审批"); } db.setApproveNodeStatus(approveNode.getApproveNodeStatus()); db.setApproveNodeReason(approveNode.getApproveNodeReason()); db.setApproveNodeRemark(approveNode.getApproveNodeRemark()); db.setApproveNodeTime(new Date()); db.setUpdateUser(loginUserId); approveNodeMapper.updateById(db); // 拒绝:一票否决,立即驳回整个流程 if (db.getApproveNodeStatus() != null && db.getApproveNodeStatus() == 2) { approveProcess.setApproveStatus(3); approveProcess.setApproveOverTime(new Date()); approveProcessMapper.updateById(approveProcess); applyBusinessStatus(approveProcess, 3); bindNodeFiles(db, approveNode.getTempFileIds()); return; } String mode = db.getApproveMode(); if (mode == null || mode.isEmpty()) { mode = "or"; } List sameOrder = nodesByOrder(db.getApproveProcessId(), db.getApproveNodeOrder()); // 管理员代审:当前节点其余待审记录一并通过 if (admin) { for (ApproveNode n : sameOrder) { if (n.getApproveNodeStatus() == null || n.getApproveNodeStatus() == 0) { n.setApproveNodeStatus(1); n.setApproveNodeRemark("管理员代审通过"); n.setApproveNodeTime(new Date()); approveNodeMapper.updateById(n); } } } boolean released; if ("and".equals(mode)) { // 会签:节点内所有人均同意才放行 released = sameOrder.stream().allMatch(n -> n.getApproveNodeStatus() != null && n.getApproveNodeStatus() == 1); } else { // 或签:节点内任一同意即放行 released = sameOrder.stream().anyMatch(n -> n.getApproveNodeStatus() != null && n.getApproveNodeStatus() == 1); } // 会签未放行:节点内仍有待审人,流程保持审核中 if (!released) { List pending = sameOrder.stream() .filter(n -> n.getApproveNodeStatus() == null || n.getApproveNodeStatus() == 0) .collect(Collectors.toList()); approveProcess.setApproveStatus(1); setCurrentApprovers(approveProcess, pending); approveProcessMapper.updateById(approveProcess); applyBusinessStatus(approveProcess, 1); bindNodeFiles(db, approveNode.getTempFileIds()); return; } // 或签放行:同节点其余未审批记录标记为自动同意 if (!"and".equals(mode)) { for (ApproveNode n : sameOrder) { if (n.getApproveNodeStatus() == null || n.getApproveNodeStatus() == 0) { n.setApproveNodeStatus(1); n.setApproveNodeRemark("或签:其他审批人已通过"); n.setApproveNodeTime(new Date()); approveNodeMapper.updateById(n); } } } List next = nodesByOrder(db.getApproveProcessId(), db.getApproveNodeOrder() + 1).stream() .filter(n -> n.getApproveNodeStatus() == null || n.getApproveNodeStatus() == 0) .collect(Collectors.toList()); if (next.isEmpty()) { // 最后节点放行:流程完成 approveProcess.setApproveStatus(2); approveProcess.setApproveOverTime(new Date()); setCurrentApprovers(approveProcess, new ArrayList<>()); approveProcessMapper.updateById(approveProcess); applyBusinessStatus(approveProcess, 2); } else { // 流转下一节点 approveProcess.setApproveStatus(1); setCurrentApprovers(approveProcess, next); approveProcessMapper.updateById(approveProcess); applyBusinessStatus(approveProcess, 1); notifyApproveUsers(next.stream().map(ApproveNode::getApproveNodeUserId).collect(Collectors.toList()), approveProcess); } bindNodeFiles(db, approveNode.getTempFileIds()); } @Override public void delApproveNodeByApproveId(String id) { List approveNodes = approveNodeMapper.selectList(new LambdaQueryWrapper() .eq(ApproveNode::getApproveProcessId, id)); UpdateWrapper queryWrapper = new UpdateWrapper<>(); queryWrapper.lambda().set(ApproveNode::getDeleteFlag, 1) .eq(ApproveNode::getApproveProcessId, id); update(queryWrapper); // 删除附件 for (ApproveNode approveNode : approveNodes) { commonFileService.deleteByBusinessId(approveNode.getId(), FileNameType.ApproveNode.getValue()); } } //审批类型获取(与前端页面对应) private String approveProcessType(Integer approveType){ switch (approveType){ case 1: return "公出管理"; case 2: return "请假管理"; case 3: return "出差管理"; case 4: return "报销管理"; case 5: return "采购审批"; case 6: return "报价审批"; case 7: return "发货审批"; case 8: return "危险作业审批"; case 9: return "用印审批"; } return null; } private void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct) { QualityInspect qualityInspect = new QualityInspect(); qualityInspect.setInspectType(0); qualityInspect.setSupplier(purchaseLedger.getSupplierName()); qualityInspect.setPurchaseLedgerId(purchaseLedger.getId()); qualityInspect.setProductId(saleProduct.getProductId()); qualityInspect.setProductName(saleProduct.getProductCategory()); qualityInspect.setModel(saleProduct.getSpecificationModel()); qualityInspect.setProductModelId(saleProduct.getProductModelId()); qualityInspect.setUnit(saleProduct.getUnit()); qualityInspect.setQuantity(saleProduct.getQuantity()); qualityInspectMapper.insert(qualityInspect); List qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(saleProduct.getProductId(), 0,null); if (qualityTestStandard.size()>0){ qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId()); qualityInspectMapper.updateById(qualityInspect); qualityTestStandardParamMapper.selectList(Wrappers.lambdaQuery() .eq(QualityTestStandardParam::getTestStandardId,qualityTestStandard.get(0).getId())) .forEach(qualityTestStandardParam -> { QualityInspectParam param = new QualityInspectParam(); com.ruoyi.common.utils.bean.BeanUtils.copyProperties(qualityTestStandardParam, param); param.setId(null); param.setInspectId(qualityInspect.getId()); qualityInspectParamMapper.insert(param); }); } } }