其他
在 NetSuite SDF 项目中生成/校验脚本对象…
当你在 NetSuite SuiteCloud Development Framework (SDF) 项目中创建或修改 SuiteScript、生成 Object XML、配置 manifest.xml/deploy.xml 或排查部署错误时,按此流程操作可避免绝大多数部署失败与治理超限问题。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 38/100 ×0.15
适用场景
在 NetSuite SDF 项目中生成特定
解决思路
- 识别脚本文件:在 /SuiteScripts/(含子文件夹)中定位 .js 文件,保留子文件夹结构用于 scriptfile 路径。
- 解析 JSDoc:读取文件顶部 JSDoc 块,提取 @NScriptType(决定 XML 结构)、@NApiVersion(应为 2.x)、@NModuleScope(可选)。
- 规范化文件名:全部转小写;将连字符 - 替换为下划线 _;总长度(含 .xml 扩展名)不超过 40 字符;移除非法字符;如发生重命名,提示用户 'Renaming file from [original] to [normalized]' 并自动重命名源 .js 文件。
- 生成脚本 ID:CustomTool 用 custtoolset_ 前缀(12 字符,文件名部分 ≤28 字符);其他脚本用 customscript_ 前缀(13 字符,文件名部分 ≤27 字符);脚本 ID 必须与规范化文件名完全一致,且不得包含脚本类型;超过 40 字符时截断文件名部分并警告用户。
- 生成部署 ID:使用 customdeploy_ 前缀(13 字符,文件名部分 ≤27 字符),同样受 40 字符限制;CustomTool 无部署,跳过此步。
- 生成人类可读名称:按下划线和驼峰拆分文件名,各词首字母大写,保留缩写,用空格连接(如 getcampaignpreflight → 'Get Campaign Preflight')。
- 收集必需信息:对 ClientScript、UserEventScript、MassUpdateScript、WorkflowActionScript 提示用户提供 recordtype(标准记录用小写名如 customer,自定义记录用 [customrecord_xxx] 方括号格式)。
- CustomTool 自动创建 schema:查找 /SuiteScripts/[subfolder]/[normalized_filename]_schema.json,若不存在则自动创建基础模板并通知用户。
- 按脚本类型生成对应 XML 对象文件到 /Objects/[script_id].xml:ClientScript 用 <clientscript>;UserEventScript 用 <usereventscript>;Suitelet 用 <suitelet>;Restlet 用 <restlet>;Portlet 用 <portlet>;MapReduceScript 用 <mapreducescript>;MassUpdateScript 用 <massupdatescript>;ScheduledScript 用 <scheduledscript>;BundleInstallationScript 用 <bundleinstallationscript>;SDFInstallationScript 用 <sdfinstallationscript>;WorkflowActionScript 用 <workflowactionscript>;CustomTool 用 <toolset>(2026.1+)或 <tool>(2025.2);SPA 用 <singlepageapp>;SuiteFlow 用 <workflow>。
- 更新 manifest.xml:对所有服务端脚本(BundleInstallationScript、MapReduceScript、MassUpdateScript、Portlet、Restlet、ScheduledScript、SDFInstallationScript、Suitelet、UserEventScript、WorkflowActionScript)在 <frameworkversion> 之后插入 <dependencies><features><feature required="true">SERVERSIDESCRIPTING</feature></features></dependencies>;CustomTool 不需要此特性。
- 声明 ERP 特性依赖:当项目含 custbody_* 交易主体字段时按适用交易类型声明 RECEIVABLES/PAYABLES/EXPREPORTS/ADVRECEIVING/OPPORTUNITIES/WEBSTORE/MULTILOCINVT;含 custitem_* 库存/装配项字段时声明 INVENTORY/ASSEMBLIES;含 customrecordtype 时必须声明 CUSTOMRECORDS;运行 suitecloud project:validate 列出所有缺失特性并逐一补齐。
- XML 字符编码:XML 标签必须使用字面 < 和 >,绝不能用 < 或 >;HTML 实体仅用于内容值中的特殊字符。
- scriptfile 与 rpcschema 路径必须用方括号包裹:<scriptfile>[/SuiteScripts/my_script.js]</scriptfile>、<rpcschema>[/SuiteScripts/my_script_schema.json]</rpcschema>;路径统一用正斜杠并以 /SuiteScripts/ 开头。
- 部署配置规则:<runasrole> 仅用于 UserEventScript、Suitelet(Portlet 故意省略以当前用户身份运行);<allroles> 仅用于 UserEventScript、Suitelet、Portlet;<status>NOTSCHEDULED</status> 用于 ScheduledScript 和 MapReduceScript,其余用 RELEASED;<title> 不适用于 ClientScript、MassUpdateScript、WorkflowActionScript、UserEventScript;<defaultfunction> 不适用于 MassUpdateScript、ScheduledScript、WorkflowActionScript。
- 安全配置:UserEventScript 和 Suitelet 使用最小权限自定义角色,绝不用 <runasrole>ADMINISTRATOR</runasrole>;Suitelet 必须显式设置 <isonline>F</isonline>(除非明确设计为匿名公开页面);<allroles>T</allroles> 仅选择内部角色,外部/门户角色需额外加 <audslctrole>。
- CustomTool 特殊处理:无 scriptdeployments;仅含 name、scriptfile、rpcschema、exposetoaiconnector(2026.1+)或 exposeto3rdpartyagents(2025.2)、permissions;不支持 description、isinactive、notifyadmins、notifyowner、defaultfunction、title、runasrole、allroles;schema 中 tools[0].name 必须与 JS 导出函数名大小写完全一致;复杂参数声明为 string 类型并在 JS 中 JSON.parse;入口函数声明为 async;绝不抛异常,始终返回 { success: false, requestId, error: { type, message } };不支持 N/http、N/https、N/sftp 模块;查询参数不得暴露原始 SuiteQL/表名/字段名,使用白名单 datasetId 与参数绑定。
- SPA 特殊处理:使用 custspa_ 前缀(8 字符,scriptid ≤28 字符);SpaServerScript 和 SpaClientScript 定义在同一个 <singlepageapp> 对象中;SpaServerScript 必须导出 initializeSpa,SpaClientScript 必须导出 run;url 字段必须全账户唯一;优先 <audienceallroles>F</audienceallroles> 加显式 <audienceroles>;先部署非 SPA 对象,成功后再添加 SPA。
- Saved Search 处理:SDF 无法从零创建 saved search(<savedsearch> 需要系统生成的二进制 blob);使用 Saved Search Creator Suitelet 模式(<isonline>F</isonline>,默认 Current Role 执行,受限受众)程序化创建,再用 suitecloud object:import --type savedsearch --scriptid xxx --destinationfolder /Objects 导入,然后停用或移除创建器 Suitelet;不要默认设为 public。
- SDFInstallationScript 处理:无 XML 入口点元素,入口为 JS 中的 run(context);检查 context.fromVersion(全新安装为 null)区分安装与升级;在 deploy.xml 中用 <script><path>~/Objects/customscript_xxx.xml</path><run>customdeploy_xxx</run></script> 引用,<path> 必须指向 Object XML 而非 JS 文件;位置决定执行时机(<objects> 之后为安装后)。
- application.xml 预卸载钩子:如需在卸载前清理 API token、webhook、外部数据,创建 src/application.xml 并在 <hooks><beforeundeploy> 中引用 SDFInstallationScript 的 Object XML 与部署。
- FileCabinet 权限:.ss 和 .ssp 文件必须在 <file> 定义中显式配置 <permissions>(FULL/VIEW/EDIT/CREATE/REMOVE),否则部署后权限回退到账户默认。
- Secret 处理:先在 NetSuite UI 创建 secret,再作为 /Objects/custsecret_xxx.xml 导入项目,XML 仅含 <secret scriptid="custsecret_xxx"/>;绝不在项目中存储 secret 值;脚本参数仅用于非机密配置。
- 防御性编码:覆盖字段前先检查当前值;用 runtime.executionContext 控制执行上下文;加载前用定向搜索或 try/catch 验证记录存在;创建前检查是否已存在(幂等);afterSubmit 中非关键操作包 try/catch;用标志字段协调脚本;处理前验证必填字段;用脚本参数启用/禁用功能。
- 治理意识:UserEvent/Suitelet/Client/Portlet/MassUpdate/WorkflowAction/CustomTool 限 1,000 单位,RESTlet 5,000,Scheduled/BundleInstallation/SDFInstallation 10,000,Map/Reduce 无总体限制;循环中昂贵操作前检查 getRemainingUsage();用 search.lookupFields()(1 单位)替代 record.load()(5-10 单位)读取单字段;用 anyof 过滤器批量操作避免 N+1 查询;重处理卸载到 Scheduled/Map-Reduce;Client Script 用 console.log() 而非 log.*(表单附加时被忽略);处理搜索结果上限(标准 1,000,保存搜索 4,000,SuiteQL 5,000);长任务用 task.create() 重新调度。
- 日志配置:开发用 <loglevel>DEBUG</loglevel>;生产用 AUDIT 或 ERROR;完全禁用则省略 <loglevel> 元素(连 log.emergency 也不记录);公司级限制 100,000 次日志调用/60 分钟;PERF_TIMING 等自检日志必须用脚本参数开关控制,默认关闭。
- 性能优化:N/cache 的 Scope.PRIVATE 按脚本隔离而非按用户,跨脚本共享用 Scope.PUBLIC;SuiteQL 用参数化查询(? 占位符 + params 数组 + customScriptId 版本标识)避免指纹碎片化;避免 JOIN SystemNote 等高容量表;Suitelet 列表视图不超过 100 行,大数据集用分页;用 url.resolveScript() 动态解析 Suitelet URL,不硬编码域名;iframe 中加载 Suitelet 时 URL 追加 &ifrmcntnr=T;服务端调用 Suitelet 用 https.requestSuitelet() 而非 https.get()。
- Client Script 注意事项:setValue() 是延迟 IO,不会立即写入服务端;每记录默认最多 10 个 Client Script 部署,超出静默忽略(可启用 'Remove Client Script Deployment Limit' 特性);sourced 字段用 postSourcing 而非 fieldChanged;访问自定义字段前用 getFields() 检查存在性;sublist 事件按 context.sublistId 过滤,避免多脚本冲突;开发时用 Ctrl+F5 或 Cmd+Shift+R 硬刷新绕过浏览器缓存。
- 日期与时区:NetSuite 内部以太平洋时间存储日期;用 N/format 的 parse/format 处理日期,绝不用 new Date('2024-01-15') 手动构造;用 runtime.getCurrentUser().getPreference({ name: 'TIMEZONE' }) 获取用户时区。
- 沙箱保护:用 runtime.accountId.includes('_SB') 检测沙箱,跳过外部 API 调用、邮件发送、集成触发等有真实副作用的操作。
- 测试与迁移:SuiteScript 2.1 运行在 GraalJS(ES2022),Jest 运行在 Node.js(V8),行为可能不同;避免 function* 生成器;用 SCUTF 处理 AMD 转译;SOAP 迁移到 REST 前完成 Phase 0 发现(记录所有集成、认证方式、记录类型、量级);并行运行 SOAP 与 REST 至少 1 个完整业务周期;保留 SOAP 凭证 30 天回滚窗口;新集成用 OAuth 2.0(RSA-PSS ≥2048 位或 EC 256/384/521 位),不用 NLAuth。
- deploy.xml 配置:Account Customization 项目必须含 <objects> 段(如 <objects><path>~/Objects/*</path></objects>);使用完整四段结构 <configuration>、<files>、<objects>、<translationimports>;避免宽泛通配符(如 ~/FileCabinet/SuiteScripts/*)以免包含 .DS_Store 等 OS 文件导致部署失败。
- 部署后输出 Change Report:在响应末尾显示汇总表(Created/Modified/Unchanged)及每个 Created/Modified 文件的 unified diff(diff 语法高亮),Created 文件显示完整内容,Modified 文件含 2-3 行上下文。
适用边界 · 注意事项
- 不适用于非 NetSuite 平台或非 SDF 项目(如直接使用 SuiteScript 1.0 或纯 UI 配置的开发)。
- 不适用于需要访问 NetSuite 账户实时验证记录类型、权限或部署状态的场景(本技能仅生成静态 XML 与配置)。
- 不适用于替代官方 NetSuite 文档或专家指导;它是实践性补充而非完整参考。