低代码平台设计文档

数据引擎

数据校验脚本

表可以定义一个 用来校验数据是否有效的脚本,该脚本在前端运行,在用户编辑完表记录之后,系统会自动调用

(ownerID,providerItemKey,compondItem,params,envViables,actionCode)=>
// 返回值 null :表示检查通过
// 返回值非null:表示检查不通过,提示原因为 result.toString()

用户定义表

Scheme表

虚拟表

字段

action

前置条件

  1. 可以针对 某一个字段 设置action的前置条件
  2. 可以 设置一个 脚本用来检查前置条件
    //#RuiScriptSetting{"kind":"ES6"}
    (ownerID,providerItemKey,item)=>
    {
    //脚本返回值的意义
    //  1 没有返回值或者return null. 表示 前置条件检查 通过
    //  2 返回 bool类型   true:前置条件检查通过    false:检查不通过 ,但是原因不明确
    //  3 返回 非bool类型   表示 检查不通过,原因为 返回对象.toString()
    }
    

全局action

全局action表示 不具体针对某一条记录的 action,例如 新建记录、月度结转等。默认会显示在 查询结果的 左上角 全局Aciotn需要勾选Global标志,表示 是全局Action 全局Action的OperateType 必须选择 未指定 或者 新建

操作类型为 未指定的 全局Action 用户自定义前后台操作 默认前端脚本

//#RuiScriptSetting{"kind":"ES6"}
(dataSource,ownerID,providerItemKey,hashKey,rangeKey,actionCode,actionName,actionOperateType,envViables)=>
{
    //默认的脚本,什么都不做
}

默认服务端脚本

//#RuiScriptSetting{"kind":"ES6"}
(ownerID, providerItemKey, envViables) => {
    //默认的脚本,什么都不做
    return true;
}

操作类型为 新建 的全局Action 表示新建一条记录 默认前端脚本

//#RuiScriptSetting{"kind":"ES6"}
(dataSource, ownerID, providerItemKey, hashKey, rangeKey, actionCode, actionName, actionOperateType, envViables) => {
    //$newItem = _getNewSingleItem(ownerID, providerItemKey,actionCode,envViables);
    const $newItem = _dataSource_NewSingleItem(dataSource, ownerID, actionCode, envViables); //modify 2022.01.30 支持GetParam功能后改进
    let $isShouldWhile = true;
    while ($isShouldWhile) {
        try {
            const $result = _showAndEdit(actionName, ownerID, actionOperateType, providerItemKey, $newItem, actionCode, envViables);
            if ($result) {
                //$newRangeKey = _dataSource_AddItem(dataSource,_getobjectproperty($newItem,"SingleItem"),actionCode,envViables);
                const $newRangeKey = _dataSource_AddItem(dataSource, $newItem, actionCode, envViables); //modify 2022.01.30 支持GetParam功能后改进
                return (!(_isnullorempty($newRangeKey)));
            } else {
                return false;
            }
        }
        catch (ex) {
            //什么都不做,执行下个循环
            _showExceptionInfo(ex);
        }
    }
}

默认服务端脚本

//#RuiScriptSetting{"kind":"ES6"}
(ownerID, providerItemKey, newCompondItem_Value, aclItem, envViables) => {
    const $result = _compondValue_InsertToDB(newCompondItem_Value);
    if ($result) {
        return true;
    } else {
        return false;
    }
}

非全局action

非全局Action 表示 针对某条具体记录的 action,例如 针对某个订单, 有 收款、取消订单、指派订单 等action。默认会显示该条记录的 右面 非选择订单 不能勾选Global标志,表示非 非全局Action

操作类型为 包含字表修改ModifyWithDetail 的非全局Action 表示修改一条记录,同时修改 其所有字表信息 默认前端脚本

//#RuiScriptSetting{"kind":"ES6"}
(dataSource, ownerID, providerItemKey, hashKey, rangeKey, actionCode, actionName, actionOperateType, envViables) => {
    const $newItem = _dataSource_GetSingleItem(dataSource, hashKey, rangeKey, actionCode, envViables);

    let $isShouldWhile = true;
    while ($isShouldWhile) {
        try {
            const $result = _showAndEdit(actionName, ownerID, actionOperateType, providerItemKey, $newItem, actionCode, envViables);
            if ($result) {
                //$count = _dataSource_UpdateItem(dataSource,_getobjectproperty($newItem,"SingleItem"),actionCode,envViables)
                const $count = _dataSource_UpdateItem(dataSource, $newItem, actionCode, envViables);//modify 2022.01.30 支持GetParam功能后改进
                return ($count > 0);
            }
            else {
                return false;
            }
        }
        catch (ex) {
            //什么都不做,执行下个循环
            _showExceptionInfo(ex);
        }
    }
}

默认服务端脚本

//#RuiScriptSetting{"kind":"ES6"}
(ownerID, providerItemKey, oldFieldValue, newCompondItem_Value, aclItem, envViables) => {
    const $result = _compondValue_SaveToDB(newCompondItem_Value);
    if ($result) {
        return true;
    } else {
        return false;
    }
}

