You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SalesForce Apex:关联Document__c对象附件至邮件失败求助

Salesforce邮件无法附加Document__c关联附件问题排查

问题详情

尝试为邮件添加与Document__c对象关联的附件,邮件可正常发送且无报错,但始终无附件附上。以下是当前使用的代码(包含多种尝试过但无效的注释实现):

global class NOAemail {
    @InvocableMethod (Label='NOA Email' 
        Description='Sends the NOA email to the specified client')
    public static void SingleMail(List<flowinput> info) {

        // Create an email message object
        
        //List<Document__c> NOAdoc = [SELECT Id FROM Document__c WHERE Id=:info[0].NOAdocId];

        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

        String[] sendingTo = new String[]{info[0].address}; 

        mail.setToAddresses(sendingTo);
        
        mail.setSubject(info[0].subject);

        mail.setPlainTextBody(info[0].body);
        
        SObject[] queryParentObject = [SELECT Id, (SELECT Id, Name FROM Attachments) FROM Document__c WHERE Id = :info[0].NOAdocId];
        for (SObject parentRecord : queryParentObject){
          Object ParentFieldValue1 = parentRecord.get('Id');       
          //Object ParentFieldValue2 = parentRecord.get('CaseNumber');   
          //Object ParentFieldValue3 = parentRecord.get('Status');
        
          // Prevent a null relationship from being accessed
          SObject[] childRecordsFromParent = parentRecord.getSObjects('Attachments');
        
          if (childRecordsFromParent != null) {
            for (SObject childRecord : childRecordsFromParent){
              //Object childFieldId = childRecord.get('Id');

              String idString = (String)childRecord.get('Id');

              //List<String> childFieldId = childRecord.get('Id');
              //Object ChildFieldValue2 = childRecord.get('Name');

              List<String> attachId = new List<String>();
              attachId.add(idString);
              mail.setEntityAttachments(attachId);
            }      
          }     
        }
        //mail.setEntityAttachments(childFieldId);
        
        /*
        List<Document__c> docs = [SELECT Id, (SELECT Id FROM Attachments) FROM Document__c WHERE Id = :info[0].NOAdocId];
        if (!docs.isEmpty()) {
        //Set<Id> attachmentIds = (new Map<Id, Document__c>(docs)).keySet();
        //Set<Id> attachmentIds = new Set<Id>();
        //attachmentIds.add(docs[0][0]);
            Set<Id> attachmentIds = (new Map<Id, SObject>(docs)).keyset();
            List<Attachment> files = [SELECT Name, Body, ContentType FROM Attachment WHERE Id IN :attachmentIds];
        }
        
        List<Messaging.EmailFileAttachment> attachments = new List<Messaging.EmailFileAttachment>();
        for (Attachment file: files) {
          Messaging.EmailFileAttachment efa = new Messaging.EmailFileAttachment();
          efa.setFileName(file.Name);
          efa.setBody(file.Body);
          efa.setContentType(file.ContentType);
          attachments.add(efa);
        }
        mail.setFileAttachments(attachments);
        */
        
        /*
        List<id> ContentDocumentIds = new List<id>();

        for(contentDocumentLink CDLink : [SELECT LinkedEntityId, ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId=:info[0].NOAdocId])
                    {
                       ContentDocumentIds.add(CDLink.ContentDocumentId);  
                    }
                    for ( ContentVersion cversion : [SELECT Title, 
                                                            PathOnClient, FileType,
                                                            versiondata 
                                                      FROM contentversion 
                                                      WHERE ContentDocumentId IN :ContentDocumentIds  
                                                       AND Title LIKE 'WOCF%'])
                     {
                      blob WOCFbody = cversion.versiondata;
                      system.debug('body : '+WOCFbody+'-----------'+cversion.title);
                      Messaging.EmailFileAttachment efa = new Messaging.EmailFileAttachment();
                      efa.setFileName('NOA Doc'+'-'+cversion.title+'.'+cversion.FileType);
                      efa.setBody(WOCFbody);
                      mail.fileAttachments.add(efa); 
                     }
        */

        /*
        List<String> contentVersionIds =  new List<String>();
        
        for(ContentDocumentLink thisCdl : [SELECT ContentDocument.LatestPublishedVersionId FROM ContentDocumentLink WHERE ContentDocumentId = :info[0].NOAdocId]) {
            contentVersionIds.add(thisCdl.ContentDocument.LatestPublishedVersionId);
        }

        mail.setEntityAttachments(contentVersionIds);
        */
        
        //SingleEmailMessage.setEntityAttachments(NOAdoc);

        //message sending

      Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail}); 

    }
     
     global class flowInput {
     
        @InvocableVariable
        global String address;
        
        @InvocableVariable
        global String subject;
        
        @InvocableVariable
        global String body;
        
        @InvocableVariable
        global String NOAdocId;
        
        //@InvocableVariable
        //global String OppAccountId;
     }

}

