Apex 检索为客户的所有联系人角色分配给当前用户的任务

Apex Retrieve Tasks Assigned to Current User For All Contact Roles on an Account

我有一个 Apex class,其目的是检索和删除用户刚刚调用的联系人角色(与帐户相关)的逾期任务。我需要修改它,以便它查询分配给帐户上所有联系人角色的用户的所有逾期任务,但我正在努力获得正确的查询。

这里是有问题的部分代码,我认为最相关的部分:

/***************************************************
Brief Description: Deletes overdue Tasks or Events.
****************************************************/
public class DSDenali_DeleteOverDueActivities {

private static List<Task> followUpTasksToDelete = new List<Task>(); 
private static List<Event> followUpEventsToDelete = new List<Event>();
private static Map<Id, Set<String>> ownerOfActivities = new Map<Id, Set<String>>();

@InvocableMethod (label = 'DialSource Denali Delete Overdue Activities')
public static void gatherData(List<Data> requests)
{
    Map<Id, String> results = new Map<Id, String>();

    for (Data request : requests)
        results.put(request.contactRoleID, request.assignedTo);

    for (Id key : results.keySet())
    {
        Set<String> assignedToValues = parseAssignedTo(results.get(key));
        System.debug('assignedToValues: ' + assignedToValues);
        ownerOfActivities.put(key, assignedToValues);
        System.debug(ownerOfActivities);
    }

    queryAndFilterData();
    deleteOverdueActivities();
}

//Query for the Tasks and Events and filter the ones to delete
private static void queryAndFilterData()
{
    List<Contact_Role__c> contactRoles = [SELECT Id, 
                                                (SELECT Id, Owner.UserRole.Name, OwnerId FROM Tasks WHERE status != 'Completed' AND ActivityDate <= :system.TODAY() AND Type__c = 'Outbound Call'),
                                                (SELECT Id, Owner.UserRole.Name, OwnerId, Description FROM Events WHERE EndDateTime <= :system.NOW())
                                         FROM Contact_Role__c
                                         WHERE Id IN :ownerOfActivities.keySet()];

    for (Contact_Role__c contactRole : contactRoles)
    {
        for (Task currentTask : contactRole.Tasks)
        {
            if (ownerOfActivities.get(contactRole.Id).contains(currentTask.OwnerId))
            {
                if (currentTask.OwnerId != '0050B000006ET37' && currentTask.Owner.UserRole != NULL && Pattern.matches('.*Altair.*', currentTask.Owner.UserRole.Name))
                    followUpTasksToDelete.add(currentTask);

                else if (currentTask.OwnerId == '0050B000006ET37')
                    followUpTasksToDelete.add(currentTask);

                else 
                    continue; 
            }

            else if (ownerOfActivities.get(contactRole.Id).contains('ALL'))
            {
                if (currentTask.Owner.UserRole != NULL && Pattern.matches('.*Altair.*', currentTask.Owner.UserRole.Name))
                    followUpTasksToDelete.add(currentTask);

                else 
                    continue;
            }
        }

        for (Event currentEvent : contactRole.Events)
        {
            if (ownerOfActivities.get(contactRole.Id).contains(currentEvent.OwnerId) && currentEvent.Description == NULL)
            {
                if (currentEvent.OwnerId != '0050B000006ET37' && currentEvent.Owner.UserRole != NULL && Pattern.matches('.*Altair.*', currentEvent.Owner.UserRole.Name))
                    followUpEventsToDelete.add(currentEvent);

                else if (currentEvent.OwnerId == '0050B000006ET37')
                    followUpEventsToDelete.add(currentEvent);

                else 
                    continue; 
            }

            else if (ownerOfActivities.get(contactRole.Id).contains('ALL') && currentEvent.Description == NULL)
            {
                if (currentEvent.Owner.UserRole != NULL && Pattern.matches('.*Altair.*', currentEvent.Owner.UserRole.Name))
                    followUpEventsToDelete.add(currentEvent);

                else 
                    continue;
            }
        }
    }                                   
}

//Delete overdue Events/Tasks
private static void deleteOverdueActivities()
{
    try{
        delete followUpTasksToDelete;
    }

    catch (DmlException e){
        System.debug('The following error occured (DSDenali_DeleteOverDueActivities): ' + e);
    }

    try{
        delete followUpEventsToDelete;
    }

    catch (DmlException e){
        System.debug('The following error occured (DSDenali_DeleteOverDueActivities): ' + e);
    }
}

//Parse the CSVs of possible owners
private static Set<String> parseAssignedTo(String assignedTo)
{
    Set<String> assignedToValues = new Set<String>();
    assignedToValues.addAll(assignedTo.deleteWhitespace().split(','));
    return assignedToValues;
}

public class Data
{
    @InvocableVariable (required=true)
    public String assignedTo;