操作类型为 不包含字表修改ModifyWithoutDetail 的非全局Action 唯一不同点:服务端多携带一个参数oldFieldValue 表示修改一条记录,但是不修改字表信息 默认前端脚本

  //等同于 ModifyWithDetail 的前端脚本

默认服务端脚本

//等同于 ModifyWithDetail 的前端脚本,因为 ES6语法支持 根据参数名匹配参数,所以 可以直接使用同样的脚本

操作类型为 删除 的非全局Action 表示删除一条记录 默认前端脚本

//#RuiScriptSetting{"kind":"ES6"}
(dataSource, ownerID, providerItemKey, hashKey, rangeKey, actionCode, actionName, actionOperateType, envViables) => {
    const $result = _confirm("Do you want to delete " + " [" + rangeKey + "] ?");
    if ($result) {
        $count = _dataSource_DeleteItem(dataSource, actionCode, hashKey, rangeKey, envViables);
        return ($count > 0);
    } else {
        return false;
    }
}

默认服务端脚本


操作类型为 查询 的非全局Action 表示 查询 一条记录 的明细 默认前端脚本

//#RuiScriptSetting{"kind":"ES6"}
(dataSource, ownerID, providerItemKey, hashKey, rangeKey, actionCode, actionName, actionOperateType, envViables) => {
    const $newItem = _dataSource_GetSingleItem(dataSource, hashKey, rangeKey, actionCode, envViables);
    _showAndEdit(actionName, ownerID, actionOperateType, providerItemKey, $newItem, actionCode, envViables);

    return true;
}

默认服务端脚本

没有后端脚本

显示操作日志 显示一条记录的 日志,前提是 该表设置了 显示日志 前端脚本

//#RuiScriptSetting{"kind":"ES6"}
(dataSource, ownerID, providerItemKey, hashKey, rangeKey, actionCode, actionName, actionOperateType, envViables) => {
    const $where = [new Condition("TableName", "=", providerItemKey), new Conditon("OperItemRangeKey", "=", rangeKey)];
    const $listResult = _queryItemsListResult(ownerID, "PUBACTIONLOG", $where);
    _showResult("操作日志", $listResult);

    return true;
}

默认服务端脚本 无 服务端脚本

Action中的事务处理

默认action中的事务处理为 自动事务处理模式,即:

  1. 平台开启事务
  2. 执行 服务端脚本
  3. 如果 没有发生异常且返回值不是false, 则提交事务
  4. 否则 回滚事务

当脚本不需要 平台自动处理事务时,则 可以在 action属性中的 事务管理类型 中选择 Manual手工模式 则脚本执行方式为:

  1. 执行 服务端脚本

在脚本中,开发者可以自己控制事务处理,示例代码如下

//#RuiScriptSetting{"kind":"ES6"}
//其他代码
_dbBeginTrans();  //开始事务
try
{
    //业务处理代码
    _dbCommitTrans();  //执行成功,提交事务
}
catch(ex)
{
    _dbRollbackTrans();  //执行失败,回滚事务
    //其他代码     
}
//其他代码

query查询

系统仅支持所有查询条件均为 add的查询,也就是说where条件中,系统不支持 or,仅仅支持and 如果需要查询条件为or的,则可以采用如下两种方式:

分成两个查询 将需要or的条件 修改为 针对一个虚拟字段的 查询条件,或者使用 in 查询

代码中查询条件的构造

//#RuiScriptSetting{"kind":"ES6"}
const $where = [];
$where.push(new Condition("字段GuidKey","=",值));
$where.push(new Condition("MyName","=","张三"));
const $listResult = _queryItemsListResult(ownerID,"TableCode",$where);

条件判断类型表

类型别名意义
GreatThan>大于
GreatAndEqual>=大于等于
LessThan<小于
LessAndEqual<=小于等于
Equal=等于
NotQqual!=不等于
StringLeftLike以某个字符串开始
StringRightLike以某个字符串结束
StirngBothLike包含某个字符串
IsNullOrEmptyValuenull或者空字符串
IsNotNULLEitherEmptyValue非 null同时也不是空字符串
IsNULLnull
IsNotNull非 null
In包含 逗号隔开的字符串
NotIn不包含 逗号隔开的字符串

trigger触发器

trigger类型

trigger 脚本

AutoEdit触发器

AutoEdit触发器可以在针对某表记录 获取之后和发送给客户端之前,进行修改操作 触发时机:

  • 黄色背景框中左右过滤条件的 记录创建之后 执行表触发器
  • 新建或者修改某条记录,系统获取到新记录之后,执行表触发器

触发时间点:

  • before
  • after

beforeAutoEdit触发器示例:before触发器的脚本没有params参数

//#RuiScriptSetting{"kind":"ES6"}
( ownerID, providerItemKey,item, dataSource, isNewCreate, envVariables)=>
{
    //...
}

after AutoEdit 触发器脚本示例和说明:(一般常用afterAutoEdit触发器)

