Skip to content

后端脚本 ​

后端脚本在 ProCode 服务的 Jint JavaScript 引擎中执行。它用于实现接口、表单提交钩子、工作流节点逻辑、数据库访问、缓存和出站 HTTP 调用。

运行语法 ​

平台把当前请求上下文注入为全局变量 App,并直接执行脚本正文:

javascript
var name = App.Request.Body && App.Request.Body.name;

App.Response.StatusCode = 200;
App.Response.Body = {
  success: true,
  name: name
};

后端脚本遵循 Jint 支持的 JavaScript 语法,并具有以下约定:

  • 直接编写脚本正文,不要添加 <script> 标签。
  • 不存在 window、document、localStorage 或 Vue 组件。
  • 平台服务通常以同步方法暴露,示例中无需 await。
  • .NET 方法和属性区分大小写,使用文档中的 PascalCase 名称。
  • 推荐使用 var、普通函数和明确的空值判断,以保持脚本兼容性。
  • 结束前设置 App.Response.Body;需要提前终止时设置 App.Stop = true。

Jint 额外为数组提供以下方法:

javascript
var first = rows.FirstOrDefault();
var required = rows.First();
var last = rows.LastOrDefault();

First() 和 Last() 在空数组上会抛出异常;不确定是否有数据时使用 FirstOrDefault() 或先判断 length。

脚本事件 ​

配置字段触发时机可用数据
接口 contextHTTP 或 MCP 调用接口时Request、Response、Services
back_before_submit_command表单写入数据库之前App.Form.Data、Operation
back_after_submit_command表单写入数据库之后App.Form.Data、RowId
back_before_delete_command删除数据之前App.Form.Data、RowId
back_after_delete_command删除完成之后App.Form
back_after_fetch_command获取单条表单数据之后App.Form.Data
back_start_command工作流节点开始时App.Workflow、App.Form
back_end_command工作流节点结束时App.Workflow、App.Form

App.Request 与 App.Response ​

读取请求 ​

javascript
var routeId = App.Request.Routes.id;
var pageNumber = App.Request.Querys.page_number;
var authorization = App.Request.Headers.Authorization;
var body = App.Request.Body;
成员说明
Request.Routes路由占位符,例如 /order/{id} 中的 id
Request.Querys查询字符串;属性名保留为 Querys
Request.HeadersHTTP 请求头
Request.Body已反序列化的请求体
Response.StatusCodeHTTP 状态码,默认 200
Response.Body响应正文,平台会在执行后序列化

参数校验与提前返回 ​

javascript
var body = App.Request.Body;

if (!body || !body.name) {
  App.Response.StatusCode = 400;
  App.Response.Body = {
    success: false,
    message: 'name 不能为空'
  };
  App.Stop = true;
  return;
}

设置 App.Stop = true 后,引擎停止后续处理;如果数据库服务处于事务中,当前实现会回滚事务。

App.Form 表单上下文 ​

表单后端事件通过 App.Form 访问数据,字段键使用表字段的 mapname:

javascript
var data = App.Form.Data;
var operation = App.Form.Operation; // get / create / update / delete
var rowId = App.Form.RowId;

if (operation === 'create') {
  data.f_status = 'draft';
  data.f_creator_id = App.CurrentUserId;
}
成员说明
Form.Form当前表单元数据
Form.FormComponent当前组件元数据,部分事件中存在
Form.Data当前数据,可在写入前修改
Form.AfterData变更前数据,更新或删除时用于对比
Form.Operationget、create、update 或 delete
Form.RowId当前行 ID

表单提交前校验示例:

javascript
var data = App.Form.Data;

if (!data.f_customer_name) {
  App.Response.StatusCode = 400;
  App.Response.Body = {
    success: false,
    message: '客户名称不能为空'
  };
  App.Stop = true;
  return;
}

if (App.Form.Operation === 'update') {
  var before = App.Form.AfterData || {};
  App.Helpers.Logger.Info(
    '状态变更: ' + String(before.f_status) + ' -> ' + String(data.f_status)
  );
}

DatabaseService 数据库语法 ​

数据库对象位于 App.Services.DatabaseService。table 使用平台表的逻辑名称或 mapname,不要把用户输入拼接到 SQL 中。

Query 查询 ​