    @InvocableVariable (required=false)
    public Id contactRoleID; 
}
}

(在 OP post 编辑了更多代码并要求进行代码审查后更新)

这不是糟糕的代码,可以使用一些注释。考虑 post 在 https://codereview.stackexchange.com/ (although not many SF-related posts end up there) or on https://salesforce.stackexchange.com

中使用它

gatherData()

您的输入变量(经过一些解析后)是 Map<Id, Set<String>>,其中键是联系人角色的 ID。用户(所有者)的那组字符串有点误导。乍一看,您会立即问自己为什么不能 Set<Id>。只有在代码的深处,您才能看到显然 "All" 是允许的值之一。这……不太好。我很想在这里制作两种方法,一种采用合法的 Map<Id, Set<Id>>,另一种采用简单的 Set<Id>,如果你知道你正在有效地跳过第二个参数的话。

queryAndFilterData()

你只有一个查询,而且不在循环中,非常好。 我的技巧(编辑之前的)对你不起作用(你实际上没有 Account__c 或者输入中的字段名为 anywere,你只有记录 id。如果你想检查/删除该帐户下角色的所有任务,最干净的方法可能是使用两个查询

// 1st the helper to collect all accounts...
Set<Id> allAccounts = new Map<Id, Account>([SELECT Id
FROM Account
WHERE Id IN (SELECT Account__c FROM Contact_Role__c WHERE Id IN :ownerOfActivities.keyset()]).keyset();

// then the outline of the main query would be bit like this
SELECT Id,
    (SELECT ... FROM Tasks WHERE ...),
    (SELECT ... FROM Events WHERE ...)
FROM Contact_Role__c
WHERE Account__c IN :allAccounts
    AND ...

我会检查有多少过滤逻辑可以推送到查询本身,而不是手动检查每个returned 行。我的意思是看看那个:

假设我们走简单的路线(忽略 "All" 用户的概念),假设您有另一个 Set<Id> allUsers; 变量(由所有数据片段中提到的所有 ID 组成)

Tasks 的查询可以变得像

一样简单
(SELECT Id
FROM Tasks
WHERE Status != 'Completed'
    AND ActivityDate <= TODAY 
    AND Type__c = 'Outbound Call'
    AND OwnerId IN :allUsers
    AND (OwnerId = '0050B000006ET37' OR Owner.UserRole.Name LIKE '%.Altair.%')
)

你仍然需要遍历它们来验证是否真的可以删除每个(仅仅匹配所有用户是不够的,它还必须检查这个特定的用户是否可以 Contact_Role__c ,对吗?)但是类似的东西应该 return 更少的行并且没有更多的正则表达式匹配......应该更快一些。

我不会为那个特殊所有者的 id 设置一个魔法变量。理想情况下,会有其他描述此特殊用户的内容(角色?个人资料?用户记录上的自定义字段?个人资料中 "Author Apex" 的权限?)。至少将它移动到文件顶部的 helper Id 变量,这样它就不会被复制粘贴到各处。并询问您的业务用户,如果那个人(默认任务所有者?某个集成帐户?)离开公司会发生什么,因为便便会严重撞击螺旋桨。

如果您对这个版本的查询感到满意,"ALL" 版本会变得更简单吗?没有 "all users",没有 "magic id" & 工作完成了吗?

(SELECT Id
FROM Tasks
WHERE Status != 'Completed'
    AND ActivityDate <= TODAY 
    AND Type__c = 'Outbound Call'
    Owner.UserRole.Name LIKE '%.Altair.%'
)

不要相信互联网上随机出现的不了解您的业务流程的人,但是,是的,还有一些改进的余地。彻底测试一下:)

deleteOverdueActivities()

这不是很好的 try-catch 模式。您只是在调试日志中提出它,但默默地吞下了错误。让它失败(让错误冒泡给用户)或者做一些适当的错误处理,比如插入一些东西到 helper Log__c 对象或者发送电子邮件/聊天 post 给管理员...

parseAssignedTo() 不错。我希望当您将 null 传递给它时它会可怕地爆炸。通过在最后几行中标记所需的变量,您可以免受它的影响,但我认为此注释仅适用于 Flows。从其他 Apex 代码调用它的保护不够。