在Salesforce平台中,审批流程是企业运营的核心功能之一。无论是请假申请、费用报销,还是合同审批,审批流程确保了业务的规范性和可控性。今天,我们将深入探讨如何通过Apex代码实现审批流程的自动化,并解析一个完整的实战示例。
一、为什么需要自动化审批流程?
在现实业务场景中,审批流程往往需要:
- 批量处理:同时提交多个记录的审批
- 系统集成:与外部系统联动触发审批
- 定时任务:在特定时间自动提交审批
- 测试环境:自动化测试审批流程逻辑
手动操作无法满足这些需求,这时Apex自动化就变得至关重要。
二、审批流程的核心组件
在深入代码之前,我们先了解几个关键概念:
| 组件 | 说明 | 类比 |
|---|---|---|
| Approval.ProcessSubmitRequest | 提交审批请求的对象 | 相当于填写申请表 |
| Approval.ProcessWorkitemRequest | 处理待审批项的对象 | 相当于审批人做决策 |
| ProcessResult | 操作结果对象 | 返回操作成功与否 |
| Workitem | 待办审批任务 | 审批人收件箱中的任务 |
三、完整代码示例解析
让我们逐段分析这个经典的审批流程自动化示例:
1. 创建记录并提交审批
// 1. 创建测试账户
Account a = new Account(Name='Test', annualRevenue=100.0);
insert a;
// 2. 构建提交请求
Approval.ProcessSubmitRequest req1 = new Approval.ProcessSubmitRequest();
req1.setComments('提交审批申请'); // 添加说明
req1.setObjectId(a.id); // 设置要审批的记录
req1.setSubmitterId(user1.Id); // 指定提交人
req1.setProcessDefinitionNameOrId('PTO_Request_Process'); // 指定流程
req1.setSkipEntryCriteria(true); // 跳过进入条件
// 3. 执行提交
Approval.ProcessResult result = Approval.process(req1);
关键点说明:
setSkipEntryCriteria(true):在测试时特别有用,可以绕过审批流程的进入条件,确保记录能被提交ProcessDefinitionNameOrId:必须与配置的审批流程名称完全匹配
2. 获取并处理待审批项
// 4. 获取新创建的待办项ID
List<Id> newWorkItemIds = result.getNewWorkitemIds();
// 5. 构建处理请求
Approval.ProcessWorkitemRequest req2 = new Approval.ProcessWorkitemRequest();
req2.setComments('批准通过'); // 审批意见
req2.setAction('Approve'); // 操作类型
req2.setNextApproverIds(new Id[] {UserInfo.getUserId()}); // 下一审批人 不填默认走Approval Process配置的审批人
req2.setWorkitemId(newWorkItemIds.get(0)); // 指定待办项
// 6. 执行批准
Approval.ProcessResult result2 = Approval.process(req2);
操作类型选项:
Approve:批准Reject:拒绝Removed:移除Reassign:重新分配
四、实际业务场景应用
场景一:自动化的费用报销流程
public class ExpenseApprovalAutomation {
public static void submitExpenseForApproval(Id expenseReportId, Id submitterId) {
// 验证记录状态
Expense_Report__c report = [
SELECT Id, Status__c, Total_Amount__c
FROM Expense_Report__c
WHERE Id = :expenseReportId
];
// 只有草稿状态且金额大于1000才需要审批
if (report.Status__c == 'Draft' && report.Total_Amount__c > 1000) {
Approval.ProcessSubmitRequest req = new Approval.ProcessSubmitRequest();
req.setObjectId(expenseReportId);
req.setSubmitterId(submitterId);
req.setProcessDefinitionNameOrId('Expense_Approval_Process');
try {
Approval.ProcessResult result = Approval.process(req);
if (result.isSuccess()) {
System.debug('费用报销单已成功提交审批');
}
} catch(Exception e) {
System.debug('提交审批失败: ' + e.getMessage());
}
}
}
}
场景二:批量审批处理
public class BatchApprovalProcessor {
public static void approveMultipleRequests(List<Id> workItemIds) {
List<Approval.ProcessWorkitemRequest> requests = new List<Approval.ProcessWorkitemRequest>();
for (Id workItemId : workItemIds) {
Approval.ProcessWorkitemRequest req = new Approval.ProcessWorkitemRequest();
req.setWorkitemId(workItemId);
req.setAction('Approve');
req.setComments('系统自动批准 - 符合快速审批条件');
requests.add(req);
}
// 批量处理所有审批请求
List<Approval.ProcessResult> results = Approval.process(requests);
// 记录处理结果
for (Integer i = 0; i < results.size(); i++) {
if (!results[i].isSuccess()) {
System.debug('审批失败,工作项ID: ' + workItemIds[i]);
}
}
}
}
五、高级技巧与最佳实践
1. 动态选择审批流程
// 根据金额选择不同的审批流程
public static String determineApprovalProcess(Decimal amount) {
if (amount <= 1000) return 'Quick_Approval_Process';
if (amount <= 5000) return 'Standard_Approval_Process';
return 'Executive_Approval_Process';
}
2. 错误处理与重试机制
public static ProcessResult submitWithRetry(Approval.ProcessSubmitRequest req, Integer maxRetries) {
Integer retryCount = 0;
while (retryCount < maxRetries) {
try {
return Approval.process(req);
} catch(Exception e) {
retryCount++;
if (retryCount == maxRetries) {
throw e; // 达到重试上限
}
// 等待后重试
Integer delay = 1000 * retryCount; // 递增延迟
Long now = DateTime.now().getTime();
while (DateTime.now().getTime() < now + delay) {
// 等待
}
}
}
return null;
}
3. 审批状态监控
public static void monitorApprovalStatus(Id recordId) {
// 查询审批过程实例
ProcessInstance instance = [
SELECT Id, Status, (SELECT Id, ActorId, Comments
FROM ProcessInstanceSteps
ORDER BY CreatedDate DESC)
FROM ProcessInstance
WHERE TargetObjectId = :recordId
ORDER BY CreatedDate DESC
LIMIT 1
];
// 记录审批历史
for (ProcessInstanceStep history : instance.ProcessInstanceSteps) {
System.debug('审批步骤: ' + history.StepStatus +
', 处理人: ' + history.ActorId +
', 意见: ' + history.Comments);
}
}
六、常见问题与解决方案
Q1: setSkipEntryCriteria(true) 在生产环境使用安全吗?
A: 不建议在生产环境使用。这个参数主要用于测试。在生产环境,应该确保记录自然满足审批流程的进入条件。
Q2: 如何获取用户的待审批项?
// 查询当前用户的待审批项
List<ProcessInstanceWorkitem> workItems = [
SELECT Id, ProcessInstance.TargetObjectId, ProcessInstance.Status
FROM ProcessInstanceWorkitem
WHERE ActorId = :UserInfo.getUserId()
];
Q3: 审批被拒绝后如何重新提交?
public static void recallAndResubmit(Id recordId) {
// 1. 先召回
ProcessInstanceWorkitem workItem = [
SELECT Id FROM ProcessInstanceWorkitem
WHERE ProcessInstance.TargetObjectId = :recordId
LIMIT 1
];
Approval.ProcessWorkitemRequest recallReq = new Approval.ProcessWorkitemRequest();
recallReq.setWorkitemId(workItem.Id);
recallReq.setAction('Removed');
Approval.process(recallReq);
// 2. 修改记录后重新提交
// ... 修改记录逻辑
// ... 重新提交审批
}
总结
Salesforce的审批流程自动化是一个强大而灵活的功能,通过Apex代码,我们可以实现高度定制化的审批逻辑。无论是简单的单步审批,还是复杂的多级动态审批,Apex都提供了完整的API支持。
关键要点:
- 理解审批流程的生命周期
- 合理使用提交和处理API
- 在生产环境避免跳过进入条件
- 实现完善的错误处理和日志记录
掌握这些技能后,你将能够构建出既符合业务需求,又具备良好用户体验的审批系统。