mcpField
G
generating-apex

generating-apex

github.com/forcedotcom开发1.1k4.2k 次安装1 天前更新
代码仓库

关于 generating-apex

用于生成和审查面向生产环境的 Salesforce Apex 的技能,涵盖类、触发器和异步任务等。它规定编写约定,并要求在汇报前执行代码分析和 Apex 测试。

主要功能

  1. 01Apex 类和触发器编写指南
  2. 02发现项目既有约定
  3. 03审阅模板和参考示例
  4. 04安全与治理限制规范
  5. 05代码分析验证
  6. 06执行 Apex 测试

适用场景

  1. 01创建 Apex 服务、选择器或异步任务
  2. 02审查现有 Apex 类或触发器
  3. 03为部署准备 Apex 变更

MCP 相关

该技能要求调用 `run_code_analyzer` MCP 工具分析生成或更新的 Apex 文件;也允许通过 MCP 执行 Apex 测试。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/forcedotcom/sf-skills --skill generating-apex

安装后在 Claude Code 中输入/generating-apex确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

CREDITS.md
assets/
abstract.cls
batch.cls
domain.cls
dto.cls
exception.cls
interface.cls
invocable.cls
queueable.cls
rest-resource.cls
schedulable.cls
selector.cls
service.cls
trigger.cls
utility.cls
references/
AccountDeduplicationBatch.cls
AccountSelector.cls
AccountService.cls

详细介绍

生成 Apex

将此技能用于生产级 Apex:新建类、选择器、服务、异步作业、可调用方法和触发器;以及基于证据审查现有 .cls 或 .trigger。

必需输入

编写前先收集或推断以下信息:

  • 类类型(服务、选择器、领域、批处理、队列、可调度、可调用、触发器、触发器操作、DTO、工具类、接口、抽象类、异常、REST 资源)
  • 目标对象和业务目标
  • 类名(使用下方的命名表推导)
  • 全新开发还是重构/修复;组织或 API 限制
  • 部署目标(默认使用 runSpecifiedTests,并在适用时使用生成的测试)

除非另有说明,默认值如下:

  • 共享模式:with sharing(参见下方各类型的共享规则)
  • 访问级别:public(仅在托管包或 @RestResource 要求时使用 global)
  • API 版本:66.0(最低版本)
  • ApexDoc 注释:是

如果用户提供了清晰、完整的请求,立即生成,无需不必要的来回沟通。


工作流

所有步骤必须依次执行。不得跳过、合并或调整顺序。如果遇到阻碍,请停止并询问缺失的上下文。如果不适用,请标记为 N/A,并在报告中用一句话说明原因。

阶段 1 — 编写
  1. 了解项目约定

    • 服务-选择器-领域分层、日志工具
    • 现有类/触发器以及当前的触发器框架或处理程序模式
    • 是否已在使用 Trigger Actions Framework(TAF)
  2. 选择最精简且正确的模式(参见下方的类型专属指南)

  3. 查看模板和资源

    • 编写前读取 assets/ 中匹配的模板(文件映射参见类型专属指南)
    • 如果该类型存在 references/ 示例,请将其作为具体的风格指南阅读
    • 对于任何测试类相关工作,始终读取并使用 generating-apex-test 技能
  4. 遵循防护规则编写 —— 应用下方规则部分中的每一项规则

    • 生成带有 ApexDoc 的 {ClassName}.cls
    • 生成 {ClassName}.cls-meta.xml
  5. 生成测试类 —— 加载 generating-apex-test 技能,以创建 {ClassName}Test.cls 和 {ClassName}Test.cls-meta.xml。部署时始终必须生成 Apex 测试。未加载 generating-apex-test 技能来生成测试之前,不得创建或编辑任何测试文件。

阶段 2 — 验证(报告前必须完成)

写入文件只是中途步骤,并非终点。步骤 6 和 7 都必须调用工具,并产生必须出现在步骤 8 报告中的输出。在这两个步骤运行并捕获输出之前,不得总结或提交报告。

  1. 运行代码分析器

    • 对所有生成或更新的 .cls 文件调用 MCP run_code_analyzer。
    • 修复所有 sev0、sev1 和 sev2 违规;反复运行,直到结果干净。
    • 为报告逐字捕获最终工具输出。
    • 备用方案:sf code-analyzer run --target <target>。如果两者均不可用,请在报告中记录 run_code_analyzer=unavailable: <error>。
  2. 执行 Apex 测试

    • 使用 sf apex run test 或 MCP 运行组织测试,其中包括 {ClassName}Test。
    • 将所有测试生成/修复/覆盖率工作委托给 generating-apex-test;反复执行,直到测试通过。
    • 为报告捕获通过/失败数量和覆盖率百分比。
    • 如果不可用,请在报告中记录 test_execution=unavailable: <error>。
