关于 business-rules
面向开发 ServiceNow 服务端自动化的开发者,介绍如何验证数据、更新记录并触发工作流。内容涵盖业务规则时机、Glide API、性能、测试,以及推送前检查远程版本的本地编辑流程。
主要功能
- 01业务规则时机与触发选项
- 02current 和 previous 记录处理
- 03条件字段与脚本配置
- 04Glide API 与性能实践
- 05测试、日志和安全脚本更新
适用场景
- 01保存前验证记录
- 02变更后创建关联记录
- 03异步执行耗时任务
- 04安全测试和更新业务规则
MCP 相关
介绍如何通过 ServiceNow MCP 工具创建、查询、测试和更新业务规则,包括 SN-Create-Record、SN-Query-Table 和 SN-Update-Record。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ happy-technologies-llc/ happy-platform-skills --skill business-rules安装后在 Claude Code 中输入/business-rules确认已加载。
详细介绍
业务规则开发
概述
业务规则是在显示、插入、更新、删除或查询记录时执行的服务器端脚本。它们是 ServiceNow 自动化的支柱。
- 解决什么问题? 自动化记录级逻辑、确保数据完整性,并根据记录变更触发工作流
- 谁应该使用此技能? 构建自定义自动化逻辑的 ServiceNow 开发者
- 预期成果: 结构良好、性能出色且遵循 ServiceNow 最佳实践的业务规则
前置条件
- 角色:
admin或作用域应用开发者角色 - 知识: JavaScript 基础、GlideRecord API 基础
- 访问权限: sys_script 表、业务规则的目标表
- 相关技能:
admin/script-execution、admin/update-set-management
何时使用业务规则
时机矩阵
| 时机 | 触发条件 | 用例 | current/previous |
|---|---|---|---|
| before | 插入/更新/删除 | 验证数据、设置字段值、中止操作 | 两者均可用 |
| after | 插入/更新/删除 | 创建相关记录、发送通知、外部集成 | 两者均可用 |
| async | 插入/更新/删除 | 长时间运行的操作、外部 API 调用 | 仅 current |
| display | 查询/显示 | 计算运行时值、填充 scratchpad | 仅 current |
决策指南
在以下情况使用 BEFORE:
- 根据其他字段设置默认值
- 保存前验证数据
- 提交前修改字段值
- 使用
current.setAbortAction(true)中止无效操作
在以下情况使用 AFTER:
- 创建子记录/相关记录
- 发送通知(记录提交后)
- 更新其他表
- 触发工作流
在以下情况使用 ASYNC:
- 发起外部 REST/SOAP 调用
- 处理大型数据集
- 失败时无需阻塞用户的操作
- 长时间运行的计算
在以下情况使用 DISPLAY:
- 仅为表单显示计算值
- 为客户端脚本填充 g_scratchpad
- 仅在运行时使用的字段值(不存储)
操作步骤
阶段 1:创建业务规则
步骤 1.1:查询表结构
首先,了解目标表的结构。
使用 MCP:
Tool: SN-Get-Table-Schema
Parameters:
table_name: incident
步骤 1.2:创建基本业务规则
使用 MCP:
Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: Set Priority Based on Impact and Urgency
collection: incident
active: true
when: before
order: 100
filter_condition: impactCHANGES^ORurgencyCHANGES
script: |
(function executeRule(current, previous /*null when async*/) {
// Calculate priority from impact and urgency matrix
var impact = parseInt(current.impact);
var urgency = parseInt(current.urgency);
// Priority matrix: 1=Critical, 2=High, 3=Moderate, 4=Low, 5=Planning
var matrix = {
'1-1': 1, '1-2': 2, '1-3': 3,
'2-1': 2, '2-2': 3, '2-3': 4,
'3-1': 3, '3-2': 4, '3-3': 5
};
var key = impact + '-' + urgency;
var newPriority = matrix[key] || 4;
if (current.priority != newPriority) {
current.priority = newPriority;
gs.info('Priority calculated: ' + newPriority + ' for ' + current.number);
}
})(current, previous);
步骤 1.3:创建 After 业务规则
使用 MCP:
Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: Create Related Task on P1 Incident
collection: incident
active: true
when: after
order: 200
filter_condition: priority=1^stateVALCHANGES1
script: |
(function executeRule(current, previous /*null when async*/) {
// Only on insert or when becoming P1
if (current.operation() == 'insert' ||
(previous && previous.priority != 1)) {
var task = new GlideRecord('sc_task');
task.initialize();
task.short_description = 'P1 Response: ' + current.short_description;
task.description = 'Critical incident requires immediate response.\n\nIncident: ' + current.number;
task.assignment_group = current.assignment_group;
task.assigned_to = current.assigned_to;
task.priority = 1;
task.parent = current.sys_id;
var taskId = task.insert();
gs.info('Created P1 response task: ' + taskId + ' for ' + current.number);
}
})(current, previous);
步骤 1.4:创建 Async 业务规则
使用 MCP:
Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: Notify External System on Incident Create
collection: incident
active: true
when: async
order: 500
action_insert: true
script: |
(function executeRule(current, previous /*null when async*/) {
try {
var request = new sn_ws.RESTMessageV2('External Notification', 'POST');
request.setStringParameterNoEscape('incident_number', current.number.toString());
request.setStringParameterNoEscape('short_description', current.short_description.toString());
request.setStringParameterNoEscape('priority', current.priority.toString());
var response = request.execute();
var httpStatus = response.getStatusCode();
if (httpStatus == 200 || httpStatus == 201) {
gs.info('External notification sent for: ' + current.number);
} else {
gs.error('External notification failed: ' + httpStatus + ' - ' + response.getBody());
}
} catch (e) {
gs.error('External notification error: ' + e.message);
}
})(current, previous);
阶段 2:使用 current 和 previous 对象
步骤 2.1:了解 current 与 previous 的区别
current 对象表示正在处理的记录。previous 对象包含当前事务发生前的字段值。
主要区别:
| 方面 | current | previous |
|---|---|---|
| 可用性 | 所有业务规则 | 仅 before/after(async 中为 null) |
| 可修改 | 是(before 规则) | 否(只读) |
| 插入操作 | 有值 | null |
| 值 | 新值/已修改的值 | 变更前的原始值 |
步骤 2.2:检测字段变更
使用 .changes() 方法(推荐):
Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: Log State Changes
collection: incident
active: true
when: after
order: 100
script: |
(function executeRule(current, previous /*null when async*/) {
// EFFICIENT: Exit early if field hasn't changed
if (!current.state.changes()) {
return; // No work to do
}
// Get old and new values
var oldState = previous ? previous.state.getDisplayValue() : '(new)';
var newState = current.state.getDisplayValue();
gs.info('Incident ' + current.number + ' state changed: ' + oldState + ' -> ' + newState);
// Add work note
current.work_notes = 'State changed from ' + oldState + ' to ' + newState;
})(current, previous);
手动检测变更(不适合使用 .changes() 时):
// For complex comparisons or calculated changes
if (previous && current.priority != previous.priority) {
// Priority changed
}
// For reference fields, compare sys_id
if (previous && current.assigned_to.toString() != previous.assigned_to.toString()) {
// Assignment changed
}
// For multiple fields
var fieldsToCheck = ['state', 'priority', 'assigned_to'];
var changedFields = [];
for (var i = 0; i < fieldsToCheck.length; i++) {
var field = fieldsToCheck[i];
if (current[field].changes()) {
changedFields.push(field);
}
}
if (changedFields.length > 0) {
gs.info('Changed fields: ' + changedFields.join(', '));
}
步骤 2.3:检查操作类型
// Determine what triggered the business rule
var operation = current.operation();
switch (operation) {
case 'insert':
// Record is being created
gs.info('New record: ' + current.getTableName());
break;
case 'update':
// Record is being updated
gs.info('Update to: ' + current.getUniqueValue());
break;
case 'delete':
// Record is being deleted
gs.info('Deleting: ' + current.getUniqueValue());
break;
}
阶段 3:条件字段与脚本条件
步骤 3.1:条件字段(简单条件的首选方式)
条件字段使用编码查询,并在脚本运行前进行评估。这样效率更高,因为:
- 在数据库层进行评估
- 条件不满足时,脚本不会执行
- 不产生 JavaScript 开销
条件字段的最佳实践:
# Only run on active P1 incidents
active=true^priority=1
# Only when state changes to Resolved
stateVALCHANGES6
# Only when assigned_to changes
assigned_toCHANGES
# Multiple conditions (AND)
active=true^priority=1^stateVALCHANGES6
# Only on insert (no previous value for state)
stateISEMPTYfalse^ORstateISEMPTY
常见条件运算符:
| 运算符 | 含义 | 示例 |
|---|---|---|
| CHANGES | 字段值已更改 | stateCHANGES |
| VALCHANGES | 更改为特定值 | stateVALCHANGES6 |
| CHANGESFROM | 从特定值更改 | stateCHANGESFROM1 |
| = | 等于 | priority=1 |
| != | 不等于 | state!=7 |
| ISEMPTY | 字段为空 | assigned_toISEMPTY |
| ISNOTEMPTY | 字段有值 | assigned_toISNOTEMPTY |
步骤 3.2:脚本条件(用于复杂逻辑)
仅当条件字段无法表达所需逻辑时,才使用脚本条件。
使用 MCP:
Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: Complex Condition Example
collection: incident
active: true
when: before
order: 100
condition: |
// Script condition - returns true/false
// Available: current, previous
// Check if escalating (priority going from higher number to lower)
if (current.priority.changes()) {
var oldPri = previous ? parseInt(previous.priority) : 5;
var newPri = parseInt(current.priority);
return newPri < oldPri; // True if escalating
}
return false;
script: |
(function executeRule(current, previous /*null when async*/) {
// This only runs if condition returned true
current.work_notes = 'Incident escalated from P' + previous.priority + ' to P' + current.priority;
gs.info('Escalation detected: ' + current.number);
})(current, previous);
何时使用脚本条件:
- 复杂的日期计算
- 跨表验证
- 根据用户角色动态生成的条件
- 涉及多个字段的计算
- 需要 GlideRecord 查询的条件
阶段 4:常见 Glide API 方法
步骤 4.1:GlideRecord 基础知识
// Query records
var gr = new GlideRecord('incident');
gr.addQuery('active', true);
gr.addQuery('priority', 1);
gr.orderByDesc('sys_created_on');
gr.setLimit(10);
gr.query();
while (gr.next()) {
gs.info('Found: ' + gr.number + ' - ' + gr.short_description);
}
// Get single record by sys_id
var incident = new GlideRecord('incident');
if (incident.get('sys_id_value')) {
gs.info('Found: ' + incident.number);
}
// Get by field value
var user = new GlideRecord('sys_user');
if (user.get('user_name', 'admin')) {
gs.info('Admin sys_id: ' + user.sys_id);
}
// Insert new record
var newTask = new GlideRecord('task');
newTask.initialize();
newTask.short_description = 'New task from business rule';
newTask.assignment_group = current.assignment_group;
var sysId = newTask.insert();
// Update record
var toUpdate = new GlideRecord('incident');
if (toUpdate.get('sys_id_value')) {
toUpdate.work_notes = 'Updated via business rule';
toUpdate.update();
}
// Delete record (use with caution)
var toDelete = new GlideRecord('task');
if (toDelete.get('sys_id_value')) {
toDelete.deleteRecord();
}
步骤 4.2:GlideSystem(gs)方法
// Logging
gs.info('Information message');
gs.warn('Warning message');
gs.error('Error message');
gs.debug('Debug message'); // Requires debug enabled
// User context
var userId = gs.getUserID();
var userName = gs.getUserName();
var userDisplayName = gs.getUserDisplayName();
// Check roles
if (gs.hasRole('admin')) {
// Admin-only logic
}
if (gs.hasRole('itil') || gs.hasRole('catalog_admin')) {
// ITIL or catalog admin logic
}
// Date/Time
var now = gs.now(); // Current date/time string
var nowDT = gs.nowDateTime(); // GlideDateTime
var today = gs.beginningOfToday(); // Start of today
var daysAgo = gs.daysAgo(7); // 7 days ago
// Properties
var propValue = gs.getProperty('my.property.name', 'default');
gs.setProperty('my.property.name', 'new_value');
// Generate GUID
var guid = gs.generateGUID();
// Include script include
gs.include('MyScriptInclude');
var util = new MyScriptInclude();
// Nil check (empty string, null, undefined)
if (gs.nil(current.assigned_to)) {
// Field is empty
}
// Event queue (trigger events)
gs.eventQueue('incident.created', current, current.number, current.priority);
步骤 4.3:GlideDateTime 操作
// Current time
var now = new GlideDateTime();
// Create from string
var dt = new GlideDateTime('2026-02-06 10:30:00');
// Add/subtract time
var future = new GlideDateTime();
future.addDays(5);
future.addHours(2);
future.addMinutes(30);
// Compare dates
var dt1 = new GlideDateTime();
var dt2 = new GlideDateTime(current.sys_created_on);
if (dt1.after(dt2)) {
gs.info('dt1 is after dt2');
}
// Calculate duration
var duration = GlideDateTime.subtract(dt2, dt1); // Returns GlideDuration
var seconds = duration.getNumericValue() / 1000;
var displayValue = duration.getDisplayValue(); // "2 Days 3 Hours"
// Business time calculations
var schedule = new GlideSchedule('sys_id_of_schedule');
var dur = schedule.duration(dt1, dt2);
// Format output
var formatted = now.getDisplayValue(); // User's timezone
var internal = now.getValue(); // Internal format (UTC)
var date = now.getLocalDate().getValue(); // Date only
步骤 4.4:使用 GlideAggregate 进行计数和求和
// Count records
var ga = new GlideAggregate('incident');
ga.addQuery('active', true);
ga.addAggregate('COUNT');
ga.query();
if (ga.next()) {
var count = ga.getAggregate('COUNT');
gs.info('Active incidents: ' + count);
}
// Count by group
var ga = new GlideAggregate('incident');
ga.addQuery('active', true);
ga.addAggregate('COUNT');
ga.groupBy('priority');
ga.orderByAggregate('COUNT', false); // Descending
ga.query();
while (ga.next()) {
var priority = ga.priority.getDisplayValue();
var count = ga.getAggregate('COUNT');
gs.info(priority + ': ' + count + ' incidents');
}
// Sum values
var ga = new GlideAggregate('sc_task');
ga.addQuery('active', true);
ga.addAggregate('SUM', 'time_worked');
ga.query();
if (ga.next()) {
var totalTime = ga.getAggregate('SUM', 'time_worked');
}
// Average
ga.addAggregate('AVG', 'priority');
// Min/Max
ga.addAggregate('MIN', 'sys_created_on');
ga.addAggregate('MAX', 'sys_created_on');
阶段 5:错误处理和日志记录
步骤 5.1:全面的错误处理模式
使用 MCP:
Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: Robust Error Handling Example
collection: incident
active: true
when: after
order: 100
script: |
(function executeRule(current, previous /*null when async*/) {
var BR_NAME = 'Robust Error Handling Example';
try {
// Validate inputs
if (gs.nil(current.sys_id)) {
throw new Error('Current record has no sys_id');
}
// Main logic
var relatedCI = new GlideRecord('cmdb_ci');
if (!relatedCI.get(current.cmdb_ci)) {
gs.warn('[' + BR_NAME + '] No CI found for incident: ' + current.number);
return; // Exit gracefully
}
// Perform operations
relatedCI.operational_status = 2; // Under maintenance
relatedCI.update();
gs.info('[' + BR_NAME + '] Updated CI: ' + relatedCI.name);
} catch (e) {
// Log error with context
gs.error('[' + BR_NAME + '] Error: ' + e.message);
gs.error('[' + BR_NAME + '] Incident: ' + current.number);
gs.error('[' + BR_NAME + '] Stack: ' + e.stack);
// Optionally create incident for business rule error
// createErrorIncident(BR_NAME, e, current);
}
})(current, previous);
步骤 5.2:提供用户反馈并中止操作
// Before business rule - prevent invalid save
(function executeRule(current, previous) {
var errors = [];
// Validation 1: Required field
if (current.priority == 1 && gs.nil(current.assigned_to)) {
errors.push('P1 incidents must have an assigned user');
}
// Validation 2: Business logic
if (current.state == 6 && gs.nil(current.resolution_notes)) {
errors.push('Resolution notes are required when resolving');
}
// Validation 3: Cross-field validation
if (current.impact == 1 && current.urgency == 1 && current.priority != 1) {
errors.push('Impact 1 + Urgency 1 must equal Priority 1');
}
// Abort if errors
if (errors.length > 0) {
var errorMsg = errors.join('\n');
gs.addErrorMessage(errorMsg);
current.setAbortAction(true);
gs.info('[Validation BR] Aborted save: ' + errorMsg);
}
})(current, previous);
阶段 6:性能优化
步骤 6.1:使用 .changes() 提升效率
错误——总是执行完整脚本:
// Inefficient - script runs on every update
(function executeRule(current, previous) {
if (previous && current.state != previous.state) {
// State changed - do work
}
})(current, previous);
正确——使用条件字段 + .changes():
Filter Condition: stateCHANGES
Script:
(function executeRule(current, previous) {
// Script only runs when state changes
// Additional validation with .changes() for safety
if (current.state.changes()) {
// Do work
}
})(current, previous);
步骤 6.2:尽量减少 GlideRecord 查询
错误——在循环中查询:
var incidents = new GlideRecord('incident');
incidents.query();
while (incidents.next()) {
// BAD: Query for each incident
var user = new GlideRecord('sys_user');
user.get(incidents.assigned_to);
// ...
}
正确——使用点遍历或批量查询:
// Option 1: Dot-walking (single query)
var incidents = new GlideRecord('incident');
incidents.query();
while (incidents.next()) {
var userName = incidents.assigned_to.name; // Dot-walk
var userEmail = incidents.assigned_to.email;
}
// Option 2: Batch query with lookup map
var userIds = [];
var incidents = new GlideRecord('incident');
incidents.query();
while (incidents.next()) {
if (!gs.nil(incidents.assigned_to)) {
userIds.push(incidents.assigned_to.toString());
}
}
var userMap = {};
if (userIds.length > 0) {
var users = new GlideRecord('sys_user');
users.addQuery('sys_id', 'IN', userIds.join(','));
users.query();
while (users.next()) {
userMap[users.sys_id.toString()] = {
name: users.name.toString(),
email: users.email.toString()
};
}
}
步骤 6.3:顺序和 Active 标志最佳实践
| 实践 | 建议 |
|---|---|
| 顺序 | 验证使用 100-199,字段赋值使用 200-399,外部/异步操作使用 400+ |
| Active | 开发期间设为 false,测试通过后启用 |
| 条件 | 使用筛选条件字段,而非脚本条件 |
| 继承 | 谨慎设置“Inherits”,通常保持未选中 |
步骤 6.4:避免递归更新
危险——可能导致无限循环:
// After business rule on incident
(function executeRule(current, previous) {
current.work_notes = 'Updated at ' + gs.now();
current.update(); // DANGER: Triggers business rules again!
})(current, previous);
安全——将 workflow 设为 false,或使用 before 规则:
// Option 1: Disable workflow on update
var gr = new GlideRecord('incident');
if (gr.get(current.sys_id)) {
gr.setWorkflow(false); // Skip business rules
gr.autoSysFields(false); // Skip sys field updates
gr.work_notes = 'Updated at ' + gs.now();
gr.update();
}
// Option 2: Use before rule instead (preferred)
// Before business rule - modifies current directly
(function executeRule(current, previous) {
current.work_notes = 'Updated at ' + gs.now();
// No .update() needed - current is saved automatically
})(current, previous);
阶段 7:测试 Business Rule
步骤 7.1:查询现有 Business Rule
使用 MCP:
Tool: SN-Query-Table
Parameters:
table_name: sys_script
query: collection=incident^active=true
fields: name,when,order,filter_condition,active
limit: 50
步骤 7.2:使用 Background Script 测试
使用 MCP:
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Test business rule by simulating record operation
var testIncident = new GlideRecord('incident');
testIncident.initialize();
testIncident.short_description = 'Test incident for BR testing';
testIncident.caller_id = gs.getUserID();
testIncident.impact = 1;
testIncident.urgency = 1;
// Insert will trigger before/after insert rules
var sysId = testIncident.insert();
gs.info('Created test incident: ' + sysId);
// Verify priority was calculated
testIncident.get(sysId);
gs.info('Priority after BR: ' + testIncident.priority.getDisplayValue());
// Clean up (optional)
// testIncident.deleteRecord();
description: Test priority calculation business rule
步骤 7.3:检查系统日志
使用 MCP:
Tool: SN-Query-Table
Parameters:
table_name: syslog
query: messageLIKEbusiness rule^ORmessageLIKE[BR]^sys_created_on>javascript:gs.minutesAgo(10)
fields: message,level,sys_created_on,source
limit: 50
步骤 7.4:明确的本地开发周期
拉取当前脚本和新鲜度元数据:
Tool: SN-Get-Record
Parameters:
table_name: sys_script
sys_id: [business_rule_sys_id]
fields: sys_id,script,sys_updated_on,sys_mod_count
instance: dev
在本地保存 script 字段,并在 IDE 中编辑。推送前立即再次执行
SN-Get-Record,并比较 sys_updated_on、sys_mod_count
和原始脚本。如果远程记录已更改,请在本地合并,而不是覆盖它。否则,只推送审核过的脚本字段:
Tool: SN-Update-Record
Parameters:
table_name: sys_script
sys_id: [business_rule_sys_id]
data:
script: [reviewed_local_script]
instance: dev
工具使用摘要
| 操作 | MCP 工具 | 用途 |
|---|---|---|
| 创建 BR | SN-Create-Record (sys_script) | 创建新的业务规则 |
| 更新 BR | SN-Update-Record | 修改现有业务规则 |
| 查询 BR | SN-Query-Table | 查找表中的业务规则 |
| 测试 BR | SN-Execute-Background-Script | 模拟记录操作 |
| 调试 | SN-Query-Table (syslog) | 检查执行日志 |
| 本地开发 | SN-Get-Record + SN-Update-Record | 明确拉取、检查新鲜度、编辑并推送 |
| 获取 Schema | SN-Get-Table-Schema | 了解表结构 |
最佳实践
代码质量
- 使用 IIFE 模式: 将所有脚本封装在
(function executeRule(current, previous) {...})(current, previous);中 - 变量命名清晰: 使用
existingTask等描述性名称,而不是gr2 - 为复杂逻辑添加注释: 解释原因,而不是说明做了什么
- 避免使用魔法数字: 对于
state=6之类的值,使用常量或添加注释
性能
- 使用 .changes() 方法: 如果字段没有变化,始终提前退出
- 优先使用条件字段: 数据库级筛选比脚本筛选更快
- 限制查询次数: 使用 setLimit(),避免在循环中查询
- 谨慎设置顺序: 数值越小,越先运行。将验证逻辑放在前面(100-199)
可靠性
- 始终处理错误: 使用 try-catch 包裹代码,尤其是外部调用
- 记录有意义的消息: 包含记录标识符和字段值
- 使用多个角色进行测试: 确保 ACL 不会破坏业务规则
- 避免递归: 更新同一张表时使用 setWorkflow(false)
可维护性
- 每条规则只做一件事: 将复杂规则拆分为专注于单一功能的组件
- 使用 Script Includes: 将可复用逻辑移至脚本包含项
- 记录依赖关系: 注明此规则与哪些其他规则或脚本交互
- 版本控制: 使用
SN-Get-Record拉取,将基础修订版本保存在 Git 中,检查内容是否为最新,然后使用SN-Update-Record推送
故障排查
业务规则未触发
症状: 记录已保存,但业务规则脚本未执行 原因:
- Active = false
- 筛选条件不匹配
- 顺序与中止操作冲突
- ACL 阻止访问字段 解决方案:
Tool: SN-Query-Table
Parameters:
table_name: sys_script
query: collection=incident^name=*priority*
fields: name,active,when,filter_condition,order
无限循环 / 堆栈溢出
症状: 错误“Maximum call stack size exceeded”或超时 原因: 业务规则在同一张表上调用 .update(),从而触发自身 解决方案:
// Use setWorkflow(false) to prevent recursion
var gr = new GlideRecord('incident');
gr.get(current.sys_id);
gr.setWorkflow(false);
gr.update();
previous 为 Null
症状: 访问 previous.fieldname 时出错 原因: 在插入操作或异步规则中,previous 为 null 解决方案:
// Always check for null
if (previous && current.state != previous.state) {
// Safe to access previous
}
// Or use operation check
if (current.operation() != 'insert' && previous) {
// previous is available
}
未检测到更改
症状: 字段已更改,但 .changes() 返回 false 原因: 字段被设为相同的值,或在查询后进行了更改 解决方案:
// Log actual values for debugging
gs.info('Current state: ' + current.state + ', Previous: ' + (previous ? previous.state : 'null'));
gs.info('Changes: ' + current.state.changes());
// Use condition field for reliable change detection
// Filter: stateCHANGES
示例
示例 1:根据类别自动分配
(function executeRule(current, previous) {
// Exit early if category hasn't changed
if (!current.category.changes()) {
return;
}
// Assignment mapping
var categoryAssignment = {
'network': 'Network Team',
'hardware': 'Desktop Support',
'software': 'Application Support',
'database': 'DBA Team'
};
var category = current.category.toString();
var groupName = categoryAssignment[category];
if (groupName) {
var group = new GlideRecord('sys_user_group');
if (group.get('name', groupName)) {
current.assignment_group = group.sys_id;
gs.info('Auto-assigned to ' + groupName + ' based on category');
}
}
})(current, previous);
示例 2:SLA 升级警告
(function executeRule(current, previous) {
// Check SLA breach approaching (80% of response time)
var sla = new GlideRecord('task_sla');
sla.addQuery('task', current.sys_id);
sla.addQuery('stage', 'in_progress');
sla.query();
while (sla.next()) {
var percentComplete = sla.percentage.floatValue();
if (percentComplete >= 80 && percentComplete < 100) {
// Approaching breach - escalate
if (current.priority > 1) {
current.priority = parseInt(current.priority) - 1;
current.work_notes = 'Auto-escalated: SLA at ' + Math.round(percentComplete) + '%';
gs.info('SLA escalation for ' + current.number + ' - ' + percentComplete + '%');
}
}
}
})(current, previous);
示例 3:敏感字段更改的审计跟踪
(function executeRule(current, previous) {
var sensitiveFields = ['priority', 'assignment_group', 'assigned_to', 'state'];
var changes = [];
for (var i = 0; i < sensitiveFields.length; i++) {
var field = sensitiveFields[i];
if (current[field].changes()) {
changes.push({
field: field,
from: previous ? previous[field].getDisplayValue() : '(empty)',
to: current[field].getDisplayValue()
});
}
}
if (changes.length > 0) {
var auditNote = 'Field Changes:\n';
for (var j = 0; j < changes.length; j++) {
var c = changes[j];
auditNote += '- ' + c.field + ': "' + c.from + '" -> "' + c.to + '"\n';
}
auditNote += '\nChanged by: ' + gs.getUserDisplayName();
current.work_notes = auditNote;
gs.info('Audit trail recorded for ' + current.number);
}
})(current, previous);
相关技能
admin/script-execution- 后台脚本模式与执行admin/update-set-management- 跟踪业务规则更改development/script-includes- 可复用的服务端库catalog/ui-policies- 客户端字段控制(与 BR 配合使用)security/acl-management- 影响 BR 执行的访问控制