通过流程全局事件给业务主表回写 流程状态、流程环节和执行者

设计思路

  1. 在一个公共服务中创建 globalFlowEvent.g.process.trait 全局事件定义,监听流程的流转、回退、终止、删除、回收事件
  2. 在事件接口中,通过流程中记录的 dataUrl 自动给业务主表回写数据

设计约定

  1. 在主表中预留 flowStatus、activityName、activityLabel、executorName 字段
  2. 通过流程变量 flowWriteBack = true 声明当前流程需要应用自动回写规则

代码实现

在一个公共服务中创建 /model/service/main/globalFlowEvent.g.process.trait 全局事件定义,监听流程的流转、回退、终止、删除、回收事件

示例中的公共服务名是 flowcommon,如果是其他服务名,需要替换下面代码中 url 的服务名
注意 /model/service/service-meta-info/data 目录是否存在,没有需要在应用发布前手动创建

{
    "listeners": [{
            "action": "advanceProcessAction",
            "event": "after",
            "handler": "invokeHttpProcedure",
            "params": {
                "template": [{
                        "method": "POST",
                        "url": "/flowcommon/main/globalfloweventservice/globalflowevent",
                        "headers": {
                            "Accept": "application/json",
                            "Content-Type": "application/json"
                        },
                        "body": {
                            "process": "${process}",
                            "processLabel": "${processLabel}",
                            "activity": "${activity}",
                            "activityLabel": "${activityLabel}",
                            "operatorID": "${operatorID}",
                            "operatorCode": "${operatorCode}",
                            "operatorName": "${operatorName}",
                            "action": "${action}",
                            "dataUrl": "${dataUrl}",
                            "sData1": "${sData1}",
                            "activeAIs": "${activeAIs}",
                            "flowWriteBack": "${flowWriteBack}",
                            "flowWriteBackKeyField": "${flowWriteBackKeyField}",
                        }
                    }
                ]
            }
        }, {
            "action": "backProcessAction",
            "event": "after",
            "handler": "invokeHttpProcedure",
            "params": {
                "template": [{
                        "method": "POST",
                        "url": "/flowcommon/main/globalfloweventservice/globalflowevent",
                        "headers": {
                            "Accept": "application/json",
                            "Content-Type": "application/json"
                        },
                        "body": {
                            "process": "${process}",
                            "processLabel": "${processLabel}",
                            "activity": "${activity}",
                            "activityLabel": "${activityLabel}",
                            "operatorID": "${operatorID}",
                            "operatorCode": "${operatorCode}",
                            "operatorName": "${operatorName}",
                            "action": "${action}",
                            "dataUrl": "${dataUrl}",
                            "sData1": "${sData1}",
                            "activeAIs": "${activeAIs}",
                            "flowWriteBack": "${flowWriteBack}",
                            "flowWriteBackKeyField": "${flowWriteBackKeyField}",
                        }
                    }
                ]
            }
        }, {
            "action": "abortProcessAction",
            "event": "after",
            "handler": "invokeHttpProcedure",
            "params": {
                "template": [{
                        "method": "POST",
                        "url": "/flowcommon/main/globalfloweventservice/globalflowevent",
                        "headers": {
                            "Accept": "application/json",
                            "Content-Type": "application/json"
                        },
                        "body": {
                            "process": "${process}",
                            "processLabel": "${processLabel}",
                            "activity": "${activity}",
                            "activityLabel": "${activityLabel}",
                            "operatorID": "${operatorID}",
                            "operatorCode": "${operatorCode}",
                            "operatorName": "${operatorName}",
                            "action": "${action}",
                            "dataUrl": "${dataUrl}",
                            "sData1": "${sData1}",
                            "activeAIs": "${activeAIs}",
                            "flowWriteBack": "${flowWriteBack}",
                            "flowWriteBackKeyField": "${flowWriteBackKeyField}",
                        }
                    }
                ]
            }
        }, {
            "action": "deletePIAction",
            "event": "after",
            "handler": "invokeHttpProcedure",
            "params": {
                "template": [{
                        "method": "POST",
                        "url": "/flowcommon/main/globalfloweventservice/globalflowevent",
                        "headers": {
                            "Accept": "application/json",
                            "Content-Type": "application/json"
                        },
                        "body": {
                            "process": "${process}",
                            "processLabel": "${processLabel}",
                            "activity": "${activity}",
                            "activityLabel": "${activityLabel}",
                            "operatorID": "${operatorID}",
                            "operatorCode": "${operatorCode}",
                            "operatorName": "${operatorName}",
                            "action": "${action}",
                            "dataUrl": "${dataUrl}",
                            "sData1": "${sData1}",
                            "activeAIs": "${activeAIs}",
                            "flowWriteBack": "${flowWriteBack}",
                            "flowWriteBackKeyField": "${flowWriteBackKeyField}",
                        }
                    }
                ]
            }
        }, {
            "action": "withdrawTaskAction",
            "event": "after",
            "handler": "invokeHttpProcedure",
            "params": {
                "template": [{
                        "method": "POST",
                        "url": "/flowcommon/main/globalfloweventservice/globalflowevent",
                        "headers": {
                            "Accept": "application/json",
                            "Content-Type": "application/json"
                        },
                        "body": {
                            "process": "${process}",
                            "processLabel": "${processLabel}",
                            "activity": "${activity}",
                            "activityLabel": "${activityLabel}",
                            "operatorID": "${operatorID}",
                            "operatorCode": "${operatorCode}",
                            "operatorName": "${operatorName}",
                            "action": "${action}",
                            "dataUrl": "${dataUrl}",
                            "sData1": "${sData1}",
                            "activeAIs": "${activeAIs}",
                            "flowWriteBack": "${flowWriteBack}",
                            "flowWriteBackKeyField": "${flowWriteBackKeyField}",
                        }
                    }
                ]
            }
        }
    ]
}

