Skip to content

培训视频

1. 何为效能

即风险防控。

基于流程与业务运转,用于监控当前业务流程是否有在时间期限内进行工作。

会给需要监控的流程设定预警或超期时间,若办理时间触及设定时间,则给用户发送及时办理提醒。

场景还原:

2023-02-28给张三4个工作日,第2天提醒张三只剩2个工作日(功能预警计算且发短信提醒),第5天提醒张三已经超期(功能超期计算和发短信提醒超期),

张三请假但没人接手要求停止计算时间,也可能其他原因(功能暂缓),回来上班后(功能取消暂缓),以上是根据场景方便理解效能功能

2. 后台管理模块

2.1. 工作时间

工作时间

配置字段说明:

  • 开始时间:该工作时间方案开始执行日期

  • 截止时间:该工作时间方案停止执行日期

  • 上午/下午开始/结束时刻:这四项填写单位为分钟,每日从 0 点开始起算。

例如:上午开始时刻为 480,意为早上 8 点上班(8*60)。

工作时间总长 420,意为,一天的工作时间为 7 小时(7*60)

录入说明:

  • 上午上班,下午请假:下午2个时刻都填写上午结束时刻,例如510,960,960,960
  • 上午请假,下午上班:上午2个时刻都填写下午开始时刻,例如960,960,960,1050
  • 总时长为0:工作时长为0,需到节假日配置去设置

2.2. 节假日配置

点击每个月右上角的【设置】按钮,可对每月的节假日日期进行配置与修改。

操作简单,不做赘述。

image-20220715164339182

image-20220715164355845

2.3. 效能方案配置

只有【是否接入效能】为是的流程会显示。

每个流程可以有很多种方案,但只有一个有效,即只有一个的【是否启动】为开启状态。

2.3.1. 表单配置

image-20220715165100252

可对流程以及流程节点进行详细的效能配置。

  • 预警时间、超期时间

