package com.mosquito.project.permission; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.*; /** * 审批流服务 */ @Service public class ApprovalFlowService { // 审批状态常量 public static final String STATUS_PENDING = "PENDING"; public static final String STATUS_APPROVED = "APPROVED"; public static final String STATUS_REJECTED = "REJECTED"; public static final String STATUS_PROCESSING = "PROCESSING"; public static final String STATUS_CANCELLED = "CANCELLED"; // 审批动作 public static final String ACTION_SUBMIT = "SUBMIT"; public static final String ACTION_APPROVE = "APPROVE"; public static final String ACTION_REJECT = "REJECT"; public static final String ACTION_TRANSFER = "TRANSFER"; private final ApprovalFlowRepository flowRepository; private final ApprovalRecordRepository recordRepository; private final ApprovalHistoryRepository historyRepository; private final ObjectMapper objectMapper; public ApprovalFlowService( ApprovalFlowRepository flowRepository, ApprovalRecordRepository recordRepository, ApprovalHistoryRepository historyRepository, ObjectMapper objectMapper) { this.flowRepository = flowRepository; this.recordRepository = recordRepository; this.historyRepository = historyRepository; this.objectMapper = objectMapper; } /** * 提交审批申请 */ @Transactional public Map submitApproval(Long flowId, String bizType, Long bizId, String title, Long applicantId, String applyReason) { // 获取审批流程配置 Optional flowOpt = flowRepository.findById(flowId); if (!flowOpt.isPresent()) { throw new IllegalArgumentException("审批流程不存在: " + flowId); } SysApprovalFlow flow = flowOpt.get(); if (!"ENABLED".equals(flow.getStatus())) { throw new IllegalArgumentException("审批流程已禁用"); } // 检查是否已有待处理的审批 List existing = recordRepository.findByBizTypeAndBizId(bizType, bizId); for (SysApprovalRecord record : existing) { if (STATUS_PENDING.equals(record.getStatus()) || STATUS_PROCESSING.equals(record.getStatus())) { throw new IllegalArgumentException("该业务已存在待处理的审批申请"); } } // 解析审批节点,获取第一个审批人 Long firstApproverId = resolveFirstApprover(flow, applicantId); // 创建审批记录 SysApprovalRecord record = new SysApprovalRecord(); record.setFlowId(flowId); record.setBizType(bizType); record.setBizId(bizId); record.setCurrentNode(0); record.setApplicantId(applicantId); record.setCurrentApproverId(firstApproverId); record.setStatus(STATUS_PROCESSING); record.setCreatedAt(LocalDateTime.now()); record.setUpdatedAt(LocalDateTime.now()); record = recordRepository.save(record); // 记录审批历史 SysApprovalHistory history = new SysApprovalHistory(); history.setRecordId(record.getId()); history.setNodeIndex(0); history.setApproverId(applicantId); history.setAction(ACTION_SUBMIT); history.setComment(applyReason); history.setCreatedAt(LocalDateTime.now()); historyRepository.save(history); Map result = new HashMap<>(); result.put("recordId", record.getId()); result.put("status", record.getStatus()); result.put("currentNode", record.getCurrentNode()); result.put("currentApproverId", firstApproverId); return result; } /** * 通过触发事件提交审批 */ @Transactional public Map submitApprovalByEvent(String triggerEvent, String bizType, Long bizId, String title, Long applicantId, String applyReason) { Optional flowOpt = flowRepository.findByTriggerEventAndStatus(triggerEvent, "ENABLED"); if (!flowOpt.isPresent()) { throw new IllegalArgumentException("未找到触发事件对应的审批流程: " + triggerEvent); } return submitApproval(flowOpt.get().getId(), bizType, bizId, title, applicantId, applyReason); } /** * 处理审批 */ @Transactional public Map handleApproval(Long recordId, String action, Long operatorId, String comment) { Optional recordOpt = recordRepository.findById(recordId); if (!recordOpt.isPresent()) { throw new IllegalArgumentException("审批记录不存在: " + recordId); } SysApprovalRecord record = recordOpt.get(); if (!STATUS_PROCESSING.equals(record.getStatus())) { throw new IllegalArgumentException("审批记录状态不是处理中: " + record.getStatus()); } // 验证审批人权限 if (!operatorId.equals(record.getCurrentApproverId())) { throw new IllegalArgumentException("您不是当前审批人,无权处理此审批"); } // 获取流程配置 Optional flowOpt = flowRepository.findById(record.getFlowId()); if (!flowOpt.isPresent()) { throw new IllegalArgumentException("审批流程配置不存在"); } SysApprovalFlow flow = flowOpt.get(); // 解析节点配置 List> nodes = parseNodes(flow.getNodes()); int currentNode = record.getCurrentNode(); // 记录审批历史 SysApprovalHistory history = new SysApprovalHistory(); history.setRecordId(recordId); history.setNodeIndex(currentNode); history.setApproverId(operatorId); history.setAction(action); history.setComment(comment); history.setCreatedAt(LocalDateTime.now()); historyRepository.save(history); Map result = new HashMap<>(); if (ACTION_APPROVE.equals(action)) { // 批准 - 检查是否还有下一个节点 if (currentNode + 1 >= nodes.size()) { // 审批完成 record.setStatus(STATUS_APPROVED); record.setUpdatedAt(LocalDateTime.now()); recordRepository.save(record); result.put("status", STATUS_APPROVED); result.put("message", "审批已通过"); } else { // 进入下一个节点 int nextNode = currentNode + 1; Long nextApproverId = resolveNextApprover(nodes, nextNode, record); record.setCurrentNode(nextNode); record.setCurrentApproverId(nextApproverId); record.setUpdatedAt(LocalDateTime.now()); recordRepository.save(record); result.put("status", STATUS_PROCESSING); result.put("currentNode", nextNode); result.put("currentApproverId", nextApproverId); result.put("message", "已提交给下一审批人"); } } else if (ACTION_REJECT.equals(action)) { // 拒绝 record.setStatus(STATUS_REJECTED); record.setUpdatedAt(LocalDateTime.now()); recordRepository.save(record); result.put("status", STATUS_REJECTED); result.put("message", "审批已拒绝"); } else if (ACTION_TRANSFER.equals(action)) { // 转交 // 转交目标ID在comment中传递,格式: "transfer:userId" if (comment != null && comment.startsWith("transfer:")) { Long transferToId = Long.parseLong(comment.substring(9)); record.setCurrentApproverId(transferToId); record.setUpdatedAt(LocalDateTime.now()); recordRepository.save(record); result.put("status", STATUS_PROCESSING); result.put("currentApproverId", transferToId); result.put("message", "已转交给其他审批人"); } else { throw new IllegalArgumentException("转交操作需要指定目标审批人,格式: transfer:userId"); } } else { throw new IllegalArgumentException("无效的审批动作: " + action); } return result; } /** * 获取待审批列表 */ public List getPendingApprovals(Long userId) { return recordRepository.findPendingByApproverId(userId); } /** * 获取已审批列表(我审批过的) */ public List getApprovedList(Long userId) { return recordRepository.findProcessedByApplicantId(userId); } /** * 获取我发起的审批 */ public List getMyApplications(Long userId) { return recordRepository.findByApplicantId(userId); } /** * 获取审批记录详情 */ public Map getRecordById(Long recordId) { Optional recordOpt = recordRepository.findById(recordId); if (!recordOpt.isPresent()) { return null; } SysApprovalRecord record = recordOpt.get(); Map result = new HashMap<>(); result.put("id", record.getId()); result.put("flowId", record.getFlowId()); result.put("bizType", record.getBizType()); result.put("bizId", record.getBizId()); result.put("currentNode", record.getCurrentNode()); result.put("applicantId", record.getApplicantId()); result.put("status", record.getStatus()); result.put("currentApproverId", record.getCurrentApproverId()); result.put("createdAt", record.getCreatedAt()); result.put("updatedAt", record.getUpdatedAt()); // 获取流程信息 Optional flowOpt = flowRepository.findById(record.getFlowId()); if (flowOpt.isPresent()) { SysApprovalFlow flow = flowOpt.get(); result.put("flowName", flow.getFlowName()); result.put("flowCode", flow.getFlowCode()); } // 获取审批历史 List histories = historyRepository.findByRecordIdOrderByNodeIndexDesc(recordId); result.put("history", histories); return result; } /** * 获取审批历史 */ public List getApprovalHistory(Long recordId) { return historyRepository.findByRecordIdOrderByNodeIndexDesc(recordId); } /** * 取消审批 */ @Transactional public boolean cancelApproval(Long recordId, Long operatorId) { Optional recordOpt = recordRepository.findById(recordId); if (!recordOpt.isPresent()) { throw new IllegalArgumentException("审批记录不存在: " + recordId); } SysApprovalRecord record = recordOpt.get(); if (!operatorId.equals(record.getApplicantId())) { throw new IllegalArgumentException("只有申请人可以取消审批"); } if (!STATUS_PROCESSING.equals(record.getStatus())) { throw new IllegalArgumentException("只有处理中的审批可以取消"); } record.setStatus(STATUS_CANCELLED); record.setUpdatedAt(LocalDateTime.now()); recordRepository.save(record); return true; } /** * 获取所有审批流程配置 */ public List getAllFlows() { return flowRepository.findAll(); } /** * 获取启用的审批流程 */ public List getEnabledFlows() { return flowRepository.findByStatus("ENABLED"); } /** * 解析节点配置JSON */ @SuppressWarnings("unchecked") private List> parseNodes(String nodesJson) { try { return objectMapper.readValue(nodesJson, List.class); } catch (JsonProcessingException e) { throw new IllegalArgumentException("审批节点配置解析失败", e); } } /** * 解析第一个审批人 */ private Long resolveFirstApprover(SysApprovalFlow flow, Long applicantId) { List> nodes = parseNodes(flow.getNodes()); if (nodes.isEmpty()) { throw new IllegalArgumentException("审批流程节点配置为空"); } return resolveApproverFromNode(nodes.get(0), applicantId); } /** * 解析下一个审批人 */ private Long resolveNextApprover(List> nodes, int nodeIndex, SysApprovalRecord record) { if (nodeIndex >= nodes.size()) { return null; } return resolveApproverFromNode(nodes.get(nodeIndex), record.getApplicantId()); } /** * 从节点配置中解析审批人 */ @SuppressWarnings("unchecked") private Long resolveApproverFromNode(Map node, Long applicantId) { String approverType = (String) node.get("approverType"); if ("SELF".equals(approverType)) { return applicantId; } else if ("ROLE".equals(approverType)) { // 根据角色ID查找用户(这里需要UserRoleService,暂时返回null) return null; } else if ("DEPARTMENT_HEAD".equals(approverType)) { // 查找部门负责人(需要DepartmentService,暂时返回null) return null; } else if ("SPECIFIC".equals(approverType)) { // 指定用户 Object approverId = node.get("approverId"); if (approverId != null) { return ((Number) approverId).longValue(); } } // 默认返回null,需要手动分配 return null; } }