创建服务 globalFlowEventService 和请求 globalFlowEvent

请求方法:post;请求参数:params(字符串、RequestBody);请求返回:字符串

package main.service;

import java.util.*;
import java.io.IOException;
import java.math.BigDecimal;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.justep.util.StringUtil;
import com.justep.util.net.ServiceUtil;

import antlr.StringUtils;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.data.domain.Page;
import main.model.*;
import main.dao.*;
import main.vo.*;

/* 约定的字段 */
/* flowStatus activityName activityLabel executorName */
/* flowStatus 审批中 审批通过 审批未通过 流程已删除 */

/* 流程环境变量 flowWriteBack true 标识需要流程回写 */
/* 流程环境变量 flowWriteBackKeyField 数据的主键字段名,默认值是fid,如果是其他字段,通过这个环境变量指定 */

@Service
public class GlobalFlowEventServiceUserService {

//系统生成代码,请不要修改或删除 <actionStart:globalFlowEvent>
    public String globalFlowEvent(String params) throws Exception {
        // 请添加你的业务代码
        System.out.println("globalFlowEvent: ***********************************");
        System.out.println(params);
/*
{
    "dataUrl": "/budget/main/dbrest/budget_change",
    "process": "/SA/wf/default/budget_change",
    "flowWriteBackKeyField": "${flowWriteBackKeyField}",
    "activity": "businessActivity1",
    "activeAIs": "[{\"process\":\"/SA/wf/default/budget_change\",\"task\":\"71E250E70EA441C9828A7659F5221C11\",\"activity\":\"businessActivity2\",\"executorFID\":\"/nOOWoc033yCxoeMn35C.ogn/nOOWJEw33n5pKS9RIZ0.dpt/nOOWQj633GHoMmAo0Vy.psm\",\"statusName\":\"正在处理\",\"executorNames\":\"文世弘\",\"taskName\":\"部门领导审批:费用预算调整单\",\"executorTasks\":[{\"process\":\"/SA/wf/default/budget_change\",\"task\":\"71E250E70EA441C9828A7659F5221C11\",\"activity\":\"businessActivity2\",\"executorFID\":\"/nOOWoc033yCxoeMn35C.ogn/nOOWJEw33n5pKS9RIZ0.dpt/nOOWQj633GHoMmAo0Vy.psm\",\"statusName\":\"正在处理\",\"taskName\":\"部门领导审批:费用预算调整单\",\"executorFName\":\"/起步科技/开发部/文世弘\",\"activityLabel\":\"部门领导审批\",\"status\":\"tesExecuting\"}],\"executorFName\":\"/起步科技/开发部/文世弘\",\"activityLabel\":\"部门领导审批\",\"status\":\"tesExecuting\"}]",
    "activityLabel": "${activityLabel}",
    "operatorCode": "wsh",
    "operatorName": "文世弘",
    "flowWriteBack": "true",
    "processLabel": "费用预算调整单",
    "action": "advanceProcessAction",
    "operatorID": "nOOWQj633GHoMmAo0Vy",
    "sData1": "C9E109FB51B0000160D4140AA6F31E1D"
}
*/
        JSONObject flowParams = JSONObject.parseObject(params);

        String flowWriteBack = flowParams.getString("flowWriteBack");
        if (!"true".equals(flowWriteBack)) return "{}";

        String flowWriteBackKeyField = flowParams.getString("flowWriteBackKeyField");
        if (flowWriteBackKeyField == null || flowWriteBackKeyField.startsWith("$")) flowWriteBackKeyField = "fid";

        String action = flowParams.getString("action");
        String dataUrl = flowParams.getString("dataUrl");
        String sData1 = flowParams.getString("sData1");

/*
[{
        "process": "/SA/wf/default/budget_change",
        "task": "71E250E70EA441C9828A7659F5221C11",
        "activity": "businessActivity2",
        "executorFID": "/nOOWoc033yCxoeMn35C.ogn/nOOWJEw33n5pKS9RIZ0.dpt/nOOWQj633GHoMmAo0Vy.psm",
        "statusName": "正在处理",
        "executorNames": "文世弘",
        "taskName": "部门领导审批:费用预算调整单",
        "executorTasks": [{
                "process": "/SA/wf/default/budget_change",
                "task": "71E250E70EA441C9828A7659F5221C11",
                "activity": "businessActivity2",
                "executorFID": "/nOOWoc033yCxoeMn35C.ogn/nOOWJEw33n5pKS9RIZ0.dpt/nOOWQj633GHoMmAo0Vy.psm",
                "statusName": "正在处理",
                "taskName": "部门领导审批:费用预算调整单",
                "executorFName": "/起步科技/开发部/文世弘",
                "activityLabel": "部门领导审批",
                "status": "tesExecuting"
            }
        ],
        "executorFName": "/起步科技/开发部/文世弘",
        "activityLabel": "部门领导审批",
        "status": "tesExecuting"
    }
]
*/

        JSONArray activeAIs = JSONObject.parseArray(flowParams.getString("activeAIs"));

        String flowStatus = "审批中";
        if ("abortProcessAction".equals(action)) {
            flowStatus = "审批未通过";
        } else if (("advanceProcessAction".equals(action) && activeAIs.size() == 0)) {
            flowStatus = "审批通过";
        } else if ("deletePIAction".equals(action)) {
            flowStatus = "流程已删除";
        }

        List<String> activityNames = new ArrayList<String>();
        List<String> activityLabels = new ArrayList<String>();
        List<String> executorNames = new ArrayList<String>();
        for (int i = 0; i < activeAIs.size(); i++) {
            JSONObject activeAI = activeAIs.getJSONObject(i);
            activityNames.add(activeAI.getString("activity"));
            activityLabels.add(activeAI.getString("activityLabel"));
            executorNames.add(activeAI.getString("executorNames"));
        }

        JSONObject fieldValues = new JSONObject();
        fieldValues.put(flowWriteBackKeyField, sData1);
        fieldValues.put("flowStatus", flowStatus);
        fieldValues.put("activityName", arr2str(activityNames));
        fieldValues.put("activityLabel", arr2str(activityLabels));
        fieldValues.put("executorName", arr2str(executorNames));
        writeBack(dataUrl, flowWriteBackKeyField, fieldValues);

        return "{}";
    }
//系统生成代码,请不要修改或删除 <actionEnd:globalFlowEvent>

