platform-apex-generate

platform-apex-generate

热门

主要的 Apex 编写技能,用于类生成、重构和审查。当用户提到 Apex、.cls、触发器,或要求创建/重构类(服务、选择器、领域、批处理、可排队、可调度、可调用、DTO、工具类、接口、抽象类、异常、REST 资源)时,始终激活。此技能适用于涉及 SObject CRUD、集合映射、获取相关记录、计划作业、批处理作业、触发器设计、@AuraEnabled 控制器、@RestResource 端点、自定义 REST API 或现有 Apex 代码审查的请求。

769Star
281Fork
更新于 2026/7/24
SKILL.md
readonly只读
name
platform-apex-generate
description

主要的 Apex 编写技能,用于类生成、重构和审查。当用户提到 Apex、.cls、触发器,或要求创建/重构类(服务、选择器、领域、批处理、可排队、可调度、可调用、DTO、工具类、接口、抽象类、异常、REST 资源)时,始终激活。此技能适用于涉及 SObject CRUD、集合映射、获取相关记录、计划作业、批处理作业、触发器设计、@AuraEnabled 控制器、@RestResource 端点、自定义 REST API 或现有 Apex 代码审查的请求。

生成 Apex

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

必需输入

在编写之前收集或推断:

  • 类类型(服务、选择器、领域、批处理、可排队、可调度、可调用、触发器、触发器动作、DTO、工具类、接口、抽象类、异常、REST 资源)
  • 目标对象和业务目标
  • 类名(使用下面的命名表推导)
  • 全新创建 vs 重构/修复;任何组织/API 约束
  • 部署目标(默认为 runSpecifiedTests,并在适用时使用生成的测试)

除非指定,否则默认值:

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

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


工作流程

所有步骤按顺序执行。不要跳过、合并或重新排序。如果受阻,停止并询问缺失的上下文。如果不适用,在报告中标记 N/A 并附上一行理由。

阶段 1 — 编写

  1. 发现项目约定

    • 服务-选择器-领域分层、日志工具
    • 现有的类/触发器和当前的触发器框架或处理程序模式
    • 是否已使用触发器动作框架(TAF)
  2. 选择最小正确模式(参见下面的类型特定指南)

  3. 审查模板和资产

    • 在编写之前,从 assets/ 读取匹配的模板(参见类型特定指南中的文件映射)
    • 如果该类型存在 references/ 示例,将其作为具体风格指南阅读
    • 对于任何测试类工作,始终阅读并使用 platform-apex-test-generate 技能
  4. 在护栏下编写 — 应用下面规则部分中的每一条规则

    • 生成带有 ApexDoc 的 {ClassName}.cls
    • 生成 {ClassName}.cls-meta.xml
  5. 生成测试类 — 加载 platform-apex-test-generate 技能以创建 {ClassName}Test.cls{ClassName}Test.cls-meta.xml。始终需要生成 Apex 测试才能部署。不加载 platform-apex-test-generate 技能,不得创建或编辑测试文件。

阶段 2 — 验证(在报告前必需)

编写文件是中间点,而不是终点。步骤 6 和 7 各需要一次工具调用,并产生必须出现在步骤 8 报告中的输出。在运行两个步骤并捕获其输出之前,不要总结或呈现报告。

  1. 运行代码分析器

    • 在所有生成/更新的 .cls 文件上调用 MCP run_code_analyzer
    • 修复所有 sev0sev1sev2 违规;重新运行直到干净。
    • 将最终工具输出逐字捕获到报告中。
    • 备用方案:sf code-analyzer run --target <target>。如果两者都不可用,在报告中记录 run_code_analyzer=unavailable: <error>
  2. 执行 Apex 测试

    • 通过 sf apex run test 或 MCP 运行组织测试,包括 {ClassName}Test
    • 将所有测试生成/修复/覆盖工作委托给 platform-apex-test-generate;迭代直到测试通过。
    • 捕获通过/失败计数和覆盖率百分比到报告中。
    • 如果不可用,在报告中记录 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 原生集合(ListMapSet)而不是 Java 类型 防止编译错误