javascript
var rows = App.Services.DatabaseService.Query({
  table: 'crm_customer',
  type: 0,
  wheres: [
    {
      type: 0,
      values: [
        {
          column: 'f_status',
          type: 0,
          value: 1,
          value_source_type: 1,
          value_type: 2
        },
        {
          column: 'f_is_delete',
          type: 0,
          value: false,
          value_source_type: 1,
          value_type: 3
        }
      ]
    }
  ],
  columns: ['f_id', 'f_name', 'f_status'],
  descs: [
    { column: 'f_create_time', type: 1 }
  ],
  page: {
    page_size: 20,
    page_number: 1
  }
});

QueryModel 结构:

字段是否必填说明
table是表逻辑名称或 mapname
type是条件组逻辑:0 为 And,1 为 Or
wheres否条件组数组
joins否关联表配置
columns否要返回的列
descs否排序;0 升序,1 降序
page否page_size 与 page_number,页码从 1 开始

条件 type:

值含义是否需要 value
0等于是
1不等于是
2包含是
3不包含是
4为空否
5非空否
6大于是
7大于等于是
8小于是
9小于等于是

value_type:0 字符串、1 长整数、2 整数、3 布尔、4 数值、5 日期时间、6 枚举;数组类型为基础值加 10。

列与列比较时使用 value_source_type: 0 和 right_column:

javascript
var rows = App.Services.DatabaseService.Query({
  table: 'crm_order',
  type: 0,
  wheres: [
    {
      type: 0,
      values: [
        {
          column: 'f_paid_amount',
          type: 8,
          right_column: 'f_total_amount',
          value_source_type: 0
        }
      ]
    }
  ]
});

Create 新增 ​

javascript
var created = App.Services.DatabaseService.Create({
  table: 'crm_customer',
  value: {
    f_name: '示例客户',
    f_status: 1,
    f_creator_id: App.CurrentUserId,
    f_create_time: new Date()
  }
});

Update 更新 ​

优先按 ID 更新:

javascript
var updated = App.Services.DatabaseService.Update({
  table: 'crm_customer',
  id: App.Request.Routes.id,
  value: {
    f_status: 2,
    f_update_time: new Date()
  }
});

也可以使用 wheres 批量更新,但必须明确限制范围:

javascript
App.Services.DatabaseService.Update({
  table: 'crm_customer',
  type: 0,
  wheres: [
    {
      type: 0,
      values: [
        { column: 'f_status', type: 0, value: 0, value_source_type: 1, value_type: 2 }
      ]
    }
  ],
  value: { f_status: 1 }
});

Delete 删除 ​

javascript
var deleted = App.Services.DatabaseService.Delete({
  table: 'crm_customer',
  type: 0,
  wheres: [
    {
      type: 0,
      values: [
        { column: 'f_id', type: 0, value: App.Request.Routes.id, value_source_type: 1, value_type: 1 }
      ]
    }
  ]
});

业务表优先使用软删除字段;直接删除前必须校验权限和关联数据。

参数化 SQL ​

表达式查询无法满足需求时才使用 SQL,并始终参数化:

javascript
var rows = App.Services.DatabaseService.Query(
  'SELECT f_id, f_name FROM t_customer WHERE f_status = @status',
  [
    { name: 'status', value: 1 }
  ],
  false
);

禁止把请求参数拼接到 SQL:

javascript
// 错误:存在 SQL 注入风险
// var sql = "SELECT * FROM t_customer WHERE f_name='" + name + "'";

其他服务 ​

出站 HTTP ​

javascript
var text = App.Services.HttpService.Post(
  'https://api.example.com/v1/items',
  { name: '示例' },
  { Authorization: 'Bearer ' + App.Helpers.Env.Get('EXTERNAL_API_TOKEN') }
);

var result = App.Helpers.Json.Deserialize(text);

缓存 ​

javascript
var key = 'customer:' + String(App.Request.Routes.id);
var cached = App.Services.CacheService.Get(key);

if (cached == null) {
  cached = App.Services.DatabaseService.Query({
    table: 'crm_customer',
    type: 0,
    wheres: []
  });
  App.Services.CacheService.Set(key, cached, null);
}

日志与工具 ​

javascript
App.Helpers.Logger.Info('开始处理接口');

var payloadText = App.Helpers.Json.Serialize(App.Request.Body);
var requestId = App.Helpers.Id.GetGuid();
var userId = App.Helpers.Convert.ToInt64(App.CurrentUserId, 0);
var enabled = App.Helpers.Convert.ToBoolean(App.Request.Querys.enabled, false);

