DEX455

Flow 与 Formula Evaluation:Apex 集成 Flow Builder 完全指南

课程介绍

Flow与Formula Evaluation完整指南:Flow.Interview.getVariableValue获取Flow变量(子Flow面包屑导航)、屏幕Flow中callout=true修饰符(提交当前事务+启动新事务三条件)、InvocableActionExtension元数据文件(参数Order排序/Group折叠分组/ProvidedValuesList静态选择列表500上限)、DynamicPicklist动态选择列表(扩展VisualEditor.DynamicPicklist/apex://引用/高效防超时)、CustomHeaderLwcName自定义头部(LWC/c:componentName)、Partial CPE部分自定义属性编辑器(CpeName+ConfiguredBy/新参数自动标准编辑器)、Process.Plugin接口(@InvocableMethod强烈推荐替代/不支持Blob+Collection+sObject+批量/Legacy不支持自动布局/describe+invoke两方法/no查询+数据修改+邮件+嵌套callout)、Process.PluginRequest(Map构造)/PluginResult(Map或单值构造)/PluginDescribeResult(InputParameter+OutputParameter/11种ParameterType枚举)、Flow↔Apex类型转换、Lead转换完整示例(8输入3输出/Database.convertLead/测试类5场景)、Formula Evaluation(FormulaBuilder.withType/global类/Address+Location+URL+UUID/多态关系/3900字符限制/Formula.recalculateFormulas)。

课程章节

本课程共有 10 个章节

  • 1

    Flows & Formula Evaluation

    第 291 页

    同学们好,这节课我们来看一个特别实用的主题——Apex 和 Flow 的集成。在 Salesforce 里,Flow Builder 是一个很强大的工具,它让管理员不用写代码就能搭建各种自动化业务流程,比如引导用户填写信息、自动更新记录、发送邮件等等。而 Apex 呢,作为编程语言,可以帮我们实现更复杂的逻辑。那它们怎么配合起来呢?我们这一页幻灯片就给出了清晰的路线图。 首先,Apex 给我们提供了 Flow.Interview 这个类。简单说,当 Flow 在运行时,Apex 可以用这个类来拿到当前 Flow 里的变量值,或者把值传回去,就像和 Flow 进行一次对话一样,很方便地在代码里控制或是监控 Flow 的状态。 第二种方式是 @InvocableMethods 注释。你可以在 Apex 类里写一个方法,加上这个注释,这个方法就会自动变成一个“行动”,可以直接在 Flow Builder 里像搭积木一样拖出来用。这样管理员不用懂代码,也能调用你写好的 Apex 功能了,大大扩展了流程的能力。 为了让管理员用起来更顺手,咱们还可以定制这个行动在界面里的样子。这就需要用到 InvocableDelivery 扩展元数据文件。通过它,你可以改行动在 Builder 里的图标、标签、说明文字,让它看起来更友好,符合自己公司的命名习惯。 另外,幻灯片里还提到了 Process.Plugin 界面,这个是用于遗留的 Apex 操作的一种方式。早些年还没有 @InvocableMethods 的时候,我们用这个接口把 Apex 暴露给流程。不过现在新开发的话,我们更推荐 @InvocableMethods,因为它更简洁、功能也更强大。 除了这些集成手段,Apex 在公式计算方面也做了优化。咱们在用 Apex 处理公式的时候,它可以避免重复计算那些不必要的公式,从而节省系统资源。同时还支持带类型的动态表达式,让表达式的处理更安全、更高效。 总结一下,这一章会深入探讨所有这些集成模式:从 Interview 类,到可调用方法,到界面定制,再到遗留的插件方式,最后还有公式计算的精巧设计。掌握了这些,你就能把 Flow 和 Apex 组合得更默契,让业务流程既灵活又强大。 好,我们接下来就会逐个展开,请跟我一起深入学习吧。

    查看详情
  • 2

    Getting Flow Variables with Flow.Interview

    第 292 页

    同学,我们来看一下 Flow.Interview 这个 Apex 类里面一个很有用的方法,叫 getVariableValue。这个名字很直白,就是获取流变量的值。 场景是这样的:我们可能在 Visualforce 页面里嵌入一个流,这个流本身可能还会调用子流,就好像俄罗斯套娃一样,一层套一层。那么问题来了,我们的页面怎么知道当前到底运行到哪一步了呢? 这时候就可以利用一个统一约定的变量,比如叫 vaBreadCrumb,也就是面包屑。你想象一下网页顶部那一行 “首页 > 产品 > 详情” 的导航路径,我们就想动态显示这种导航信息。 getVariableValue 方法允许我们在 Apex 代码里,拿到当前正在运行的那个流里面 vaBreadCrumb 的值,不管这个流是主流程,还是被主流程调用的子流。只要它含有这个变量名,我们就可以取到。 而且,贴心的 Salesforce 还考虑了变量可能不存在或者值为空的情况。你可以在获取的时候传入一个默认值,如果没取到就返回你指定的内容。比如面包屑例子中,如果没有任何导航信息,就让它返回 “Home” ,这样用户至少知道自己在首页,体验就不会突兀。 所以总结一下,Flow.Interview 的 getVariableValue 就像一把万能钥匙,帮我们跨越主流程和子流程,拿到关键的状态信息,让你的页面和流配合得更默契。 这样讲解,你理解了吗?

    查看详情
  • 3

    Callouts from Invocable Actions in Screen Flows

    第 293 页

    同学们,咱们这节课讲一个在屏幕流当中特别实用的知识点。 当你写了一个 Apex 方法,想把它作为“可调用动作”放到屏幕流里,而且这个方法还要去调用外部系统、发消息、查接口,也就是我们常说要做“外部调用”——在 Slide 里有个词叫“收件箱修饰符”,指的就是这个事儿。 那怎么告诉屏幕流“我这个方法要跟外面打交道”呢? 关键就在 ,@InvocableMethod, 这个注解上,给它加上 ,收件箱修饰符,,实际上就是把它的 `callout` 属性设为 `true`。 屏幕流看到这个修饰符,就知道:“哦,这个动作要往外打电话了,我得考虑一下事务安全。” 它会这么处理: - 如果当前事务里还有没保存的数据库变更,比如你改了记录信息还没提交,那屏幕流就会先自动帮你提交这些东西,,结束当前事务,然后开一个新事务,,在新事务里安安全全地去执行外部调用。 - 要是没收件箱修饰符,也就是 `callout` 没开,那这个调用就会混在当前事务里执行,有可能因为数据库锁或者平台限制而出问题。 这样一来,咱们的流管理员在设计流程时,就有很大的灵活度了——他们不用担心底层事务冲突,只要给方法标上这个“收件箱”标记,系统就会自己处理好保存和调用的顺序,让整个流程又稳又安全。 你可以这么记:,想从流程里往外发消息、调接口,就在注解上打个“允许收件箱(callout=true)”的勾,剩下的事务控制,交给屏幕流去操心就行。, 这样讲清楚了吗?有什么问题随时提出来。

    查看详情
  • 4

    Extend Flow Builder — InvocableActionExtension

    第 294 页

    好,现在我们来讲一个很实用的技巧——在Flow Builder里调试Apex可调用动作,通过一个扩展元数据文件来美化配置界面。 你平时在Flow里调用Apex动作时,参数一多,是不是觉得有点乱?所有输入框从上到下一字排开,既不好看,也不方便业务人员理解。这时候,我们就可以用一个小文件来定制它在Flow Builder里的显示方式。 这个文件的后缀是 `.invocableactionextension-Meta.html`,文件名必须和你的Apex类名完全一致。注意大小写也要对得上。然后把它放在一个叫 `invocableactionextensions` 的文件夹里。这是Salesforce规定好的路径,放错地方可就认不到了。 文件里你可以定义每个输入参数在界面上的显示顺序和分组。有两个关键属性:一个是 `Order`,它控制垂直方向的显示序号;另一个是 `Group`,可以把几个参数收进一个可折叠的分组里,让界面更加整洁。 举个例子,假设你有个叫 `BookingAction` 的Apex可调用动作,它需要用户输入开始日期和结束日期。你可以把它们归到一个叫“预订日期”的组里,让 `startDate` 的 Order 设为 1,排在组内第一个,`endDate` 的 Order 设为 2,排在第二个。这样Flow Builder里就会看到一个“预订日期”的可折叠区域,点开后两个日期字段按你指定的顺序排列。 这里有一个重要规则要记住:只要你给其中任何一个参数设置了 Order,就必须给这个动作的所有输入参数都设置 Order,一个也不能漏。否则系统会报错,因为顺序不完整。 这样一来,业务人员在Flow里配置你的动作时,就能看到清晰、分组合理的界面,而不是一长串冷冰冰的变量名。调试起来也就更直观了。

    查看详情
  • 5

    Static & Dynamic Picklists for Flow Builder

    第 295 页

    同学们,咱们今天来看这一页幻灯片,讲的是Salesforce里的静态选择列表和动态选择列表。内容不多,但很实用,我慢慢给你们梳理一下。 先说静态选择列表吧,这个很简单。咱们在定义字段或者流程参数的时候,直接把选项用逗号一个个隔开就行了。比如“苹果,香蕉,橘子”。如果你想显示的标签和实际存的值不一样,还可以用竖线来分隔,左边是值,右边是标签,像这样:“CA|加州,NY|纽约”。不过要记住,一个静态列表最多只能塞500个值,超过就不行了。 那如果选项是需要实时查的,比如说你要列出所有活跃的客户,总不能每次手动改静态列表吧?这时候就要用动态选择列表了。它算是静态列表的升级版,把可视化编辑器给扩展了,允许咱们用Apex代码来动态生成选项。 怎么实现呢?你需要写一个Apex类,让它继承一个叫 `VisualEditor.DynamicPickList` 的抽象类。然后最关键的两步是:重写两个方法。幻灯片上写的“getProtect Value”,其实应该是 `getDefaultValue`,也就是定义默认选中的值;另一个是 `getValues`,在这个方法里写你的查询逻辑,比如用SOQL从数据库捞数据,然后包装成 `VisualEditor.DataRow` 的列表返回,这样数据源就活起来了。 写完类之后,怎么用呢?在Lightning App Builder或者Flow里面,你得用一种特殊格式来引用它:`apex://你的类名`。就类似 `apex://MyDynamicPickList`,这样平台就知道要去调用你这个Apex类了。 不过,有一个特别重要的提醒:动态选择列表的逻辑,是在你配置Flow或操作的时候直接运行的,也就是在Flow Builder里点来点去配置参数时它就开始执行Apex代码了。所以一定要让代码效率高一点,别搞那种慢悠悠的复杂查询或者调用外部接口,不然很容易卡住或者超时。一般我们就写个简单的SOQL,加上合适的 `LIMIT`,瞬间跑完,配置体验就会很顺畅。 好了,这一页的重点咱们就讲完了。你只要记住:静态列表用逗号和管道分隔,上限500;动态列表要继承 `DynamicPickList`,重写默认值和数据源方法,用 `apex://` 引用,并且保证代码轻快。掌握这些,就能灵活处理各种选择列表的需求了。

    查看详情
  • 6

    Custom Headers & Partial Custom Property Editors

    第 296 页

    你有没有想过,咱们在Salesforce的属性面板上,怎么把顶部那个自定义的标题区域,换成自己开发的Lightning Web Components? 其实很简单,就是用 CustomHeaderLwcName 这个参数来引用你的 LWC 组件。它专门配合收件箱定义的目标来显示,这个目标就是告诉系统:嘿,这个 LWC 要显示在属性面板的最上面,作为标题栏存在。 再往下看,怎么跟下面的可配置属性联动呢? 有的 CPD——也就是自定义属性定义——会带上一些自己特有的参数,但还有些 CPD 直接用标准编辑器就行,不需要额外改动。关键在这儿:主参数靠着 CpeName 把自己的 LWC 分配过来,然后相关的附属参数就可以用 IntegratedBy 这个属性,跟主参数“绑”在一起,这样它们就能联动显示或隐藏。 好消息是,如果新增一个参数,它会自动用标准编辑器,你完全不用去改原来 CPD 的代码,是不是很方便? 还有一个重要的点:CPD 在属性面板上的显示顺序,完全由主参数的 Order 属性来控制。你设个数字,它就能乖乖排到你要的位置。 这样咱们就能用自定义 LWC 做出更灵活的属性面板标题了,整体上既直观又好维护。

    查看详情
  • 7

    Process.Plugin Interface — Overview & Implementation

    第 297 页

    同学们,我们来看看这张幻灯片上讲的内容——一个有点“历史悠久”的技术点,叫做 Process.Plugin 接口。 简单说,它就像一个“翻译官”,让我们写的 Apex 代码能和 Salesforce 里的“流”(也就是那个拖拽式的自动化工具)互相传递数据。为什么需要它呢?因为流很聪明,能可视化地操作数据,但有些复杂逻辑还得靠代码,这时候就得有个桥梁,把流的输入传给 Apex,Apex 处理完再把结果送回给流。 不过大家注意,这个 Process.Plugin 现在已经是遗留接口了。用是可以用的,但 Salesforce 官方更推荐我们使用 @InvocableMethod 注解。为什么呢?因为新的注解好太多了——它支持所有数据类型,还能一次性批量处理很多条记录。更厉害的是,不仅流能调用,流程构建器、甚至 REST API 都能直接调用这些加上注解的方法,特别灵活。所以我们现在学 Process.Plugin,主要是为了看懂老代码,或者维护旧系统。 那这个 Process.Plugin 具体怎么用呢?它内部需要配合三个小帮手类一起工作。 第一个类叫 Process.PluginRequest,你可以理解成“请求包裹”。它专门用来接收从流传过来的输入参数,里面其实就是一个 Map 结构,就像个 key-value 对的小字典,流的参数就打包在里面。幻灯片里提到它的构造函数可以传入一个 Map,用起来很方便。 第二个类叫 Process.PluginResponse,它负责“回传结果”。返回的时候,可以是一个 Map 包含很多字段,也可以只返回一个 key-value 对,看实际需要。相当于把处理好的数据重新打包,交给流。 第三个类是用来描述插件自身信息的,名字有点像 Process.PluginDescribeResult(但咱们幻灯片里叫 PluginCredibeRecord,大家知道有这个描述类就行)。这个类里会定义插件的名称、标签,还有输入参数的数据类型是什么——幻灯片提到一个叫 inputMechanics 的枚举字段,用来告诉流这个输入是怎么个形式。另外还有 outputFields,列出插件会输出哪些字段。等于说,这个描述类就是插件的“说明书”,让流知道该怎么跟它对接。 幻灯片里还举了一个 FlowChat 示例。这个示例演示了怎么用 Process.Plugin 接收一个输入参数(幻灯片上误写成了 “WRING”,估计是想说“一个单一的输入参数”),然后把这个参数作为内容,从流里发一条消息到 Chatter 协作平台上去。你们可以想象,用户在流里输入一段文字,点一下按钮,背后代码就自动发出一条 Chatter 动态,很实用。 不过再强调一次,虽然 Process.Plugin 能干活,但它确实老了。咱们现在写新功能,一定要优先考虑 @InvocableMethod,写起来更简单,功能更强大,还支持批量处理。但万一维护旧项目时遇到了,你也能知道它是干什么的,怎么读。 好了,关于这个小知识就先讲到这里,有什么问题随时问。

    查看详情
  • 8

    Process.Plugin — Input/Output Parameters & Types

    第 298 页

    好,同学们,今天我们来讲一下 Apex 里的 Process.Plugin 接口,它是用来让我们在流程里调用自定义的 Apex 代码的。你写一个插件类,实现这个接口,就能在 Process Builder 或者 Flow 里使用它了。 首先,这个插件怎么告诉流程:我有哪些输入、哪些输出呢?答案就是用 InputTerm 和 OutputTerm 这两个内部类。你在插件类里必须定义一个 `describe` 方法,返回一个描述对象,里面就有这两个列表。 在 InputTerm 里,你会告诉流程:这个参数叫什么名字,是不是必填的,它是什么类型。构造函数需要三个东西:名字(字符串),一个可选的描述(也是字符串),还有一个 MechanterType 枚举,以及一个布尔值,用来标这个参数是不是必填的。必填的那个布尔值不可以省略,一定要写。而 OutputTerm 就简单一点,只需要名字、可选的描述和 MechanterType 枚举,没有必填这个字段,因为输出肯定是插件返回的。 那 MechanterType 能支持哪些类型呢?从布尔值到字符串,一共覆盖了 11 种基础的原始类型。比如 BOOLEAN, NUMBER, CURRENCY, DATE, DATETIME, PERCENT, TEXT, EMAIL 等等,一直到 WRING 都在内。但是它不支持复杂类型,像 Blob、集合(List、Map 这些)还有 sObject 都是不行的,这是插件的限制。 在数据层面,还有自动转换的规则,这点要留意。比如,如果你在流程里输入一个数字,但插件的 InputTerm 声明为 DECIMAL,系统会自动转换;日期和日期时间之间也能互相转换,只是时间部分可能被截掉;布尔值可以自动转成布尔,也可以被当作 1 或 0 的数字来用;文本类数据会自动映射成 Apex 里的字符串。这些转换都是平台帮你做的,不需要你写额外代码。 接下来讲讲怎么写测试类。因为插件的 invoke 方法是实打实要被流程调用的,所以测试的时候,你需要模拟这个调用过程。你可以这样写:先实例化你的插件类,然后用 Map<String, Object> 搭一个 inputParams,里面放的 key 就是 InputTerm 的名字,value 就是你要传的测试值。然后构造一个 Process.PluginRequest,把 inputParams 和插件描述传进去。最后调用 invoke 方法,拿到 Process.PluginResult,并断言 result.outputValues 里对应的输出参数名和数值是不是你期望的。这样就能保证你的插件逻辑是对的。 不过要注意,在你写插件逻辑的时候,有些操作用不了。因为插件是在流程的上下文中执行,它不支持查询数据库(SOQL)、发送外发消息(比如 Email)、做全局描述报告,也不允许在插件内部触发别的流程或再次调用嵌套的插件。这些限制是为了保证流程执行的安全性和可控性,所以你的插件里要避开这些操作。 好了,记住:定义输入输出用 InputTerm 和 OutputTerm,注意类型转换规则,测试时手动构造 request,以及避开那些不支持的调用,你就能顺利写出安全的流程插件了。今天就到这里,下次我们再深入练习。

    查看详情
  • 9

    Process.Plugin — Lead Conversion Sample

    第 299 页

    同学,我们来看这个关于“VWFConvertLead 示例”的内容。这个示例演示了怎么用 Process.Plugin 来实现 Lead(潜在客户)的转换。你想象一个流程,把一个潜在客户变成真正的客户、联系人和商机,这个插件就是用来干这件事的。 我们来看看它的设计。它需要八个输入参数,其中两个是必须的:,LeadID,(你要转换哪个潜在客户)和 ,ConvertedStatus,(转换后变成什么状态),其他六个是可选的,比如 AccountID、ContactID,还有一些布尔标志来控制转换行为。输出是三个:转换后产生的 AccountID、ContactID 和 OpportunityID。 关键在它的调用方法里。它会先处理好那些可能为空的布尔值,给个默认值,避免出错。然后它有个智能的地方:如果没指定账户,它会按公司名称去自动匹配已有的 Account,尽量帮你关联上。接着它构建一个 Database.LeadConvert 对象,把所有参数设置好,最后调用数据库的 convertLead 方法,执行转换,并把结果 ID 返回出来。 为了确保一切正常,这个示例还带了五个测试方法,把测试覆盖得挺全面: - 第一个,,基本转换,,就是最常见的场景。 - 第二个,,指定账户转换,,测试传入特定 AccountID 时是否顺利。 - 第三个,,多个匹配账户,,看看当公司名称找到多个匹配的 Account 时,插件能不能处理。 - 第四个,,传入无效 ID,,这是负面测试,确保它不会崩溃,能正确处理错误。 - 第五个,,描述方法验证,,验证插件的描述信息是否正确。 所以这个示例很典型,既告诉你 Process.Plugin 接口怎么实现,又让你看到实际业务中如何处理边界情况和测试。好好理解它,对你以后写自己的流程插件会很有帮助。

    查看详情
  • 10

    Formula Evaluation in Apex

    第 300 页

    同学们,咱们今天来聊一个非常实用的小技巧:怎么用 Apex 里的公式引擎,帮咱们省去那些不必要的数据库操作。 你想想,很多时候我们想在代码里算出一个公式字段的值,或者根据某个对象动态地算一个表达式,按老办法,是不是得先把记录保存到数据库,让系统触发公式字段的重新计算,再查出来?这一来一回的 DML 语句,不光浪费资源,还拖慢性能。现在不用了,Apex 直接提供了一套公式计算工具,可以让你在内存里就把这事儿办了。 具体怎么做呢?核心是借助一个叫 `FormulaBuilder` 的类。你只要调用它的 `withType()` 方法,告诉它你想基于哪种对象来算——这个对象可以是标准的 sObject,比如账户、联系人,也可以是咱们自定义的 Apex 类对象。相当于你把一个上下文对象传进去,说“嘿,帮我基于这个家伙身上的字段,算一下这个公式”。 那支持哪些类型的上下文呢?除了我们熟悉的 sObject 字段,像地址、位置、网址还有 UUID 这些系统类型也都能当上下文丢进去。更有意思的是,标准查找、自定义查找,甚至多态关系字段,它都能处理。比如你有一个“负责人”字段,不管它指向的是用户还是联系人,公式引擎都能顺着这张关系拿出来用。 这里有几个贴心的限制要记住:你写的公式文本本身不能超过 3900 个字符,这跟咱们在验证规则里写的公式基本一个样。但是别担心,它不会受到 Apex 代码整体编译大小的限制,也就是说,即使你有很多公式表达式,也不会让你的类编译不过去。用起来相当自由。 还有一个好搭档叫 `Formula.recalculateFormulas()`。当你手上有一批记录,想把上面那些不需要存盘就能算的公式值刷新一遍,就可以直接调用这个方法。它会代替你完成一次“数据库往返”——但这并不是真的 DML,而是用一种高效的方式重新计算公式字段,让你在没做任何修改保存的情况下,拿到最新的计算结果。 简单总结一下,以后你在代码里遇到需要动态计算公式、或者想批量刷新公式值却不想碰数据库的时候,就记得用 `FormulaBuilder` 搭上下文,`Formula.recalculateFormulas` 拿结果。公式写法和验证规则完全一样,限制在 3900 字符以内,随便用。这招用好了,你的触发器、批处理操作会轻快很多。 咱们今天就先讲到这,你可以回去试试,特别方便。

    查看详情