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);下方的未注释代码是当前使用的实现。
问题原因分析
setEntityAttachments调用逻辑错误:当前代码在循环中每次创建新的List<String>并调用setEntityAttachments,这会覆盖之前设置的附件列表,最终仅保留最后一个附件ID。如果Document__c有多个附件,只有最后一个会被尝试添加;若没有附件则完全无效果。- 混淆两种文件存储类型: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; } }
验证步骤
- 确认
Document__c记录确实关联了附件/文件 - 检查代码运行权限:确保执行该类的用户拥有
Attachment/ContentDocument/ContentVersion的访问权限 - 添加
System.debug输出附件ID列表或文件数量,确认查询到了正确的附件
内容的提问来源于stack exchange,提问作者AlDavis455
相关产品推荐
相关产品推荐