常用服务还包括 StorageService、FormService、WorkflowService、UserService、DepartmentService、RoleService、MongoDbService 和 TimeSeriesDatabaseService。某项服务是否可用取决于当前站点配置,以代码编辑器类型提示和部署版本为准。

完整接口示例 ​

下面的脚本校验参数、查询数据并构造统一响应:

javascript
try {
  var id = App.Request.Routes.id;

  if (!id) {
    App.Response.StatusCode = 400;
    App.Response.Body = { success: false, message: '缺少 id' };
    App.Stop = true;
    return;
  }

  var rows = App.Services.DatabaseService.Query({
    table: 'crm_customer',
    type: 0,
    wheres: [
      {
        type: 0,
        values: [
          { column: 'f_id', type: 0, value: id, value_source_type: 1, value_type: 1 },
          { column: 'f_is_delete', type: 0, value: false, value_source_type: 1, value_type: 3 }
        ]
      }
    ],
    page: { page_size: 1, page_number: 1 }
  });

  if (rows.length === 0) {
    App.Response.StatusCode = 404;
    App.Response.Body = { success: false, message: '数据不存在' };
    App.Stop = true;
    return;
  }

  App.Response.StatusCode = 200;
  App.Response.Body = {
    success: true,
    data: rows[0]
  };
} catch (e) {
  App.IsException = true;
  App.Helpers.Logger.Error('查询客户失败: ' + String(e));
  App.Response.StatusCode = 500;
  App.Response.Body = {
    success: false,
    message: '服务器处理失败'
  };
}

事务、错误与安全 ​

  • 脚本异常时引擎会标记 App.IsException,回滚活动事务并返回 500。
  • 使用 DatabaseService 的事务能力时,确保成功路径提交、失败路径回滚;不要让连接跨脚本保存。
  • 对所有路由、查询和请求体参数执行类型、长度与业务范围校验。
  • 只返回业务需要的数据,不要把异常堆栈、连接字符串或密钥写入响应。
  • 凭据应通过环境或安全配置读取,不要直接写在脚本中。
  • 更新和删除必须带有限制条件;禁止无条件批量修改生产数据。
  • 后端脚本没有 App.Services.InterfaceService,接口之间调用应使用 HttpService 或抽取公共服务。

API 全量参考 ​

DatabaseService ​

查询与写入 ​

方法签名返回值说明
Query(query)Array<Record>使用当前数据库和 QueryModel 查询。
Query(databaseName, query)Array<Record>在指定逻辑数据库执行表达式查询。
Query(sql)Array<Record>在当前数据库执行无参数 SQL。
Query(sql, parameters?, enableTransaction?)Array<Record>在当前数据库执行参数化 SQL。
Query(databaseName, sql, parameters, enableTransaction)Array<Record>在指定数据库执行参数化 SQL。
Create(create)Record在当前数据库新增一条数据;参数为 { table, value }。
Create(databaseName, create)Record在指定数据库新增数据。
Update(update)Record | Record[]按 id 或 wheres 更新当前数据库数据。
Update(databaseName, update)Record | Record[]在指定数据库更新数据。
Delete(query)boolean按 QueryModel 删除当前数据库数据。
Delete(databaseName, query)boolean在指定数据库删除数据。
Execute(sql)number执行无参数 SQL,返回影响行数。
Execute(sql, parameters?, enableTransaction?)number执行参数化 SQL。
Execute(databaseName, sql, parameters, enableTransaction)number在指定数据库执行参数化 SQL。

事务与辅助方法 ​

方法签名返回值说明
Commit()void提交当前活动事务。
Rollback()void回滚当前活动事务。
IsTransaction()boolean判断当前是否存在活动事务。
ToTree(items, key, parent, value, children?)Record[]将平铺记录按主键和父键转换为树。
Close()void关闭并释放数据库连接。

最小调用:

javascript
var rows = App.Services.DatabaseService.Query({
  table: 'crm_customer',
  type: 0,
  wheres: [],
  page: { page_size: 20, page_number: 1 }
});

DatabaseSchemaService ​

