关于 omnistudio-callable-apex-generate
用于创建和审查 Salesforce Industries Common Core callable Apex 实现的技能,面向开发者,支持 System.Callable 动作分派、安全输入输出契约,以及从旧版 Vlocity Open Interface 扩展迁移。
主要功能
- 01生成 System.Callable 类
- 02定义并验证动作契约
- 03执行共享与 CRUD/FLS 检查
- 04迁移旧版 Open Interface 实现
- 05评分并测试 callable 代码
适用场景
- 01添加可由 OmniStudio 调用的动作
- 02审查 callable 的安全性与批量处理
- 03将 VlocityOpenInterface2 迁移至 System.Callable
- 04测试支持和不支持的动作
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill omnistudio-callable-apex-generate安装后在 Claude Code 中输入/omnistudio-callable-apex-generate确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.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.clsMyCustomClass.clsTRANSCRIPT.md详细介绍
omnistudio-callable-apex-generate: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 类(使用
platform-apex-generate);构建 Integration Procedure(使用omnistudio-integration-procedure-generate);编写 OmniScript(使用omnistudio-omniscript-generate);部署 Apex 类(使用platform-metadata-deploy)
核心职责
- 生成 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 文档标准) |
门槛:[PASS] 90+(就绪)| [REVIEW] 70-89(审核)| [BLOCK] <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)
- 未针对不支持的操作编写测试
跨技能集成
| 技能 | 使用时机 | 示例 |
|---|---|---|
| platform-apex-generate | 可调用实现之外的一般 Apex 工作 | "Create trigger for Account" |
| platform-custom-object-generate / platform-custom-field-generate | 编写代码前确认对象/字段是否可用 | "Describe Product2 fields" |
| platform-metadata-deploy | 验证/部署可调用类 | "Deploy to 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 | 参考 — Quote-by-Product 可调用示例的推理记录 |
examples/Test_VlocityOpenInterfaceConversion/MyCustomCallable.cls | 阶段 3 — 从旧版 VlocityOpenInterface 迁移的模式 |
examples/Test_VlocityOpenInterfaceConversion/MyCustomCallableTest.cls | 阶段 4 — VlocityOpenInterface 迁移示例的测试类 |
examples/Test_VlocityOpenInterfaceConversion/IndustriesCallableException.cls | 阶段 3 — 与 VlocityOpenInterface 转换一同部署的自定义异常类 |
examples/Test_VlocityOpenInterfaceConversion/MyCustomClass.cls | 阶段 3 — 迁移前原始的旧版 VlocityOpenInterface 类 |
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 转换的推理记录 |
交付前检查清单
-
call()在访问键之前会对args进行空值检查 - 部署包中包含
IndustriesCallableException类 - 为
omnistudio.VlocityOpenInterface2添加命名空间限定(如适用) - 按操作记录
outputMap的键(成功和错误场景) - 迁移后的调用方读取返回值,而非通过引用读取
outputMap