您位於此處:
建立自訂轉介朋友小工具
在「轉介促銷」引導式流程上,行銷經理可以變更「轉介朋友」小工具的樣式和文字,但無法新增客戶輸入或自訂小工具狀態。若要自訂業務邏輯或新增元件 (例如行動電話號碼欄位、同意核取方塊或條款和條件),請建立並自訂小工具的 Apex 類別。接著,更新小工具的資料來源,讓「轉介行銷」在使用者存取小工具時使用該類別。
必要版本
| 提供版本:Lightning Experience |
| 提供版本:具備推薦行銷的 Enterprise、Performance、Unlimited 及 Developer Edition |
- 使用 範例 Apex 類別程式碼,為小工具建立 Apex 類別。
- 在 Flexcard 上設定 Apex 遠端資料來源。
- 從 Flexcard 上的動作更新資料來源。
- 建立推薦好友小工具的範例 Apex 類別
範例 Apex 類別,您可以自訂以建立符合您業務需求的自訂小工具。
建立推薦好友小工具的範例 Apex 類別
範例 Apex 類別,您可以自訂以建立符合您業務需求的自訂小工具。
global with sharing class WidgetReferAFriend implements System.Callable {
Map<String, String> messages = new Map<String, String>();
global Object call(String action, Map<String, Object> arguments) {
Map<String, Object> input = (Map<String, Object>)arguments.get('input');
Map<String, Object> output = (Map<String, Object>)arguments.get('output');
Map<String, Object> options = (Map<String, Object>)arguments.get('options');
initMessages(output);
if (action == 'load') {
return load(input, output, options);
}
if (action == 'enrollNew') {
return enrollNew(input, output, options);
}
if (action == 'enrollExisting') {
return enrollExisting(input, output, options);
}
if (action == 'sendMail') {
return sendMail(input, output, options);
}
return null;
}
private void initMessages(Map<String, Object> output) {
output.put('errorMessage', null);
output.put('successMessage', null);
messages.put('GenericError', 'We couldn’t load the widget details. Refresh the page or try again later.');
messages.put('GenericSuccess', 'Emails sent to your referrals.');
messages.put('InvalidProgramAndPromo', 'We couldn’t get the details of the referral program. Try again or contact your Salesforce admin for help.');
messages.put('InvalidContactAndMail', 'We couldn’t load the widget details because the specified email ID and contact ID don’t belong to the same Contact record. Specify the email ID and the ID of the same Contact record and try again.');
messages.put('InvalidContact', 'We couldn’t load the widget because you specified the details of a Contact record that isn’t available in the org. Specify the details of a valid Contact record and try again.');
messages.put('InvalidUser', 'We couldn’t load the widget because a Contact record isn’t associated with the person viewing the widget. Try again after you associate a Contact record with the user viewing the widget.');
messages.put('DuplicateMembers', 'We couldn’t load the widget because we found two or more referral program members with the specified details. Specify the details of a unique referral program member and try again.');
messages.put('NoContactForMail', 'We couldn’t load the widget because the specified email ID doesn’t belong to a contact. Specify the email ID of a contact and try again.');
messages.put('CannotCreateMember', 'We couldn’t create a member record for the customer. Try again.');
messages.put('InvalidEmailFormat', 'Enter a valid email address and try again.');
messages.put('MaxEmailsSent', 'Specify up to 200 email addresses in the referralEmails field and try again.');
messages.put('DetailsDoNotMatch', 'We couldn’t load the widget because we couldn’t find the details of the contact associated with the specified email ID. Specify another email ID and try again.');
messages.put('CannotCreateContact', 'We couldn’t create a Contact record for the customer. Try again.');
messages.put('MissingDetails', 'We couldn’t load the widget because you haven’t specified values for all the required fields. Specify values for all the required fields and try again.');
messages.put('PromotionNotStarted', 'We couldn’t load the widget because the start date of the promotion associated with the widget is in the future. Specify the code of a promotion that’s currently running and try again.');
messages.put('PromotionExpired', 'We couldn’t load the widget because the end date of the promotion associated with the widget is in the past. Specify the code of a promotion that’s currently running and try again.');
}
private Map<String, Object> load(Map<String, Object> input, Map<String, Object> output, Map<String, Object> options) {
Long startTimeInMillis = System.currentTimeMillis();
String contactId = (String)input.get('contactId');
String promotionCode = (String)input.get('promotionCode');
String programName = (String)input.get('programName');
String emailId = (String)input.get('emailId');
String userId = (String)input.get('userId');
String url = (String)input.get('url');
if (!isUserIdEmpty(userId)) {
contactId = [SELECT Id, ContactId FROM User
WHERE Id =:userId
WITH SECURITY_ENFORCED LIMIT 1].ContactId;
if (contactId == null) {
output.put('errorMessage', messages.get('InvalidUser'));
output.put('state', 'ErrorState');
return output;
}
}
Map<String, Object> programAndPromoDetails = validateAndGetProgramAndPromotion(programName, promotionCode);
if (programAndPromoDetails == null) {
// Error state. Invalid program or promotion.
output.put('errorMessage', messages.get('InvalidProgramAndPromo'));
output.put('state', 'ErrorState');
} else {
Map<String, Object> details = validatePromotionDate(programAndPromoDetails);
if (details.get('errorMessage') != null) {
output.put('errorMessage', details.get('errorMessage'));
output.put('state', 'ErrorState');
}
else {
output.put('programId', (String)programAndPromoDetails.get('programId'));
output.put('programName', programName);
output.put('promotionId', (String)programAndPromoDetails.get('promotionId'));
output.put('promotionCode', (String)programAndPromoDetails.get('promotionCode'));
output.put('startDate', (String)programAndPromoDetails.get('startDate'));
output.put('endDate', (String)programAndPromoDetails.get('endDate'));
output.put('refLink', (String)programAndPromoDetails.get('promotionPageUrl'));
if (!isContactIdEmpty(contactId) || !isEmailIdEmpty(emailId)) {
Contact contactRecord = null;
// Give precedence to contact id.
if (!isContactIdEmpty(contactId)) {
List<Contact> contacts = [SELECT Id, FirstName, LastName, Email FROM Contact where Id = :contactId WITH SECURITY_ENFORCED];
if (contacts.size() == 1) {
// If an email was provided, check if the passed email matches with contact's email.
if (isEmailIdEmpty(emailId) || contacts.get(0).Email == emailId) {
contactRecord = contacts.get(0);
} else {
// Error State - Invalid Contact Id and Mail Combination.
output.put('errorMessage', messages.get('InvalidContactAndMail'));
output.put('state', 'ErrorState');
}
} else {
// Error State - Invalid contact Id
output.put('errorMessage', messages.get('InvalidContact'));
output.put('state', 'ErrorState');
}
} else if (!isEmailIdEmpty(emailId)) {
List<Contact> contacts = [SELECT Id FROM Contact where Email = :emailId WITH SECURITY_ENFORCED];
if (contacts.size() == 1) {
contactRecord = contacts.get(0);
} else if (contacts.size() > 0) {
// Error State - Duplicate members
output.put('errorMessage', messages.get('DuplicateMembers'));
output.put('state', 'ErrorState');
} else {
// Error State - No contact with email address found.
output.put('errorMessage', messages.get('NoContactForMail'));
output.put('state', 'ErrorState');
}
}
if (contactRecord != null) {
output.put('contactId', contactRecord.Id);
// Check if the record has a Program Member associated with it.
List<LoyaltyProgramMember> loyaltyProgramMembers = [SELECT Id, ReferralCode, MembershipNumber FROM LoyaltyProgramMember where programId = :(String)programAndPromoDetails.get('programId') and contactId = :contactRecord.Id WITH SECURITY_ENFORCED];
if (loyaltyProgramMembers.size() != 0) {
boolean validMemberPromotionExists = false;
for (LoyaltyProgramMember loyaltyProgramMember : loyaltyProgramMembers) {
String loyaltyProgramMemberId = loyaltyProgramMember.Id;
List<LoyaltyProgramMbrPromotion> loyaltyProgramMemberPromotions = [SELECT Id, LoyaltyProgramMemberId, PromotionId, IsAutoEnrolled, IsEnrollmentActive FROM LoyaltyProgramMbrPromotion where promotionId = :(String)programAndPromoDetails.get('promotionId') and loyaltyProgramMemberId = :loyaltyProgramMemberId WITH SECURITY_ENFORCED];
if (loyaltyProgramMemberPromotions.size() != 0 && (loyaltyProgramMemberPromotions.get(0).IsEnrollmentActive || loyaltyProgramMemberPromotions.get(0).IsAutoEnrolled)) {
validMemberPromotionExists = true;
// Show Sharing State
output.put('referralCode', loyaltyProgramMember.ReferralCode);
output.put('promotionCode', (String)programAndPromoDetails.get('promotionCode'));
output.put('state', 'SharingState');
break;
}
}
if (!validMemberPromotionExists) {
// Show Enroll State
output.put('state', 'EnrollState');
}
} else {
// Show Enroll State
output.put('state', 'EnrollState');
}
}
} else if (isContactIdEmpty(contactId) && isEmailIdEmpty(emailId)) {
// Show Form State
output.put('state', 'FormState');
}
}
}
return output;
}
private Map<String, Object> enrollNew(Map<String, Object> input, Map<String, Object> output, Map<String, Object> options) {
Long startTimeInMillis = System.currentTimeMillis();
String programName = (String)input.get('programName');
String programId = (String)input.get('programId');
String promotionCode = (String)input.get('promotionCode');
String firstName = (String)input.get('firstName');
String lastName = (String)input.get('lastName');
String emailId = (String)input.get('emailId');
Contact contactRecord = null;
if (!String.isEmpty(promotionCode) && !String.isEmpty(programName) && !String.isEmpty(lastName) && !String.isEmpty(emailId)) {
// set inputs
ConnectApi.ReferralMemberEnrollmentInput referralMemberEnrollmentInput = new ConnectApi.ReferralMemberEnrollmentInput();
referralMemberEnrollmentInput.memberStatus = 'Active';
ConnectApi.MemberPersonAccountInput personAccountInput = new ConnectApi.MemberPersonAccountInput();
personAccountInput.firstName = firstName;
personAccountInput.lastName = lastName;
personAccountInput.email = emailId;
personAccountInput.allowDuplicateRecords = false;
referralMemberEnrollmentInput.associatedPersonAccountDetails = personAccountInput;
try {
ConnectApi.ReferralMemberEnrolmentOutput referralMemberOutput = ConnectApi.ReferralManagementConnect.enrollReferralMember(programName,
promotionCode, referralMemberEnrollmentInput);
output.put('referralCode', referralMemberOutput.promotionReferralCode.split('-')[0]);
output.put('promotionCode', promotionCode);
output.put('state', 'SharingState');
} catch(ConnectApi.ConnectApiException e) {
output.put('errorMessage', e.getMessage());
output.put('state', 'ErrorState');
}
} else {
// Show Error State - Invalid params.
output.put('errorMessage', messages.get('MissingDetails'));
output.put('state', 'ErrorState');
}
return output;
}
private Map<String, Object> enrollExisting(Map<String, Object> input, Map<String, Object> output, Map<String, Object> options) {
Long startTimeInMillis = System.currentTimeMillis();
String contactId = (String)input.get('contactId');
String programId = (String)input.get('programId');
String promotionId = (String)input.get('promotionId');
String promotionCode = (String)input.get('promotionCode');
String programName = (String)input.get('programName');
if (!isContactIdEmpty(contactId) && !String.isEmpty(promotionCode) && !String.isEmpty(programId)) {
if (String.isBlank(programName)) {
List<LoyaltyProgram> programs = [SELECT Name FROM LoyaltyProgram where Id = :programId WITH SECURITY_ENFORCED];
programName = programs.get(0).Name;
}
ConnectApi.ReferralMemberEnrollmentInput referralMemberEnrollmentInput = new ConnectApi.ReferralMemberEnrollmentInput();
// set inputs
referralMemberEnrollmentInput.contactId = contactId;
referralMemberEnrollmentInput.memberStatus = 'Active';
try {
ConnectApi.ReferralMemberEnrolmentOutput referralMemberOutput = ConnectApi.ReferralManagementConnect.enrollReferralMember(programName,
promotionCode, referralMemberEnrollmentInput);
output.put('referralCode', referralMemberOutput.promotionReferralCode.split('-')[0]);
output.put('promotionCode', promotionCode);
output.put('state', 'SharingState');
} catch(ConnectApi.ConnectApiException e) {
output.put('errorMessage', e.getMessage());
output.put('state', 'ErrorState');
}
} else {
// Show Error State - Invalid params.
output.put('errorMessage', messages.get('MissingDetails'));
output.put('state', 'ErrorState');
}
return output;
}
private Map<String, Object> sendMail(Map<String, Object> input, Map<String, Object> output, Map<String, Object> options) {
Long startTimeInMillis = System.currentTimeMillis();
String promotionCode = (String)input.get('promotionCode');
String referralCode = (String)input.get('referralCode');
String emails = (String)input.get('emails');
List<String> emailsList = emails.split(',');
if (emailsList.size() > 200) {
output.put('errorMessage', messages.get('MaxEmailsSent'));
output.put('promotionCode', promotionCode);
output.put('referralCode', referralCode);
output.put('state', 'SharingState');
}
if (emailsList.size() > 0) {
ConnectApi.ReferralEventInput reInput = new ConnectApi.ReferralEventInput();
reInput.eventType = ConnectApi.EventTypeResource.Refer;
reInput.referralCode = referralCode + '-' + promotionCode;
reInput.referralEmails = emailsList;
reInput.joiningDate = date.today();
reInput.referralAdditionalAttributes = new Map<String, String>();
try{
ConnectApi.ReferralEventOutput resp = ConnectApi.ReferralEventConnect.referralEvent(reInput);
output.put('emails', '');
output.put('successMessage', messages.get('GenericSuccess'));
} catch (ConnectApi.ConnectApiException e){
output.put('errorMessage', e.getMessage());
output.put('state', 'SharingState');
}
output.put('promotionCode', promotionCode);
output.put('referralCode', referralCode);
}
return output;
}
private Map<String, Object> validateAndGetProgramAndPromotion(String programName, String promotionCode) {
List<LoyaltyProgram> programs = [SELECT Id FROM LoyaltyProgram where Name = :programName WITH SECURITY_ENFORCED];
if (programs.size() != 1) {
return null;
}
List<Promotion> promotions = [SELECT Id, PromotionCode, PromotionPageUrl, StartDate, EndDate FROM Promotion where PromotionCode = :promotionCode AND LoyaltyProgramId = :(String)programs.get(0).Id WITH SECURITY_ENFORCED];
if (promotions.size() == 0) {
return null;
}
Map<String, Object> details = new Map<String, Object>();
DateTime startDate = DateTime.newInstance(promotions.get(0).Startdate.year(), promotions.get(0).Startdate.month(), promotions.get(0).Startdate.day());
DateTime endDate = null;
if (promotions.get(0).EndDate != null) {
endDate = DateTime.newInstance(promotions.get(0).EndDate.year(), promotions.get(0).EndDate.month(), promotions.get(0).EndDate.day());
}
details.put('programId', programs.get(0).Id);
details.put('promotionId', promotions.get(0).Id);
if (endDate != null) {
details.put('endDate', String.valueOf(endDate));
}
String pageUrl = promotions.get(0).PromotionPageUrl != null ? promotions.get(0).PromotionPageUrl : '';
details.put('startDate', String.valueOf(startDate));
details.put('promotionCode', promotions.get(0).PromotionCode);
details.put('promotionPageUrl', pageUrl);
return details;
}
private Map<String, Object> validatePromotionDate(Map<String, Object> promotionDetails) {
Map<String, Object> details = new Map<String, Object>();
details.put('status', 'valid');
Date startDate = Date.valueOf((String)promotionDetails.get('startDate'));
Date endDate = null;
if (promotionDetails.get('endDate') != null) {
endDate = Date.valueOf((String)promotionDetails.get('endDate'));
}
if (startDate > Date.today()) {
details.put('errorMessage', messages.get('PromotionNotStarted'));
details.put('status', 'invalid');
} else if (endDate != null && endDate < Date.today()) {
details.put('errorMessage', messages.get('PromotionExpired'));
details.put('status', 'invalid');
}
return details;
}
private boolean validateForm(Map<String, Object> input) {
if (String.isEmpty((String)input.get('lastName')) || String.isEmpty((String)input.get('emailId'))) {
return false;
}
return true;
}
private boolean isUserIdEmpty(String userId) {
return (String.isEmpty(userId) || userId == '{Session.UserId}');
}
private boolean isContactIdEmpty(String contactId) {
return (String.isEmpty(contactId) || contactId == '{Session.ContactId}');
}
private boolean isEmailIdEmpty(String emailId) {
return (String.isEmpty(emailId) || emailId == '{Session.EmailId}');
}
private boolean isProgramIdEmpty(String programId) {
return String.isEmpty(programId);
}
private boolean isPromotionIdEmpty(String promotionId) {
return String.isEmpty(promotionId);
}
private boolean isLastNameEmpty(String lastName) {
return String.isEmpty(lastName);
}
private boolean isFirstNameEmpty(String firstName) {
return String.isEmpty(firstName);
}
private String generateRandomString(Integer len) {
final String chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
String randStr = '';
while (randStr.length() < len) {
Integer idx = Math.mod(Math.abs(Crypto.getRandomInteger()), chars.length());
randStr += chars.substring(idx, idx+1);
}
return randStr;
}
private LoyaltyProgramMember createMemberFromContact(String contactId, String programId, String promotionId) {
LoyaltyProgramMember loyaltyProgramMember = new LoyaltyProgramMember();
loyaltyProgramMember.contactId = contactId;
loyaltyProgramMember.ProgramId = programId;
loyaltyProgramMember.EnrollmentDate = date.today();
loyaltyProgramMember.memberStatus = 'Active';
boolean memberInsertedSuccessfully = false;
for (Integer retryAttempt = 0; retryAttempt < 3; retryAttempt++) {
try {
loyaltyProgramMember.MembershipNumber = generateRandomString(10);
INSERT loyaltyProgramMember;
memberInsertedSuccessfully = true;
break;
} catch(Exception ex) {
}
}
if (memberInsertedSuccessfully) {
List<LoyaltyProgramMember> loyaltyProgramMembers = [Select Id, ReferralCode, MembershipNumber from LoyaltyProgramMember where membershipnumber = :loyaltyProgramMember.MembershipNumber WITH SECURITY_ENFORCED];
createEnrollmentTransactionJournal(loyaltyProgramMembers.get(0).Id, programId, promotionId);
return loyaltyProgramMembers.get(0);
}
return null;
}
private void createEnrollmentTransactionJournal(String memberId, String programId, String promotionId) {
String jTypeId;
String jsTypeId;
String programName;
List<LoyaltyProgram> lp = [Select Name from LoyaltyProgram where Id = :programId];
if(lp.size() > 0) {
programName = lp.get(0).Name;
}
List<JournalType> journalTypes = [Select Id from JournalType where Name = 'Referral'];
if (journalTypes.size() > 0) {
jTypeId = journalTypes.get(0).Id;
}
List<JournalSubType> journalSubTypes = [Select Id from JournalSubType where Name = 'Advocate Enrollment' and JournalType.Name = 'Referral'];
if (journalSubTypes.size() > 0) {
jsTypeId = journalSubTypes.get(0).Id;
}
TransactionJournal tj = new TransactionJournal();
tj.JournalTypeId = jTypeId;
tj.JournalSubTypeId = jsTypeId;
tj.MemberId = memberId;
tj.PromotionId = promotionId;
tj.ActivityDate = datetime.now();
tj.Status = 'Pending';
INSERT tj;
processTransactionJournal(tj.Id, programName);
}
private void processTransactionJournal(String transactionJournalId, String programName) {
Invocable.Action action = Invocable.Action.createStandardAction('runProgramProcessForTransactionJournal');
action.setInvocationParameter('transactionJournalId', transactionJournalId);
List<Invocable.Action.Result> results = action.invoke();
for (Invocable.Action.Result result : results) {
if (result.isSuccess()) {
System.debug('Success: Transaction Journal processed successfully.');
} else {
System.debug('Error: ' + result.getErrors()[0].getMessage());
}
}
}
}範例
如果您想要在小工具中包含「連絡人」上的自訂欄位,例如「目前城市」,則以下是 Apex 類別的更新片段:
// In private Map<String, Object> load(...)
// ... (existing code)
String emailId = (String)input.get('emailId');
String userId = (String)input.get('userId');
String url = (String)input.get('url');
String currentCity = (String)input.get('currentCity'); // <--- ADD THIS LINE (Optional)
// ... (rest of the code)
private Map<String, Object> enrollNew(Map<String, Object> input, Map<String, Object> output, Map<String, Object> options) {
Long startTimeInMillis = System.currentTimeMillis();
String programName = (String)input.get('programName');
String programId = (String)input.get('programId');
String promotionCode = (String)input.get('promotionCode');
String firstName = (String)input.get('firstName');
String lastName = (String)input.get('lastName');
String emailId = (String)input.get('emailId');
String currentCity = (String)input.get('currentCity'); // ADDED: Get new input
Contact contactRecord = null;
if (!String.isEmpty(promotionCode) && !String.isEmpty(programName) && !String.isEmpty(lastName) && !String.isEmpty(emailId)) {
// set inputs
ConnectApi.ReferralMemberEnrollmentInput referralMemberEnrollmentInput = new ConnectApi.ReferralMemberEnrollmentInput();
referralMemberEnrollmentInput.memberStatus = 'Active';
ConnectApi.MemberPersonAccountInput personAccountInput = new ConnectApi.MemberPersonAccountInput();
personAccountInput.firstName = firstName;
personAccountInput.lastName = lastName;
personAccountInput.email = emailId;
// ADDED: Pass custom field value to the Contact/Person Account object
personAccountInput.put('Current_City__c', currentCity);
personAccountInput.allowDuplicateRecords = false;
// ... (rest of the code)
另請參照:
此文章是否解決您的問題?
請讓我們知道,以便我們改進!

