安全与风控
在 Salesforce Industries…
用于生成、审查或从 VlocityOpenInterface/VlocityOpenInterface2 迁移到 System.Callable 的 Industries/OmniStudio Apex 扩展类。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
在 Salesforce Industr特定
解决思路
- Phase 1 需求收集:向用户询问入口点(OmniScript、Integration Procedure、DataRaptor 或其他 Industries hook)、传入 call 的 action 名称字符串、输入/输出契约(必需键、类型、响应结构)、数据访问需求(对象/字段、CRUD/FLS 规则)以及副作用(DML、callout、异步需求)。
- Phase 1 扫描现有 callable 类:使用 Glob: **/*Callable*.cls,识别 Industries 扩展使用的共享工具类或基类,并创建任务列表。
- Phase 2 定义 callable 契约:明确 action 列表(显式、带版本的字符串)、输入 schema(必需键+类型)、输出 schema(一致的响应信封)。
- Phase 2 使用推荐响应信封:{ "success": true|false, "data": {...}, "errors": [ { "code": "...", "message": "..." } ] }。
- Phase 2 分发规则:使用 switch on action;default 分支抛出类型化异常;禁止动态方法调用或反射。
- Phase 2 映射 VlocityOpenInterface/VlocityOpenInterface2 契约:invokeMethod(String methodName, Map<String, Object> inputMap, Map<String, Object> outputMap, Map<String, Object> options);methodName 对应 call(action, args) 中的 action;inputMap 对应 args.get('inputMap');outputMap 为按引用写出的可变 map,Callable 改为返回信封;options 对应 args.get('options')。
- Phase 2 Open Interface 契约设计规则:将 inputMap 与 options 视为组合输入 schema;为每个 action 定义成功与错误情况下必须写入 outputMap 的键;保持 methodName 字符串与 Callable 的 action 字符串一致;记录每个 action 中 options 是必需、可选还是未使用。
- Phase 3 实现 vanilla System.Callable(扁平 args,无 Open Interface 耦合):public with sharing class 实现 System.Callable,call(String action, Map<String, Object> args) 内使用 switch on action,when 'createOrder' 返回 createOrder(args != null ? args : new Map<String, Object>()),when else 抛出 IndustriesCallableException('Unsupported action: ' + action)。
- Phase 3 实现 Callable 骨架(与 VlocityOpenInterface 相同输入):在 args 中读取 inputMap 与 options 键;若 args 含 'inputMap' 则取之,否则将 args 本身作为 inputMap;options 缺失时用空 map;对 inputMap/options 做 null 兜底;switch on action 分发到 createOrder(inputMap, options)。
- Phase 3 输入格式:调用方传 args 为 { 'inputMap' => Map<String, Object>, 'options' => Map<String, Object> };为兼容扁平调用方,若 args 缺少 'inputMap',则将 args 本身视为 inputMap,options 用空 map。
- Phase 3 实现规则:保持 call() 精简,委托给私有方法或服务类;尽早校验并强制转换输入类型(null 安全);强制 CRUD/FLS 与共享(with sharing、Security.stripInaccessible());当 args 含记录集合时进行批量处理;适当时对 SOQL 使用 WITH USER_MODE。
- Phase 3 实现 VlocityOpenInterface/VlocityOpenInterface2:使用签名 global Boolean invokeMethod(String methodName, Map<String, Object> inputMap, Map<String, Object> outputMap, Map<String, Object> options);类声明为 global with sharing class 实现 omnistudio.VlocityOpenInterface2。
- Phase 3 Open Interface 实现规则:通过 putAll() 或逐个 put() 将结果写入 outputMap,不要从 invokeMethod 返回信封;成功返回 true,不支持或失败返回 false;与 Callable 共用相同的私有方法(相同 inputMap 与 options 参数),仅入口不同;outputMap 填充相同的信封结构(success、data、errors)以保持一致。
- Phase 4 最少测试:正向(支持的 action 成功执行)、负向(不支持的 action 抛出预期异常)、契约(缺失/无效输入返回错误信封)、批量(处理列表输入且不触及限制)。
- Phase 4 测试类示例:@IsTest private class Industries_OrderCallableTest,testCreateOrder 中构造 args 含 inputMap 与 options,调用 svc.call('createOrder', args) 并 Assert.isTrue((Boolean) result.get('success'));testUnsupportedAction 中调用 svc.call('unknownAction', new Map<String, Object>()),Assert.fail('Expected IndustriesCallableException'),捕获 IndustriesCallableException 并断言消息包含 'Unsupported action'。
- 迁移 VlocityOpenInterface 到 System.Callable:保留 action 名称(methodName)作为 call() 中的 action 字符串;将 inputMap 与 options 作为 args 的键传入:{ 'inputMap' => inputMap, 'options' => options };返回一致的响应信封而非修改 outMap;保持 call() 精简,委托给相同签名的内部方法 (inputMap, options);为每个 action 及不支持 action 添加测试。
- 按 120 分评分标准执行最佳实践:Contract & Dispatch 20 分(显式 action 列表、switch on、带版本 action 字符串);Input Validation 20 分(校验必需键、安全类型转换、null 防护);Security 20 分(with sharing、CRUD/FLS 检查、Security.stripInaccessible());Error Handling 15 分(类型化异常、一致错误信封、无空 catch);Bulkification & Limits 20 分(循环中无 SOQL/DML、支持列表输入);Testing 15 分(正向/负向/契约/批量测试);Documentation 10 分(类与 action 方法的 ApexDoc)。
- 评分阈值:✅ 90+(Ready)| ⚠️ 70-89(Review)| ❌ <70(Block)。
- 强制护栏:若将引入基于用户输入的动态方法执行(反射)、循环内 SOQL/DML、callable 类上的 without sharing、静默失败(空 catch、吞异常)或跨 action 不一致的响应结构,必须停止并询问用户。
- 避免常见反模式:call() 内含业务逻辑而非委托;action 名称无版本或未文档化;未检查就假定输入 map 含键;响应类型混杂(有时 Map 有时 String);没有针对不支持 action 的测试。
- 跨技能集成:通用 Apex 工作用 sf-apex;编码前验证对象/字段可用性用 sf-metadata(如 Describe Product2);验证/部署 callable 类用 sf-deploy。
- 参考核心 Apex 标准、测试模式与护栏:skills/sf-apex/SKILL.md。
- 使用捆绑示例:examples/Test_QuoteByProductCallable/(使用 WITH USER_MODE 的只读查询示例)、examples/Test_VlocityOpenInterfaceConversion/(从旧 VlocityOpenInterface 迁移)、examples/Test_VlocityOpenInterface2Conversion/(从 VlocityOpenInterface2 迁移)。
- 注意事项:优先确定性、感知副作用的 callable action;保持 action 契约稳定,破坏性变更通过新增 action 实现;避免在同步 callable 中执行长时间任务,必要时使用异步。
适用边界 · 注意事项
- 通用 Apex 类或触发器(应使用 sf-apex)
- 构建 Integration Procedure(应使用 sf-industry-commoncore-integration-procedure)
- 编写 OmniScript(应使用 sf-industry-commoncore-omniscript)、配置 Data Mapper(应使用 sf-industry-commoncore-datamapper)或分析命名空间/依赖问题(应使用 sf-industry-commoncore-omnistudio-analyze)