关于 platform-custom-setting-generate
该 Salesforce 技能用于创建和校验层级或列表自定义设置,包括元数据 XML 与字段规则。它还区分设置定义和设置值,并指导处理部署限制及触发器绕过开关。
主要功能
- 01生成层级或列表自定义设置
- 02生成和校验元数据 XML
- 03添加或校验支持的字段
- 04说明设置值的部署限制
- 05排查自定义设置部署错误
适用场景
- 01为功能开关创建层级设置
- 02为命名查找表创建列表设置
- 03校验现有设置的字段定义
- 04排查设置部署失败原因
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill platform-custom-setting-generate安装后在 Claude Code 中输入/platform-custom-setting-generate确认已加载。
详细介绍
Salesforce 自定义设置生成器和验证器
何时使用此技能
在以下情况下使用此技能:
- 创建层次结构或列表自定义设置
- 生成自定义设置元数据 XML
- 为现有自定义设置添加或验证字段
- 填充自定义设置值(这些是数据,不是元数据——参见第 6 节)
- 排查自定义设置部署错误
触发器绕过开关 / 紧急关闭开关属于自定义设置——不是 Apex,也不是
__mdt。 当用户想要一个管理员可以切换、以启用或禁用行为的开关时——例如“在数据加载期间禁用我的 Account 触发器”、功能开关或维护模式标志——只生成带有Checkbox字段的层次结构自定义设置(例如Disable_Triggers__c/Bypass__c)。请勿编写读取该字段的 Apex 触发器、处理程序或测试,也不要将其建模为自定义元数据类型:绕过标志所需的按配置文件 / 用户覆盖功能,正是层次结构自定义设置具备而__mdt不具备的。检查该标志的 Apex 由开发人员编写——此技能只生成设置。
如需了解此技能之外权威的 Metadata API 类型、字段和枚举详情,请使用 platform-metadata-api-context-get 技能。
1. 概述和用途
本文档规定了生成自定义设置元数据时必须遵守的约束。自定义设置是设置了 <customSettingsType> 的 CustomObject——它不是独立的元数据类型。
文件扩展名: .object-meta.xml
文件路径: force-app/main/default/objects/<Name>__c/<Name>__c.object-meta.xml
API 名称后缀: __c(与常规自定义对象相同——该后缀无法区分它们)
值是数据,不是元数据。 你可以将设置的定义生成为 XML,但不能以这种方式部署其值。自定义设置没有与
customMetadata/等效的源格式。切勿生成声称包含设置值的文件——应改为参见第 6 节。
2. 语法要点(第 1 级)
<customSettingsType> 为必填项——关键
这是此技能中严重级别最高的一条规则。 省略 <customSettingsType> 不会产生“忘记填写 customSettingsType”错误。该组件会悄然不再是自定义设置,而是按普通自定义对象进行验证。
这种失败很危险,因为它会朝错误方向修复:Agent 省略该元素后,再照着平台报告的每个错误逐一修复,最终会得到部署成功,但组件类型完全错误的结果。
| 你看到的内容 | 含义 |
|---|---|
Must specify a non-empty plural label for the CustomObject | 你并未在构建自定义设置。缺少 customSettingsType。请添加它——不要添加 pluralLabel。 |
Cannot specify: nameField for CustomSettings | 你确实在构建自定义设置。请移除指定的元素。 |
这两条字符串代表互斥的情况。第一条的关键在于短语 for the CustomObject,以及其中完全没有提及自定义设置。
如果部署在用户要求创建自定义设置的内容上报告 Must specify a non-empty plural label for the CustomObject,切勿通过添加 <pluralLabel> 来解决该错误。 添加该元素(以及 nameField、deploymentStatus 和 sharingModel)会使部署成功,但创建的是用户并未要求的普通自定义对象。
必需和允许的元素
| 元素 | 要求 | 说明 |
|---|---|---|
<customSettingsType> | 必需 | Hierarchy 或 List — 参见第 3 节 |
<label> | 必需 | 单数形式的 UI 名称 |
<visibility> | 始终包含 | Public;仅在开发/沙盒/临时组织中可用 Protected(第 5 节) |
<description> | 始终包含 | 说明此设置控制什么,以及由谁编辑 |
<enableFeeds> | 可选 | 接受 |
<listViews> | 可选 | 接受 — 尽管禁止使用 recordTypes 和 compactLayouts,此元素仍允许使用 |
禁止的元素
以下每个元素都会导致 Cannot specify: <element> for CustomSettings:
| 禁止的元素 | 说明 |
|---|---|
<pluralLabel> | 常规自定义对象必需,此处禁止。 正好相反。 |
<nameField> | List 设置中隐式存在 Name 字段 |
<deploymentStatus> | |
<sharingModel> | 自定义设置不是共享记录 |
<enableActivities> <enableReports> <enableHistory> <enableSearch> | |
<validationRules> | 请改用 Apex 强制执行这些规则 |
<recordTypes> | |
<compactLayouts> |
错误 — 包含常规自定义对象必需的元素:
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<customSettingsType>Hierarchy</customSettingsType>
<label>Feature Flags</label>
<pluralLabel>Feature Flags</pluralLabel>
<sharingModel>ReadWrite</sharingModel>
<deploymentStatus>Deployed</deploymentStatus>
<nameField>
<label>Name</label>
<type>Text</type>
</nameField>
<visibility>Public</visibility>
</CustomObject>
错误信息: Cannot specify: pluralLabel for CustomSettings · Cannot specify: sharingModel for CustomSettings · Cannot specify: deploymentStatus for CustomSettings · Cannot specify: nameField for CustomSettings
正确 — 最简有效的自定义设置:
<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<customSettingsType>Hierarchy</customSettingsType>
<label>Feature Flags</label>
<description>Per-profile and per-user toggles for beta features in the ordering app. Edited by admins in Setup.</description>
<visibility>Public</visibility>
</CustomObject>
请注意,没有 <fullName>。根级别的 <fullName> 会被容忍并忽略,但应省略它 — API 名称取自目录和文件名。
部署 <customSettingsType>List</customSettingsType> 时,无需在 Setup 中启用“Manage List Custom Settings Type”开关。某些组织中存在此开关,因此,如果 List 设置在其他组织中因 customSettingsType 问题而被拒绝,请先检查 Schema Settings,再判断 XML 是否有误。
3. List 与 Hierarchy 的选择(第 2 层)
<customSettingsType> 只有两个可选值,而选择会改变行的寻址方式。
Hierarchy | List | |
|---|---|---|
| 适用场景 | 值可按配置文件或用户变化,并具有组织范围的回退值 | 设置是一个小型键值参考表,对所有人都相同 |
| 行键 | SetupOwnerId(Organization、Profile 或 User) | Name |
| 解析方式 | 用户值 → 配置文件值 → 组织默认值 | 按 Name 查找 |
| Apex 读取 | MySetting__c.getInstance() / getOrgDefaults() | MySetting__c.getValues('Key') / getAll() |
| 典型场景 | 功能开关、按配置文件设置的限制、调试开关 | 国家代码、按地区设置的税率、按键值索引的集成端点 |
当用户描述的是开关、限制或任何“可以覆盖”的值时,默认选择 Hierarchy。如果用户描述的是包含命名行的查找表,则选择 List。
如果用户需要的是一个可在组织之间部署的键值参考表,List 自定义设置通常不是正确选择 — 其中的行属于数据,不会随部署迁移。请改用 platform-custom-metadata-type-generate(第 7 节)。
4. 字段规则
自定义设置中的字段是普通的 CustomField 组件,路径为
objects/<Name>__c/fields/<Field>__c.field-meta.xml。
此技能仅负责自定义设置特有的差异 — 即下文所列的支持类型允许列表和 fieldManageability 禁止规则。对于所有通用规则(<fullName> 的推导、<label>、<description>、<inlineHelpText>、精度/小数位数、<length>、externalId),请遵循
platform-custom-field-generate。
支持的字段类型
Checkbox、Currency、Date、DateTime、Email、Number、Percent、Phone、Text、TextArea、Url
自定义设置字段接受 required、unique、externalId 和 defaultValue。这些属性是否适用于特定类型,属于通用字段规则 — 请参阅 platform-custom-field-generate。
不支持的字段类型
Picklist、MultiselectPicklist、LongTextArea、Html、Lookup、MasterDetail、AutoNumber、
Location、Time、EncryptedText,以及 Formula(任何类型上的 <formula> 元素)。
这些类型都会失败,并返回同一条简短且没有说明的字符串:
Invalid data type.
此错误既没有指出字段,也没有指出类型。 部署多个字段时,请读取 --json 输出中的 componentFailures[].fullName
以找出问题字段——不要猜。
汇总字段在结构上无法实现——Summary 需要一个主从关系子项,而自定义设置本身又不支持
MasterDetail。不要生成汇总字段;错误文本不固定,因此不要根据特定字符串进行匹配。
Picklist 是常见陷阱。 用户经常要求在自定义设置中使用 picklist,但这不受支持。请使用
Text,并在 Apex 中强制执行允许的值;或者将请求转交给
platform-custom-metadata-type-generate——CMDT 确实支持 Picklist。
与 CMDT 几乎相反:Currency 在这里可用,但 CMDT 不支持;Picklist/LongTextArea 在 CMDT 中可用,
但这里不支持。切勿将一种类型的字段假设套用到另一种类型上。
<fieldManageability> 禁止使用
该元素仅属于 CMDT 字段。在自定义设置字段上使用会失败,并返回:
Field manageability cannot be set on this entity.
5. 可见性、机密信息与禁止静默降级规则
Protected 取决于组织类型
<visibility>Protected</visibility> 仅可部署到开发组织、沙盒组织或临时组织。在其他任何组织中:
You can't set the visibility for a Custom Setting to Protected unless you are in a developer, sandbox, or scratch org.
禁止静默降级规则 — 至关重要
如果因组织类型而导致 Protected 失败,绝不要通过切换到 Public 来“修复”。 静默降级会将有意做出的保密选择变成任何人都可读取的组件,且不提供任何提示。请明确说明:该组织不允许使用 Protected,因此可选方案是使用开发组织、沙盒组织或临时组织,或者接受 Public——由用户决定。此规则适用于任何收窄可见性的情况,而不仅是此错误。
机密信息不应存放在自定义设置中
自定义设置不是机密信息存储区。任何能够查询该对象的人都能读取其中的值,而对于同一命名空间中的代码,Protected 也不会改变这一点。
当用户要求将 API 密钥、密码、令牌、客户端密钥或证书存储在自定义设置中时:
- 首先明确警告——在生成任何内容之前——自定义设置不适合存放凭据,而且其中的值可被读取。
- 推荐合适的组件——用于调用身份验证的 Named Credential 和 External Credential。请明确说明此技能不会生成这些组件。
- 如果用户仍然坚持,就照办——生成自定义设置,但在回复中保留警告。不要默默拒绝,也不要不作说明地照办。
绝不要将机密信息转为 Protected 可见性来折中:如果 Protected 不可用(见上文),则必须严格遵守禁止静默降级规则,因为降级会公开机密信息。
6. 设置值是数据,而非元数据
自定义设置的行无法以 XML 部署。 它们没有类似 customMetadata/ 的文件夹。
只有对象定义及其字段属于元数据。
当用户要求创建带值的设置时——例如“添加一个供管理员启用 Beta 的功能标记设置”——请完成以下所有事项:
- 像往常一样生成对象和字段 XML。
- 用一行说明限制: 设置值属于数据,而非元数据,因此不会随部署发布。
- 在聊天回复中直接提供可立即运行的命令,完整替换为实际的对象和字段 API 名称及实际值——不是占位符模板,也不要写入文件。
以下三种形式均已验证可正常运行。
层级 — 组织范围的默认行
省略 SetupOwnerId。 它默认采用 Organization Id,正好对应组织默认行。无需查询 Id。
sf data create record --sobject Feature_Flags__c \
--values "Enable_Beta__c=true Max_Retries__c=3" \
--target-org <alias>
层级 — 配置文件或用户覆盖
这一步需要 Id,因此分为两步。用户级覆盖使用相同的结构,但用 User Id
代替 Profile Id(根据配置文件的情况推断,尚未单独验证)。
sf data query --query "SELECT Id FROM Profile WHERE Name='System Administrator'" --target-org <alias>
sf data create record --sobject Feature_Flags__c \
--values "SetupOwnerId=00eXXXXXXXXXXXXXXX Enable_Beta__c=false" \
--target-org <alias>
覆盖行与组织默认行共存,不会取代它。
列表 — 每个键对应一行
Name 是行键,且为必填项。
sf data create record --sobject Country_Codes__c \
--values "Name='US' Iso_Code__c='USA' Dial_Prefix__c='+1'" \
--target-org <alias>
引号
将整个 --values 参数用双引号括起来;包含空格或 shell 特殊字符的值要用单引号括起来
(Environment_Label__c='org default')。普通数字、布尔值和 Id 无需加内层引号。
在 Apex 中使用设置
通过生成的类型化类读取值——值会被缓存,因此读取不消耗 SOQL,在循环和触发器中使用也安全。对于层级设置:
Feature_Flags__c cfg = Feature_Flags__c.getInstance(); // running user: user → profile → org default
Feature_Flags__c org = Feature_Flags__c.getOrgDefaults(); // org-default row only, no hierarchy
Feature_Flags__c forProfile = Feature_Flags__c.getInstance(profileId); // a User Id works too
Boolean beta = cfg.Enable_Beta__c;
getInstance() 和 getOrgDefaults() 永远不会返回 null(API ≥ 22)——记录缺失时会返回空行,因此如果需要确认记录是否实际存在,请检查 org.Id != null。对于列表设置,使用 getValues('Key') 获取一行,或使用 getAll() 获取包含所有行的 Map<String, Feature_Flags__c>。
事务内 DML 规则
填充或更改值属于运行时 DML(Apex insert/update/upsert,或上述 sf 命令)——没有元数据路径。这正是“值属于数据”在操作层面的体现,并有两个值得向用户说明的后果:
- 写入会计入 DML 管理限制;读取不会。 对包含 200 条记录的触发器,切勿每条记录都
upsert一次设置——将写入移出循环,只执行一次。 - 跨事务传播存在延迟。 写入后,在组织缓存传播期间,其他事务可能会短暂读取到旧值;在同一事务内,新值会立即可见。
7. 选择合适的组件
开始生成前,请确认自定义设置确实符合用户需求。如果不符合,请说明原因并指出合适的技能,而不是构建错误的组件。
| 如果配置是…… | 使用 | 原因 |
|---|---|---|
| 可由管理员按配置文件/用户编辑,或仅在一个组织中使用的小型键值表 | 自定义设置(此技能) | 值属于数据;部署时不会随之迁移 |
| 必须连同记录一起部署到不同组织的参考数据 | platform-custom-metadata-type-generate | CMDT 记录属于元数据,可以部署 |
| 用户在运行时创建和编辑的业务记录 | platform-custom-object-generate | 自定义设置用于配置,不是事务数据 |
| 可翻译的 UI 文本 | Custom Label — 此技能不会生成它 | 标签是受支持的翻译方式 |
| 凭据、API 密钥、令牌、带身份验证的端点 | Named Credential / External Credential — 此技能不会生成它们 | 参见第 5 节 |
| Apex 或流程中的权限检查 | Custom Permission — 此技能不会生成它 | 布尔访问检查应由权限模型处理 |
最重要的区别:CMDT 记录可以部署,自定义设置值不能。 如果用户说“而且它应该随这些值一起发布”或“在每个组织中都相同”,这就是 CMDT 需求。
8. 常见部署错误
| 错误消息 | 原因 | 修复方法 |
|---|---|---|
Must specify a non-empty plural label for the CustomObject | 缺少 <customSettingsType>,因此系统会将其作为普通自定义对象进行验证 | 添加 <customSettingsType>。不要添加 <pluralLabel>(第 2 节) |
Cannot specify: pluralLabel for CustomSettings | 存在 <pluralLabel> | 删除它——自定义对象必须包含此项,但此处禁止使用 |
Cannot specify: <element> for CustomSettings(<element> = nameField、sharingModel、deploymentStatus、validationRules、recordTypes 或 compactLayouts) | 存在仅适用于自定义对象的元素 | 删除它(改为在 Apex 中实施验证逻辑) |
Invalid data type. | 不支持此字段类型(第 4 节) | 查看 componentFailures[].fullName 找出字段;改用受支持的类型 |
Field manageability cannot be set on this entity. | 设置字段上存在 <fieldManageability> | 删除它——仅适用于 CMDT |
You can't set the visibility for a Custom Setting to Protected unless you are in a developer, sandbox, or scratch org. | 在类似生产环境的组织中使用了 Protected | 告知用户;绝不能静默改为 Public(第 5 节) |
Cannot specify: 后面的后缀是 for CustomSettings(一个单词),而 CMDT 的后缀是 for Custom Metadata Type;可见性错误使用的是 for a Custom Setting(带空格,单数)。匹配这些字符串时,不要假设它们共用同一个模板。
9. 验证清单
生成自定义设置 XML 之前,请验证以下各项:
组件身份检查(关键)
- 是否存在
<customSettingsType>,且其值为Hierarchy或List? - 是否根据请求说明了选择 Hierarchy 还是 List(可覆盖的设置还是键控表)?
- 如果部署报告了
Must specify a non-empty plural label for the CustomObject,是否通过添加<customSettingsType>修复,而非添加<pluralLabel>? - 成功部署新的设置后,是否确认它确实是自定义设置?检查 XML 无法发现此问题,因为错误组件仍会显示绿色部署成功。运行
sf sobject describe --sobject <Name>__c --target-org <alias>,并确认customSetting为true。
语法检查
- 是否存在
<label>和<visibility>? - 是否存在
<description>,并具体说明该设置控制什么? - 是否不存在
<pluralLabel>? - 是否不存在
<nameField>、<sharingModel>和<deploymentStatus>? - 是否不存在
<enableActivities>、<enableReports>、<enableHistory>和<enableSearch>? - 是否不存在
<validationRules>、<recordTypes>和<compactLayouts>? - 文件名是否与预期的 API 名称一致,并以
__c结尾?
字段检查
- 每个字段类型是否都在受支持的列表中(第 4 节)?
- 是否不存在
Picklist?(不受支持——请使用Text,或改用 CMDT) - 是否不存在任何
<formula>元素?(不支持公式) - 每个字段上是否都不存在
<fieldManageability>? - 通用字段属性是否遵循
platform-custom-field-generate?
值检查
- 如果用户要求提供值,是否明确说明了数据不属于元数据的限制?
- 是否在回复中直接给出
sf命令,并将其完整替换为真实 API 名称和真实值——而不是写入文件或保留为模板? - 对于 Hierarchy 组织默认值,是否省略
SetupOwnerId,而不是猜测其值? - 对于 List 行,是否提供了
Name?
使用检查
- 如果用户询问如何读取设置,是否提供了使用生成的访问器的 Apex 代码片段(Hierarchy 使用
getInstance/getOrgDefaults,List 使用getValues/getAll)? - 是否说明了事务内 DML 规则——写入属于会计入限制的 DML(应批量处理,并移出循环),读取不受限制,而跨事务的缓存传播会有延迟?
安全检查(关键)
- 如果请求涉及机密、密钥、令牌或密码,是否在生成之前发出了警告,并建议使用 Named Credential?
- 如果因组织类型错误,是否保留了
Protected,而不是悄悄降级为Public?
组件选择检查
- 如果这些值必须在组织之间部署,是否改为推荐
platform-custom-metadata-type-generate? - 如果请求实际涉及业务记录,是否改为推荐
platform-custom-object-generate?