在使用前验证方法在 Apex 中存在 防止依赖不存在的 API
避免在主代码路径中使用 System.debug() 调试语句即使日志未激活也会求值并消耗 CPU。如果主代码路径需要,使用日志框架
绝不使用 @future 方法 使用带有 System.Finalizer 的 Queueable;@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 BYAggregateResult 进行汇总计算,而不是查询并在 Apex 中计数
  • 仅对实际更改的记录执行 DML — 在添加到更新列表之前,与 Trigger.oldMap 或先前状态进行比较
  • 使用 Limits.getQueries()Limits.getDmlStatements()Limits.getCpuTime() 监控复杂事务中的消耗

SOQL 优化

  • 使用带有适当 WHERE 子句的选择性查询;尽可能在过滤器中使用索引字段(IdNameOwnerId、查找/主从字段、ExternalId 字段、自定义索引)
  • SOQL 中不存在 SELECT * — 始终指定所需的确切字段
  • 应用 LIMIT 子句限制结果集;使用 ORDER BY 确保确定性结果
  • 查询自定义元数据类型(以 __mdt 结尾的对象)时,不要使用 SOQL — 使用内置方法({CustomMdt__mdt}.getAll().values()getInstance() 等)

缓存

  • 对频繁访问但很少更改的数据使用平台缓存(Cache.Org / Cache.Session);设置 TTL 并始终处理缓存未命中 — 缓存随时可能被逐出
  • 使用 private static Map 字段作为事务作用域缓存,防止在同一执行上下文中重复查询;在首次访问时惰性初始化

安全性

  • 默认使用 with sharing;记录使用 without sharinginherited sharing 的理由
  • 在 SOQL 中使用 WITH USER_MODE,在 Database DML 中使用 AccessLevel.USER_MODE 以强制执行 CRUD/FLS
  • 通过允许列表或 Schema.describe 验证动态字段/操作符名称
  • 所有外部凭据/API 密钥使用命名凭据
  • 对于 @AuraEnabled 面向用户的错误,使用 AuraHandledException(不包含内部细节)
  • without sharing 需要自定义权限检查
  • without sharing 逻辑隔离在专用的辅助类中;从 with sharing 入口点调用以限制提升访问的范围
  • 通过平台加密静态加密 PII/敏感数据;绝不在调试语句、错误消息或 API 响应中暴露 PII

安全性验证

在最终确定之前,验证:CRUD/FLS 已强制执行(SOQL + DML)· 每个类上都有显式的共享关键字 · 没有硬编码的秘密或记录 ID · PII 已从日志和错误消息中排除 · 错误消息已为最终用户清理。

错误处理

  • 在捕获通用 Exception 之前先捕获特定异常;在消息中包含上下文
  • 仅在可能抛出异常的代码周围使用 try/catch(DML、调用、JSON 解析、类型转换);避免防御性地包装简单赋值/集合操作/算术运算
  • 保留异常原因链:new CustomException('message', cause)(不要用拼接的消息替换堆栈跟踪)
  • 在有意义的情况下,为每个服务域提供自定义异常类
  • @AuraEnabled 方法中,捕获异常并重新抛出为 AuraHandledException
  • 备用方案:当没有有意义的域异常时,捕获通用 Exception 并重新抛出或将其包装在保留原始原因的最小自定义异常中