//#RuiScriptSetting{"kind":"ES6"}
//收款支付表的 触发器
( ownerID, providerItemKey,item, dataSource, isNewCreate, envVariables, paramList)=>
{
    cosnt $payPlanCode = item['12dff5a75b964419a389e7c1322c1881'];// 收款计划编号
    if (!(_isnullorempty($payPlanCode )))
    {
        const $moneyFieldCode =  _fromFieldReferenceCodeToFieldCode( ownerID, "435b9a60f1fa467795efa120b891ea3e");
          //如果是由 支付计划发起的,则固定 金额
          const $moneyParam  = paramList.find(g=>g.ParamCode == $moneyFieldCode);
          const $money = [435b9a60f1fa467795efa120b891ea3e:item];
          $moneyParam.MinValue = _to_str($money);
          $moneyParam.MaxValue = _to_str($money);
          $moneyParam.IsReadonly = true;
    }
    else
    {
        if(envVariables != null)  //add 2021.06.15 当作为查询条件黄色背景框出现的时候,此处没有 环境变量
        {
            const $triggerCallBackFunctionName = _8939bf4ce78346d5a8d4a980fb6c4713_GetAfterAutoEditTrigger(envVariables);

            if (!(_isnullorempty($triggerCallBackFunctionName)))
            {
                //调用这个回调函数,给业务系统执行自己触发器的机会,此时可以修改 字段的设置
                const $paramList =  [ownerID, providerItemKey,item, dataSource, isNewCreate, envVariables, paramList];
                _callScriptFunction($triggerCallBackFunctionName,$paramList);        
            }
        }
    }
}

表单引擎

权限

权限设计采用多层次设计方案,模块中先 通过权限组,将 一组 Menu和一组 Actions 定义到一个权限组中 然后,权限组可以授权给 Role,然后通过Role间接地授权到某个 FutuID,这样 某个FutuID就获得到了 一组 Menu和Action的权限

如下图所示:

App 模块入口

用户登录到 模块之后,系统会做如下操作:

  1. 通过用户的有权限的 menus,根据每个Menu的归属的 App,提取出 该用户 所有有权限的 Apps,例如 老板的账号登录,有权限的 App是 餐馆管理和客户点餐。 普通客户有权限的 App是客户点餐。 已经注册的外卖骑手 有权限的 App是 骑手
  2. 如果是有多个 App,则显示 一个 App列表给用户选择 决定进入 哪个 App,如果 只有一个 App,则转入下一步,直接进入App
  3. 系统根据App中配置的 PC入口点Menu和移动入口点Menu,根据当前是PC还是手机,自动选择一个 Menu,显示这个Menu对应的Page内容
  4. 如果App中没有定义 入口点Menu,则显示第一个Menu的内容
  5. 如果 App中的入口点Menu是不存在的(该Menu被删除或者没有权限),则 按照最传统的方式,显示出所有的 菜单

jobs

定时任务。

备注: 定时任务执行的时候,平台不做任何事务性处理,这个和action的服务端脚本是有区别的,原因是 因为 jobs一般是进行批量处理,系统不好进行事务性处理 如果定时任务脚本 需要事务处理,可以参考 上面章节中 事务处理 `javascript //#RuiScriptSetting{"kind":"ES6"} (ownerID,params)=> { //... }

<center>返回值意义</center>
<table style="font-size:13px" >
<tr style="background-color:lightgrey">
<th>返回值</th><th>意义</th>
</tr>
<tr>
<td>没有返回值或者返回null</td><td>执行成功</td>
</tr>
<tr>
<td>返回整型值</td><td>0:失败   <br />1:成功 <br />  2,成功但是携带提示信息,错误信息就是返回值</td>
</tr>
<tr>
<td>返回bool类型</td><td>true:成功<br />false:失败</td>
<tr>
<td>返回其他类型</td><td>失败。 失败原因为返回值.toString()</td>
</tr>
</table>

<b>Job中的信息输出</b>
Job中需要记录信息的时候,可以使用如下函数记录信息,该信息会被记录到 Job的执行日志中
```javascript
void _appendLog(string log)

jobs的执行结果

jobs会被调度定时执行,执行结果在系统的 定时任务执行结果表中,在管理界面可以查看

jobs的刷新

系统在启动的时候以及每到整点会进行jobs的刷新,刷新的结果也会 以任务名: refreshJobs的日志 存储在 jobs执行结果日志中,可以通过Jobs执行结果日志查看 定时刷新jobs的结果。如果可以访问服务端的话,也可以通过wildfly的console界面看到刷新结果

关于jobs的重要说明

  1. 因为jobs会被定时执行,所以 需要谨慎编写jobs的代码
  2. 因为jobs会被定时执行,所以 需要谨慎 设计 jobs的执行频率,建议执行时间放在非工作时间,例如夜间
  3. 不使用的 job可以 修改属性 Enable, 所有 disbaled的jobs不会被调度执行,已经被调度执行的disbaled的jobs也会被在下次刷新的时候,自动从 调度系统删除
  4. 如果job代码中需要进行事务处理,则需要开发者手工进行事务处理
  5. 定时 检查 job执行日志,发现异常情况,需要马上处理

数据流引擎

待完成