Java审批流程案例如何开发

wen java案例 27

本文目录导读:

Java审批流程案例如何开发

  1. 审批流程的核心要素
  2. 完整实现示例 - 请假审批流程
  3. 进阶功能
  4. 开发建议

我来详细介绍Java审批流程案例的开发方法和核心思路。

审批流程的核心要素

核心数据结构

// 审批节点
@Entity
public class ApprovalNode {
    private Long id;
    private String nodeName;       // 节点名称
    private Integer nodeType;      // 节点类型:1-审批,2-会签,3-知会
    private Long approverId;       // 审批人ID
    private Integer orderNum;      // 节点序号
    private Long processId;        // 所属流程ID
}
// 审批记录
@Entity
public class ApprovalRecord {
    private Long id;
    private Long businessId;       // 业务ID
    private Long nodeId;           // 节点ID
    private String status;         // PENDING, APPROVED, REJECTED
    private String comment;        // 审批意见
    private Long operatorId;       // 操作人
    private Date createTime;
    private Date updateTime;
}
// 流程实例
@Entity
public class ProcessInstance {
    private Long id;
    private Long processDefId;     // 流程定义ID
    private Long businessId;       // 业务ID
    private String status;         // RUNNING, COMPLETED, TERMINATED
    private Integer currentNode;   // 当前节点序号
}

完整实现示例 - 请假审批流程

1 流程定义

@Component
public class LeaveProcessConfig {
    // 定义请假审批流程
    public static List<ApprovalNode> defineLeaveProcess() {
        List<ApprovalNode> nodes = new ArrayList<>();
        // 节点1: 部门经理审批
        ApprovalNode node1 = new ApprovalNode();
        node1.setNodeName("部门经理审批");
        node1.setNodeType(1); // 审批
        node1.setOrderNum(1);
        // 节点2: 人事审批 (请假超过3天)
        ApprovalNode node2 = new ApprovalNode();
        node2.setNodeName("人事审批");
        node2.setNodeType(1);
        node2.setOrderNum(2);
        nodes.add(node1);
        nodes.add(node2);
        return nodes;
    }
}

2 审批服务实现

@Service
@Transactional
public class ApprovalService {
    @Autowired
    private ApprovalNodeRepository nodeRepository;
    @Autowired
    private ApprovalRecordRepository recordRepository;
    @Autowired
    private ProcessInstanceRepository processRepository;
    /**
     * 发起审批
     */
    public Long startProcess(ProcessInstance process, List<ApprovalNode> nodes) {
        // 1. 创建流程实例
        process.setStatus("RUNNING");
        process.setCurrentNode(1);
        processRepository.save(process);
        // 2. 保存节点定义
        for (ApprovalNode node : nodes) {
            node.setProcessId(process.getId());
            nodeRepository.save(node);
        }
        // 3. 创建第一个节点的审批记录
        ApprovalRecord firstRecord = new ApprovalRecord();
        firstRecord.setBusinessId(process.getBusinessId());
        firstRecord.setNodeId(nodes.get(0).getId());
        firstRecord.setStatus("PENDING");
        recordRepository.save(firstRecord);
        return process.getId();
    }
    /**
     * 审批处理
     */
    public ApprovalResult approve(Long processId, Long nodeId, 
                                   String status, String comment, Long operatorId) {
        // 1. 校验权限
        ProcessInstance process = processRepository.findById(processId)
            .orElseThrow(() -> new RuntimeException("流程不存在"));
        // 2. 更新审批记录
        ApprovalRecord record = recordRepository
            .findByNodeIdAndBusinessId(nodeId, process.getBusinessId())
            .orElseThrow(() -> new RuntimeException("记录不存在"));
        record.setStatus(status);
        record.setComment(comment);
        record.setOperatorId(operatorId);
        recordRepository.save(record);
        // 3. 处理审批结果
        if ("REJECTED".equals(status)) {
            // 驳回:终止流程
            process.setStatus("TERMINATED");
            processRepository.save(process);
            return new ApprovalResult(false, "审批被驳回");
        }
        // 4. 判断是否还有下一个节点
        List<ApprovalNode> nextNodes = getNextNodes(processId, nodeId);
        if (nextNodes.isEmpty()) {
            // 没有下一个节点:流程结束
            process.setStatus("COMPLETED");
            processRepository.save(process);
            return new ApprovalResult(true, "审批通过,流程完成");
        }
        // 5. 前进到下一个节点
        for (ApprovalNode nextNode : nextNodes) {
            forConditionHandle(nextNode, process);
            ApprovalRecord nextRecord = new ApprovalRecord();
            nextRecord.setBusinessId(process.getBusinessId());
            nextRecord.setNodeId(nextNode.getId());
            nextRecord.setStatus("PENDING");
            nextRecord.setComment("等待审批");
            recordRepository.save(nextRecord);
        }
        process.setCurrentNode(process.getCurrentNode() + 1);
        processRepository.save(process);
        return new ApprovalResult(true, "审批通过,已进入下一节点");
    }
    /**
     * 条件处理 - 动态判断是否需要跳过节点
     */
    private void forConditionHandle(ApprovalNode node, ProcessInstance process) {
        // 示例:请假3天以内不需要人事审批
        if (node.getNodeName().contains("人事")) {
            LeaveRequest leaveRequest = getLeaveRequest(process.getBusinessId());
            if (leaveRequest.getDays() <= 3) {
                // 自动跳过该节点
                recordRepository.save(createAutoPassRecord(node, process));
                // 查找下一个节点
                List<ApprovalNode> nextNodes = getNextNodes(process.getId(), node.getId());
                for (ApprovalNode nextNode : nextNodes) {
                    // 递归处理
                }
            }
        }
    }
    /**
     * 获取下一个审批节点
     */
    private List<ApprovalNode> getNextNodes(Long processId, Long currentNodeId) {
        ApprovalNode currentNode = nodeRepository.findById(currentNodeId)
            .orElseThrow(() -> new RuntimeException("节点不存在"));
        return nodeRepository.findByProcessIdAndOrderNumGreaterThan(
            processId, 
            currentNode.getOrderNum()
        );
    }
    /**
     * 创建自动通过的记录
     */
    private ApprovalRecord createAutoPassRecord(ApprovalNode node, ProcessInstance process) {
        ApprovalRecord record = new ApprovalRecord();
        record.setBusinessId(process.getBusinessId());
        record.setNodeId(node.getId());
        record.setStatus("APPROVED");
        record.setComment("条件满足,自动通过");
        record.setOperatorId(0L); // 系统操作
        return record;
    }
}