    private void writeBack(String dataUrl, String keyField, JSONObject fieldValues) throws ClientProtocolException, IOException {
        /* 根据 dataUrl 获取 serviceName 和 apiPath,转换为内部服务访问地址 */
        String serviceName = getServiceName(dataUrl);
        String apiPath = getSevicePath(dataUrl);
        String url = ServiceUtil.getServiceUrl(serviceName) + apiPath + "?pk=" + keyField; 
        System.out.println(url);

        /* 通过 dbrest put 请求更新数据 */
        HttpPut httpPut = new HttpPut(url);
        httpPut.setHeader("Content-Type", "application/json");
        httpPut.setHeader("dbrest-resource", "class");

        StringEntity entity = new StringEntity(fieldValues.toJSONString(), "utf-8");
        entity.setContentEncoding("utf-8");
        entity.setContentType("application/json");
        httpPut.setEntity(entity);

        CloseableHttpClient closeableHttpClient = HttpClients.createDefault();
        closeableHttpClient.execute(httpPut);
    }

    private String getServiceName(String dataUrl) {
        return dataUrl.substring(1, dataUrl.indexOf("/", 1));
    }

    private String getSevicePath(String dataUrl) {
        return dataUrl.substring(dataUrl.indexOf("/", 1));
    }

    private String arr2str(List<String> arr) {
        StringBuffer sb = new StringBuffer();
        for (String str : arr) {
            if (sb.length() > 0) {
                sb.append(",");
            }
            sb.append(str);
        }
        return sb.toString();
    }
}

部署使用

  1. 公用服务发布,服务注册发现
  2. 企业工作流服务重启(注意必须重启后流程全局事件才生效),在企业工作流的数据库 sa_processtrait 表中会找到 trait 的数据
  3. 对于同一个租户中,其他业务服务中需要应用全局流程回写规则的业务流程
    • 在主表中预留 flowStatus、activityName、activityLabel、executorName 字段,考虑到流程可能执行者过多,executorName 的字段类型建议为长文本
    • 通过流程变量 flowWriteBack = true 声明当前流程需要应用自动回写规则
    • 如果业务主表的主键字段不是 fid,通过流程变量 flowWriteBackKeyField 指定
  4. 注意:其他业务服务需要发布后从正式门户运行才能正确回写数据

results matching ""

    No results matching ""

    results matching ""

      No results matching ""