- 新增实体类: SysApprovalFlow, SysApprovalRecord, SysApprovalHistory - 新增Repositories: ApprovalFlowRepository, ApprovalRecordRepository, ApprovalHistoryRepository - 完整实现ApprovalFlowService: 提交审批、处理审批、取消审批等 - 更新ApprovalController连接实际服务 - 添加单元测试ApprovalFlowServiceTest - 更新Ralph状态文件 (Phase 3: 90%)
375 lines
14 KiB
Java
375 lines
14 KiB
Java
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<String, Object> submitApproval(Long flowId, String bizType, Long bizId,
|
||
String title, Long applicantId, String applyReason) {
|
||
// 获取审批流程配置
|
||
Optional<SysApprovalFlow> flowOpt = flowRepository.findById(flowId);
|
||
if (!flowOpt.isPresent()) {
|
||
throw new IllegalArgumentException("审批流程不存在: " + flowId);
|
||
}
|
||
|
||
SysApprovalFlow flow = flowOpt.get();
|
||
if (!"ENABLED".equals(flow.getStatus())) {
|
||
throw new IllegalArgumentException("审批流程已禁用");
|
||
}
|
||
|
||
// 检查是否已有待处理的审批
|
||
List<SysApprovalRecord> 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<String, Object> 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<String, Object> submitApprovalByEvent(String triggerEvent, String bizType, Long bizId,
|
||
String title, Long applicantId, String applyReason) {
|
||
Optional<SysApprovalFlow> 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<String, Object> handleApproval(Long recordId, String action, Long operatorId, String comment) {
|
||
Optional<SysApprovalRecord> 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<SysApprovalFlow> flowOpt = flowRepository.findById(record.getFlowId());
|
||
if (!flowOpt.isPresent()) {
|
||
throw new IllegalArgumentException("审批流程配置不存在");
|
||
}
|
||
SysApprovalFlow flow = flowOpt.get();
|
||
|
||
// 解析节点配置
|
||
List<Map<String, Object>> 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<String, Object> 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<SysApprovalRecord> getPendingApprovals(Long userId) {
|
||
return recordRepository.findPendingByApproverId(userId);
|
||
}
|
||
|
||
/**
|
||
* 获取已审批列表(我审批过的)
|
||
*/
|
||
public List<SysApprovalRecord> getApprovedList(Long userId) {
|
||
return recordRepository.findProcessedByApplicantId(userId);
|
||
}
|
||
|
||
/**
|
||
* 获取我发起的审批
|
||
*/
|
||
public List<SysApprovalRecord> getMyApplications(Long userId) {
|
||
return recordRepository.findByApplicantId(userId);
|
||
}
|
||
|
||
/**
|
||
* 获取审批记录详情
|
||
*/
|
||
public Map<String, Object> getRecordById(Long recordId) {
|
||
Optional<SysApprovalRecord> recordOpt = recordRepository.findById(recordId);
|
||
if (!recordOpt.isPresent()) {
|
||
return null;
|
||
}
|
||
|
||
SysApprovalRecord record = recordOpt.get();
|
||
Map<String, Object> 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<SysApprovalFlow> flowOpt = flowRepository.findById(record.getFlowId());
|
||
if (flowOpt.isPresent()) {
|
||
SysApprovalFlow flow = flowOpt.get();
|
||
result.put("flowName", flow.getFlowName());
|
||
result.put("flowCode", flow.getFlowCode());
|
||
}
|
||
|
||
// 获取审批历史
|
||
List<SysApprovalHistory> histories = historyRepository.findByRecordIdOrderByNodeIndexDesc(recordId);
|
||
result.put("history", histories);
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 获取审批历史
|
||
*/
|
||
public List<SysApprovalHistory> getApprovalHistory(Long recordId) {
|
||
return historyRepository.findByRecordIdOrderByNodeIndexDesc(recordId);
|
||
}
|
||
|
||
/**
|
||
* 取消审批
|
||
*/
|
||
@Transactional
|
||
public boolean cancelApproval(Long recordId, Long operatorId) {
|
||
Optional<SysApprovalRecord> 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<SysApprovalFlow> getAllFlows() {
|
||
return flowRepository.findAll();
|
||
}
|
||
|
||
/**
|
||
* 获取启用的审批流程
|
||
*/
|
||
public List<SysApprovalFlow> getEnabledFlows() {
|
||
return flowRepository.findByStatus("ENABLED");
|
||
}
|
||
|
||
/**
|
||
* 解析节点配置JSON
|
||
*/
|
||
@SuppressWarnings("unchecked")
|
||
private List<Map<String, Object>> 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<Map<String, Object>> nodes = parseNodes(flow.getNodes());
|
||
if (nodes.isEmpty()) {
|
||
throw new IllegalArgumentException("审批流程节点配置为空");
|
||
}
|
||
return resolveApproverFromNode(nodes.get(0), applicantId);
|
||
}
|
||
|
||
/**
|
||
* 解析下一个审批人
|
||
*/
|
||
private Long resolveNextApprover(List<Map<String, Object>> nodes, int nodeIndex, SysApprovalRecord record) {
|
||
if (nodeIndex >= nodes.size()) {
|
||
return null;
|
||
}
|
||
return resolveApproverFromNode(nodes.get(nodeIndex), record.getApplicantId());
|
||
}
|
||
|
||
/**
|
||
* 从节点配置中解析审批人
|
||
*/
|
||
@SuppressWarnings("unchecked")
|
||
private Long resolveApproverFromNode(Map<String, Object> 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;
|
||
}
|
||
}
|