3 业务对象示例

// 请假申请
@Entity
public class LeaveRequest {
    @Id
    private Long id;
    private String applicantId;    // 申请人
    private Date startDate;        // 开始日期
    private Date endDate;          // 结束日期
    private int days;              // 请假天数
    private String reason;         // 请假原因
    private String status;         // DRAFT, SUBMITTED, APPROVED, REJECTED
    // getters and setters
}
// 请假申请服务
@Service
public class LeaveService {
    @Autowired
    private ApprovalService approvalService;
    @Autowired
    private LeaveRequestRepository leaveRepository;
    /**
     * 发起请假审批
     */
    public void submitLeaveRequest(LeaveRequest leave) {
        // 1. 保存请假申请
        leave.setStatus("SUBMITTED");
        leave = leaveRepository.save(leave);
        // 2. 创建流程实例
        ProcessInstance process = new ProcessInstance();
        process.setBusinessId(leave.getId());
        process.setProcessDefId(1L); // 请假流程定义ID
        // 3. 获取流程节点
        List<ApprovalNode> nodes = LeaveProcessConfig.defineLeaveProcess();
        // 4. 设置条件判断
        for (ApprovalNode node : nodes) {
            if (node.getNodeName().contains("人事")) {
                // 可以通过自定义字段控制是否需要该节点
                // 这里由approvalService中的forConditionHandle处理
            }
        }
        // 5. 发起审批
        approvalService.startProcess(process, nodes);
    }
    /**
     * 获取当前审批状态
     */
    public ApprovalStatus getCurrentStatus(Long leaveId) {
        LeaveRequest leave = leaveRepository.findById(leaveId)
            .orElseThrow(() -> new RuntimeException("请假申请不存在"));
        List<ApprovalRecord> records = approvalService.getRecordsByBusinessId(leaveId);
        ApprovalStatus status = new ApprovalStatus();
        status.setBusinessId(leaveId);
        status.setApplicant(leave.getApplicantId());
        status.setRecords(records);
        status.setCurrentStatus(leave.getStatus());
        return status;
    }
}

4 REST API 接口

@RestController
@RequestMapping("/api/approval")
public class ApprovalController {
    @Autowired
    private LeaveService leaveService;
    @Autowired
    private ApprovalService approvalService;
    /**
     * 提交请假审批
     */
    @PostMapping("/leave/submit")
    public Result submitLeave(@RequestBody LeaveRequest leave) {
        leaveService.submitLeaveRequest(leave);
        return Result.success("提交成功");
    }
    /**
     * 审批操作
     */
    @PostMapping("/process/{processId}")
    public Result approve( @PathVariable Long processId,
                           @RequestParam Long nodeId,
                           @RequestParam String action, // APPROVED/REJECTED
                           @RequestParam(required = false) String comment) {
        ApprovalResult result = approvalService.approve(
            processId, nodeId, action, comment, 
            getCurrentUserId()
        );
        return result.isSuccess() 
            ? Result.success(result.getMessage())
            : Result.fail(result.getMessage());
    }
    /**
     * 查询审批进度
     */
    @GetMapping("/progress/{businessId}")
    public Result getProgress(@PathVariable Long businessId) {
        ApprovalStatus status = leaveService.getCurrentStatus(businessId);
        return Result.success(status);
    }
}

