本文目录导读:

我来详细介绍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解耦
- 乐观锁:处理并发审批问题
这个案例展示了完整的审批流程开发,你可以根据实际需求扩展功能,如加签、转审、代理审批等高级功能。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播