Reports API、Sites、Support Classes 与 Territory Management
Reports API/Sites/Support/Territory综合指南:Reports API(同步runReport/异步runAsyncReport+getAsyncReportResults/500同步+1200异步限速/异步Batch+Trigger禁止/同步计入SOQL50K/测试忽略SeeAllData/异步stopTest后执行)、报表元数据(describeReport/ReportDescribeResult/类型+字段+过滤器)、Fact Map(Tabular T!T+行号/Summary分组键层级/Matrix行列键交叉点/aggregates汇总+dataCells数据单元格)、过滤(ReportFilter/20个自定义字段过滤器/operator+value/日期范围)、解码Fact Map键模式(三种格式递归遍历)、Sites URL Rewriting(Site.UrlRewriter/mapRequestUrl+generateUrlFor)、Support Classes(BusinessHours.add+diff+isWithin/多时区节假日/Case+sObject管理)、Territory Management 2.0(Territory2+UserTerritory2Association触发器/区域自动化)。
本课程共有 37 个章节
同学们,今天我们来学习这一章——在Apex中使用报表和仪表板。这章内容非常实用,可以让你在代码里灵活地读取和分析报表数据,甚至操控仪表板信息。 我们先会从报表和仪表板的API总览开始,了解它到底能做什么,核心的能力有哪些。接着,我们会深入去看看它的需求和限制,清楚边界在哪,才不会踩坑。再往下,我会教大家怎么同步运行报表,也有异步运行的方式,以及如何把异步任务的列表抓出来、查看运行状态。然后我们还会讲到,怎样去获取报表的元数据,方便你了解报表的结构。 接着要重点聊一聊事实映射,通过它可以直接访问报表里的数据单元格,对于表格、摘要、矩阵这几种格式,事实映射的键值模式是完全不同的,我会一一拆解给你听。另外,动态过滤报表也是经常用到的技巧,可以让你的代码根据运行时的条件筛选数据。 这部分内容讲完后,我们也不能忽略测试——报表代码写好了怎么测?这里有一套测试策略要分享。最后,还会顺带补充几个和报表相关的话题:Salesforce站点URL的重写、营业时间和案例的支持类,以及Territory Management 2.0里的触发器写法。这些都是在真实项目中可能遇到的好帮手。 好了,接下来我们就从报表和仪表板API的概览开始,一步步深入吧。
同学们,我们来看这一页内容,它讲的是怎样用Apex程序去操控Salesforce里的报表和仪表板。传统上我们都是在界面上点开报表看数据,但如果有程序化的需要呢?Salesforce给我们提供了Reports和Dashboards API,通过Apex可以直接调用。 简单来说,这套API能让你用代码去读取已经在报表生成器里定义好的报表数据。不管你是想在Salesforce内部做一些定制,还是把报表数据搬到外部的网页、手机应用里,都可以做到。比如,你可以写一个小程序,在每个季度结束时自动运行一张“最佳销售代表”的报表,然后把结果抓出来,生成一条Chatter动态发布出去,让全公司都能马上看到谁是本季的Top Sales。这样就不用人工跑报表再截图手工发帖了,全部自动化。 从更抽象一点的层面看,这些API资源能帮你做几件核心的事:你可以像查数据库一样去查询和过滤报表里的数据;可以以同步或异步的方式运行表格、摘要、矩阵这三种类型的报表;还能在代码里动态地传入筛选条件,只拉取你关心的那部分数据;另外,你不仅可以拿到报表里的具体数字,也可以查到报表本身的元数据,比如它有哪些字段、哪些过滤条件。这样一来,就能非常灵活地把报表能力嵌入到你自己的业务逻辑里。 好,关于Reports和Dashboards API的基本概念我们就讲到这儿,接下来我们看怎么具体使用它。
同学们,咱们这节课来看一个特别强大的功能,就是 Salesforce 里的报告 API。说真的,这个 API 一出来,可以说彻底改变了我们访问和可视化数据的方式。 以前你想拿报告里的数据去做点别的事情,可能得手动导出再导入,非常麻烦。但现在,有了这个 API,你可以直接把报告数据集成到你自己的自定义对象里,比如说你有一个自定义的项目管理工具,想把销售报告的数据实时同步进去,完全没问题。你还可以用它来构建非常丰富的可视化图表,比如把数据变成动画,让报表动起来,观感更直观。甚至,你还可以创建属于自己的自定义仪表板,完全按照自己的想法来布局,还能自动化很多报告相关的任务,比如定时拉取数据、定期发送汇总邮件等等,非常灵活。 那这个 API 到底怎么给你数据呢?它支持三种不同的报告格式,你要根据具体需求来选择。 第一种,叫表格格式。这就像一张最简单的电子表格,每一行就是一条记录,没有分组、没有小计,就是干干净净的平面数据。如果你只想把最原始的报告数据拉出来,比如导出所有客户的列表,用表格格式就对了。 第二种,是摘要格式。这个格式是带分组和分类汇总的。比如你的报告里按“地区”分组,然后每个地区下面列出该地区的销售记录,最后再来一个小计。摘要格式会直接把这些分组后的数据和汇总信息返回给你,让你不用自己再去计算。想做带小计的统计报表,选这个。 第三种,矩阵格式。这个可能稍微复杂一点,它是按行和列两个维度同时分组的数据。就像咱们看财务报表,行可能是产品类别,列可能是季度,交叉的格子就是该产品在该季度的销售额。这种格式非常适合做交叉分析,返回的数据结构也是按照行列分组来的。 所以说,这个报告 API 的应用范围特别广,从最简单的数据提取,比如定时拉取报告里的数据存到外部系统,到非常复杂的分析应用程序,比如你开发一个 App,里面内嵌动态图表,数据源就来自报告 API,都可以实现。它能帮你把 Salesforce 里的报告能力,真正延伸到任何你需要的地方。 好了,今天的这个点我们就讲到这儿。大家先消化一下这三种格式的区别,下节课咱们看看怎么用 Apex 实际调用这个 API,好不好?
嗨,同学们,我们接着来看看在Apex中使用报告和仪表板API时需要知道的一些要求和限制。这块儿虽然看起来简单,但踩坑的还真不少,所以我们多花两分钟把它理解透。 首先,这个功能不是所有Salesforce版本都默认打开的。你要想在Apex代码里操作报告或者仪表板,组织必须已经启用了API访问权限。通常来说,如果你是Enterprise、Unlimited或者Performance版本,那一般就带这个权限;如果是专业版,可能需要单独购买。总之,写代码前跟管理员确认一下,API有没有开,否则调用时会直接报错。 那开了API就万事大吉了吗?其实不完全是。我们以前学过的一般API限制,像24小时内整个组织能发起的API请求总数,这些当然还是算在里面的。但我想让你特别留意的是,报告和仪表板API还有自己额外的、很重要的限制. 第一,同步报告执行限制。什么意思呢?就是当你在Apex里直接同步调用Report对象,等待马上拿回结果的时候,每个用户在同一时间最多只能跑20个同步报告。所以,如果你在循环里动不动就同步取一份大报告,还忘关调用,用户一多很容易撞到上限。 第二,异步报告限制。如果你改用异步方式,把报告丢到后台去跑,那每个用户最多可以有200个异步报告作业在排队。这个池子大一点,但也不是无限,设计功能时得留个余地,别一股脑儿全塞进去。 第三,仪表板刷新也有类似的并发限制。通过Apex去触发仪表板刷新的时候,整个组织同时处理的刷新请求不能太多——通常最多也就3个。所以如果是给很多用户并发刷新不同仪表板,那可能会被婉拒掉一部分。 最后还有一个容易忽略的,就是数据量的限制。你通过API取报告结果时,返回的行数可能会被截断。即便你的报表本身定义能展示几千行,API默认一次可能只给一部分,而且Apex里有堆内存和CPU时间的限制,一次取太多数据容易让整个事务超时或撑爆堆。所以咱们的实际代码里,尽量只请求当前业务真正需要的那部分字段和记录数,能加过滤就加过滤。 总的来说,记好这几点:先确认组织开了API,注意一般API请求次数,再小心报告和仪表板特有的并发与数据量限制。这样你的Apex代码在和报告交互时就能稳稳当当地跑起来,不会莫名其妙撞墙了。 好了,这节课就先讲到这儿,大家有任何问题随时提出来。
大家好,今天我们一起来看看在使用Salesforce的报表API时,有哪些限制需要特别留意。这些限制会直接影响到你抓取数据、生成报告的方式,所以理解了它们,平时开发才会更顺手。 首先,当你需要过滤数据的时候,有三种过滤方式是不能用的:交叉过滤器、标准报告过滤器,还有按行限制的过滤器。交叉过滤器呢,通常是用来跨对象筛选的,比如“找出所有没有活动的客户”这类条件;标准报告过滤器就是你在界面上拖拽的那种日期、金额的简单筛选;按行限制过滤器则像是“只显示前10行”这种。在高阶API场景下它们会被暂时关掉。 接下来是关于历史跟踪报告。想知道字段值是什么时候被谁改过的吗?那就会用到历史跟踪报告。但这里有两个限制:第一,它只支持矩阵格式的报告,汇总表和普通表格都不行;第二,历史跟踪报告不能用订阅功能,也就是没法定时自动推送到邮箱里。 再来看字段数量上限。如果你用API把一个报告里的列展示出来,那这个报告最多只能选100个字段作为列。超过100个的话,API就处理不了了,这一点在创建复杂报告时要提前规划好。 说到报告列表,API一次最多只返回200条你最近查看过的报告。如果你是做“最近使用”的菜单,记住上限就是200。 在运行报告获取数据行的时候,API默认最多返回前2000行。数据量大的时候,你不能直接把几万行一次性全拉下来。不过别担心,你可以利用过滤器缩小范围,每次只取自己真正要的那一部分,然后分批处理。 还有,在跑报告的时候,你自己额外添加的自定义字段过滤器,最多只能加20个。比如说你临时想按“客户等级”和“地区”组合筛选,那加起来别超过20个。 简单总结一下:从过滤限制,到历史和字段数,再到列表和数据行的容量,这些边界条件都是为了保证系统稳定、响应迅速。你写代码调用API的时候,只要心里装着这三个“最多”——字段100、列表200、数据行2000,再加一个20个过滤器限制,就能少踩很多坑。 好了,这一页的限制我们就说到这儿,下次课咱们再继续聊如何在实际代码里规避这些问题。
同学们,咱们这节课来看一看Salesforce里关于报告API的一些限制,特别是同步报告运行的限制,这些记清楚了,可以避免你在开发时踩坑。 首先,什么是同步报告运行呢?就是你调用API去运行一份报告,然后立刻等着它出结果。这种方式很直接,但Salesforce为了保障平台的稳定性,给你设了一些“天花板”。 我们一条一条看。你的组织每小时最多能发起500次同步报告运行请求,这是一个硬性的配额,超过了就会报错。同时呢,这个API允许同时处理最多20个正在运行的同步报告请求,也就是说并发数不要超过20个。 当你调用完想查看有哪些同步运行的报告实例时,你一次最多能拉回2000个实例的列表。而如果你要获取这些运行的具体结果,这个API一次最多只允许你发200个请求去拿结果。还有一点,每个小时你同步请求的总数上限是1200次,注意这和前面的500次不矛盾,它指的是各种同步报告相关调用的总量。而且,运行出来的结果只在24小时的滚动期内可以拿到,过了时间就清掉了,这点也要留心。 这些是API层面的限制。但更要紧的是,在Apex代码里,有两条特殊限制,你必须刻在脑子里。第一,在批处理Apex中,不允许进行同步报告调用。为什么?因为批处理本来就是异步大量处理数据的,如果还在里面跑同步报告,会严重拖累性能,而且容易造成锁冲突。第二,在Apex触发器里,也同样不允许任何报告调用。触发器是在数据操作时立刻执行的,绝对不能再跑去运行报告,那样会大大增加事务的响应时间,甚至引发锁等待,所以Salesforce直接就一刀切禁止了。 简单来说,记住这几个数字:每小时500次运行、20个并发、列表2000个、获取结果200个请求、总请求1200次、结果保留24小时。再记住Apex里两个绝对禁区:批处理和触发器里不能调同步报告。把这些限制放在心里,你以后写代码就不会被这些墙撞得莫名其妙了。如果有什么没听懂的地方,可以随时问我。
同学们,今天我们来聊聊在Apex里运行报告时,几个容易混淆的限值规则。幻灯片上这句话看起来有点绕,我给大家梳理清楚。 首先,同步方式运行报告,也就是我们在代码里直接调用获取报告结果时,报告返回的记录行数是会算进调节器限制的。这个限制规定:在一个事务里,所有SOQL查询总共最多只能检索五万行数据。也就是说,如果你的报告结果加上其他查询超过了五万行,就会触发限制异常。 但幻灯片紧接着又写“当报告同步运行时,不会施加此限制”,这其实是个笔误,容易把人搞晕。正确的理解是:同步报告运行,要算进,五万行的坑里,而,异步,报告运行才不会受这个限制。所以,平时写代码时,如果数据量很大,建议使用异步方式跑报告,避免撑爆同步的事务限额。 接下来我们看看Apex测试。在测试类里,运行报告时有个特别的地方——报告会,完全忽略, SeeAllData 这个注解,不管你是设为 true 还是 false。也就是说,
同学们,我们来看一下,在Apex中怎么去运行Salesforce的报告。 其实很简单,你可以想象成,我们平时在界面上点开一个报告,看到一堆数据。那在代码里呢,也有办法做同样的事情,这就是通过Salesforce的报告和仪表板API。这个API支持两种运行方式,一种是同步运行,另一种是异步运行。同步就是,我发出请求,然后等着结果回来;异步呢,就是我先请求,可以先去干别的事,回头再来拿结果,比较适合大批量的报告。 运行报告的时候,你可以选择要不要把每一行的详细信息都拉出来。有时候你只关心汇总数字,比如总和、平均值,那把详细信息关掉,速度会更快,数据量也小。反过来,如果你需要看到每一条记录,那就带上详细信息。 另外,我们经常需要动态地过滤数据,而不是每次都跑固定的报告。这时候你就可以在代码里,对报告原有的元数据进行设置,改一下过滤条件。比如,原本报告是看所有商机,你可以设置成只看上个月的商机。 关键是,不管你怎么调用,通过API跑出来的数据,跟你直接登录Salesforce,手动运行那个报告看到的记录条数,是完全一样的。它背后遵守的是同样的权限和数据可见性规则,不会多也不会少。 所以总结一下,在Apex里运行报告,就是通过API以同步或异步方式执行,可以选择是否显示详细行,还可以动态修改过滤条件,并且返回的结果和界面上手动跑的数据保持一致。这样你就可以把报告数据融入到你的自动化流程里了。 好,这部分就到这里,有什么问题吗?
同学们,我们来看这一页幻灯片,它讲的是报表的同步运行和异步运行。 幻灯片上写的是:如果你想快速完成一个报表的运行,请使用,同步运行,;如果不是为了快速,出于下面三个原因,我们也建议同步运行——第一,长时间运行的报表在同步运行时,超时限制的风险更低;第二,API 一次能处理更多数量的同步运行请求;第三,同步运行的结果会保存 24 小时,可以定期直接访问。最后还写了,同步运行用 `ReportManager.runReport` 方法,异步运行用 `ReportManager.runAsyncReport` 方法。 听到这里你可能已经觉得有点不对劲了。既然快速也推荐同步,不快速也推荐同步,那还要异步方法干什么呢?而且经验告诉我们,同步执行会受到比较严格的超时限制,长时间报表很容易达到上限,怎么反而说它“超时风险低”呢? 其实呀,这是幻灯片里的一个笔误,意思恰好写反了。正确的指导应该是这样的: 如果你想快速拿到报表结果,可以用,同步运行,,因为请求会立刻处理并返回。但如果你的报表数据量大、运算复杂,就可能跑很久,这时候用同步运行很容易触发 60 秒超时。所以对于这种,长时间运行的报表,,我们应该使用,异步运行,。异步报表在后台执行,突破了超时限制,API 也能同时处理更多的异步请求,运行出的结果同样会保存 24 小时,方便你随时来取。 因此,大家在实际开发中,简单快速的报表可以放心用 `runReport` 同步方法;一旦涉及可能耗时较长的报表,记得用 `runAsyncReport` 异步方法,这样才是既安全又高效的做法。 好,这一页的内容我们就梳理清楚了,有没有什么问题?
同学们,今天我们聊聊怎么在Apex代码里运行Salesforce的报表。 先想象一个场景:你写了一个自动化程序,需要立刻拿到报表结果来发邮件或者做判断,这时候我们就用,同步运行,。同步的意思就是代码会等着,直到报表跑完返回数据。使用方法叫 `ReportManager.runReport`。你需要告诉它两样东西:一是报表的ID,二是你要不要带上详细数据,就是一个布尔值,true就包含详细记录,false就只拿汇总信息。 那报表ID从哪儿来呢?你不能直接手写一个,得用SOQL去查。比如,你可以这样查:`SELECT Id FROM Report WHERE DeveloperName = '你的报表唯一名称'`,拿到的Id就是我们要用的。注意,`DeveloperName`是报表在系统中的API名称,其他字段也行,只要能定位到你要的报表。 但如果报表数据量特别大,可能跑很久,你不想让代码一直卡住,那就用,异步运行,。方法叫 `ReportManager.runAsyncReport`。这个方法不会当场给你结果,而是返回一个 `ReportInstance` 对象。这个对象就像一个工单,报表在后台跑,你可以拿着这个工单的ID,过一会儿再用查询去看它跑完了没,最后再拿结果。 `runAsyncReport` 比同步方法多一个可选的参数:`ReportMetadata`,用来附加过滤条件,比如只跑某个部门的数据。参数顺序通常是:报表ID、可选的报表元数据、还有一个布尔值控制是否包含详细信息。 总结一下:同步直接拿结果,用 `runReport`;异步不卡顿,先拿个工单,用 `runAsyncReport`。报表ID通过SOQL从Report对象获取,就这么简单。 这样讲是不是清晰多了?下次你自己写代码跑报表,就知道怎么选了。
我们先轻松一点,直接看这个知识点,它其实很简单。 你想想,当我们让系统异步跑报表的时候,系统不是马上给你结果,而是把任务放到后台去处理。每一个这样的任务,我们就叫它一个“实例”。那么,你现在想把之前跑过的这些实例列表拿出来看一下,系统会怎么做呢? 这里有三个关键点,你记住就行: 第一,系统最多只允许你检索到最近的2000个实例。所以,如果你历史跑过的异步报表特别多,超过2000个的那些很老的实例,就搜不到了。 第二,这个实例的列表,它是根据运行报表的日期和时间来排序的。也就是说,最近运行的会排在前面,老的就靠后。 第三,每一个异步报表跑出来的结果,系统只帮你保存24小时。过了这一天,结果就自动清掉了,你想看也看不着了。 总结起来就是:异步报表的历史任务,你能查到最多2000条,按时间排,而且每个任务的结果只在一天内有效。这其实就是在提醒我们,别把异步报表当成永久存储的工具,要及时把需要的结果保存下来。 好,这一页的内容就是这么简单,记住这几个限制条件就够用了。
好,咱们今天讲个挺实用的小功能——在Apex里怎么拿到已经跑完的报告实例,还有这些实例对应的结果。不少同学在做定时任务或者监控类需求的时候,经常会碰到这个场景,所以这块儿咱们好好捋一捋。 你看,Slide上先写了,获取实例列表用的是 `ReportManager.getReportHandler` 方法。你就这么记:`ReportManager` 这个类,专门管报告相关的操作,调用它的 `getReportHandler` 方法,就会返回一个实例列表。这个列表自动帮你按报告运行的日期和时间排好序,最新的在前面。是不是很贴心? 那这些报告的结果能存多久呢?Slide上说了,结果是连续存储24小时。也就是说,从报告跑完那一刻算起,一天之内你都可以拿到它当时跑出来的数据。超过24小时,系统就自动清掉了,这一点要留意,别存着过期的ID去查。 在你拿到实例列表之后,能不能看到每个实例的结果呢?这取决于你的用户访问级别。你只能看到你有权查看的报告实例,系统会按权限控制的,很安全。 那如果你已经知道某个具体实例的ID,想直接拿到它的结果,怎么办?很简单——用 `ReportManager.getReportInstance`,把实例ID传进去。这个方法会返回一个实例对象,然后在这个对象上再调用 `getReportReports`,就能得到这个实例的结果了。你把它理解成两步:先根据ID拿到实例,再从实例里把结果掏出来。 这样讲是不是就清晰多了?其实就是记住几个方法:拿列表用 `getReportHandler`,拿单个实例用 `getReportInstance`,最后实例点上 `getReportReports` 就有结果了。把握好24小时的存储窗口和权限限制,在实际开发中就能用得稳稳当当。
好,我们来看看报告元数据是个什么概念。你可以把它理解成报表的“说明书”——里面记录了这个报表是怎么设计的,用了哪些字段来做筛选、分组,哪些是详细数据,哪些是汇总后的摘要信息。 在 Apex 里,我们能够通过代码去读取这些元数据,也就是说,不需要打开报表界面,就能知道它的结构是什么样的。比如,你可以知道这个报表是按“客户区域”分组的,又用了“金额”字段来做汇总,筛选条件可能是只显示本季度的数据。这样一来,你就有了很大的灵活性,可以写代码动态地处理和展现这些报表的信息了。
同学们好,今天我们来聊一聊 Salesforce 报表背后的“说明书”——报表元数据。 报表元数据,说白了,就是库存信息,它详细记录了一个报表里有哪些字段可以用,哪些字段做了筛选,哪些被用来分组,以及汇总、明细数据是怎么构成的。有了这些信息,就像拿到了一份地图,我们可以搞明白:在这个报表类型下,到底能过滤什么字段、能选哪些值。 那这个东西有什么用呢?应用场景很多。比如,你想自己画一个漂亮的图表,不用从头乱猜,可以直接根据元数据里关于字段和分组的描述,构造出自定义的图表可视化。再比如,在程序里运行报表的时候,我们随时可以读出这份元数据,然后动态修改里头的过滤条件,让报表适应不同的需求,非常灵活。 那么在 Apex 里怎么拿到这份元数据呢?Salesforce 提供了一个专门的方法,叫做 `Reports.ReportManager.describeReport`,它会返回一个报表的描述对象 `ReportDescribe`。接着,调用这个对象的 `getReportMetadata()` 方法,就能得到真正的元数据对象——`ReportMetadata` 实例。这也就是咱们幻灯片里提到的那个“ReportMetals”,你可能看到的拼写会稍有不同,但它指的其实就是这个 `ReportMetadata`。 拿到 `ReportMetadata` 之后,我们可以用它的各种 `get` 方法来读取具体的元数据值,比如用 `getGroupingsDown()` 看看纵向分组有哪些,用 `getDetailColumns()` 看看明细字段,这样就能轻松获取筛选器、分组以及摘要信息了。 总结一下就是:先描述报表,再取元数据,最后用 get 方法拿到需要的细节。这就像打开一个精心打包的盒子,一层一层拆开,就能看到报表的全部“底细”。 好,今天关于报表元数据的基础就讲到这里,大家可以在练习时亲自动手试试,把一个报表的元数据打印出来,看看里面都藏着什么宝贝。下节课我们继续深入。
好,我们来看一下,当你调用报告的 `getReportMeta` 方法之后,会得到一个报告元数据对象,这个对象里藏着特别多有用的信息。怎么把这些信息拿出来呢?非常简单,我来给你挨个说一下。 首先,是一些最基本的报告属性。比如你想知道报告的名字,就可以用 `getName`;想拿到报告的 ID,就用 `getId`;要查看币种代码,调 `getCurrencyCode`;报告的开发名称呢,对应的是 `getDeveloperName`;还有报告格式,用 `getReportFormat` 就能取到。这些都是一看就懂的常用方法。 接下来,如果报告里做了分组,我们可以通过 `getGroupingsDown` 拿到一个分组信息对象的数组。每一个分组对象都提供了 `getName` 来获取分组字段名,`getSortOrder` 查看排序方式,还有 `getDateGranularity` 来看日期粒度,比如是按天、按月还是按年分的组。这样你就能完全搞清楚报告是怎么把数据归类在一起的。 再来看聚合计算的部分。用 `getAggregates` 可以拿到一个聚合函数的列表,这里会告诉你报告里到底用了哪些汇总方式,比如求和、计数、平均值等等,一目了然。 最后,如果你需要查看报告里每一条详细记录到底显示了哪些列,那就调用 `getDetailColumns` 方法,它会返回详细信息列的相关信息,让你知道明细区域都包含了哪些字段。 这样一梳理,是不是就很清晰了?关键就是这几个
我们来看看怎么在Apex里获取报告的数据。这里要用到一个类,叫 `ReportEffects`。听起来有点神秘,其实很简单。 你可以把这个 `ReportEffects` 想象成一个工具,它能帮你拿到报告里的“事实地图”。什么是事实地图呢?就是一份早已帮你整理好的、既能看到整体汇总,也能看到一行行明细的数据集合。比如说,你有一份销售报告,事实地图里就有总销售额这个汇总数字,同时也会有每条销售纪录的具体金额。这样一来,你不用自己去拼凑数据,直接就能用。
好,咱们现在来看看这个幻灯片上提到的一个很实用的概念,叫,事实地图,。你可能会问,这是什么?其实就是 Salesforce 报告结果里一个打包好的数据容器,它里面装着两种东西:一个是,摘要数据,,比如合计、平均值这种统计出来的总数;另一个是,记录级别的详细数据,,也就是每一行具体的数据值。 打个比方,你运行一份商机报告,如果只看汇总,那事实地图里就只有按阶段分组的总金额;如果你运行的是详细报告,它就会既包含每一笔商机的金额,也包含那个汇总值。这取决于你运行报告时,是只要摘要,还是带上了详细行。 那么代码里怎么拿到这些数据呢?事实地图是用,键值对,来表示的,这个“键”就像一把钥匙,你用它就能找到对应的值,从而在 Apex 里把数据取出来做图表、做可视化。 这些键可不是随便起的,它们本身就提供了事实地图每个部分的,索引,,告诉你数据藏在哪儿。你可以通过键,很方便地访问到摘要,也能访问到底下的明细。 但是这里有个很重要的细节:,这些键的命名模式,会随着报告格式的变化而变化,。也就是说,表格格式、摘要格式、矩阵格式,它们生成的键长得不一样。所以你在写代码时,得先弄清楚这份报告到底是什么格式,才能准确地用对键,把想要的数据给拎出来。 好,这样一听,是不是觉得事实地图就是一张带导航的结构化地图?它帮你把零散的报告数据组织起来,有索引,有分层,写起代码来就清晰多了。咱们接着往下看。
同学们,刚才我们聊到了报表结果里的分组,那现在来看看,怎么去取每个分组的小计数据,也就是摘要信息。在Salesforce的报表API里,这叫做“事实映射”。 别被名字吓到,其实很简单。打个比方,你用报表按“客户行业”分组,想知道每个行业的总金额是多少,这个小计值就藏在事实映射里。 那么怎么拿到它呢?我们一步步走。 首先,通过`results.getGroupingsDown`这个方法,你可以拿到所有向下分组的列表。我们通常先取第一个分组对象,也就是第一个`GroupingValue`。 然后,调用这个分组对象的`getKey`方法,获取这个分组的键值。比如说,行业是“金融业”,那键值可能就是“金融业”。 接下来是关键一步,我们要把这个分组键,变成事实映射的钥匙。怎么变呢?在键值的后面,加上一个感叹号和字母T,也就是“!T”。这个“!T”代表的是这个分组的汇总数据。假设键值是“金融业”,那钥匙就变成了“金融业!T”。 现在拿着这把钥匙,到报表结果里去取事实映射。用这个钥匙去`getFactMap`里拿,就能拿到对应的条目。 拿到条目后,有两个常用操作: - 如果你想直接看汇总值,比如求和、平均值这些,就用条目的`getAggregates`方法,它会给你`SummaryValue`对象,里面装着数字。 - 如果你想看这个分组下,都有哪些具体的记录,那就用`getHandler`方法,里面存着每一行的详细数据。 简单回顾一下:先拿分组键,在后面加“!T”变成钥匙,开锁取事实映射,然后看汇总还是看明细,随你选。 好了,这一页的内容就是这样,我们下段继续。
好,我们来说说报告过滤。有时候你不需要跑整份报告,只想要其中的一部分数据,比如只看某个时间段或者某个地区的销售情况。这时候,你就可以通过API来给报告加过滤条件。 这样做的好处是,你可以实时地拿到你想要的结果,而且你加的过滤条件并不会修改报告原本的定义。也就是说,别人再打开这份报告时,看到的还是完整的、没被过滤的数据。这样一来,既灵活又不会影响其他人使用。简单吧?
好,我们来看看报表API中关于过滤器的一些使用技巧和限制。你可能已经知道,用API操作报表时,你可以添加自定义的字段过滤器,最多可以加20个。而且这些过滤器之间还可以用AND和OR这样的逻辑来组合,让筛选条件更灵活。 但也要注意,并不是所有报表里的过滤器都能通过API来用。比如说,像范围筛选、按行数限制,还有交叉过滤器这些标准功能,在API里就不支持。所以使用前,我们最好先做几个检查,确保字段是可用的。 首先,可以调用`ReportTypeColumn.getFilterable`方法,它会告诉你某个字段能不能被筛选。如果返回true,那就没问题。 然后,如果这个字段是可筛选的,我们还可以通过`filterValues`属性拿到它所有可能的筛选值,方便我们在代码里做选择。 另外,不同数据类型的字段支持的比较运算符也不一样,比如数字字段可以用大于、小于,文本字段可能是包含或不包含。这个对应关系可以通过`ReportManager.dataTypeFilterOperatorMap`来查看。 最后,在写代码之前,我们可以先看看报表里已经有哪些过滤器了,避免冲突或者重复。这点可以用`ReportMetadata.getReportFilters`方法,它会返回当前报表中所有已经存在的过滤器列表。 简单来说,就是先看能不能用,再看有什么可选值,再看能用什么运算符,最后查一下现有的过滤器。这样一步步来,就能安全地在API里操作自定义筛选器了。
好,我们来看一看在Apex里怎么做报表的筛选,这个功能在实际项目中非常实用。 想象一下,你有一张销售报表,想看“本周的数据”,过一会儿又想看“上个月的数据”。难道每次都要手动进报表改筛选条件吗?不用,用代码就可以灵活控制。 整个过程大致分三步:,先看报表长什么样,再给它戴上一副“筛选眼镜”,最后戴着这副眼镜去看数据。, 具体到代码里是这样操作的: 1. 首先,用`ReportManager.describeReport`拿到这个报表的“描述信息”,也就是它的元数据,里面包含了所有列、分组、筛选条件的定义。 2. 接着,我们创建新的过滤器。比如你想按“创建日期”筛选,就新建一个`ReportFilter`对象,然后告诉它: - `setColumn`:对哪个列进行筛选,比如“CREATED_DATE”。 - `setOperator`:怎么个筛选法,比如`equals`就是等于,还可以是`greaterThan`等等。 - `setValue`:具体要等于啥,传入一个日期字符串,比如“LAST_WEEK”。 3. 做好一个过滤器后,把它放进一个列表里。一个报表可以有多个过滤器,所以用列表装起来。 4. 然后调用`setReportFilters`,把这一堆过滤器赋值给先前拿到的报表元数据。 5. 最后,拿着这个修改过的元数据去`ReportManager.runReport`运行报表,拿到的结果就是筛选后的数据啦。 最棒的是,你可以,复用,这一套元数据。想换个条件看数据?只需要重新调`setValue`,再运行一次报表就行。比如第一次筛选“本周”,拿到总计A;第二次把值改成“上周”,再运行,总计就变成B了。这样同一个逻辑,轻松对比不同时间段的数据,是不是既清晰又省事? 好了,这个知识点你学会了吗?动手试试吧,运行两次看看总计值的变化,感受一下筛选器的魅力。
同学们,刚才我们提到了报表的数据结构,现在我们来仔细看看其中一个核心概念——“事实地图”。你把它理解成一份报表的“数据藏宝图”就好。事实地图里,同时装着两样东西:一是报表的摘要信息,比如总和、平均值、记录数这些;二是每一行、每一条记录的具体数值。这样,你不管是想看全貌还是细看某一行,都能在一个结构里找到。 那怎么从这张藏宝图里找到具体的数据呢?答案是靠,关键字,来索引。事实地图用关键字给每个数据部分打了个标签,顺着关键字就能定位到是总计行、是小计行、还是某一行记录。而且你会发现,这个关键字的组成模式,会根据报表的格式——表格、摘要、矩阵——发生变化。 在,表格报表,里,结构最扁,通常关键字就是由行号和代表“总计”的标记拼起来的,比如行索引加一个大写的“T”,来区分这是某一行,还是整个报表的最底下一行的汇总。因为表格没有分组,所以关键字相对简单直接。 到了,摘要报表,,就出现了分组,这时关键字就会把分组的层级也编码进去。比如第一层分组的小计,可能就是“分组层级+特殊标记”,到第二层分组,又会把两组标识拼在一起。这样一层套一层,即便分组嵌套多深,也能精准找到是哪个维度上的小计或明细行。 而,矩阵报表,最复杂,因为它有行分组和列分组交叉。这时关键字就成了“行索引!列索引!标记”这种拼合模式,用特定分隔符把行位置、列位置连起来,来指向交叉处的单元格数据、行合计或列合计。 简单总结一下,事实地图就是一个带索引的容器,索引的设计规则完全跟着你建报表时选的那种格式走。理解这一点之后,你用Apex代码解析报表数据,就能像翻目录一样,轻松拿到你要的某个具体值了。记住这些关键字的差异,写代码调API时就不会迷路。
好,咱们今天来聊聊一个有点细但特别实用的知识点——事实地图里的“键”是怎么构成的。简单说,这个键就像一把钥匙,能帮你准确找到报告里的每一个数值。不过要注意,不同报告格式,这把钥匙的写法是不一样的。 先说最简单的表格报告。表格报告没有分组,所有数据平铺直叙,所以它的键固定就是“T!T”,前面T代表总计行,后面T代表数值本身,因为就这一层嘛,一眼就能看明白。 接下来是摘要报告。摘要报告有分组了,那键的写法就变成“分组键后面跟一个感叹号和大写T”。比如某个分组值叫“华北”,那键就是“华北!T”,这里的T指的就是这个分组的小计行。 再复杂一点,矩阵报告,它有行分组和列分组,所以键的格式是“行分组键!列分组键”。这两个分组内部的项目都是从0开始编号的。如果有多层嵌套,就用下划线隔开。举个例子,“0_1!2”表示什么意思呢?就是行分组里第一级的第一项、第二级的第二项,搭配列分组里的第三项。 总结一下:表格报告就是死记“T!T”;摘要报告是“分组名!T”;矩阵报告是“行键!列键”,嵌套用下划线。这样你再看事实地图的时候,就不会被那些奇怪的字符串弄晕了。
同学们,咱们今天来看一个比较细节但也特别实用的知识点:,怎么在 Apex 里读懂报表的事实数据键,尤其是分组报表那些奇怪的字符串到底是什么意思,。 你可能已经写过程序去跑报表,用 `Reports.ReportManager` 拿到 `ReportResult`,然后调用它的 `getFactMap()`。这个方法返回一个 Map,键是字符串,值就是汇总数据。不同的报表类型,这个键长得完全不一样,所以咱们得搞清楚命名规则。 先看最简单的:,表格式报表,,也就是没有任何分组,数据一行行打出来的那种。因为没分组,所有数据的汇总就只有一个层级——总计。在事实 Map 里,那条唯一的键就写作 `"T!T"`。注意,前面的“T”代表整个报表的总计行,感叹号后面的“T”是汇总字段的默认别名,一般来说金额、数量这类数值汇总的别名默认就是“T”。所以看到 `"T!T"`,你就知道这是整张报表唯一的一个总计块。 好,下面重点来了:,摘要报表,,有分组的那种。这时候每个分组级别都会在键里体现出来,而且索引从 0 开始。 比如只有一个分组级别,比如按“地区”分组看销售额。钥匙的格式就是 `"分组索引!T"`。给你举例,`"0!T"` 就是第一个分组值(索引0)下的总计。如果地区有“北美”“欧洲”,那 `"0!T"` 对应“北美”的汇总,`"1!T"` 对应“欧洲”的汇总。 如果有两个分组级别,键就再复杂一点。它会把各级分组的索引用下划线连起来。比如先按“地区”分,再在地区下面按“产品线”分。那么 `"1_0!T"` 是什么意思呢?我们一看:第一个数字“1”是第一个分组(地区)的索引,索引1就是第二个地区;下划线后的“0”是第二个分组(产品线)的索引,0代表该地区下的第一个产品线;最后的 `!T` 还是指汇总值别名。所以整把钥匙锁定的是:,第二个地区下面第一个产品线组合的总计数据,。 你可以把这个规则总结成:,键 = 一级分组索引_二级分组索引_... ! 聚合字段别名,。有多少级分组就拼多少级索引,用下划线隔开。每个数字都从0开始数,很简单对吧。 知道了键,接下来值也很重要。事实 Map 里的值有两种类型: - 如果报表只展示汇总信息,比如摘要报表里没有勾选“展示明细”,那这些条目就是 ,ReportFact,。你可以用 `getAggregates()` 拿到一个汇总数值数组,配合 `ReportResult` 的元数据,就能知道每个值对应哪个字段。 - 如果是包含明细行的报表,Map 里的值可能变成 ,ReportFactWithDetails,。它除了包含 `ReportFact` 的汇总功能,还能用 `getRows()` 拉出具体的明细记录列表
同学们,咱们接着聊一个比较关键但又容易让人头疼的知识点,就是矩阵报表里的“事实映射键”。你看,在Salesforce的报表里,矩阵报表的结构是最特殊的,因为它同时有行分组和列分组,所以就导致它的键格式最复杂。但别担心,我帮你把它拆开了看,其实也很容易理解。 你记住一个核心:在事实映射键中,感叹号的两边是两种分组。感叹号之前的全部,代表行分组上的取值路径;感叹号之后的全部,代表列分组上的取值路径。也就是说,数据在报表里是被行和列交叉定位的,这个键刚好就描述了交叉点在哪里。 举个最简单的例子,如果有一个键是 “0!0”,那意思就是行分组的第一个值,和列分组的第一个值,交汇处的那个单元格数据。前面的0是行,后面的0是列。 那如果行或者列有多层嵌套分组怎么办?比如行这边你按“区域”再按“产品”分组,列那边按“年份”再按“季度”分组。这种情况下,各层级的值会用下划线连起来。拿 “0_0!0_0” 这个键来说,你先看感叹号前面的 “0_0”,它表示行分组里,第一层取的是第一个值,第二层取的也是第一个值;感叹号后面的 “0_0” 同理,表示列分组里,第一层和第二层也都是第一个值。这样一交叉,你就知道这个键对应的是所有这些“第一项”交汇的那一格数据。 理解了这些键的逻辑,无论你以后是想用Apex代码动态提取报表数据,还是自己去做可视化的定制,都会非常受用。因为它能让你精准地定位到每一个汇总格里的数字。好,这块内容我们今天就说到这儿,下个小节继续。
同学们,我们接着说测试这件事儿。 大家可能已经习惯了,自己写的Apex代码要写测试类来保证覆盖率。那么,当你用Apex去调用Salesforce的报表和仪表板API的时候,是不是就不用测试了呢?不是的,同样需要写测试,也要有测试覆盖率,这一样都不能少。 不过这里有个特别的地方要注意:我们平时写的一些Apex类,有时候会在“系统模式”下运行,也就是忽略当前用户的权限。但是,报表相关的Apex方法可不是这样,它们就在当前这个真实用户的上下文中运行。也就是说,这个用户能看到哪些报表、有哪些字段的访问权限,Apex代码就只能拿到这些数据。 这就意味着,我们在写测试的时候,必须模拟出不同用户的权限场景。比如说,你创建了一个测试用户,只给了他几张报表的查看权限,然后让这个用户去跑那段访问报表的代码。这样你才能确保,代码在真实环境中,无论哪个用户来用,它都能正常工作,而不会因为权限不够突然报错。 所以记住两个要点:第一,报表和仪表板的Apex代码也需要测试覆盖率;第二,它们以当前用户身份运行,所以测试时要考虑权限。好,我们继续下一张。
大家好,我们接着来看这一页的内容,讲的是 Apex 里的报告方法在执行时的一些特殊行为,尤其在写测试的时候非常关键。 首先你要记住一点,报告 Apex 方法不是以系统模式运行的。所谓系统模式,就是可以无视权限、访问所有数据的那种超级模式,但报告不是。报告是在当前用户的上下文中运行的,这个用户也叫上下文用户,其实就是登录的那个用户。所以,报告方法能访问到的数据,就是当前登录用户能看到的东西。他自己有什么权限,报告就展示什么,跟他在界面上打开报告是一样的逻辑。 那到了写测试的时候,情况就有点特殊了。我们通常可以通过 SeeAllData 注解来控制测试类能不能看到组织里预先存在的数据。但报告在测试里非常特别,不管你把这个注解设成 true 还是 false,它都直接忽略。也就是说,报告在测试里总是能看到真实环境中已有的数据,即便你写了 SeeAllData=false 也没用。那结果里会混进很多你不想看到的历史数据,所以你得在报告定义本身加好过滤器,用过滤条件把结果限定在你需要的数据上,这是控制结果的唯一可靠办法。 还有一个非常重要的运行时机:在 Apex 测试里,如果你用的是同步报告,比如直接调 runReport 这种,那它不会马上执行,而是会延迟到 Test.stopTest 被调用时才真正跑起来。也就是说,你必须在测试方法里调用 Test.stopTest 之后,报告才会执行,然后你才能拿到最终的结果去做断言。 总结一下就是:报告 Apex 方法跟着用户权限走;测试里它无视 SeeAllData;过滤全靠报告自身的筛选条件;同步报告要等到 Test.stopTest 之后才真正运行。这几个点记牢了,以后处理报告相关的测试就不容易踩坑啦。
我们接着聊聊在Apex里怎么测试报表代码。想象一下,你要写个测试,验证某个报表运行后汇总的数据对不对。这思路其实很直接——先准备好数据,再触发报表执行,最后核对结果。 在Salesforce里测试报表,最常用的做法就是先构造一些测试数据,比如新建一个“商机”记录,或者合同、客户这些报表会依赖的对象。我们把这些记录按照报表的筛选条件准确地设好,比如阶段、金额、日期,这样报表跑起来就能抓到它们。你可以把这一步理解为:“亲手把答案填进考卷里”,后面报表算出来的结果我们就知道该是什么了。 然后,接下来是重点——如果你要测的是一个异步报表,也就是用 `runAsyncReport` 方式调用的报表,那么执行的方式有点特殊。在同步测试里,我们必须用 `Test.startTest()` 和 `Test.stopTest()` 这对括号把调用包起来。代码写起来大概是这样: ```apex Test.startTest(); // 这里触发异步报表运行,例如:Reports.ReportInstance instance = report.runAsync(); Test.stopTest(); ``` 请注意,当代码执行到 `Test.stopTest()` 的时候,Salesforce才会真正地把那个异步的报表请求执行完成。所以在我们退出 `stopTest` 之前,报表结果其实还没生成好。等 `stopTest` 一过,报表实例就填充好了,我们就可以拿过来检查。 拿过来之后,先看看状态。我们检查 `getStatus()`,它应该返回字符串 `'RST'`,代表报表已经成功完成。如果不是这个状态,那可能是测试环境有问题或者代码哪里没写对。 状态确认没问题了,我们就可以去取报表里的汇总数据。这里有一个小技巧,可以用 `T!T` 这种双字母键来访问“事实地图(Fact Map)”。这个 `T!T` 是一个约定好的键,专门用来提取报表里的总计行。你想象一下,报表像一个表格,最底下有个“总计”行,`T!T` 就是直接找到这一行里的聚合数值。用它获取到总计值之后,你就可以用 `System.assertEquals` 之类的断言,跟我们早先造的数据做比对,验证报表是否准确地把总额、记录数等聚合值计算正确了。 举个例子,你可能在测试里验证某个商机报表的总金额是否等于你创建的两个商机金额之和。这样就形成了一个完整的验证流程:造数据 → 触发报表 → 检查状态 → 核对总计值。 整体上这就是测试异步报表的套路,记住几个关键点:用 `Test.startTest/stopTest` 包裹、在 `stopTest` 之后再去拿实例、检查状态是否为 `'RST'`、再用 `T!T` 取总计进行断言。是不是挺清晰的?下次写测试的时候你完全可以照这个模板来。
好,同学们,咱们现在来聊聊Salesforce里一个特别实用的功能,叫 Salesforce Sites。 简单来说,Salesforce Sites 允许我们利用平台已有的各种强大能力,去构建对外公开的自定义页面,甚至是完整的 Web 应用程序。这些能力具体指什么呢?比如你之前学过的报表和分析功能、工作流和审批流程,还有我们用Apex写的可编程逻辑,Sites 都能直接继承过来,不用再重新造轮子。 这样一来,我们就可以给没有Salesforce账号的外部用户,比如客户、合作伙伴,直接展示数据或者提供服务,非常方便。 那在代码层面,我们怎么去管理这些网站呢?Apex 提供了两个很关键的类:一个是 Site 类,另一个是 Cookie 类。 通过 Site 类提供的方法,我们可以获取当前正在运行的网站的各种信息,比如网站的域名、前缀等。而 Cookie 类呢,则专门用来在网站访问者的浏览器里创建、读取或者删除 Cookie,帮我们实现一些像记住偏好、维持会话之类的功能。 好了,这样大家就对 Salesforce Sites 的整体概念和在 Apex 中的管理方式有了一个清晰的认识。咱们继续往下。
好,同学们,咱们今天聊一聊URL重写这个功能。 你可以把它想象成,网站自己有一套聪明的规则,能把那些又长又难记的网址,自动变成简短、好懂的链接。比如说,用户可能在浏览器地址栏里敲入一串复杂的地址,或者从收藏夹里点开一个链接,甚至从别的地方跳转过来——只要咱们提前设好了规则,系统就会自动把这些请求,重写成更友好的样子。 不只是从外面进来的链接,其实网站内部各个页面之间的那些链接,同样可以用这套规则去重写。 这样做有什么好处呢?首先,对于咱们普通用户来说,看到一个直观、含有意义的网址,比如 /产品/手机,肯定比看到一堆乱码加问号的感觉舒服多了,也更方便记忆。其次,对于搜索引擎,像Google、百度,它们也更喜欢这样结构清晰、有描述性的网址,更容易抓取我们网站的内容,排名也会更友好。 简单讲,URL重写就是帮我们把杂乱的门牌号,换成整齐好认的路牌,既照顾了人的体验,也方便了机器来“读”我们的网站。这一课的内容,今后咱们在实际开发中会经常用到,先把这个概念印在脑子里就好。
同学们,今天我们来讲一讲怎么让网站上的网址看起来更简洁、更好看,这个技术就叫做 URL 重写。比如说,你本来要访问一个又长又复杂的地址,如果能变成短小精悍、容易记的样子,用户体验就会好很多。在 Salesforce 里,想实现这个,就要用到一个叫 Site.UrlRewriter 的接口。 我们来一步步看怎么操作。首先,你要创建一个 Apex 类,让这个类去实现 Site.UrlRewriter 接口。这个类和它里面的方法,都必须声明成 global 的,也就是全局可见,这样 Salesforce 网站的底层才能找得到它们。 这个接口要求你必须实现两个方法,缺一不可,哪怕你暂时用不上,也得把方法体写出来,让它返回 null 就行。 第一个方法叫 mapRequestUrl。它的任务是处理“入站”的请求,也就是当用户在你的网站上输入一个友好的、简短的网址时,这个方法会把这个友好网址翻译成 Salesforce 实际上要展示的 Visualforce 页面。它需要返回一个 PageReference 对象,指向那个 Visualforce 页面;如果你对这个请求不想做任何重写,直接返回 null 就可以了。 第二个方法叫 generateUrlFor,它是负责“出站”的,也就是当你的网站代码要生成一个链接时,这个方法会把 Salesforce 内部原本那个长长的页面地址,重写成你对外显示的友好网址。它返回一个字符串,也就是你最终想让用户看到的短网址。同样,如果不需要重写,就返回 null。 这里要特别注意,这个重写机制只对和你的网站关联在一起的 Visualforce 页面有效,并不是对所有页面都起作用的。 总结一下,你只需要创建一个带 global 关键字的 Apex 类,实现上面说的两个方法,把该映射的网址逻辑写进去,然后把类配置到你的 Salesforce 站点设置里,就能轻松拥有漂亮的网站 URL 了。是不是很简单?快去动手试一下吧!
好,我们来看一下URL重写器的几个限制,这部分很重要,不然你配置了规则却发现不生效,就会很困惑。 首先,想被重写的URL必须是对用户友好的,不能直接暴露出Salesforce原生的那种系统格式。什么意思呢?比如你有一个记录,它的URL里带了个15位或18位的记录ID,或者开头是三个字符的实体前缀,这种URL是不会被重写的。URL重写器识别不了,它只处理你自定义的那些善意、干净的路径。 其次,注意不要在你的自定义路径里使用句点,就是那个英文句号。唯一例外的是“.well-known”这个特殊路径组件,它是通用的互联网标准,Salesforce保留了这个。除此之外,你如果写个“/my.page”之类的,是不行的。 第三,有一些字符串是Salesforce保留的,你不能把它们放在网站基本URL或域名后面的第一个路径位置上。比如你不能用“apexcomponent”、“lightning”、“entry”、“set”这些词打头。举个例子,如果你的站点是example.com,那么“example.com/entry/something”就不被允许,因为“entry”是保留字。 最后,你在代码里用Site类的getUrl方法去获取重写后的URL时,要清楚它返回的只是域名之后的那部分路径,不包括域名和站点前缀本身。这样你在构建链接时才能正确拼接。 记住这些限制,在规划和写规则的时候多加留意,可以帮你省去很多排错的时间。
同学们,我们接下来聊聊Apex中的支持课程。这些支持课程呢,就是Salesforce专门为客服支持场景设计的一些标准对象。比如说,工作时间这个对象,它用来定义你们客服团队什么时候在线接听电话;还有案例,这个大家最熟了,就是用来记录客户问题、跟踪解决状态的。通过这些支持课程,我们可以直接用Apex代码去读取、修改或者和这些客服记录互动,比如自动化分配案例,或者检查现在是不是在服务时间内。这样一来,原本需要手动操作的支持流程,就可以轻松地用代码实现了。好,这一页的内容就先讲到这儿。
好,同学们,我们来看一下这节课要讲的重要知识点——Salesforce里的“工作时间”,在Apex中对应的就是BusinessHours这个类。这个概念听起来很简单,但它在实际业务中非常关键。 首先想想,什么是工作时间呢?就是我们客户支持团队真正在线、能处理问题的时间段,通常是周一到周五,早上九点到下午五点,可能还会排除掉节假日。而BusinessHours类,就是帮我们在代码里精准地操作这种业务时间的。 它提供了两个加时间的方法,一个叫add,一个叫addGmt。这两个有什么区别呢?假设你要在某个时间点上,加上两个工作小时。add方法加完之后,返回给你的结果是按照你当地时区来显示的。而addGmt呢,它加的同样是两个工作小时,但返回给你的结果统一是格林尼治标准时间,也就是GMT。这一点在做跨时区处理的时候格外有用,千万别弄混了。 接下来是diff方法。它的作用是把两个日期时间相减,但减的不是日历上那二十四小时,而是刨掉休息和假期后,纯工作时间的毫秒数。举个例子,如果你想知道一个工单从创建到解决,在客服手里实际处理了多久,就要用这个diff。还要注意,如果结束时间跑到了开始时间的前面,它会返回一个负值,这样你就知道时间顺序有问题了。 这些功能主要用在哪里呢?就是在处理SLA计算、支持权利验证这类场景中。比如说,你承诺用户两小时响应,这里的两小时必须是工作小时,不能是自然时间。否则晚上或者周末也算进去,那就闹笑话了。所以,只要是需要用业务术语,而不是日历时间来衡量时间的地方,这个BusinessHours类就至关重要。 好,关于BusinessHours类的基础用法我们就讲到这里。大家只要记住add、addGmt和diff这三个方法的核心区别,做时间计算的时候就不会错了。
同学们,今天我们来聊一个特别实用的小方法,它藏在`Cases`这个类里,名字叫`getCaseIdFromMailThreadId`。别被长长的名字吓到,其实它做的事情特别简单直接。 咱们先想象一个场景:你们公司肯定启用了“电子邮件到案例”这个功能吧?就是客户发一封邮件到支持邮箱,Salesforce会自动把它变成一个服务案例。然后相关的邮件往来,全都会串成一条对话线,就像咱们用微信聊天一样,不管谁回复,消息都归在同一个话题底下。 那么Salesforce是怎么把这些邮件和案例绑在一起的呢?靠的就是一个叫“邮件线程ID”的东西。每封邮件都带着一个独一无二的线程ID,就好比是一个跟踪号码。创建案例的那封原始邮件有这个ID,后续客户或客服再回复、转发的邮件,线程ID都不变,始终指向同一个源头。 好,现在问题来了。如果我们在写Apex代码,想在程序里处理一封邮件——也许是自动提取附件,或者做智能分类——这时候我们手里只有邮件的线程ID,但不知道它属于哪一个案例,怎么办呢?对,就用我们今天说的方法。 你只需要简单调用 `Cases.getCaseIdFromMailThreadId`,把那个线程ID这个字符串传进去,它就会立刻返回给你对应的案例记录ID。这样一来,后面的逻辑就活了:比如你可以拿着这个案例ID去更新案例状态、把邮件内容追加到案例的评论里,或者做任何进一步的自动化处理。 用起来也很安全,如果万一这个线程ID还没有对应的案例(比如还没被转发成案例),它会返回一个空值(null),不会让程序崩掉。 所以这个方法本质上就是在打通“电子邮件线程”和“案例记录”之间的桥梁,让你在编程时能够轻松地把收发邮件和客户案例关联起来,实现更灵活的案例管理和邮件集成。以后在遇到需要处理邮件相关业务的Apex代码里,别忘了还有这么个顺手的小工具。
同学们,咱们今天要聊的是区域管理2.0,这个功能啊,主要是帮我们更灵活地划分和管理销售区域的。现在有一个特别好的消息——Apex触发器已经可以支持Territory2和UserTerritory2Association这两个标准对象了。 那这到底意味着什么呢?简单说,过去如果你想把区域变化和一些自动化流程串起来,可能得绕些弯路。但现在,有了触发器的支持,你就可以直接在Territory2或UserTerritory2Association的记录被插入、更新甚至删除时,用代码去自动触发你想做的任何事。 举个例子,假如一个销售被新分配到一个区域,触发器马上就能捕捉到这个变动,然后自动帮他开通相关的客户数据权限,或者给团队经理发一封提醒邮件。又或者,某个区域规则改了,你也可以立刻去更新所有相关成员的配额。这样一来,整个区域管理就不再是孤立的数据,而是能无缝融入到你更大范围的自动化业务流程里,让你的系统反应更及时、管理也更省心。 这个小小的支持升级,在实际开发中会带来非常大的便利,大家未来写代码的时候可以好好利用起来。
同学们,咱们来看今天这一页 Slide,它讲的是两个和 Territory 2 相关的 Apex 触发器示例。我会用最直白的语言,帮你理解它们干了什么,以及为什么这么做是符合“批量安全模式”的。 我们先看第一个触发器——它是建在 Territory2 这个对象上的。什么时候触发呢?当有新的区域记录被,创建,,或者已有的区域被,删除,时,这个触发器就会跑起来。它的任务,是去维护 Territory2Model(也就是区域模型)上的一个自定义字段,叫 TerritoryCount,用来记录这个模型下面总共有多少个区域。 那它是怎么算的呢?巧妙的地方来了:它没去一条一条数,而是用一个 Map,或者叫“计分板”。每当创建一个区域,就给对应模型的计数器 ,+1,;每当删除一个,就 ,-1,。等触发器里所有的变更都过完一遍,手里就拿着一份“每个模型净增或净减了多少”的记录。最后,只发,一次,批量更新,把所有受影响模型的 TerritoryCount 字段一次性改好。这样,不管你是建了1个区域还是200个区域,都只做一次 DML 操作,效率非常高,这就是批量安全的核心思路。 第二个触发器是建在 UserTerritory2Association 上的,也就是用户和区域的关联表。它只在,插入,后触发,意思是有新的用户被分配到某个区域时,它就动起来。它的目的是通知销售运营团队。它会收集这次插入里所有的新关联记录,把“哪个用户”被添加到了“哪个区域”,以及这个区域属于“哪个模型”,用 String.format 方法拼成一段易读的邮件正文,然后通过 Messaging.sendMail 把这一封邮件发出去。注意,它也是攒齐了信息只发一封邮件,而不是每新增一个关联就发一封邮件去轰炸运营团队的收件箱,同样是批量处理的思想。 这两个触发器,背后都在遵循我们 Apex 开发里必须遵守的,批量安全模式,。简单说就是把所有记录攒在一起处理,不在循环里写 SOQL 查询,也不在循环里做 DML 语句,这样不管一次性来多少条数据,代码都能稳稳地跑,不会撞上 Governor Limits。这个习惯,大家以后写触发器的时候一定要烙在脑子里。 好,这一页就讲到这里,有什么不清楚的随时可以问我。