本文由 Gideon(AI)翻译自英文原版。

我到目前为止已经大约 36 个小时没有睡觉了,请多包涵。

以下解决方案受到了 这篇文章的启发。

使用场景 链接到标题

用户希望能够自动将用户动态分配到队列,同时希望在满足条件时,根据商机所有者所在部门,动态将商机提交给相关队列进行审批。

拆解问题 链接到标题

我从算法学习中学到的一件事,就是拆解问题,分而治之。

在我们的使用场景中,主要有 2 个问题:

  1. 动态将用户分配到队列
  2. 动态将商机提交给队列

一旦将问题拆解成这两个部分,解决方案就清晰了。

  1. 动态将用户分配到队列:使用触发器动态添加/移除用户
  2. 动态将商机提交给队列:使用流程在满足特定条件时提交审批

动手实践 链接到标题

我们需要:

  • 自定义对象:审批矩阵(Approval Matrix)
    • 用于映射商机记录与应提交至哪个队列
    • 将该对象的"所有者"字段作为审批人,这将帮助我们解决 2 个问题:
      • 商机队列(此功能将在 2022 年版本中推出,如果我没记错的话)
      • 查找至队列的自定义字段不存在
  • 审批流程
  • 流程触发器:在创建商机记录时创建"审批矩阵"记录,在商机更新时更新"审批矩阵"记录所有者(使用触发器代码可能比流程更简单)
  • 流程触发器:在满足特定条件时提交审批;在本例中,我做了简化,当商机阶段 = “需求分析"时,自动提交审批
  • 自定义元数据类型:我仅用它来映射部门和队列名称,但你可以添加更复杂的条件,例如,如果用户属于某个特定客户,则应被分配到特定队列。你也可以将这类逻辑放在这里。
  • 用户触发器:将用户添加/移除到适当的队列(也可以使用流程,只是对我来说,触发器更容易操作,有时候我就是不想拖拖拽拽……)

请注意,我的流程完全没有做批量化处理……你应该始终这样做,我只是需要一个概念验证。

自定义对象 链接到标题

这只是一个简单的记录,用于映射商机和审批人队列

注意这里的"所有者"字段,它将作为审批人使用。在我的流程中,当新的商机记录创建时,审批矩阵记录的所有者字段会自动分配给"空队列”(一个没有任何用户的队列)。在实际操作中,你应该优化此流程,自动分配到正确的队列,除非找不到合适的队列。我这里只是偷了个懒。

审批流程 链接到标题

你的审批流程设置如下

审批流程

注意"指定审批人"设置为"相关用户:所有者"

邮件模板将是一个 VF 邮件模板,你可以在其中查询相关记录

邮件模板示例

流程触发器:创建"审批矩阵"记录 链接到标题

我构建的流程截图

正如我之前所说,你应该优化以下内容:

  1. 批量化处理
  2. 创建记录时也设置队列 –> 这将改善用户体验

流程触发器:提交审批 链接到标题

当阶段变更为"需求分析"时,将提交审批

流程截图

  • 用户触发器:

自定义元数据类型 链接到标题

以下是我的自定义元数据类型设置

自定义元数据类型设置

至少在我的场景中,我们只需要在这里映射部门和队列。你可以根据需要做得更复杂。例如,如果用户属于某个特定客户,则应被分配到特定队列,你也可以在这里添加这类逻辑。

以下是记录示例:

自定义元数据类型记录示例

用户触发器 链接到标题

将用户添加/移除到适当的队列(也可以使用流程,只是对我来说,触发器更容易操作,有时候我就是不想拖拖拽拽……)

以下是我的代码:

触发器

trigger UserTrigger on User (after insert, after update, before insert, before update) {

if(trigger.isBefore && (trigger.isUpdate || trigger.isInsert) ) {

UserTriggerHandler.addUserToQueue(Trigger.New,Trigger.Old);

}

}

处理器