配置单位为分钟。例如:预警时间2520,意为预警天数为 6 天(60min * 7h * 6d

  • 条件表达式

因为一个流程对应多个业务,不同的业务有可能需要走不同的效能。所以当同一流程的不同业务需要走不同的效能配置时,就需要用到条件表达式。

条件表达式的编写使用规则,请看 【4. 表达式】 项。

2.4. 风险等级管理

主要为等级详细内容。

image-20220715170801937

  • 发送频率:消息提醒发送频率

第一次发送

  1. 检测到且符合预警条件就发送 --预警:>=预警时间 and <超期时间

  2. 检测到且符合超期条件就发送 --超期:>=超期

第二次发送 1. 若第一次是上午发送 --第二次下午及以后的第一个工作时间发送(例如,第一次的下午放假,那就下个工作日的早上,如果还放假,那就下个工作日的下午)

  1. 若第一次是下午 --第二次是下一个工作日的早上或下午(早上放假则就下午再发送)
  • 风险等级:预警、超期、抄送

  • 等级类型:这一项其实对应的是风险短信发送模板代码

效能提供给短信模板的字段信息配置方法为 ${ 字段名 },例:

字段名注释
realName用户真实姓名
bizName业务名称
flowName流程名称
nodeName环节名称
deadLine流程到期时间
nodeDeadLine环节到期时间
diffTimeCn剩余/超期时间
  • 条件表达式单词:NOW_DATE系统当前时间,OVERDUE_DATE超期时间,WARNING_DATE预警时间

  • 条件表达式:用于判断什么时候发送消息提醒。如果表达式为空,则默认一到时间节点就发送消息。

风险等级的条件表达式使用规则请看 【4. 表达式】 项。

例如:

0 < ':OVERDUE_DATE' - ':NOW_DATE' <= 1440 ,表示超期一天之内的,消息提醒配置内容。

':OVERDUE_DATE' - ':NOW_DATE' > 1440,表示为超期一天后,消息提醒的配置内容。

其中,1440 单位为分钟(60 * 24),表示一天的时间。

当风险等级,等级类型一样的时,非空表达式与空表达式同时成立,这个时候只会按非空表达式发送,空表达式的不再发送,不然用户会同时收到多条一样的短信

2.5. 抄送人管理

配置入口在效能方案配置里配置,主要用于,风控消息提醒抄送人的配置。

image-20220715171815890

可对每个流程和流程中的节点进行抄送人的配置。

当前只支持选择指定用户,暂不支持角色选择、部门选择。后续可优化。

2.6. 效能日志

image-20220715172058952

效能操作日志。

不做赘述。

3. HTTP 调用接口

3.0. HTTP 请求代码示例

调用注意事项:

  • 调用方式皆为 POST
  • ContentType类型为 application/json
  • 参数以json字符串格式传入

请求代码示例:

java
/**
 * 发送http请求
 *
 * @param url   请求地址
 * @param param 请求参数
 * @return 请求响应
 * @throws Exception 抛出异常
 */
public static String httPost(String url, Object param) throws Exception {
    return httpPostWithNoHandle(
            url,
            null,
            null,
            param,
            ContentType.APPLICATION_JSON.getMimeType(),
            ContentType.APPLICATION_JSON.getCharset().toString(),
            false);
}


/**
 * 不处理异常的http post 请求
 *
 * @param url           请求地址
 * @param token         token
 * @param headers       请求头
 * @param params        请求参数
 * @param contentType   contentType类型
 * @param encoding      contentEncoding类型
 * @param isReturnEmpty 是否返回空值
 * @return 返回结果
 * @throws Exception 抛出异常
 */
public static String httpPostWithNoHandle(String url,
                                          String token,
                                          JSONObject headers,
                                          Object params,
                                          String contentType,
                                          String encoding,
                                          boolean isReturnEmpty) throws Exception {
    HttpPost httpPost = new HttpPost(getUrl(url));
    //如果 headers 不为空 循环遍历JSONObject,添加请求头
    if (headers != null) {
        for (String str : headers.keySet()) {
            httpPost.addHeader(str, headers.get(str).toString());
        }
    }


//token
    if (StringUtil.isNotEmpty(token)) {
        httpPost.setHeader(Constants.AUTHORIZATION, token);
    }


//创建http客户端
    CloseableHttpClient client = HttpClients.createDefault();
    if (params != null) {
        /*params是需要序列化的对象*/
        String s = JSONObject.toJSONString(params);
        //使用二进制数据将序列化好的  gson数据转换成 二进制流
        ByteArrayEntity byteArrayEntity = new ByteArrayEntity(s.getBytes(encoding));
        byteArrayEntity.setContentEncoding(encoding);
        byteArrayEntity.setContentType(contentType);
        httpPost.setEntity(byteArrayEntity);
    }


HttpResponse resp = client.execute(httpPost);
    if (ActionResultCode.Success.getCode().equals(resp.getStatusLine().getStatusCode())) {
        return EntityUtils.toString(resp.getEntity(), SystemConstant.UTF_8);
    }
    //返回响应回来的数据
    return isReturnEmpty ? null : resp.toString();
}

3.1. 设置效能所需数据

接口:

http://xxx:xxx/api/efficacy/setBizParam

类型:post

入参:

json
{
  "bizType": "业务类型",
  "bizId": "业务id",
  "bizName": "业务名称",
  "flowTaskId": "流程任务id",
  "paramJson": "参数JSON,以key-value形式存储"
}

入参示例:

json
{
  "bizType": "0101",
  "bizId": "24eea52ee5wf48b4a957dadf19af8b48",
  "bizName": "海籍调查成果确认测试类型",
  "flowTaskId": "24eea52ee52f48b4a957dadf19af8b40",
  "paramJson": "{\"SFTJBS_TD\": 1}"
}

接口调用测试用例

java
@PostMapping("/setBizParam")
@ApiOperation(value = "设置效能所需参数")
public ActionResult<Object> setBizParam(@RequestBody EffParametersEntity parametersEntity) throws Exception {
    return ActionResult.success(httPost("http://172.16.112.92:8881/api/efficacy/setBizParam", parametersEntity));
}

【注:需要与效能初始化接口配置使用。】

3.2. 效能初始化

接口:发起流程的时候调用, 产生与流程任务对应效能任务和第一个节点效能实例化

http://xxx:xxx/api/efficacy/init

类型post

入参

json
{
  "flowEngineId": "流程引擎id",
  "flowTaskId": "流程任务id",
  "flowTaskName": "流程名称",
  "startDate": "开始日期",
  "flowNodeList": [
    {
      "nextNodeId": "流程节点id",
      "nextNodeCode": "流程节点code",
      "nextNodeName": "流程节点名称",
      "nextCustomNodeCode": "流程节点自定义编码"
    }
  ]
}

示例

json
{
  "flowEngineId": "94507d5ec3984d9a868b2778cb99ded2",
  "flowTaskId": "24eea52ee52f48b4a957dadf19af8b40",
  "flowTaskName": "海籍调查成果确认测试流程",
  "startDate": "2022-06-27 09:01:47",
  "flowNodeList": [
    {
      "nextNodeId": "4rlMS21",
      "nextNodeCode": "testCode",
      "nextNodeName": "派件",
      "nextCustomNodeCode": "PAI_JIAN"
    }
  ]
}

接口调用测试用例

java
@PostMapping("/init")
@ApiOperation(value = "效能初始化")
public ActionResult<Object> init(@RequestBody EffParamModel model) throws Exception {
    return ActionResult.success(httPost("http://172.16.112.92:8881/api/efficacy/init", model));
}

【注:需要与设置效能参数接口配合使用。初始化参数默认过期时间为一天。】

3.3. 节点效能初始化

接口:节点流转调用,用于计算流转出去的下个节点开始算效能

http://xxx:xxx/api/efficacy/initNode

类型post

入参

json
{
  "flowTaskId": "流程任务id",
  "flowNodeList": [
    {
      "nextNodeId": "流程节点id",
      "nextNodeCode": "流程节点code",
      "nextNodeName": "流程节点名称",
      "nextCustomNodeCode": "流程节点自定义编码",
      "currentNodeId": "前一个节点id"
    }
  ]
}

示例

json
{
  "flowTaskId": "24eea52ee52f48b4a957dadf19af8b40",
  "flowNodeList": [
    {
      "nextNodeId": "NZwMS21",
      "nextNodeCode": "testCode2",
      "nextNodeName": "红笑天/181047",
      "nextCustomNodeCode": "twoNode",
      "currentNodeId": "4rlMS21"
    }
  ]
}

接口调用测试用例

java
@PostMapping("/initNode")
@ApiOperation(value = "效能节点初始化")
public ActionResult<Object> initNode(@RequestBody EffNodeParam model) throws Exception {
    return ActionResult.success(httPost("http://172.16.112.92:8881/api/efficacy/initNode", model));
}

3.4. 暂缓效能

接口:相等于暂停键的功能,适用场景例如当用户请假没有人接手的时候,起到暂停计算效能的作用

http://xxx:xxx/api/efficacy/pauseCal

类型post

入参

json
{
  "flowTaskId": "流程任务id"
}

接口调用测试用例

java
@PostMapping("/pauseCal")
@ApiOperation(value = "效能暂缓")
public ActionResult<Object> pauseCal(@RequestBody EffPauseModel model) throws Exception {
    return ActionResult.success(httPost("http://172.16.112.92:8881/api/efficacy/pauseCal", model));
}

3.5. 取消暂缓效能

接口:取消暂停的功能,恢复继续计算效能

http://xxx:xxx/api/efficacy/cancelPauseCal

类型post

入参

json
{
  "flowTaskId": "流程任务id"
}

接口调用测试用例

java
@PostMapping("/cancelPauseCal")
@ApiOperation(value = "取消效能暂缓")
public ActionResult<Object> cancelPauseCal(@RequestBody EffPauseModel model) throws Exception {
    return ActionResult.success(httPost("http://172.16.112.92:8881/api/efficacy/cancelPauseCal", model));
}

3.6. 效能删除

接口:当流程被删除的时候,也需删除效能的时候可以使用

http://xxx:xxx/api/efficacy/deleteEfficacy

类型post

入参

json
flowTaskIds: [
    "xxx",
    "xxx"
]

接口调用测试用例

java
@PostMapping("/deleteEfficacy")
@ApiOperation(value = "删除效能")
public ActionResult<Object> deleteEfficacy(@RequestBody EffDelModel model) throws Exception {
    return ActionResult.success(httPost("http://172.16.112.92:8881/api/efficacy/deleteEfficacy", model));
}

3.7. 撤回到开始节点效能处理

接口:驳回到初始化节点的时候使用,将已经产生的节点效能记录结束

http://xxx:xxx/api/efficacy/revokeToStart

类型post

入参

json
{
  "flowTaskId": "流程任务id"
}

接口调用测试用例

java
@PostMapping("/revokeToStart")
@ApiOperation(value = "撤回到开始节点效能处理")
public ActionResult<Object> revokeToStart(@RequestBody EffRevokeModel model) throws Exception {
    return ActionResult.success(httPost("http://172.16.112.92:8881/api/efficacy/revokeToStart", model));
}

3.8. 效能终止

接口:与效能删除的区别是,删除是真删除不再显示,终止是类似禁用,流程可再显示

http://xxx:xxx/api/efficacy/stop

类型post

入参

json
{
  "flowTaskId": "流程任务id"
}

接口调用测试用例

java
@PostMapping("/stop")
@ApiOperation(value = "效能终止")
public ActionResult<Object> stop(@RequestBody EffStopModel model) throws Exception {
    return ActionResult.success(httPost("http://172.16.112.92:8881/api/efficacy/stop", model));
}

3.9. 效能计算(定时器调用)

接口:计算非结束和非挂起的效能记录的是否预警,是否超期,已花费时间

http://xxx:xxx/api/efficacy/stop

类型post

入参:无

接口调用测试用例

java
@PostMapping("/calculate")
@ApiOperation(value = "效能计算")
public ActionResult<Object> calculate() throws Exception {
    return ActionResult.success(httPost("http://172.16.112.92:8881/api/efficacy/calculate", null));
}

4. 表达式

其中涉及到了条件表达式的编写规则,以及后台代码表达式解析器的扩展。

4.1. 使用规则

4.1.1. 一般规则

  • 变量需要以 ': 开头,以 ' 结尾,也就是说需要用 ':' 包裹起来,比如,变量 PD 表示为:':PD'

  • 数字不需要使用单引号,但字符串需要使用单引号,时间表示格式例如: '2022-02-09 00:00:00'

  • 逻辑语法支持 + , - , * , / , < , > , <= , >= , == , != , % , mod【取模等同于 % 】, ++ , -- , && , || , and , or , like

举几个例子:

java
  // 示例1,下面的表达式等同于 ===> "':PX'==23 or ':PE'=='ddd'"
  String express1 = "':PX'==23 || ':PE'=='ddd'";
  // 示例2
  String express2 = "':PD'-':PX'<=5 and ':PE' like 'ddd'";
  // 示例3
  String express3 = "':PD'+':PX'!=10 && ':PE'!='aaa'";

4.1.2. 风险等级配置特殊规则

  • 当前时间规定变量:':NOW_DATE'
  • 预警时间规定变量:':WARNING_DATE'
  • 超期时间规定变量:':OVERDUE_DATE'
  • 时间对比都是以分钟计。如:':WARNING_DATE' - ':NOW_DATE' < 1440 (24 * 60 = 1440)

4.2. 表达式解析器

条件表达式解析器采用约定大于配置思想,对表达式有一定的使用规则。

解析引擎使用的是阿里开发的QLExpress组件,优点包括线程安全、高效执行等。

4.2.1. 表达式编写规则

4.2.1.1. 一般规则
  • 变量需要以 ': 开头,以 ' 结尾,也就是说需要用 ':' 包裹起来,比如,变量 PD 表示为:':PD'

  • 数字不需要使用单引号,但字符串需要使用单引号,时间表示格式例如: '2022-02-09 00:00:00'

  • 逻辑语法支持 + , - , * , / , < , > , <= , >= , == , != , % , mod【取模等同于 % 】, ++ , -- , && , || , and , or , like

举几个例子:

java
  // 示例1,下面的表达式等同于 ===> "':PX'==23 or ':PE'=='ddd'"
  String express1 = "':PX'==23 || ':PE'=='ddd'";
  // 示例2
  String express2 = "':PD'-':PX'<=5 and ':PE' like 'ddd'";
  // 示例3
  String express3 = "':PD'+':PX'!=10 && ':PE'!='aaa'";
4.2.1.2. 风险等级配置特殊规则
  • 当前时间规定变量:':NOW_DATE'
  • 预警时间规定变量:':WARNING_DATE'
  • 超期时间规定变量:':OVERDUE_DATE'
  • 时间对比都是以分钟计。如:':WARNING_DATE' - ':NOW_DATE' < 1440 (24 * 60 = 1440)

4.2.2. 使用示例

数值类型与字符串类型使用:

java
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date time = df.parse("2022/6/13 15:25:58");


String express = "':PD'+':PX'!=10 and ':PE'=='ddd' and ':TIME' > ':CURR_DATE'";
Map<String, Object> map = new HashMap<>();
map.put("PD", 6);
map.put("PX", 2);
map.put("PE", "DDD");
map.put("TIME", time);
map.put("CURR_DATE", new Date());
// 结果为false
System.out.println(Expression.boolExecute(express, map));

注意:

  • 日期类型如果要做加减法操作必须转换为数值类型(时间戳、年、月、日、时、分、秒等),日期类型不能直接做加减法。需要如何计算请使用者根据自身情况自定义。

4.2.3. 参数传递规则

条件表达式解析器类:Expression

逻辑判断结果输出类:

java
/**
 * 根据条件表达式和对应参数进行条件判断
 *
 * @param express  条件表达式
 * @param paramMap 参数列表,key为参数名称,value为参数值
 * @return 判断结果
 * @throws Exception 抛出异常
*/
static Boolean boolExecute(String express, Map<String, Object> paramMap) throws Exception

4.2.4. 扩展操作符

编写示例:

java
// 对 '=' 这个操作符进行重写
@ExExpressReplaceOperator("=")
public class StrEqualsOperator extends Operator {


@Override
    public Object executeInner(Object[] objects) {
        // ... 操作符运算逻辑
    }
}


// 扩展新增 '*.' 这个操作符
@ExExpressAddOperator("*.")
public class StrEqualsOperator extends Operator {


@Override
    public Object executeInner(Object[] objects) {
        // ... 操作符运算逻辑
    }
}