脚本参考
本文档为在工作流规则中编写脚本提供了全面的参考。脚本允许您使用 JavaScript 实现高级自动化逻辑。
概述
工作流规则中的 Run Script 操作执行 JavaScript 代码以自动化复杂的工单处理场景。脚本在沙盒环境中运行,可以访问工单数据、消息上下文和辅助函数。
主要功能:
- 读取和修改工单属性
- 访问客户和所有者信息
- 使用自定义字段
- 使用条件逻辑和循环
- 10 秒执行超时以确保安全
Ticket 对象
ticket 对象是脚本的主要上下文。它提供对工单数据的读写访问。
只读属性
// 基本工单信息
ticket.id // UUID 字符串
ticket.subject // 工单主题行
// 当前状态(对象)
ticket.status.id // 状态 UUID
ticket.status.code // 状态代码(例如,"open"、"closed")
ticket.status.name // 状态显示名称
// 当前优先级(对象)
ticket.priority.id // 优先级 UUID
ticket.priority.name // 优先级显示名称
// 当前部门(对象)
ticket.department.id // 部门 UUID
ticket.department.name // 部门显示名称
// 分配的所有者(对象,可能未定义)
ticket.owner.id // 用户 UUID
ticket.owner.email // 用户电子邮件
ticket.owner.firstname // 用户名字
ticket.owner.lastname // 用户姓氏
// 客户信息(对象)
ticket.customer.id // 客户 UUID
ticket.customer.email // 客户电子邮件
ticket.customer.firstname // 客户名字
ticket.customer.lastname // 客户姓氏
ticket.customer.name // 客户全名
// 文件夹和标签
ticket.folder // 当前文件夹:"INBOX"、"ASSIGNED"、"SPAM"、"ARCHIVE"
ticket.tags // 标签字符串数组
可写属性
您可以通过直接赋值修改这些属性:
// 按代码或 ID 设置状态
ticket.status = "closed";
ticket.status = "550e8400-e29b-41d4-a716-446655440000";
// 按名称或 ID 设置优先级
ticket.priority = "High";
ticket.priority = "550e8400-e29b-41d4-a716-446655440001";
// 按名称或 ID 设置部门
ticket.department = "Sales";
ticket.department = "550e8400-e29b-41d4-a716-446655440002";
// 按电子邮件或 ID 设置所有者
ticket.owner = "[email protected]";
ticket.owner = "550e8400-e29b-41d4-a716-446655440003";
// 设置文件夹
ticket.folder = "ASSIGNED";
ticket.folder = "SPAM";
ticket.folder = "ARCHIVE";
ticket.folder = "INBOX";
// 设置标签(替换所有现有标签)
ticket.tags = ["urgent", "vip"];
自定义字段
可以使用其代码(而不是显示名称)读取和写入自定义字段。
读取自定义字段
// 直接访问自定义字段值
var orderId = ticket.order_id; // 文本字段
var amount = ticket.order_amount; // 数字字段
var isVip = ticket.is_vip; // 布尔字段
var region = ticket.region; // 选择字段
写入自定义字段
// 设置文本自定义字段
ticket.order_id = "ORD-12345";
// 设置数字自定义字段
ticket.order_amount = 199.99;
// 设置布尔自定义字段
ticket.is_vip = true;
// 设置选择自定义字段
ticket.category = "billing";
// 设置日期自定义字段(ISO 8601 格式)
ticket.due_date = "2024-12-31T23:59:59Z";
ticket.due_date = "2024-12-31";
Message 对象
当规则由消息事件触发时(New Customer Reply、New Staff Reply 等),message 对象可用。
if (message) {
message.id // 消息 UUID
message.body_text // 消息内容(纯文本)
message.body_html // 消息内容(HTML)
message.createdAt // ISO 8601 时间戳
}
示例:
if (message && message.body_text) {
var bodyLower = message.body_text.toLowerCase();
if (bodyLower.includes("cancel subscription")) {
ticket.department = "Retention";
ticket.priority = "High";
}
}
辅助函数
getStatuses()
返回所有可用状态的数组。
var statuses = getStatuses();
// 返回:[{ id: "...", code: "open", name: "Open" }, ...]
// 查找特定状态
for (var i = 0; i < statuses.length; i++) {
if (statuses[i].code === "pending") {
ticket.status = statuses[i].id;
break;
}
}
getPriorities()
返回所有可用优先级的数组。
var priorities = getPriorities();
// 返回:[{ id: "...", name: "Low" }, { id: "...", name: "High" }, ...]
getDepartments()
返回所有可用部门的数组。
var departments = getDepartments();
// 返回:[{ id: "...", name: "Support" }, { id: "...", name: "Sales" }, ...]
getUsers()
返回所有可用用户(客服)的数组。
var users = getUsers();
// 返回:[{ id: "...", email: "[email protected]", firstname: "John", lastname: "Doe" }, ...]
fetch()
向外部 API 发出 HTTP 请求。同步 - 直接返回结果。
var response = fetch(url, options);
// response: { ok, status, statusText, text, json }
选项: method(GET/POST/PUT/DELETE)、headers(对象)、body(字符串或对象)
示例:从 CRM 获取并更新自定义字段
var response = fetch("https://api.example.com/customers/" + ticket.customer.email, {
headers: { "Authorization": "Bearer YOUR_API_KEY" }
});
if (response.ok && response.json) {
ticket.customer_tier = response.json.tier;
ticket.lifetime_value = response.json.lifetime_value;
if (response.json.tier === "enterprise") {
ticket.priority = "High";
}
}
示例:POST 到 webhook
var response = fetch("https://hooks.example.com/helpdesk", {
method: "POST",
body: { ticket_id: ticket.id, customer_email: ticket.customer.email }
});
注意: 仅 HTTPS,5 秒超时,1MB 响应限制,内部网络被阻止。
常见模式
条件分配
// 根据主题设置优先级
if (ticket.subject.toLowerCase().includes("urgent")) {
ticket.priority = "High";
} else if (ticket.subject.toLowerCase().includes("question")) {
ticket.priority = "Low";
}
部门路由
var subject = ticket.subject.toLowerCase();
if (subject.includes("billing") || subject.includes("payment") || subject.includes("invoice")) {
ticket.department = "Billing";
} else if (subject.includes("technical") || subject.includes("bug") || subject.includes("error")) {
ticket.department = "Technical Support";
} else if (subject.includes("sales") || subject.includes("pricing") || subject.includes("quote")) {
ticket.department = "Sales";
}
基于客户域的逻辑
if (ticket.customer && ticket.customer.email) {
var email = ticket.customer.email;
var domain = email.split("@")[1];
// 企业客户处理
if (domain === "bigcorp.com" || domain === "enterprise.org") {
ticket.priority = "High";
ticket.department = "Enterprise Support";
ticket.is_enterprise = true;
}
// 教育机构
if (domain.endsWith(".edu")) {
ticket.segment = "education";
}
}
标签管理
// 添加标签而不丢失现有标签
var existingTags = ticket.tags || [];
existingTags.push("processed");
existingTags.push("automated");
ticket.tags = existingTags;
使用消息
if (message && message.body_text) {
var body = message.body_text.toLowerCase();
// 检测挫折关键字
var frustrationKeywords = ["frustrated", "angry", "terrible", "worst", "unacceptable"];
for (var i = 0; i < frustrationKeywords.length; i++) {
if (body.includes(frustrationKeywords[i])) {
ticket.priority = "High";
ticket.needs_attention = true;
break;
}
}
}
轮询分配
var users = getUsers();
if (users.length > 0) {
// 基于工单 ID 哈希的简单轮询
var ticketHash = 0;
for (var i = 0; i < ticket.id.length; i++) {
ticketHash += ticket.id.charCodeAt(i);
}
var assigneeIndex = ticketHash % users.length;
ticket.owner = users[assigneeIndex].email;
}
错误处理
脚本在安全环境中执行:
- 语法错误被捕获并记录;脚本被跳过。
- 无效的字段值(例如,不存在的状态名称)被忽略。
- 超时(10 秒)防止无限循环。
- 错误不会级联到其他规则或系统组件。
最佳实践:
// 访问属性前检查 null/undefined
if (ticket.customer && ticket.customer.email) {
// 安全访问 ticket.customer.email
}
if (message) {
// 安全访问 message 属性
}
// 对复杂逻辑使用 try-catch
try {
var data = JSON.parse(ticket.custom_json);
ticket.parsed_field = data.value;
} catch (e) {
// 优雅地处理错误
}
限制
- 执行超时: 脚本在 10 秒后终止。
- 无文件系统访问: 文件操作不可用。
- 无持久状态: 变量在脚本执行之间不持久。
- 只读字段:
ticket.id、ticket.subject、ticket.customer不能被修改。 - ES5 JavaScript: 现代 JavaScript 功能(箭头函数、const/let 等)可能不可用。