public class UserTriggerHandler {

public static void addUserToQueue(List<User> TriggerNew, List<User> TriggerOld) {

map<String, String> manager_dep_map = new map<String, String>();

set<String> keySet_dep = new set<String>();


list<GroupMember> gmLists = new list<GroupMember>();

List<String> depNames = new List<String>();


for(User_Assignment_Queue__mdt cmdt_User :[select Id,Queue__c, Department__c

from User_Assignment_Queue__mdt

]){

manager_dep_map.put(cmdt_User.Department__c, cmdt_User.Queue__c);

}


for(User usrO : TriggerOld){

removeUserHelper(usrO);

}

system.debug('depNames = '+depNames);

for(User usr: TriggerNew){


if(usr.Manager__c && usr.Department <> NULL) {

if(manager_dep_map.get(usr.Department) <> NULL){

Id qId = [select Id from Group where Type =: 'Queue' AND Name =: manager_dep_map.get(usr.Department) ].Id;

GroupMember gm = new GroupMember(GroupId = qId, UserOrGroupId = usr.Id);

gmLists.add(gm);

}

else{

Group g = new Group(Type='Queue',

Name = usr.Department + 'Team Queue');

insert g;

GroupMember gm = new GroupMember(GroupId = g.Id, UserOrGroupId = usr.Id);

gmLists.add(gm);

usr.addError('This is a new department, please setup the queue and conditions accordingly');

}

}

}

system.debug('gmLists = '+ gmLists);

depNames.clear();

insert gmLists;


}


public static void removeUserHelper(User UserOld){

list<GroupMember> DeleteGroupMemberRecord = New list<GroupMember>();

list<Id> qId = new list<Id>();

list<Id> oldUId = new list<Id>();

List<String> depNames = new List<String>();

map<String, String> manager_dep_map = new map<String, String>();


for(User_Assignment_Queue__mdt cmdt_User :[select Id,Queue__c, Department__c

from User_Assignment_Queue__mdt

]){

manager_dep_map.put(cmdt_User.Department__c, cmdt_User.Queue__c);

}

String groupName = manager_dep_map.get(UserOld.Department);

system.debug('groupName = '+groupName);

if(UserOld.Manager__c){

for(GroupMember gm:[

select Id, Group.Name, UserOrGroupId from GroupMember where UserOrGroupId =: UserOld.Id AND Group.Name =: groupName

]) {

DeleteGroupMemberRecord.add(gm);

}

}

else{

for(GroupMember gm:[

select Id, Group.Name, UserOrGroupId from GroupMember where UserOrGroupId =: UserOld.Id

]) {

DeleteGroupMemberRecord.add(gm);

}

}

System.debug('DeleteGroupMemberRecord = '+DeleteGroupMemberRecord);


if(!DeleteGroupMemberRecord.isEmpty()) {

delete DeleteGroupMemberRecord;

}

}

}

关于队列设置的完整说明:

队列

见证真相的时刻! 链接到标题

首先测试用户部分

我们有这个用户 John Doe,我添加了另一个名为"Manager"(经理)的复选框字段,原因是在这个使用场景中,一个部门有多个经理。这样就解决了一个用户只能有 1 个经理的问题,因为在这个使用场景中,可以有多个经理。(我知道,有点奇怪)

在用户记录上,根据需要设置部门字段,在我的案例中,我将其设置为"Sales"并勾选经理复选框,保存后你会看到该用户已被分配到一个队列。

John Doe

经理复选框

成功分配队列

创建一个新商机,同时应该会创建一条新的审批矩阵记录

新商机

审批矩阵记录已创建

现在我将商机阶段更新为"需求分析"

阶段已更新

现在如果你导航回审批矩阵记录,你应该看到所有者已相应更新为"销售团队队列"

审批矩阵已更新

如果你进入"相关"选项卡,你应该看到一个审批流程已提交给队列(此记录的所有者)

审批已提交

成功了!!!!!

结论 链接到标题

根据你自己的业务流程,这里的逻辑可能会变得非常复杂。

一旦把问题拆解开来,事情就会在某种程度上变得更清晰、更简单。


好了,我现在已经连续工作了整整 44 个小时。我要去补个觉了。

如有任何问题请告诉我,我也欢迎大家的反馈。谢谢!!