关于 building-omnistudio-callable-apex
用于创建、审查和迁移基于 System.Callable 的 Salesforce Industries 可调用 Apex 技能,面向集成 OmniStudio 扩展点的开发者,强调明确的操作契约、安全数据访问、一致响应和测试覆盖。
主要功能
- 01生成 System.Callable 实现
- 02审查可调用代码风险
- 03迁移旧版 Vlocity 接口
- 04定义操作与响应契约
- 05落实 CRUD/FLS 和共享安全
- 06按 120 分量表验证
适用场景
- 01实现 OmniStudio 可调用操作
- 02将 VlocityOpenInterface 类现代化
- 03测试输入契约和批量处理
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill building-omnistudio-callable-apex安装后在 Claude Code 中输入/building-omnistudio-callable-apex确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
CREDITS.mdREADME.mdassets/pattern_callable_openinterface.clspattern_callable_vanilla.clspattern_migration.clspattern_openinterface.clspattern_test_class.clsexamples/Test_QuoteByProductCallable/Industries_QuoteByProductCallable.clsIndustries_QuoteByProductCallableTest.clsIndustriesCallableException.clsTRANSCRIPT.mdexamples/Test_VlocityOpenInterface2Conversion/IndustriesCallableException.clsMyCustomCallable.clsMyCustomCallableTest.clsMyCustomRemoteClass.clsTRANSCRIPT.mdexamples/Test_VlocityOpenInterfaceConversion/IndustriesCallableException.clsMyCustomCallable.clsMyCustomCallableTest.clsMyCustomVlocityOpenInterface2.clsTRANSCRIPT.md详细介绍
building-omnistudio-callable-apex:Salesforce Industries Common Core 的 Callable Apex
专注于 Salesforce Industries Common Core 的 Callable Apex 实现。编写安全、确定且可配置的 Apex,确保与 OmniStudio 和 Industries 扩展点顺畅集成。
范围
- 范围内:为 Industries 扩展点创建
System.Callable类;审查 Callable 实现的正确性和风险;将VlocityOpenInterface/VlocityOpenInterface2迁移到System.Callable;进行 120 分制评分和验证 - 范围外:不包含 Callable 接口的通用 Apex 类(使用
generating-apex);构建 Integration Procedure(使用building-omnistudio-integration-procedure);编写 OmniScript(使用building-omnistudio-omniscript);部署 Apex 类(使用deploying-metadata)
核心职责
- 生成 Callable:构建具有安全操作分派机制的
System.Callable类 - 审查 Callable:审计现有 Callable 实现的正确性和风险
- 验证与评分:根据 120 分制评分标准进行评估
- 适配 Industries:确保兼容 OmniStudio/Industries 扩展点
工作流(4 阶段模式)
阶段 1:收集需求
询问:
- 入口点(OmniScript、Integration Procedure、DataRaptor 或其他 Industries 挂钩)
- 操作名称(传递给
call的字符串) - 输入/输出约定(必需键、类型和响应结构)
- 数据访问需求(对象/字段、CRUD/FLS(创建/读取/更新/删除和字段级安全)规则)
- 副作用(DML、调用、异步要求)
然后:
- 扫描现有 Callable 类:
Glob: **/*Callable*.cls - 确定 Industries 扩展使用的共享工具类或基类
- 创建任务列表
阶段 2:设计与约定定义
定义 Callable 约定:
- 操作列表(明确列出并进行版本控制的字符串)
- 输入架构(必需键 + 类型)
- 输出架构(一致的响应封装)
建议的响应封装:
{
"success": true|false,
"data": {...},
"errors": [ { "code": "...", "message": "..." } ]
}
操作分派规则:
- 使用
switch on action - 默认分支抛出类型化异常
- 不使用动态方法调用或反射
VlocityOpenInterface / VlocityOpenInterface2 约定映射:
为旧版 Open Interface 扩展(或同时支持 Callable 和 Open Interface)设计时,映射以下签名:
invokeMethod(String methodName, Map<String, Object> inputMap, Map<String, Object> outputMap, Map<String, Object> options)
| 参数 | 作用 | Callable 对应项 |
|---|---|---|
methodName | 操作选择器(与 action 语义相同) | call(action, args) 中的 action |
inputMap | 主要输入数据(必需键、类型) | args.get('inputMap') |
outputMap | 写入结果的可变映射(按引用传出) | 返回值;Callable 返回封装对象 |
options | 其他上下文(父级 DataRaptor/OmniScript 上下文、调用元数据) | args.get('options') |
Open Interface 约定的设计规则:
- 将
inputMap和options视为组合输入架构 - 定义每种操作在成功和错误情况下必须写入
outputMap的键 - 保留
methodName字符串,使其与 Callable 的action字符串一致 - 记录每种操作的
options是必需、可选还是未使用
阶段 3:实现模式
原生 System.Callable(扁平参数,不耦合 Open Interface):
生成前请先阅读 assets/pattern_callable_vanilla.cls——当调用方传入扁平参数且不需要集成 VlocityOpenInterface 时使用。
Callable 骨架(输入与 VlocityOpenInterface 相同):
生成前请先阅读 assets/pattern_callable_openinterface.cls——与 Open Interface 集成时,或调用方传入该结构时,在 args 中使用 inputMap 和 options 键。
输入格式:调用方将 args 作为 { 'inputMap' => Map<String, Object>, 'options' => Map<String, Object> } 传入。为保持向后兼容,如果 args 中没有 'inputMap',则将 args 本身视为 inputMap,并将 options 设为空 Map。
实现规则:
- 保持
call()精简;委托给私有方法或服务类 - 尽早验证并转换输入类型(安全处理 null)
- 强制执行 CRUD/FLS(创建/读取/更新/删除和字段级安全)及共享规则(
with sharing、Security.stripInaccessible()) - 当 args 包含记录集合时进行批量化处理
- 适当时在 SOQL 中使用
WITH USER_MODE - 命名空间处理:
System.Callable是标准接口(无需命名空间前缀);omnistudio.VlocityOpenInterface2使用托管包命名空间omnistudio——务必始终使用限定名称。如果 Callable 类将部署到带命名空间的托管包中,请向用户确认命名空间前缀,并将其应用于自定义类名(例如,myns__Industries_XxxCallable)
VlocityOpenInterface / VlocityOpenInterface2 实现:
实现 omnistudio.VlocityOpenInterface 或 omnistudio.VlocityOpenInterface2 时,使用以下签名:
global Boolean invokeMethod(String methodName, Map<String, Object> inputMap,
Map<String, Object> outputMap, Map<String, Object> options)
生成前请先阅读 assets/pattern_openinterface.cls——其中包含完整的 VlocityOpenInterface2 骨架,以及使用 switch on 进行分发和 outputMap 契约的示例。
Open Interface 实现规则:
- 使用
putAll()或单独调用put()将结果写入outputMap;不要从invokeMethod返回封套 - 成功时返回
true,不支持或失败的操作返回false - 使用与 Callable 相同的内部私有方法(参数相同,均为
inputMap和options);只有入口点不同 - 使用相同的封套结构(
success、data、errors)填充outputMap,确保一致性
Callable 和 Open Interface 接受相同的输入(inputMap、options),并委托给签名相同的私有方法来共享逻辑。
阶段 4:测试与验证
最低测试要求:
- 正向:支持的操作成功执行
- 负向:不支持的操作抛出预期异常
- 契约:缺失或无效的输入返回错误封套
- 批量:处理列表输入时不触发限制
生成测试前,请先阅读 assets/pattern_test_class.cls——其中包含完整的测试类骨架,涵盖正向、负向、契约、批量和 null 参数用例。
迁移:从 VlocityOpenInterface 迁移到 System.Callable
现代化 Industries 扩展时,将 VlocityOpenInterface 或
VlocityOpenInterface2 实现迁移到 System.Callable,并保持
操作契约稳定。
指南:
- 将操作名称(
methodName)保留为call()中的action字符串 - 将
inputMap和options作为args中的键传递:{ 'inputMap' => inputMap, 'options' => options } - 返回一致的响应封套,而不是修改
outMap - 保持
call()精简;委托给签名为(inputMap, options)的相同内部方法 - 为每项操作和不支持的操作添加测试
开始迁移前,请先阅读 assets/pattern_migration.cls——其中包含标注过的迁移前后示例(从 VlocityOpenInterface2 迁移到 System.Callable)。
最佳实践(120 分制)
| 类别 | 分值 | 关键规则 |
|---|---|---|
| 契约与分发 | 20 | 明确的操作列表;switch on;带版本号的操作字符串 |
| 输入验证 | 20 | 验证必需键;安全地转换类型;空值保护 |
| 安全性 | 20 | with sharing;CRUD/FLS 检查;Security.stripInaccessible() |
| 错误处理 | 15 | 类型化异常;统一的错误封装;不得使用空 catch |
| 批量处理与限制 | 20 | 循环中不得执行 SOQL/DML;支持列表输入 |
| 测试 | 15 | 正向、负向、契约和批量测试 |
| 文档 | 10 | 为类和操作方法编写 ApexDoc(/** ... */ 块注释——Salesforce Apex 文档标准) |
门槛:✅ 90 分以上(就绪) | ⚠️ 70–89 分(审核) | ❌ 低于 70 分(阻止)
⛔ 防护措施(强制要求)
如果要引入以下任何一项,请暂停并询问用户:
- 根据用户输入动态执行方法(禁止反射)
- 在循环中执行 SOQL/DML
- 在可调用类上使用
without sharing - 静默失败(空
catch、吞掉异常) - 不同操作的响应格式不一致
易错点
| 问题 | 解决方案 |
|---|---|
调用方传入扁平参数,但代码要求 inputMap 键 | 进行防御性处理:如果 args 不包含 'inputMap' 键,则将 args 本身视为输入映射 |
call() 收到的 args 为 null | 访问键之前始终检查 args 是否为 null;如果为 null,则初始化为空映射 |
测试类使用 (Map<String, Object>) svc.call(...),但 call 返回了错误类型 | 确保每个操作都返回相同的封装类型(Map<String, Object>)——混合返回类型会导致调用方出错 |
迁移 VlocityOpenInterface2 后,读取 outputMap 引用的调用方出错 | 迁移到 Callable 后,调用方必须读取返回值,而不是读取 outputMap——更新所有调用方 |
项目中缺少 IndustriesCallableException 类 | 必须将此自定义异常与可调用类一同部署——将其包含在每个部署包中 |
| 组织中同时将旧版 Open Interface 和新版 Callable 绑定到同一操作 | 同一时间只能启用一个入口点;确认 Callable 正常工作后,停用旧接口 |
常见反模式
call()包含业务逻辑,而不是委派处理- 操作名称没有版本号或未记录
- 假定输入映射包含所需键,却不进行检查
- 响应类型混杂(有时是 Map,有时是 String)
- 未测试不支持的操作
跨技能集成
| 技能 | 使用时机 | 示例 |
|---|---|---|
| generating-apex | 可调用实现以外的一般 Apex 工作 | “为 Account 创建触发器” |
| generating-custom-object / generating-custom-field | 编码前确认对象/字段是否可用 | “描述 Product2 字段” |
| deploying-metadata | 验证/部署可调用类 | “部署到 sandbox” |
参考技能
请使用以下位置的核心 Apex 标准、测试模式和防护措施:
随附示例
- examples/Test_QuoteByProductCallable/ — 使用
WITH USER_MODE的只读查询示例 - examples/Test_VlocityOpenInterfaceConversion/ — 从旧版
VlocityOpenInterface迁移 - examples/Test_VlocityOpenInterface2Conversion/ — 从
VlocityOpenInterface2迁移
输出要求
此技能生成的交付物:
<ClassName>.cls— 实现System.Callable并使用switch on action进行分发的 Callable 类<ClassName>Test.cls— 包含正向、负向、契约和批量测试方法的测试类IndustriesCallableException.cls— 自定义异常类(如果项目中尚不存在)
说明
- 优先选择确定性强、能感知副作用的可调用操作
- 保持操作契约稳定;对于破坏性变更,应引入新操作
- 避免在同步可调用项中执行长时间运行的工作;需要时使用异步方式
参考文件索引
| 文件 | 阅读时机 |
|---|---|
assets/pattern_callable_vanilla.cls | 第 3 阶段——纯 System.Callable 框架(扁平参数,不耦合 Open Interface) |
assets/pattern_callable_openinterface.cls | 第 3 阶段——带有 inputMap/options 参数的 System.Callable 框架(兼容 Open Interface) |
assets/pattern_openinterface.cls | 第 3 阶段——使用 switch on 分派和 outputMap 契约的 VlocityOpenInterface2 框架 |
assets/pattern_test_class.cls | 第 4 阶段——测试类框架(涵盖正向、负向、契约、批量和空参数用例) |
assets/pattern_migration.cls | 迁移——带注释的迁移前后模式(VlocityOpenInterface2 → System.Callable) |
examples/Test_QuoteByProductCallable/Industries_QuoteByProductCallable.cls | 第 3 阶段——完整的可调用实现,包含 WITH USER_MODE SOQL 和错误封装 |
examples/Test_QuoteByProductCallable/Industries_QuoteByProductCallableTest.cls | 第 4 阶段——完整测试类,涵盖正向、契约和不支持的操作用例 |
examples/Test_QuoteByProductCallable/IndustriesCallableException.cls | 第 3 阶段——用于不支持操作的自定义异常模式 |
examples/Test_QuoteByProductCallable/TRANSCRIPT.md | 参考——按产品报价可调用示例的推理记录 |
examples/Test_VlocityOpenInterfaceConversion/MyCustomCallable.cls | 第 3 阶段——从旧版 VlocityOpenInterface 迁移的模式 |
examples/Test_VlocityOpenInterfaceConversion/MyCustomCallableTest.cls | 第 4 阶段——VlocityOpenInterface 迁移示例的测试类 |
examples/Test_VlocityOpenInterfaceConversion/IndustriesCallableException.cls | 第 3 阶段——随 VlocityOpenInterface 转换一同部署的自定义异常类 |
examples/Test_VlocityOpenInterfaceConversion/MyCustomVlocityOpenInterface2.cls | 第 3 阶段——迁移前的原始旧版 VlocityOpenInterface2 类 |
examples/Test_VlocityOpenInterfaceConversion/TRANSCRIPT.md | 参考——VlocityOpenInterface 转换的推理记录 |
examples/Test_VlocityOpenInterface2Conversion/MyCustomCallable.cls | 第 3 阶段——从 VlocityOpenInterface2 迁移的模式 |
examples/Test_VlocityOpenInterface2Conversion/MyCustomCallableTest.cls | 第 4 阶段——VlocityOpenInterface2 迁移示例的测试类 |
examples/Test_VlocityOpenInterface2Conversion/IndustriesCallableException.cls | 第 3 阶段——随 VlocityOpenInterface2 转换一同部署的自定义异常类 |
examples/Test_VlocityOpenInterface2Conversion/MyCustomRemoteClass.cls | 第 3 阶段——VlocityOpenInterface2 迁移示例所用的远程类 |
examples/Test_VlocityOpenInterface2Conversion/TRANSCRIPT.md | 参考——VlocityOpenInterface2 转换的推理记录 |