package com.ruoyi.approve.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.approve.mapper.ApproveLogMapper; import com.ruoyi.approve.mapper.ApproveNodeMapper; import com.ruoyi.approve.mapper.ApproveProcessMapper; import com.ruoyi.approve.pojo.ApproveLog; import com.ruoyi.approve.pojo.ApproveNode; import com.ruoyi.approve.pojo.ApproveProcess; import com.ruoyi.approve.service.IApproveNodeService; import com.ruoyi.approve.vo.ApproveNodeVO; import com.ruoyi.common.enums.FileNameType; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.other.service.impl.TempFileServiceImpl; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysUserMapper; import com.ruoyi.sales.mapper.CommonFileMapper; import com.ruoyi.sales.pojo.CommonFile; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.io.IOException; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; import java.util.List; @Service @RequiredArgsConstructor public class ApproveNodeServiceImpl extends ServiceImpl implements IApproveNodeService { private final ApproveNodeMapper approveNodeMapper; private final ApproveProcessMapper approveProcessMapper; private final ApproveLogMapper approveLogMapper; private final SysUserMapper sysUserMapper; private final TempFileServiceImpl tempFileService; private final CommonFileMapper fileMapper; private ApproveProcess getApproveById(String code) { ApproveProcess process = approveProcessMapper.selectOne(new LambdaQueryWrapper() .eq(ApproveProcess::getApproveId, code) .eq(ApproveProcess::getApproveDelete, 0) .eq(ApproveProcess::getTenantId, tenantId()) .last("limit 1")); if (process == null) throw new RuntimeException("审批不存在"); return process; } @Override public void initApproveNodes(String approveUserIds, String approveID, Long tenantId) { if (!StringUtils.hasText(approveUserIds)) throw new RuntimeException("请选择审批人"); List nodes = new ArrayList<>(); for (String id : approveUserIds.split(",")) { ApproveNodeVO node = new ApproveNodeVO(); node.setUserId(Long.parseLong(id.trim())); node.setConditionOperator("ALWAYS"); nodes.add(node); } initApproveNodes(nodes, approveID, tenantId); } @Override public void initApproveNodes(List nodes, String approveID, Long ignoredTenantId) { if (CollectionUtils.isEmpty(nodes)) throw new RuntimeException("审批流程至少包含一个节点"); Long tenantId = tenantId(); Long currentUserId = SecurityUtils.getUserId(); ApproveProcess process = getApproveById(approveID); int order = 1; ApproveNode first = null; Long previousUserId = null; for (ApproveNodeVO request : nodes) { if (request.getUserId() == null) throw new RuntimeException("审批人不能为空"); if (request.getUserId().equals(previousUserId)) throw new RuntimeException("相邻审批节点不能选择同一审批人"); previousUserId = request.getUserId(); if (!matchesCondition(process.getAmount(), request)) continue; SysUser user = sysUserMapper.selectUserById(request.getUserId()); if (user == null || !tenantId.equals(user.getTenantId())) throw new RuntimeException("审批人不存在或不属于当前租户"); ApproveNode node = new ApproveNode(); node.setApproveProcessId(process.getApproveId()); node.setApproveNodeOrder(order++); node.setApproveNodeName(StringUtils.hasText(request.getNodeName()) ? request.getNodeName() : "审批"); node.setApproveNodeUser(user.getNickName()); node.setApproveNodeUserId(user.getUserId()); node.setApproveNodeStatus(0); node.setConditionField(request.getConditionField()); node.setConditionOperator(StringUtils.hasText(request.getConditionOperator()) ? request.getConditionOperator() : "ALWAYS"); node.setConditionValue(request.getConditionValue()); node.setIsCurrent(first == null ? 1 : 0); node.setTenantId(tenantId); node.setDeleteFlag(0); node.setCreateUser(currentUserId); node.setUpdateUser(currentUserId); node.setCreateTime(LocalDateTime.now()); node.setUpdateTime(LocalDateTime.now()); approveNodeMapper.insert(node); if (first == null) first = node; } if (first == null) throw new RuntimeException("没有满足条件的审批节点"); process.setApproveUserCurrentId(first.getApproveNodeUserId()); process.setApproveUserCurrentName(first.getApproveNodeUser()); process.setUpdateTime(LocalDateTime.now()); approveProcessMapper.updateById(process); } private boolean matchesCondition(BigDecimal amount, ApproveNodeVO node) { String operator = node.getConditionOperator(); if (!StringUtils.hasText(operator) || "ALWAYS".equalsIgnoreCase(operator)) return true; if (!"amount".equals(node.getConditionField())) throw new RuntimeException("审批条件仅支持金额字段"); if (amount == null || !StringUtils.hasText(node.getConditionValue())) return false; try { if ("BETWEEN".equalsIgnoreCase(operator)) { String[] values = node.getConditionValue().replace("[", "").replace("]", "").split(","); if (values.length != 2) throw new RuntimeException("金额区间格式不正确"); return amount.compareTo(new BigDecimal(values[0].trim())) >= 0 && amount.compareTo(new BigDecimal(values[1].trim())) <= 0; } int compared = amount.compareTo(new BigDecimal(node.getConditionValue())); if ("GT".equalsIgnoreCase(operator)) return compared > 0; if ("GTE".equalsIgnoreCase(operator)) return compared >= 0; if ("LT".equalsIgnoreCase(operator)) return compared < 0; if ("LTE".equalsIgnoreCase(operator)) return compared <= 0; if ("EQ".equalsIgnoreCase(operator)) return compared == 0; } catch (NumberFormatException ex) { throw new RuntimeException("审批条件金额格式不正确"); } throw new RuntimeException("不支持的审批条件操作符"); } @Override public List details(String id) { getApproveById(id); List nodes = approveNodeMapper.selectList(new LambdaQueryWrapper() .eq(ApproveNode::getApproveProcessId, id) .eq(ApproveNode::getDeleteFlag, 0) .eq(ApproveNode::getTenantId, tenantId()) .orderByAsc(ApproveNode::getApproveNodeOrder)); for (ApproveNode node : nodes) { List files = fileMapper.selectList(new LambdaQueryWrapper() .eq(CommonFile::getCommonId, node.getId()) .eq(CommonFile::getType, FileNameType.ApproveNode.getValue())); if (!CollectionUtils.isEmpty(files)) node.setUrl(files.get(0).getUrl()); node.setIsShen(Integer.valueOf(1).equals(node.getIsCurrent()) && node.getApproveNodeUserId().equals(SecurityUtils.getUserId())); } nodes.sort(Comparator.comparingInt(ApproveNode::getApproveNodeOrder)); return nodes; } @Override @Transactional(rollbackFor = Exception.class) public void updateApproveNode(ApproveNode request) throws IOException { if (request == null || request.getId() == null) throw new RuntimeException("审批节点不能为空"); if (request.getApproveNodeStatus() == null || (request.getApproveNodeStatus() != 1 && request.getApproveNodeStatus() != 2)) { throw new RuntimeException("审批结果不正确"); } if (!StringUtils.hasText(request.getApproveNodeReason())) throw new RuntimeException("审批意见不能为空"); Long tenantId = tenantId(); ApproveNode node = approveNodeMapper.selectOne(new LambdaQueryWrapper() .eq(ApproveNode::getId, request.getId()) .eq(ApproveNode::getTenantId, tenantId) .eq(ApproveNode::getDeleteFlag, 0)); if (node == null) throw new RuntimeException("审批节点不存在"); if (!Integer.valueOf(0).equals(node.getApproveNodeStatus()) || !Integer.valueOf(1).equals(node.getIsCurrent())) { throw new RuntimeException("该节点已经处理或不是当前审批节点"); } if (!SecurityUtils.getUserId().equals(node.getApproveNodeUserId())) throw new RuntimeException("当前用户无权审批此节点"); ApproveProcess process = getApproveById(node.getApproveProcessId()); if (Integer.valueOf(2).equals(process.getApproveStatus()) || Integer.valueOf(3).equals(process.getApproveStatus())) { throw new RuntimeException("审批流程已经结束"); } node.setApproveNodeStatus(request.getApproveNodeStatus()); node.setApproveNodeReason(request.getApproveNodeReason().trim()); node.setApproveNodeRemark(request.getApproveNodeRemark()); node.setApproveNodeTime(new Date()); node.setIsCurrent(0); node.setUpdateUser(SecurityUtils.getUserId()); node.setUpdateTime(LocalDateTime.now()); approveNodeMapper.updateById(node); if (node.getApproveNodeStatus() == 2) { process.setApproveStatus(3); process.setApproveUserCurrentId(null); process.setApproveUserCurrentName(null); process.setApproveOverTime(new Date()); } else { ApproveNode next = approveNodeMapper.selectOne(new LambdaQueryWrapper() .eq(ApproveNode::getApproveProcessId, node.getApproveProcessId()) .eq(ApproveNode::getTenantId, tenantId) .eq(ApproveNode::getDeleteFlag, 0) .eq(ApproveNode::getApproveNodeStatus, 0) .gt(ApproveNode::getApproveNodeOrder, node.getApproveNodeOrder()) .orderByAsc(ApproveNode::getApproveNodeOrder) .last("limit 1")); if (next == null) { process.setApproveStatus(2); process.setApproveOverTime(new Date()); process.setApproveUserCurrentId(null); process.setApproveUserCurrentName(null); } else { next.setIsCurrent(1); next.setUpdateTime(LocalDateTime.now()); approveNodeMapper.updateById(next); process.setApproveStatus(1); process.setApproveUserCurrentId(next.getApproveNodeUserId()); process.setApproveUserCurrentName(next.getApproveNodeUser()); } } process.setUpdateTime(LocalDateTime.now()); approveProcessMapper.updateById(process); ApproveLog log = new ApproveLog(); log.setApproveId(process.getId()); log.setApproveProcessId(process.getId()); log.setApproveNodeId(node.getId()); log.setApproveNodeOrder(node.getApproveNodeOrder()); log.setApproveUser(SecurityUtils.getUserId()); log.setApproveUserName(node.getApproveNodeUser()); log.setApproveTime(new Date()); log.setApproveStatus(node.getApproveNodeStatus()); log.setApproveRemark(node.getApproveNodeReason()); log.setTenantId(tenantId); approveLogMapper.insert(log); if (!CollectionUtils.isEmpty(request.getTempFileIds()) && node.getApproveNodeStatus() == 1) { tempFileService.migrateTempFilesToFormal(node.getId(), request.getTempFileIds(), FileNameType.ApproveNode.getValue()); } } @Override public void delApproveNodeByApproveId(String id) { UpdateWrapper wrapper = new UpdateWrapper<>(); wrapper.lambda().set(ApproveNode::getDeleteFlag, 1) .set(ApproveNode::getIsCurrent, 0) .eq(ApproveNode::getApproveProcessId, id) .eq(ApproveNode::getTenantId, tenantId()); update(wrapper); } private Long tenantId() { return SecurityUtils.getLoginUser().getTenantId(); } }