通过流程全局事件给业务主表回写 流程状态、流程环节和执行者
设计思路
- 在一个公共服务中创建 globalFlowEvent.g.process.trait 全局事件定义,监听流程的流转、回退、终止、删除、回收事件
- 在事件接口中,通过流程中记录的 dataUrl 自动给业务主表回写数据
设计约定
- 在主表中预留 flowStatus、activityName、activityLabel、executorName 字段
- 通过流程变量 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();
}
}
部署使用
- 公用服务发布,服务注册发现
- 企业工作流服务重启(注意必须重启后流程全局事件才生效),在企业工作流的数据库 sa_processtrait 表中会找到 trait 的数据
- 对于同一个租户中,其他业务服务中需要应用全局流程回写规则的业务流程
- 在主表中预留 flowStatus、activityName、activityLabel、executorName 字段,考虑到流程可能执行者过多,executorName 的字段类型建议为长文本
- 通过流程变量 flowWriteBack = true 声明当前流程需要应用自动回写规则
- 如果业务主表的主键字段不是 fid,通过流程变量 flowWriteBackKeyField 指定
- 注意:其他业务服务需要发布后从正式门户运行才能正确回写数据