阶段 3 — 报告
  1. 报告 —— 使用本文件底部的输出格式。
    • Analyzer 行必须包含步骤 6 的实际工具输出(或尝试调用后记录 run_code_analyzer=unavailable: <reason>)。
    • Testing 行必须包含步骤 7 的实际结果(或尝试调用后记录 test_execution=unavailable: <reason>)。
    • 缺少任一行的报告均不完整。记录不可用之前,务必先尝试调用工具。

规则

必须遵守的硬性约束

如果生成的代码会违反任何约束,请停止并在继续之前说明问题:

约束原因
将所有 SOQL 查询放在循环之外避免触发查询限制(100 次查询)
将所有 DML 操作放在循环之外避免触发 DML 限制(150 条语句)
每个类都要声明共享关键字防止意外使用 without sharing 默认设置并导致数据泄露
使用自定义元数据、标签或 describe 调用,而不是硬编码 ID确保可在不同组织间移植
始终处理异常(记录、重新抛出或恢复)防止静默失败
所有包含用户输入的动态 SOQL 都使用绑定变量防止 SOQL 注入
使用 Apex 原生集合(List、Map、Set),而不是 Java 类型防止编译错误
使用前先确认方法在 Apex 中存在防止依赖不存在的 API
避免在主代码路径中使用 System.debug()即使未启用日志记录,调试语句也会执行并消耗 CPU。若主代码路径需要日志记录,请使用日志框架
绝不使用 @future 方法使用 Queueable 和 System.Finalizer;@future 无法链式调用、无法从 Batch 调用,也无法接受非原始类型
批量化与限制
  • 所有公共 API 都接受并处理集合;单记录重载应委托给批量方法
  • 在批处理/批量流程中,优先使用部分成功 DML(Database.update(records, false)),并处理错误对应的 SaveResult
  • 使用 Map<Id, SObject> 构造函数,从查询结果中高效地按 ID 查找记录
  • 使用 Map<Id, List<SObject>> 按父记录对关联子记录分组;处理前在单个循环中构建映射
  • 使用 Set<Id> 进行去重和成员检查;优先使用 Set.contains(),而非 List.contains()
  • 需要同时获取父记录和子记录时,使用关系子查询,通过单条 SOQL 获取
  • 汇总计算使用带 GROUP BY 的 AggregateResult,而不是在 Apex 中查询并计数
  • 仅对实际发生变化的记录执行 DML——将记录添加到更新列表前,与 Trigger.oldMap 或之前的状态进行比较
  • 在复杂事务中使用 Limits.getQueries()、Limits.getDmlStatements()、Limits.getCpuTime() 监控资源消耗
SOQL 优化
  • 使用带有适当 WHERE 子句的选择性查询;筛选时尽可能使用索引字段(Id、Name、OwnerId、查找/主从关系字段、ExternalId 字段、自定义索引)
  • SOQL 不支持 SELECT *——始终明确指定所需字段
  • 使用 LIMIT 子句限制结果集;使用 ORDER BY 确保结果具有确定性
  • 查询自定义元数据类型(对象名称以 __mdt 结尾)时,不要使用 SOQL——请使用内置方法({CustomMdt__mdt}.getAll().values()、getInstance() 等)
缓存
  • 对频繁访问且很少变更的数据使用 Platform Cache(Cache.Org/Cache.Session);设置 TTL,并始终处理缓存未命中——缓存可能随时被逐出
  • 使用 private static Map 字段作为事务范围内的缓存,防止在同一执行上下文中重复查询;首次访问时再延迟初始化
安全
  • 默认使用 with sharing;对 without sharing 或 inherited sharing 的使用应记录说明
  • 在 SOQL 中使用 WITH USER_MODE,并在 Database DML 中使用 AccessLevel.USER_MODE,以强制执行 CRUD/FLS
  • 使用允许列表或 Schema.describe 验证动态字段名/运算符
  • 所有外部凭据/API 密钥均使用 Named Credentials
  • 使用 AuraHandledException 返回面向用户的 @AuraEnabled 错误(不包含内部细节)
  • 使用 without sharing 时必须检查自定义权限
  • 将 without sharing 逻辑隔离在专用辅助类中;从 with sharing 入口调用,以限制提升权限的作用范围
  • 使用 Platform Encryption 加密静态存储的 PII/敏感数据;绝不在调试语句、错误消息或 API 响应中暴露 PII