5 数据库表结构

-- 流程定义表
CREATE TABLE process_definition (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    process_name VARCHAR(100) NOT NULL,
    description VARCHAR(500),
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 流程节点表
CREATE TABLE approval_node (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    process_id BIGINT NOT NULL,
    node_name VARCHAR(100) NOT NULL,
    node_type INT DEFAULT 1, -- 1:审批 2:会签 3:知会
    approver_id BIGINT,
    order_num INT NOT NULL,
    FOREIGN KEY (process_id) REFERENCES process_definition(id)
);
-- 流程实例表
CREATE TABLE process_instance (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    process_def_id BIGINT NOT NULL,
    business_id BIGINT NOT NULL,
    status VARCHAR(20) DEFAULT 'RUNNING', -- RUNNING, COMPLETED, TERMINATED
    current_node INT DEFAULT 1,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (process_def_id) REFERENCES process_definition(id)
);
-- 审批记录表
CREATE TABLE approval_record (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    business_id BIGINT NOT NULL,
    node_id BIGINT NOT NULL,
    status VARCHAR(20) DEFAULT 'PENDING', -- PENDING, APPROVED, REJECTED
    comment VARCHAR(500),
    operator_id BIGINT,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

进阶功能

1 会签功能

// 会签:需要多人审批通过
@Service
public class CountersignService {
    /**
     * 会签处理
     */
    public void handleCountersign(Long processId, Long nodeId) {
        // 1. 获取会签节点配置
        CountersignConfig config = getCountersignConfig(nodeId);
        // 2. 创建多个审批任务
        for (Long approverId : config.getApproverIds()) {
            createApprovalTask(processId, nodeId, approverId);
        }
        // 3. 设置计数规则
        int totalApprovers = config.getApproverIds().size();
        int approvedCount = countApproved(processId, nodeId);
        // 4. 判断是否达到会签条件
        if (approvedCount >= config.getPassCount()) {
            // 会签通过,进入下一节点
            advanceToNextNode(processId, nodeId);
        }
    }
}

2 驳回重审

// 驳回时可以选择回到指定节点
public ApprovalResult reject(Long processId, Long nodeId, 
                              String comment, Long rejectToNodeId) {
    // 1. 更新当前节点状态
    updateNodeStatus(nodeId, "REJECTED", comment);
    // 2. 删除中间节点的审批记录
    if (rejectToNodeId != null) {
        deleteRecordsBetween(processId, rejectToNodeId, nodeId);
    }
    // 3. 回退到指定节点
    ProcessInstance process = getProcess(processId);
    process.setCurrentNode(getNodeOrder(rejectToNodeId));
    saveProcess(process);
    // 4. 创建新的待审批记录
    createPendingRecord(processId, rejectToNodeId);
}

3 审批通知

// 使用观察者模式或事件驱动
@Component
public class ApprovalNotifier {
    @EventListener
    public void handleApprovalEvent(ApprovalEvent event) {
        switch (event.getType()) {
            case SUBMITTED:
                // 发送邮件通知审批人
                sendEmail(event.getApproverEmail(), "新的审批任务");
                break;
            case APPROVED:
                // 通知发起人
                sendSms(event.getApplicantPhone(), "审批通过");
                break;
            case REJECTED:
                // 通知发起人驳回原因
                sendNotification(event.getApplicantId(), "审批被驳回");
                break;
        }
    }
}

开发建议

代码组织

src/main/java/com/example/approval/
├── config/
│   └── ApprovalConfig.java        // 流程配置
├── controller/
│   └── ApprovalController.java    // REST接口
├── service/
│   ├── ApprovalService.java       // 审批核心服务
│   ├── LeaveService.java          // 业务服务
│   └── NotifyService.java         // 通知服务
├── model/
│   ├── ApprovalNode.java          // 节点实体
│   ├── ApprovalRecord.java        // 记录实体
│   └── ProcessInstance.java       // 流程实例
├── repository/
│   ├── ApprovalNodeRepository.java
│   ├── ApprovalRecordRepository.java
│   └── ProcessInstanceRepository.java
└── util/
    └── WorkflowEngine.java        // 工作流引擎

最佳实践

  1. 状态机模式:使用状态机管理流程状态
  2. 策略模式:处理不同审批规则
  3. 模板方法:固定审批流程骨架
  4. 事件驱动:使用Spring Event解耦
  5. 乐观锁:处理并发审批问题

这个案例展示了完整的审批流程开发,你可以根据实际需求扩展功能,如加签、转审、代理审批等高级功能。

抱歉,评论功能暂时关闭!