方法签名返回值说明
GetDatabases()string[]获取可用逻辑数据库名称。
GetTablesByDatabase(database)string[]获取指定物理库的表名。
GetTableByName(name)string[]获取当前绑定数据库中指定表的列名。
GetColumnsByTableName(name)BackendEngineColumn[]获取引擎元数据中的列定义。
UpsertTable(databaseId, table, columns)Promise<boolean>写入或更新表与列元数据,并同步物理结构。
SchemaSyncEngineAsync(databaseId, tableName)Promise<TableSchemaSyncOutcome>将物理库表结构同步到引擎元数据。
EngineSyncSchemaAsync(databaseId, tableName)Promise<TableSchemaSyncOutcome>将引擎元数据同步到物理库。

同步结果:0 数据库不存在、1 表不存在、2 无列定义、3 成功。

HttpService ​

方法签名返回值说明
Get(url, header?, cookie?)string发送 GET 请求并返回响应文本。
Post(url, body, header?, cookie?)string发送 POST 请求。
Put(url, body, header?, cookie?)string发送 PUT 请求。
Delete(url, header?, cookie?)string发送 DELETE 请求。
javascript
var text = App.Services.HttpService.Get(
  'https://api.example.com/v1/status',
  { Accept: 'application/json' }
);
var data = App.Helpers.Json.Deserialize(text);

CacheService ​

方法签名返回值说明
Get(key)T读取缓存;不存在时返回空值。
Set(key, value, duration?)boolean写入缓存和可选有效期。
Remove(...keys)void删除一个或多个缓存键。

AuthService ​

方法签名返回值说明
Get(id){ access_token, expires, token_type }获取指定认证配置对应的访问令牌。

StorageService ​

方法签名返回值说明
DownloadFile(fileId, fileName?, expireHours?, queryParams?)string | null生成文件下载地址。
DownloadThumbFile(fileId)string | null生成缩略图下载地址。

FormService ​

方法签名返回值说明
GetList(name?)Array<Record>获取表单列表,可按名称过滤。
GetById(id)Record | null获取表单定义。
Create(model)Record创建表单。
Update(id, model)Record更新表单。
Delete(id)boolean删除表单。

WorkflowService ​

方法签名返回值说明
Start(model)Record | null启动工作流实例。
Approve(model)Record | null审批当前流程节点。

TimeSeriesDatabaseService ​

该服务只有在 t_time_series_database 已配置连接时可用。

方法签名返回值说明
GetMapNames()string[]获取已配置的时序库 map_name。
Query(mapName, sql, parameters?)Array<Record>在指定时序库执行查询。
Insert(mapName, pointOrBatch)void写入单条或一组测点。
InsertBatch(mapName, points)number批量写入并返回条数。
Ping(mapName)boolean探测连接是否可用。

IsConfigured 是只读属性,不是方法。

javascript
if (App.Services.TimeSeriesDatabaseService != null &&
    App.Services.TimeSeriesDatabaseService.Ping('iot')) {
  App.Services.TimeSeriesDatabaseService.Insert('iot', {
    path: 'device_01',
    timestamp: new Date().getTime(),
    fields: { temperature: 23.6 }
  });
}

UserService ​

方法签名返回值说明
GetCurrent()User | null获取当前用户摘要。
GetById(userId)User | null按 ID 获取用户。
GetByIds(userIds)User[]批量获取用户。
List(keyword?, pageNumber?, pageSize?){ items, total, page_number, page_size }分页查询用户。
GetByDepartment(departmentId?)User[]获取部门成员;空值表示无部门,0 表示全部。
GetByRole(roleId)User[]获取角色成员。

DepartmentService ​

方法签名返回值说明
GetContext(){ departments, users }获取部门与用户组织上下文。
GetTree()Department[]获取部门树。
GetById(departmentId)Department | null获取指定部门。
GetByUser(userId?)Department[]获取用户所属部门;不传表示当前用户。

RoleService ​

方法签名返回值说明
GetList()Role[]获取角色列表。
GetById(roleId)Role | null获取指定角色。
GetByUser(userId?)Role[]获取用户角色;0 或不传表示当前用户。

Logger ​

方法签名返回值说明
App.Helpers.Logger.Debug(message)void写入调试日志。
App.Helpers.Logger.Info(message)void写入信息日志。
App.Helpers.Logger.Warn(message)void写入警告日志。
App.Helpers.Logger.Error(message)void写入错误日志。

Env 与 Json ​

方法签名返回值说明
App.Helpers.Env.Get(key)string | null读取环境或平台配置。
App.Helpers.Env.GetApiBase()string获取 API 基础地址。
App.Helpers.Json.Serialize(value)string序列化为 JSON。
App.Helpers.Json.Deserialize(json)T | undefined将 JSON 反序列化为对象。

