Salesforce Lead同步至SAP Hybris:REST API实现及批量处理咨询
问题解答与源码示例
核心疑问解答
1. 是否需要批量处理?
必须做批量处理。Salesforce的Trigger默认是批量触发(例如批量导入、数据批量更新场景),如果每次只处理单条Lead,会导致多次重复调用SAP API,不仅会触发Salesforce的API请求限流,也会给SAP Hybris带来不必要的请求压力。批量处理能将多条Lead数据一次性打包发送,提升两端的处理效率。
2. 是否需要Wrapper Class?
强烈推荐使用Wrapper Class。原因有两点:
- SAP Hybris的API通常有特定的数据格式要求,直接序列化Salesforce的Lead对象会包含大量无用字段,且无法直接关联Account数据;Wrapper可以精准定义要发送的字段和嵌套结构,完全匹配SAP的接口规范。
- Wrapper能让代码结构更清晰,便于维护和扩展(比如后续新增字段或调整数据结构)。
适配SAP Hybris的完整实现代码
1. Lead Trigger 触发类
trigger LeadSyncToSAP on Lead (after insert, after update) { // 过滤需要同步的Lead(可根据业务规则调整,比如仅同步特定状态的Lead) List<Lead> syncLeads = new List<Lead>(); for(Lead l : Trigger.new) { // 示例:仅同步已转换或特定来源的Lead,可根据业务修改 if(l.IsConverted || l.LeadSource == 'Web') { syncLeads.add(l); } } if(!syncLeads.isEmpty()) { // 异步执行避免触发同步超时 LeadSyncToSAPHandler.syncLeadsWithSAP(syncLeads); } }
2. 同步处理核心类
public class LeadSyncToSAPHandler { // 建议使用Named Credential存储SAP的认证信息和端点,避免硬编码 private static final String SAP_OAUTH_ENDPOINT = 'https://your-sap-hybris-domain.com/oauth/token'; private static final String SAP_API_ENDPOINT = 'https://your-sap-hybris-domain.com/api/v1/leads'; private static final String SAP_CLIENT_ID = 'your-sap-client-id'; private static final String SAP_CLIENT_SECRET = 'your-sap-client-secret'; @future(callout=true) public static void syncLeadsWithSAP(List<Lead> leadList) { // 1. 获取SAP的访问Token SAPAccessToken token = getSAPAccessToken(); if(token == null || String.isBlank(token.access_token)) { System.debug('获取SAP Token失败,终止同步'); // 可添加错误日志或告警逻辑 return; } // 2. 批量构建符合SAP格式的请求数据 List<SAPLeadRequestWrapper> sapRequestList = new List<SAPLeadRequestWrapper>(); // 查询关联的Account数据(仅针对已转换的Lead) Set<Id> convertedAccountIds = new Set<Id>(); for(Lead l : leadList) { if(l.IsConverted) { convertedAccountIds.add(l.ConvertedAccountId); } } Map<Id, Account> accountMap = new Map<Id, Account>([SELECT Id, Name, BillingStreet, BillingCity, BillingPostalCode, Phone FROM Account WHERE Id IN :convertedAccountIds]); // 构建每个Lead的请求结构 for(Lead l : leadList) { SAPLeadRequestWrapper req = new SAPLeadRequestWrapper(); // Lead核心字段(根据SAP接口要求调整) req.leadId = l.Id; req.leadName = l.FirstName + ' ' + l.LastName; req.email = l.Email; req.phone = l.Phone; req.status = l.Status; req.leadSource = l.LeadSource; // 关联Account数据(仅已转换的Lead) if(l.IsConverted && accountMap.containsKey(l.ConvertedAccountId)) { Account acc = accountMap.get(l.ConvertedAccountId); req.associatedAccount = new SAPAccountWrapper(); req.associatedAccount.accountId = acc.Id; req.associatedAccount.accountName = acc.Name; req.associatedAccount.address = acc.BillingStreet + ', ' + acc.BillingCity + ' ' + acc.BillingPostalCode; req.associatedAccount.phone = acc.Phone; } sapRequestList.add(req); } // 3. 批量发送请求到SAP Hybris String requestBody = JSON.serialize(sapRequestList); HttpRequest req = new HttpRequest(); req.setEndpoint(SAP_API_ENDPOINT); req.setMethod('POST'); req.setHeader('Authorization', 'Bearer ' + token.access_token); req.setHeader('Content-Type', 'application/json'); req.setBody(requestBody); try { HTTP http = new HTTP(); HttpResponse res = http.send(req); System.debug('SAP API响应状态码:' + res.getStatusCode()); System.debug('SAP API响应内容:' + res.getBody()); // 处理响应结果,比如记录同步状态到Lead字段 if(res.getStatusCode() == 201 || res.getStatusCode() == 200) { // 示例:更新Lead的同步状态字段 List<Lead> updateLeads = new List<Lead>(); for(Lead l : leadList) { l.SAP_Sync_Status__c = '已同步'; updateLeads.add(l); } update updateLeads; } else { System.debug('SAP API调用失败:' + res.getBody()); // 可添加重试逻辑或错误记录 } } catch(Exception e) { System.debug('API调用异常:' + e.getMessage()); // 异常处理逻辑 } } // 获取SAP OAuth Token的方法 private static SAPAccessToken getSAPAccessToken() { String requestBody = 'grant_type=client_credentials&client_id=' + SAP_CLIENT_ID + '&client_secret=' + SAP_CLIENT_SECRET; HttpRequest req = new HttpRequest(); req.setEndpoint(SAP_OAUTH_ENDPOINT); req.setMethod('POST'); req.setHeader('Content-Type', 'application/x-www-form-urlencoded'); req.setBody(requestBody); try { HTTP http = new HTTP(); HttpResponse res = http.send(req); if(res.getStatusCode() == 200) { return (SAPAccessToken)JSON.deserialize(res.getBody(), SAPAccessToken.class); } else { System.debug('获取Token失败:' + res.getBody()); return null; } } catch(Exception e) { System.debug('获取Token异常:' + e.getMessage()); return null; } } // SAP Token的Wrapper类 public class SAPAccessToken { public String access_token; public String token_type; public Integer expires_in; } // 适配SAP接口的Lead请求Wrapper类 public class SAPLeadRequestWrapper { public String leadId; public String leadName; public String email; public String phone; public String status; public String leadSource; public SAPAccountWrapper associatedAccount; } // 关联Account的Wrapper类 public class SAPAccountWrapper { public String accountId; public String accountName; public String address; public String phone; } }
对现有代码的优化建议
- 避免硬编码凭证:不要直接在代码里写Client ID、Secret等敏感信息,建议使用Named Credential或Custom Setting存储,提升安全性和可维护性。
- 异步执行:Trigger中直接调用同步API容易导致超时,示例中使用
@future方法实现异步执行,也可选择Queueable Apex更灵活地处理重试。 - 错误处理与重试:添加异常捕获机制,对于API调用失败的情况,可结合Platform Event或重试框架实现重试,避免数据丢失。
- 字段过滤:不要直接序列化整个Lead对象,只发送SAP需要的字段,减少数据传输量和接口报错概率。
内容的提问来源于stack exchange,提问作者Sourabh Aglave
相关产品推荐
相关产品推荐