安全验证

完成前,请验证:已强制执行 CRUD/FLS(SOQL + DML)· 每个类都显式声明了共享关键字 · 未硬编码机密或记录 ID · 日志和错误消息中不包含 PII · 已清理面向最终用户的错误消息。

错误处理
  • 在通用 Exception 前捕获特定异常;在消息中包含上下文
  • 仅在可能引发异常的代码(DML、调用、JSON 解析、类型转换)周围使用 try/catch;避免对简单赋值、集合操作和算术运算进行防御性包装
  • 保留异常原因链:new CustomException('message', cause)(不要将堆栈跟踪替换为拼接后的消息)
  • 在有意义时,为每个服务领域提供一个自定义异常类
  • 在 @AuraEnabled 方法中,捕获异常并将其重新抛出为 AuraHandledException
  • 备用方案:如果没有有意义的领域异常,则捕获通用 Exception,并选择重新抛出该异常,或将其包装在能保留原始原因的最简自定义异常中。
空值安全
  • 在每个公共方法开头添加空值/空输入保护子句;根据上下文匹配风格:在私有方法/触发器处理程序中提前 return,在公共 API 中抛出异常,在验证服务中使用 record.addError()
  • 返回空集合,而不是 null
  • 对链式属性访问使用安全导航(?.)
  • 除非能确保存在,否则绝不要内联解引用 map.get(key);应先使用 containsKey、赋值后检查空值,或使用安全导航
  • 使用空值合并运算符(??)提供默认值
  • 优先使用 String.isBlank(value),而不是 value == null || value.trim().isEmpty() 之类的手动检查
