在企业管理的世界里,审批流程就像是一条永不停歇的河流,承载着组织运转的重要决策。从请假条到报销单,从采购申请到合同审核,每一个环节都需要层层把关,确保万无一失。然而,传统的纸质审批流程往往如同一场漫长的马拉松,文件在各个部门之间辗转,审批人忙碌或出差时更是雪上加霜。
今天,就让我们一起走进代码的世界,看看如何用几百行Java代码,构建一个轻量级却功能强大的审批系统,让繁琐的流程变得简单高效。
一、审批系统的核心思想
想象一下,如果能用一个简单的注解,就能让普通的业务方法自动具备审批能力,是不是很酷?
@PostMapping("/submit")
@Approval(processCode = "LEAVE_APPROVAL", businessType = "LEAVE", businessKey = "#leaveId")
public String submitLeave(@RequestParam Long leaveId) {
return "请假申请已提交,等待审批";
}
没错,就是这么简单!只需在方法上添加@Approval注解,指定流程编码、业务类型和业务主键,这个方法就具备了发起审批的能力。当它被调用时,系统会自动创建一个审批实例,并按照预设的流程节点推进。
二、系统架构:麻雀虽小,五脏俱全
我们的审批系统虽然代码量不大,但结构清晰,功能完备。它主要包含以下几个部分:
注解层:提供了一系列注解,用于标记审批相关的操作
切面层:通过AOP拦截注解标记的方法,实现审批逻辑的自动处理
服务层:实现审批流程的核心业务逻辑
数据层:存储审批流程、节点、实例和记录等信息
2.1 注解:优雅的审批入口
系统提供了五种核心注解,分别对应审批流程中的不同操作:
@Approval // 发起审批
@Agree // 同意审批
@Reject // 拒绝审批
@Cancel // 取消审批
@Back // 退回审批
每个注解都包含相同的属性:
/**
* 审批注解
* 用于标记需要进行审批的方法
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Approval {
/**
* 流程编码
*/
String processCode();
/**
* 业务类型
*/
String businessType();
/**
* 业务主键的SpEL表达式
*/
String businessKey();
/**
* 审批意见
*/
String comment() default "";
}
2.2 切面:审批的魔法所在
审批切面是整个系统的核心,它通过AOP技术拦截带有审批注解的方法,提取注解中的信息,并在方法执行前后自动处理审批逻辑:
@Around("@annotation(cn.eking.approval.annotation.Approval)")
@Transactional(rollbackFor = Exception.class)
public Object aroundApproval(ProceedingJoinPoint point) throws Throwable {
return handleApproval(point, Approval.class);
}
切面的处理逻辑非常巧妙:先执行业务方法,如果业务方法执行成功,再处理审批逻辑;如果审批逻辑出错,则通过事务回滚整个操作,确保数据一致性。
// 先执行业务逻辑
Object result = point.proceed();
// 业务逻辑执行成功后,处理审批逻辑
if (annotation instanceof Approval) {
approvalService.startApproval(processCode, businessType, businessKey, comment);
} else if (annotation instanceof Agree) {
approvalService.agree(processCode, businessType, businessKey, comment);
}
// ... 其他审批操作
三、审批流程:从提交到完成的旅程
一个完整的审批流程通常包含以下几个步骤:
发起审批:创建审批实例,设置初始状态为"待审批"
审批处理:审批人进行同意、拒绝、退回等操作
流程流转:根据审批结果,流程向下一节点推进或结束
让我们看看发起审批的核心代码:
@Transactional(rollbackFor = Exception.class)
public void startApproval(String processCode, String businessType, String businessKey, String comment) {
// 获取流程定义
ApprovalProcess process = getProcess(processCode);
// 获取开始节点
ApprovalNode startNode = getStartNode(process.getId());
// 创建审批实例
ApprovalInstance instance = new ApprovalInstance();
instance.setProcessId(process.getId());
instance.setBusinessType(businessType);
instance.setBusinessKey(businessKey);
instance.setStatus(ApprovalStatusEnum.PENDING.name());
instance.setCurrentNodeId(startNode.getId());
instance.setCreatorId(userService.getCurrentUserId());
instance.setCreatorName(userService.getCurrentUserName());
instance.setCreateTime(LocalDateTime.now());
instance.setUpdateTime(LocalDateTime.now());
instanceMapper.insert(instance);
// 创建审批记录
createRecord(instance.getId(), startNode.getId(), ApprovalActionEnum.APPROVE, comment);
// 处理自动审批
handleAutoApprove(instance, startNode);
}
当审批人同意一个审批时,系统会执行以下逻辑:
@Transactional(rollbackFor = Exception.class)
public void agree(String processCode, String businessType, String businessKey, String comment) {
// 获取审批实例
ApprovalInstance instance = getInstance(processCode, businessType, businessKey);
// 获取当前节点
ApprovalNode currentNode = getNode(instance.getCurrentNodeId());
// 检查当前用户是否有权限审批
checkApprovalPermission(currentNode);
// 创建审批记录
createRecord(instance.getId(), currentNode.getId(), ApprovalActionEnum.AGREE, comment);
// 判断节点类型(普通节点或会签节点)并处理
// ...
// 获取下一个节点
ApprovalNode nextNode = getNextNode(instance.getProcessId(), currentNode.getOrderNum());
if (nextNode == null) {
// 没有下一个节点,流程结束
instance.setStatus(ApprovalStatusEnum.APPROVED.name());
instance.setCurrentNodeId(null);
} else {
// 设置下一个节点
instance.setCurrentNodeId(nextNode.getId());
// 处理自动审批
handleAutoApprove(instance, nextNode);
}
// 更新实例
instance.setUpdateTime(LocalDateTime.now());
instanceMapper.updateById(instance);
}
四、灵活多变的审批节点
审批系统支持多种类型的节点,满足不同场景的需求:
普通节点:由指定人员审批
会签节点:需要多人共同审批,支持"全部同意"、"任一同意"或"百分比同意"等规则
自动节点:系统自动审批,无需人工干预
节点的审批人也支持多种配置方式:
/**
* 审批人类型:USER-指定用户,ROLE-指定角色,DEPARTMENT-指定部门
*/
private String approverType;
/**
* 审批人ID,多个用逗号分隔
*/
private String approverValue;
这种设计使得系统能够灵活应对各种复杂的组织结构和审批场景。
五、实战案例:请假审批流程
让我们通过一个请假审批的例子,看看整个系统是如何工作的:
定义流程:在数据库中创建"请假审批流程"及其节点
-- 插入审批流程定义
INSERT INTO approval_process (process_code, process_name, process_type, status) VALUES
('LEAVE_APPROVAL', '请假审批流程', 'LEAVE', 1);
-- 插入请假审批流程的节点
INSERT INTO approval_node (process_id, node_code, node_name, node_type, approver_type, approver_value, auto_approve, order_num) VALUES
-- 开始节点
(1, 'START', '开始节点', 'START', 'USER', '1', 0, 1),
-- 直接主管审批
(1, 'DIRECT_MANAGER', '直接主管审批', 'APPROVAL', 'ROLE', 'MANAGER', 0, 2),
-- 部门经理审批
(1, 'DEPARTMENT_MANAGER', '部门经理审批', 'APPROVAL', 'ROLE', 'DEPT_MANAGER', 0, 3),
-- 人事审批
(1, 'HR_APPROVAL', '人事审批', 'APPROVAL', 'DEPARTMENT', 'HR', 0, 4),
-- 结束节点
(1, 'END', '结束节点', 'END', 'USER', '1', 0, 5);
创建控制器:提供请假审批的API接口
@RestController
@RequestMapping("/api/approval")
@RequiredArgsConstructor
public class ApprovalController {
private final ApprovalService approvalService;
/**
* 提交请假申请
*/
@PostMapping("/submit")
@Approval(processCode = "LEAVE_APPROVAL", businessType = "LEAVE", businessKey = "#leaveId")
public String submitLeave(@RequestParam Long leaveId) {
return "请假申请已提交,等待审批";
}
/**
* 同意请假申请
*/
@PostMapping("/agree")
@Agree(processCode = "LEAVE_APPROVAL", businessType = "LEAVE", businessKey = "#leaveId")
public String agreeLeave(@RequestParam Long leaveId) {
return "请假申请已同意";
}
/**
* 拒绝请假申请
*/
@PostMapping("/reject")
@Reject(processCode = "LEAVE_APPROVAL", businessType = "LEAVE", businessKey = "#leaveId")
public String rejectLeave(@RequestParam Long leaveId) {
return "请假申请已拒绝";
}
}
流程执行:
员工提交请假申请,系统创建审批实例,状态为"待审批"
直接主管收到审批任务,进行审批
如果同意,流程流转到部门经理;如果拒绝,流程结束
部门经理审批同意后,流转到人事审批
人事审批同意后,流程结束,状态变为"已通过"
六、系统的亮点与思考
这个轻量级审批系统虽然代码不多,但设计精巧,有几个值得称赞的亮点:
注解驱动:通过注解实现审批功能,对业务代码几乎零侵入
AOP切面:利用Spring AOP实现审批逻辑与业务逻辑的分离
事务管理:确保业务操作与审批操作的原子性
灵活配置:支持多种节点类型和审批人配置方式
当然,系统也有一些可以改进的地方:
流程定义:可以支持可视化的流程设计
审批规则:可以增加更复杂的条件判断和分支流转
消息通知:集成消息推送,及时通知审批人
移动端支持:提供移动端API,实现随时随地审批
结语
从纸质流程到电子审批,从繁琐操作到一键处理,技术的力量正在改变企业的运作方式。这个小巧而精致的审批系统,展示了如何用简洁的代码解决复杂的业务问题。
在代码的世界里,我们不仅仅是在编写程序,更是在创造改变工作方式的工具。希望这个审批系统能给你带来启发,让你在自己的项目中也能设计出优雅而实用的解决方案。
记住,好的系统不在于代码的多少,而在于思想的精妙。正如这个审批系统所展示的,有时候,几百行代码就能实现一个企业级的功能模块,关键在于如何巧妙地利用已有的技术和框架,设计出简洁而强大的解决方案。
下次当你面对一个看似复杂的业务需求时,不妨试着从简单入手,寻找那个优雅的设计点,或许你会发现,复杂问题往往有着出人意料的简单解法。