Crypto ​

方法签名返回值说明
Md5(value)string计算 MD5。
Md5Guid(value)string | null生成 GUID 形态 MD5。
GenerateBearerToken(length?, key?)string生成随机令牌。
Base64Encode(value)string | nullBase64 编码。
Base64Decode(value)string | nullBase64 解码。
AesEncrypt(input, iv, key)stringAES 加密。
AesDecrypt(input, iv, key)stringAES 解密。
GenerateAesKey(keySize?)string生成 AES 密钥。
GenerateAesIv()string生成 AES IV。
RsaEncrypt(input, publicKey)stringRSA 公钥加密。
RsaDecrypt(input, privateKey)stringRSA 私钥解密。
RsaSign(input, privateKey)stringRSA 私钥签名。
RsaVerify(input, signature, publicKey)booleanRSA 公钥验签。

这些方法通过 App.Helpers.Crypto 调用。

Id 与 Validation ​

方法签名返回值说明
App.Helpers.Id.IsGuid(value)boolean判断字符串是否为 GUID。
App.Helpers.Id.GetGuid()string生成 GUID。
App.Helpers.Id.GetSnowflake()number生成雪花 ID。
App.Helpers.Validation.IsEmail(value)boolean校验邮箱格式。

Dictionary 与 List ​

方法签名返回值说明
Dictionary.AddOrSet(dict, key, value)Record新增或覆盖字典项。
Dictionary.Remove(dict, key)Record删除字典项。
List.AddOrSet(array, value)Array向数组增加值,已有时按实现更新。
List.AddOrSet(array, object, name)Array按字段名增加或更新对象。
List.GetList(objects, name)Array从对象数组提取指定字段。
List.Remove(array, value)Array从数组移除值。
List.ContainsKey(array, value)boolean判断数组是否包含值。

以上方法分别通过 App.Helpers.Dictionary 与 App.Helpers.List 调用。

Convert ​

方法签名返回值说明
ToString(value)string | null转为可空字符串。
ToStringOrEmpty(value)string转为字符串,空值返回空字符串。
ToInt32(value, defaultValue?)number转为 32 位整数。
ToInt64(value, defaultValue?)number转为 64 位整数。
ToDouble(value, defaultValue?)number转为双精度数值。
ToDecimal(value, defaultValue?)number转为十进制数值。
ToBoolean(value, defaultValue?)boolean转为布尔值。
ToDateTime(value, defaultValue?)Date转为日期时间。
ToNullableDateTime(value)Date | null转为可空日期时间。
ToGuidString(value)string | null转为 GUID 字符串。
To(value, defaultValue?)T | null转为指定泛型类型。
JsonTo(value)T | null通过 JSON 转换对象类型。
ChangeType(value, typeName, defaultValue?)any按 .NET 类型名转换。

以上方法通过 App.Helpers.Convert 调用。

IO ​

方法签名返回值说明
App.Helpers.IO.SelectFile(options?)Promise<File[]>请求宿主选择文件;是否可用取决于运行环境。
App.Helpers.IO.SelectFolder()Promise<File[]>请求宿主选择文件夹;是否可用取决于运行环境。

App 控制属性 ​

下列是属性而非方法,但会直接影响脚本执行:

属性类型说明
App.Debugboolean是否启用调试模式。
App.CurrentUserIdstring当前用户 ID。
App.Triggerobject | null当前触发器。
App.Paramany扩展参数。
App.IsExceptionboolean是否已发生异常。
App.IsAsyncboolean是否异步执行。
App.Stopboolean停止后续执行并按当前 Response 返回。

未形成稳定契约的服务 ​

SMSService、EmailService、InboundEmailService、QrCodeService、JPushService、MongoDbService 与 InboxMessageService 已存在于 ProCode 服务容器,但当前脚本编辑器未公开稳定的方法签名,因此不把其内部方法作为可兼容的脚本 API。后续一旦进入公开类型定义,应同步补充到本页。

常见问题 ​

DatabaseService 查不到表 ​

table / 列使用 map_name;多库须传数据源 map_name。见 关系数据库。

时序库怎么写 ​

用 TimeSeriesDatabaseService,先判 IsConfigured,再传路由名。见 时序数据库。

异常后接口仍返回 200 ​

设置 App.IsException = true 并指定 App.Response.StatusCode;必要时 App.Stop = true。

相关文档 ​

白泽 ProCode 开发者文档