常量与字面量
  • 尽可能使用枚举而非字符串常量;枚举值遵循 UPPER_SNAKE_CASE
  • 将重复出现的字符串/数字字面量提取为 private static final 常量或常量类
  • 用户可见字符串使用 Label. 自定义标签
  • 可配置值(阈值、映射、功能标志)使用 Custom Metadata
  • 代码中绝不要输出 HTML 转义实体(例如 &#39;);在 Apex 字符串字面量中使用单引号 '
命名约定
类型模式示例
服务{SObject}ServiceAccountService
选择器{SObject}SelectorAccountSelector
领域{SObject}DomainOpportunityDomain
批处理{Descriptive}BatchAccountDeduplicationBatch
队列任务{Descriptive}QueueableExternalSyncQueueable
可调度任务{Descriptive}SchedulableDailyCleanupSchedulable
DTO{Descriptive}DTOAccountMergeRequestDTO
包装器{Descriptive}WrapperOpportunityLineWrapper
工具类{Descriptive}UtilStringUtil
接口I{Descriptive}INotificationService
抽象类Abstract{Descriptive}AbstractIntegrationService
异常{Descriptive}ExceptionAccountServiceException
REST 资源{SObject}RestResourceAccountRestResource
触发器{SObject}TriggerAccountTrigger
触发器操作TA_{SObject}_{Action}TA_Account_SetDefaults

其他命名规则:

  • 类:PascalCase
  • 方法:camelCase,以动词开头(get、create、process、validate、is、has、can)
  • 变量:camelCase,使用描述性名词;列表使用复数名词(例如 accounts、relatedContacts);映射使用 {value}By{key}(例如 accountsById);集合使用 {noun}Ids
  • 常量:UPPER_SNAKE_CASE
  • 使用完整描述性名称,不要使用缩写(acc、tks、rec)
ApexDoc
  • 类头以及每个 public/global 方法都必须编写
  • 包含:简要描述、@param、@return、@throws,以及在有帮助时添加 @example

类级格式:

/**
 * Provides services for geolocation and address conversion.
 */
public with sharing class GeolocationService { }

方法级格式:

/**
 * @param paramName Description of the parameter
 * @return Description of the return value
 * @example
 * List<Account> results = AccountService.deduplicateAccounts(accountIds);
 */
代码结构与架构
  • 每个类只负责一项职责;最多 500 行——超过时应拆分
  • 提前返回:在方法开头验证前置条件,并立即返回或抛出异常
  • 方法超过约 40 行时,提取私有辅助方法
  • 使用依赖注入(构造函数/方法参数)提升可测试性
  • 优先采用组合和窄接口,而非深层继承;通过新增实现扩展,而不是修改现有内容
  • 跨层时,每个方法只采用单一层级的抽象:
层负责内容不得包含
触发器仅负责事件路由业务逻辑、编排
处理器/服务流程控制、协调内联 SOQL/DML/HTTP/解析
领域业务规则、验证查询、外部调用、持久化细节
数据/集成SOQL、DML、HTTP业务决策
  • 禁止:在同一方法中混合编排与内联 SOQL/DML/HTTP;将业务规则与解析实现细节混在一起;在一个方法中同时处理验证、持久化和跨系统连接逻辑

异步决策矩阵

场景默认选择主要特征
标准异步工作Queueable作业 ID、链式调用、非原始类型、可配置延迟(通过 AsyncOptions 最长 10 分钟)、去重签名
超大型数据集Batch Apex分块处理、最多 5 个并发作业;大范围数据集使用 QueryLocator
现代批处理替代方案CursorStep(Database.Cursor)每块处理 2000 条记录、吞吐量更高、不受 5 个作业的限制
定期计划Scheduled Flow(首选)或 SchedulableSchedulable 的作业数上限为 100;仅在需要链式调用 Batch 或复杂 Apex 逻辑时使用
作业完成后的清理Finalizer(System.Finalizer)无论 Queueable 成功还是失败都会运行
长时间运行的外部调用Continuation每个事务最多 3 个,并行执行 3 个
延迟超过 10 分钟System.scheduleBatch()在指定的未来时间安排 Batch 作业
旧式即发即弃模式@future不要在新代码中使用——参见硬性限制;改用 Queueable + Finalizer

按类型分类的指导

Service
  • 模板:assets/service.cls · 参考:references/AccountService.cls
  • 使用 with sharing;保持无状态——不使用 public 字段或可变实例状态;保持公共 API 专注,并在合理情况下使用 static
  • 将所有 SOQL 委托给 Selectors,将 SObject 行为委托给 Domains
  • 将业务错误包装在自定义异常中(例如 AccountServiceException)
Selector
  • 模板:assets/selector.cls · 参考:references/AccountSelector.cls
  • 使用 inherited sharing;每个 SObject 或查询领域对应一个 Selector
  • 返回 List<SObject> 或 Map<Id, SObject>;使用共享的基础字段列表常量(不要内联重复定义)
  • 接受筛选参数;始终包含 WITH USER_MODE
Domain
  • 模板:assets/domain.cls
  • 使用 with sharing;封装字段默认值、派生逻辑和验证
  • 仅操作内存中的列表;不执行 SOQL/DML(应由 Services/Selectors 负责)
Batch
  • 模板:assets/batch.cls · 参考:references/AccountDeduplicationBatch.cls
  • 使用 with sharing;实现 Database.Batchable<SObject>(跨块跟踪状态时添加 Database.Stateful)
  • start() = 查询定义;execute() = 业务逻辑;finish() = 日志记录/通知
  • 大型数据集使用 QueryLocator;通过 Database.SaveResult 处理部分失败
  • 通过构造函数接受筛选参数,以便重复使用
Queueable
  • 模板:assets/queueable.cls
  • 使用 with sharing;实现 Queueable;需要 HTTP 外部调用时,可选实现 Database.AllowsCallouts
  • 通过构造函数接收数据
  • 添加链深度保护,防止无限链式调用
  • 可选实现 Finalizer,用于恢复/清理
  • 使用 AsyncOptions 设置可配置延迟(最长 10 分钟)和去重签名
可调度
  • 模板:assets/schedulable.cls
  • 使用 with sharing;execute() 委托给 Queueable 或 Batch
  • 提供 CRON 常量和便捷的 scheduleDaily() 辅助方法
DTO / 包装类
  • 模板:assets/dto.cls
  • 无需共享关键字(纯数据容器)
  • 使用简单的 public 属性;提供无参和带参构造函数;需要排序时实现 Comparable
  • 对要序列化/反序列化的 private/protected 内部 DTO 使用 @JsonAccess
工具类
  • 模板:assets/utility.cls
  • 无需共享关键字;所有方法均为 public static;使用 private 构造函数
  • 保持纯函数、无副作用;不使用 SOQL/DML
接口
  • 模板:assets/interface.cls
  • 定义清晰的契约,并为每个方法签名编写 ApexDoc
抽象类
  • 模板:assets/abstract.cls
  • 使用 with sharing;通过 virtual 方法提供默认行为
  • 将扩展点标记为 protected virtual 或 protected abstract
  • 在 ApexDoc 中提供一个展示如何扩展该类的具体示例
自定义异常
  • 模板:assets/exception.cls
  • 无需共享关键字;使用描述性名称扩展 Exception
  • 支持的构造函数:(), ('msg'), (cause), ('msg', cause)
触发器
  • 模板:assets/trigger.cls
  • 每个对象使用一个触发器;将所有逻辑委托给处理器/TAF 操作类
  • 包含所有相关的 DML 上下文;如果使用 TAF:new MetadataTriggerHandler().run();
触发器操作(TAF)
  • 每个关注点、每个上下文使用一个类;实现 TriggerAction.{Context}
  • 通过 Trigger_Action__mdt 注册(未注册的操作不会激活)
  • 命名格式:TA_{SObject}_{ActionName};优先使用字段值比较,而非静态布尔值来防止递归
可调用方法(@InvocableMethod)
  • 模板:assets/invocable.cls
  • 使用 with sharing;内部定义包含 @InvocableVariable 的 Request/Response
  • 方法必须为 public static;非静态方法或单对象签名无法编译
  • 接受 List<Request>,返回 List<Response>;实现批量化(在循环外执行 SOQL/DML)
  • 装饰器参数:label(必填——Flow Builder 显示名称)、description、category(在 Builder 中对操作分组)、callout=true(方法执行 HTTP 调用时必填)
  • @InvocableVariable 参数:label(必填)、description、required=true/false
  • @InvocableVariable 支持:基本类型、Id、SObject、仅限 List<T>(不支持 Map/Set/Blob);Flow 集合输入/输出请使用 List<Id> 或 List<SObject> 字段
  • 始终在 Response 中包含 isSuccess、errorMessage 和 errorType(e.getTypeName())
  • 建议在 Response 中返回错误;抛出异常会触发 Flow Fault 路径,仅在无法恢复的故障时使用
REST 资源(@RestResource)
  • 模板:assets/rest-resource.cls
  • 使用 global with sharing;类和方法都必须为 global
  • 使用带版本号的 URL:@RestResource(urlMapping='/{resource}/v1/*')
  • 根据不同分支使用适当的 HTTP 状态码(200/201/400/404/422/500);不要将所有错误都默认为 500
  • 验证输入(Id 格式:Pattern.matches('[a-zA-Z0-9]{15,18}', value));在 SOQL 中绑定所有用户输入
  • 在查询中包含 LIMIT/ORDER BY;实现分页(pageSize/offset)
  • 使用标准化的 ApiResponse 包装类(success、message、data/records);定义内部请求/响应 DTO
  • 控制器保持精简:将业务逻辑委托给 Service 类
@AuraEnabled 控制器
  • 使用 with sharing;所有 SOQL 均使用 WITH USER_MODE
  • 仅对只读查询使用 @AuraEnabled(cacheable=true);执行 DML 操作时不设置 cacheable
  • 捕获异常,并使用面向用户的友好消息重新抛出为 AuraHandledException

输出要求

每个类的交付物:

  • {ClassName}.cls
  • {ClassName}.cls-meta.xml(除非另有说明,默认 API 版本为 66.0 或更高)
  • {ClassName}Test.cls(使用 generating-apex-test 技能生成)
  • {ClassName}Test.cls-meta.xml(使用 generating-apex-test 技能生成)

每个触发器的交付物:

  • {TriggerName}.trigger
  • {TriggerName}.trigger-meta.xml(除非另有说明,默认 API 版本为 66.0 或更高)

元数据 XML 模板:

<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>{API_VERSION}</apiVersion>
    <status>Active</status>
</ApexClass>

按以下顺序报告:

Apex work: <summary>
Files: <paths>
Design: <pattern / framework choices>
Workflow: all steps completed (1-8); any N/A justified
Risks: <security, bulkification, async, dependency notes>
Analyzer: <REQUIRED -- paste actual run_code_analyzer output or state "run_code_analyzer=unavailable: <reason>">
Testing: <REQUIRED -- paste actual test execution results (pass/fail, coverage) or state "test_execution=unavailable: <reason>">
Deploy: <dry-run or next step>

跨技能集成

需求委派给
Apex 测试/修复失败generating-apex-test 技能
描述对象/字段metadata 技能(如果可用)
部署到 orgdeploy 技能(如果可用)
Flow 调用 ApexFlow 技能(如果可用)
LWC 调用 ApexLWC 技能(如果可用)

故障排查范围

此技能仅处理生产环境中的 .cls/.trigger/.apex 问题:编译/解析失败、部署依赖错误、运行时 governor limit 错误。对于测试执行、断言、覆盖率或 sf apex run test 失败,请委派给 generating-apex-test。

评论

支持 Markdown