空安全

  • 在每个公共方法的顶部添加对空/输入的保护子句;匹配上下文风格:在私有/触发器处理程序方法中提前 return,在公共 API 中 throw 异常,在验证服务中使用 record.addError()
  • 返回空集合而不是 null
  • 对链式属性访问使用安全导航(?.
  • 除非保证存在,否则不要内联解引用 map.get(key);先使用 containsKey、赋值+空检查或安全导航
  • 对默认值使用空合并(??
  • 优先使用 String.isBlank(value) 而不是手动检查如 value == null || value.trim().isEmpty()

常量与字面量

  • 尽可能使用枚举而不是字符串常量;枚举值遵循 UPPER_SNAKE_CASE
  • 将重复的字面字符串/数字提取为 private static final 常量或常量类
  • 对面向用户的字符串使用 Label. 自定义标签
  • 对可配置值(阈值、映射、功能标志)使用自定义元数据
  • 绝不在代码中输出 HTML 转义实体(例如 &#39;);在 Apex 字符串字面量中使用单引号 '

命名约定

类型 模式 示例
服务 {SObject}Service AccountService
选择器 {SObject}Selector AccountSelector
领域 {SObject}Domain OpportunityDomain
批处理 {Descriptive}Batch AccountDeduplicationBatch
可排队 {Descriptive}Queueable ExternalSyncQueueable
可调度 {Descriptive}Schedulable DailyCleanupSchedulable
DTO {Descriptive}DTO AccountMergeRequestDTO
包装器 {Descriptive}Wrapper OpportunityLineWrapper
工具类 {Descriptive}Util StringUtil
接口 I{Descriptive} INotificationService
抽象类 Abstract{Descriptive} AbstractIntegrationService
异常 {Descriptive}Exception AccountServiceException
REST 资源 {SObject}RestResource AccountRestResource
触发器 {SObject}Trigger AccountTrigger
触发器动作 TA_{SObject}_{Action} TA_Account_SetDefaults

其他命名规则:

  • 类:PascalCase
  • 方法:camelCase,以动词开头(getcreateprocessvalidateishascan
  • 变量:camelCase,描述性名词;列表使用复数名词(例如 accountsrelatedContacts);映射使用 {value}By{key}(例如 accountsById);集合使用 {noun}Ids
  • 常量:UPPER_SNAKE_CASE
  • 使用完整的描述性名称而不是缩写(acctksrec

ApexDoc

  • 在类头部和每个 public/global 方法上必需
  • 包括:简要描述、@param@return@throws@example(如有帮助)

类级别格式:

/**
 * 提供地理定位和地址转换的服务。
 */
public with sharing class GeolocationService { }

方法级别格式:

/**
 * @param paramName 参数描述
 * @return 返回值描述
 * @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
现代批处理替代方案 CursorStepDatabase.Cursor 2000 条记录块,更高吞吐量,无 5 作业限制
定期计划 计划流(首选)或 Schedulable Schedulable 有 100 作业限制;仅在需要链式调用 Batch 或复杂 Apex 逻辑时使用
作业后清理 FinalizerSystem.Finalizer 无论 Queueable 成功/失败都运行
长时间运行的调用 Continuation 每个事务最多 3 个,3 个并行
延迟超过 10 分钟 System.scheduleBatch() 在特定未来时间计划 Batch 作业
遗留的即发即弃 @future 不要在新代码中使用 — 参见硬性约束;替换为 Queueable + Finalizer

类型特定指南

服务

  • 模板:assets/service.cls · 参考:references/AccountService.cls
  • with sharing;无状态 — 没有 public 字段或可变实例状态;保持公共 API 集中,并在合理时使用 static
  • 将所有 SOQL 委托给选择器,将 SObject 行为委托给领域
  • 将业务错误包装在自定义异常中(例如 AccountServiceException

选择器

  • 模板:assets/selector.cls · 参考:references/AccountSelector.cls
  • inherited sharing;每个 SObject 或查询域一个
  • 返回 List<SObject>Map<Id, SObject>;使用共享的基础字段列表常量(无内联重复)
  • 接受过滤器参数;始终包含 WITH USER_MODE

领域

  • 模板:assets/domain.cls
  • with sharing;封装字段默认值、派生和验证
  • 仅操作内存中的列表;无 SOQL/DML(属于服务/选择器)

批处理

  • 模板:assets/batch.cls · 参考:references/AccountDeduplicationBatch.cls
  • with sharing;实现 Database.Batchable<SObject>(在跨块跟踪时添加 Database.Stateful
  • start() = 查询定义;execute() = 业务逻辑;finish() = 日志/通知
  • 对大型数据集使用 QueryLocator;通过 Database.SaveResult 处理部分失败
  • 通过构造函数接受过滤器参数以实现可重用性

可排队

  • 模板:assets/queueable.cls
  • with sharing;实现 Queueable,并在需要 HTTP 调用时可选实现 Database.AllowsCallouts
  • 通过构造函数接受数据
  • 添加链深度保护以防止无限链
  • 可选实现 Finalizer 用于恢复/清理
  • 使用 AsyncOptions 实现可配置延迟(最多 10 分钟)和去重签名

可调度

  • 模板:assets/schedulable.cls
  • with sharingexecute() 委托给 Queueable 或 Batch
  • 提供 CRON 常量和便捷的 scheduleDaily() 辅助方法

DTO / 包装器

  • 模板:assets/dto.cls
  • 不需要共享关键字(纯数据容器)
  • 简单的公共属性;无参 + 参数化构造函数;当排序重要时实现 Comparable
  • 对序列化/反序列化的私有/受保护内部 DTO 使用 @JsonAccess

工具类

  • 模板:assets/utility.cls
  • 不需要共享关键字;所有方法 public staticprivate 构造函数
  • 纯函数,无副作用;无 SOQL/DML

接口

  • 模板:assets/interface.cls
  • 在每个方法签名上使用 ApexDoc 定义清晰的契约

抽象类

  • 模板:assets/abstract.cls
  • with sharing;通过 virtual 方法提供默认行为
  • 将扩展点标记为 protected virtualprotected 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;内部 Request/Response 带有 @InvocableVariable
  • 方法必须是 public static;非静态或单对象签名将无法编译
  • 接受 List<Request>,返回 List<Response>;批量处理(SOQL/DML 放在循环外)
  • 装饰器参数:label(必需 — Flow Builder 显示名称)、descriptioncategory(在 Builder 中对动作分组)、callout=true(当方法进行 HTTP 调用时必需)
  • @InvocableVariable 参数:label(必需)、descriptionrequired=true/false
  • @InvocableVariable 支持:原始类型、IdSObject、仅 List<T>(不支持 Map/Set/Blob);对 Flow 集合 I/O 使用 List<Id>List<SObject> 字段
  • 始终在 Response 中包含 isSuccesserrorMessageerrorTypee.getTypeName()
  • 建议在 Response 中返回错误;抛出异常会触发 Flow 故障路径 — 仅保留给不可恢复的失败

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 包装器(successmessagedata/records);内部请求/响应 DTO
  • 薄控制器:将业务逻辑委托给服务类

@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(通过 platform-apex-test-generate 技能生成)
  • {ClassName}Test.cls-meta.xml(通过 platform-apex-test-generate 技能生成)

每个触发器的交付物:

  • {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: <摘要>
Files: <路径>
Design: <模式/框架选择>
Workflow: 所有步骤已完成(1-8);任何 N/A 已说明理由
Risks: <安全性、批量处理、异步、依赖说明>
Analyzer: <必需 — 粘贴实际的 run_code_analyzer 输出或说明 "run_code_analyzer=unavailable: <reason>">
Testing: <必需 — 粘贴实际的测试执行结果(通过/失败、覆盖率)或说明 "test_execution=unavailable: <reason>">
Deploy: <试运行或下一步>

跨技能集成

需求 委托给
Apex 测试 / 修复失败 platform-apex-test-generate 技能
描述对象/字段 元数据技能(如果可用)
部署到组织 部署技能(如果可用)
Flow 调用 Apex Flow 技能(如果可用)
LWC 调用 Apex LWC 技能(如果可用)

故障排除边界

此技能仅处理生产级 .cls/.trigger/.apex 问题:编译/解析失败、部署依赖错误、运行时调控器限制失败。对于测试执行、断言、覆盖率或 sf apex run test 失败,委托给 platform-apex-test-generate