本文目录导读:

我来详细介绍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 // 工作流引擎
最佳实践
- 状态机模式:使用状态机管理流程状态
- 策略模式:处理不同审批规则
- 模板方法:固定审批流程骨架
- 事件驱动:使用Spring Event解耦
- 乐观锁:处理并发审批问题
这个案例展示了完整的审批流程开发,你可以根据实际需求扩展功能,如加签、转审、代理审批等高级功能。