注:代码块中被注释的部分均为尝试过但无效的实现方式,mail.setPlainTextBody(info[0].body);下方的未注释代码是当前使用的实现。

问题原因分析

  1. setEntityAttachments调用逻辑错误:当前代码在循环中每次创建新的List<String>并调用setEntityAttachments,这会覆盖之前设置的附件列表,最终仅保留最后一个附件ID。如果Document__c有多个附件,只有最后一个会被尝试添加;若没有附件则完全无效果。
  2. 混淆两种文件存储类型:Salesforce中有两种文件存储方式:
    • 传统Attachment对象:通过sObject.Attachments关联
    • 现代ContentDocument/ContentVersion(Salesforce Files):通过ContentDocumentLink关联到sObject
      用户的注释代码尝试了两种方式,但均存在逻辑问题(如查询Attachment时误用了Document__c的ID而非Attachment的ID)。

解决方案

方案1:处理传统Attachment对象

先收集所有关联的Attachment ID,再一次性调用setEntityAttachments:

global class NOAemail {
    @InvocableMethod (Label='NOA Email' 
        Description='Sends the NOA email to the specified client')
    public static void SingleMail(List<flowinput> info) {
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

        String[] sendingTo = new String[]{info[0].address}; 
        mail.setToAddresses(sendingTo);
        mail.setSubject(info[0].subject);
        mail.setPlainTextBody(info[0].body);
        
        // 收集所有关联的Attachment ID
        List<String> attachIds = new List<String>();
        Document__c doc = [SELECT Id, (SELECT Id FROM Attachments) FROM Document__c WHERE Id = :info[0].NOAdocId];
        if (doc.Attachments != null && !doc.Attachments.isEmpty()) {
            for (Attachment att : doc.Attachments) {
                attachIds.add(att.Id);
            }
        }
        
        // 一次性设置所有附件
        if (!attachIds.isEmpty()) {
            mail.setEntityAttachments(attachIds);
        }

        Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail}); 
    }
     
     global class flowInput {
        @InvocableVariable global String address;
        @InvocableVariable global String subject;
        @InvocableVariable global String body;
        @InvocableVariable global String NOAdocId;
     }
}

方案2:处理ContentDocument(Salesforce Files)

如果Document__c关联的是Salesforce Files,需要通过ContentDocumentLink查询并构建EmailFileAttachment:

global class NOAemail {
    @InvocableMethod (Label='NOA Email' 
        Description='Sends the NOA email to the specified client')
    public static void SingleMail(List<flowinput> info) {
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

        String[] sendingTo = new String[]{info[0].address}; 
        mail.setToAddresses(sendingTo);
        mail.setSubject(info[0].subject);
        mail.setPlainTextBody(info[0].body);
        
        // 查询关联的ContentDocument
        List<ContentDocumentLink> cdlList = [SELECT ContentDocumentId FROM ContentDocumentLink 
                                             WHERE LinkedEntityId = :info[0].NOAdocId 
                                             AND ShareType IN ('V', 'C')]; // 仅查询可访问的文件
        
        if (!cdlList.isEmpty()) {
            Set<Id> contentDocIds = new Set<Id>();
            for (ContentDocumentLink cdl : cdlList) {
                contentDocIds.add(cdl.ContentDocumentId);
            }
            
            // 查询最新版本的ContentVersion
            List<ContentVersion> cvList = [SELECT Title, FileType, VersionData, PathOnClient 
                                          FROM ContentVersion 
                                          WHERE ContentDocumentId IN :contentDocIds 
                                          AND IsLatest = true];
            
            // 构建EmailFileAttachment
            List<Messaging.EmailFileAttachment> attachments = new List<Messaging.EmailFileAttachment>();
            for (ContentVersion cv : cvList) {
                Messaging.EmailFileAttachment efa = new Messaging.EmailFileAttachment();
                efa.setFileName(cv.PathOnClient != null ? cv.PathOnClient : cv.Title + '.' + cv.FileType);
                efa.setBody(cv.VersionData);
                efa.setContentType(cv.FileType);
                attachments.add(efa);
            }
            
            mail.setFileAttachments(attachments);
        }

        Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail}); 
    }
     
     global class flowInput {
        @InvocableVariable global String address;
        @InvocableVariable global String subject;
        @InvocableVariable global String body;
        @InvocableVariable global String NOAdocId;
     }
}

验证步骤

  1. 确认Document__c记录确实关联了附件/文件
  2. 检查代码运行权限:确保执行该类的用户拥有Attachment/ContentDocument/ContentVersion的访问权限
  3. 添加System.debug输出附件ID列表或文件数量,确认查询到了正确的附件

内容的提问来源于stack exchange,提问作者AlDavis455

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 11:40:39