编写版本:v3.5.3
适用版本:所有版本
流程设置-前后置事件脚本
什么是事件脚本
事件脚本是针对流程任务创建或完成的处理逻辑进行增强,如普通任务创建、会签任务创建、任务完成等动作的逻辑增强;
前置事件脚本,即在普通任务创建、会签任务创建逻辑后执行,代码切入点
com.lc.ibps.bpmn.activiti.ext.listener.task.AbstractTaskListener.exeEventScript(BpmDelegateTask);后置事件脚本,即在任务完成动作逻辑后执行,代码切入点
com.lc.ibps.bpmn.activiti.ext.listener.task.AbstractTaskListener.exeEventScript(BpmDelegateTask);脚本内容是groovy脚本,如:
import java.util.HashMap; import java.util.Map; Map<String, String> params = new HashMap<>(); params.put("ming_cheng_", "id_"); params.put("shu_liang_", "ku_cun_"); businessScript.syncNumberDataBySql(businessKey_, "t_enterdetail", "parent_id_", "ming_cheng_", "t_consumablesbasic", "id_", params, true);
事件脚本如何使用
- 在流程设置前后置事件脚本中填写脚本内容即可,如:
import java.util.HashMap;
import java.util.Map;
Map<String, String> params = new HashMap<>();
params.put("ming_cheng_", "id_");
params.put("shu_liang_", "ku_cun_");
businessScript.syncNumberDataBySql(businessKey_, "t_enterdetail", "parent_id_", "ming_cheng_", "t_consumablesbasic", "id_", params, true);脚本常用逻辑示例
脚本语言是Groovy,它可以理解为java的一个动态方法,我们只需要编写方法体内容即可
它可以调用到任何注入到spring容器中的bean,写对beanId就可以
语法就是java语法,我们在一个java方法内怎么写代码这里就这么写
里面内置的可用对象:
- cmd,流程命令对象,可以获取业务数据、跳转节点
- [业务对象编码],业务对象实体,可以获取或修改业务字段值
- 其他注入到Spring容器的Bean,如:cscript,通用脚本、bpmnScript,流程相关脚本、businessScript,业务相关脚本、jdbcScript,数据库操作相关脚本等
常用Bean列表
| BeanID | Bean说明 | 使用文档 |
|---|---|---|
| cmd | IBPS流程执行对象(任务、实例) | 在这里http://doc.bpmhome.cn/docs/ibps_v3_develop/ActionCmd |
| execution | Activiti任务执行对象 | 在这里http://doc.bpmhome.cn/docs/ibps_v3_develop/BpmDelegateTask |
| mybatisTemplateProvider | SQL执行对象(基于Mybatis,事务一致) | 在这里http://doc.bpmhome.cn/docs/ibps_v3_develop/MybatisTemplateProvider |
| jdbcScript | SQL脚本对象(基于JdbcTemplate,事务不一致) | 在这里http://doc.bpmhome.cn/docs/ibps_v3_develop/JdbcScript |
| bpmnScript | 流程脚本对象 | 在这里http://doc.bpmhome.cn/docs/ibps_v3_develop/BpmnScript |
| businessScript | 业务脚本对象 | 在这里http://doc.bpmhome.cn/docs/ibps_v3_develop/BusinessScript |
| cscript | 通用脚本对象 | 在这里http://doc.bpmhome.cn/docs/ibps_v3_develop/CommonScript |
| thridServiceScript | 第三方服务脚本对象 | 在这里http://doc.bpmhome.cn/docs/ibps_v3_develop/ThridServiceScript |
| validationScript | 校验脚本对象 | 在这里http://doc.bpmhome.cn/docs/ibps_v3_develop/ValidationScript |
说明:功能操作使用说明,移步地址:Groovy脚本使用说明!
如何获取业务数据
- 如何获取业务数据
String busData = cmd.getBusData(); //或者直接使用业务对象编码调用getData()方法获取 // [业务对象编码].get("字段名") - 业务数据需要调用时设置到请求参数中,如果是外部url表单方式需要按照前端编写getFormData方法给接口返回表单数据;
如何获取动作类型
- 获取方式
// 枚举详见com.lc.ibps.bpmn.api.constant.NodeStatus String actionName = cmd.getActionName(); - 使用示例,根据动作类型确定如何修改业务数据
import com.lc.ibps.bpmn.api.constant.NodeStatus; String actionName = cmd.getActionName(); if(NodeStatus.AGREE.getKey().equals(actionName)) { // 执行同意动作该执行什么逻辑 // 如:将业务数据中的状态改为“审批中” } else if(NodeStatus.OPPOSE.getKey().equals(actionName)) { // 执行反对动作该执行什么逻辑 // 如:将业务数据中的状态改为“审批不通过” }
如何获取界面或接口传入下一个节点ID
最终体现都是接口参数传入,详情请查阅:
获取接口指定的节点
String destination = cmd.getDestination();使用示例,根据节点确定如何修改业务数据
String destination = cmd.getDestination(); if("End_Event_ssgu7".equals(destination)) { // 跳转到结束节点该执行什么逻辑 // 如:将业务数据中的状态改为“审批不通过” } else if("Activiti_jfsjh&&^".equals(destination)) { // 跳转到下一个审批节点该执行什么逻辑 // 如:将业务数据中的状态改为“审批中” }
获取当前节点审批人
仅在节点
前置事件可用
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.lc.ibps.base.core.util.BeanUtils;
import com.lc.ibps.bpmn.api.constant.BpmConstants;
import com.lc.ibps.bpmn.api.model.delegate.BpmDelegateTask;
import com.lc.ibps.bpmn.api.model.identity.BpmIdentity;
List<BpmIdentity> executors = execution.getExecutors();
if (BeanUtils.isEmpty(executors)) {
executors = cmd.getBpmIdentities().get(execution.getTaskDefinitionKey());
}
if (BeanUtils.isNotEmpty(executors)) {
return executors;
}
BpmIdentity taskExecutor = (BpmIdentity) execution.getVariable(BpmConstants.ASIGNEE);
if (BeanUtils.isEmpty(taskExecutor)) {
return Collections.emptyList();
}
executors = new ArrayList<BpmIdentity>();
executors.add(taskExecutor);获取流程图下一个节点(v3.5.1以下)
仅在
后置事件可用
下一个节点是结束节点的后置事件不可用
import java.util.Optional;
import com.lc.ibps.bpmn.persistence.entity.BpmInstPo;
import java.util.List;
import com.lc.ibps.bpmn.api.model.node.IBpmNodeDefine;
import com.lc.ibps.bpmn.api.service.BpmIdentityService;
import com.lc.ibps.base.core.util.AppUtil;
import com.lc.ibps.bpmn.api.identity.BpmIdentityExtractService;
import com.lc.ibps.bpmn.api.constant.BpmConstants;
import java.util.Map;
Object processInstObject = cmd.getVariable(BpmConstants.PROCESS_INST);
processInstObject =
Optional.ofNullable(processInstObject).orElse(cmd.getTransitVars(BpmConstants.PROCESS_INST));
BpmInstPo bpmInstPo = (BpmInstPo)processInstObject;
if (bpmInstPo != null) {
List<IBpmNodeDefine> outcomeNodes = nodeDef.getOutgoingNodeList();
BpmIdentityService bpmIdentityService = AppUtil.getBean(BpmIdentityService.class);
BpmIdentityExtractService bpmIdentityExtractService = AppUtil.getBean(BpmIdentityExtractService.class);
Map<String, Object> variables = bpmIdentityService.createVariableMap(bpmInstPo.getId());
String startId = (String)variables.get(BpmConstants.START_USER);
for (IBpmNodeDefine nodeDefItem : outcomeNodes) {
List<BpmIdentity> userList = bpmIdentityService.findByDefIdNodeIdData(startId,
bpmInstPo.getProcDefId(), bpmInstPo.getId(), nodeDefItem.getNodeId(), true, true);
List<Map<String, String>> userMapList = bpmIdentityExtractService.extractUser(userList);// TODO 每个节点的用户
}
}获取流程图下一个节点(v3.5.1以上(含))
仅在
后置事件可用
下一个节点是结束节点的后置事件不可用
import java.util.Optional;
import com.lc.ibps.bpmn.persistence.entity.BpmInstPo;
import java.util.List;
import com.lc.ibps.bpmn.api.model.node.IBpmNodeDefine;
import com.lc.ibps.bpmn.api.service.BpmIdentityService;
import com.lc.ibps.base.core.util.AppUtil;
import com.lc.ibps.bpmn.api.identity.BpmIdentityExtractService;
import com.lc.ibps.bpmn.api.constant.BpmConstants;
import java.util.Map;
Object processInstObject = cmd.getVariable(BpmConstants.PROCESS_INST);
processInstObject =
Optional.ofNullable(processInstObject).orElse(cmd.getTransitVars(BpmConstants.PROCESS_INST));
BpmInstPo bpmInstPo = (BpmInstPo)processInstObject;
if (bpmInstPo != null) {
List<IBpmNodeDefine> outcomeNodes = nodeDef.getOutgoingNodeList();
BpmIdentityService bpmIdentityService = AppUtil.getBean(BpmIdentityService.class);
BpmIdentityExtractService bpmIdentityExtractService = AppUtil.getBean(BpmIdentityExtractService.class);
Map<String, Object> variables = bpmIdentityService.createVariableMap(bpmInstPo.getId());
String startId = (String)variables.get(BpmConstants.START_USER);
for (IBpmNodeDefine nodeDefItem : outcomeNodes) {
List<BpmIdentity> userList = bpmIdentityService.findByDefIdNodeIdData(variables, startId,
bpmInstPo.getProcDefId(), bpmInstPo.getId(), nodeDefItem.getNodeId(), true, true);
List<Map<String, String>> userMapList = bpmIdentityExtractService.extractUser(userList);// TODO 每个节点的用户
}
}通过httpclient请求接口
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
String taskId = execution.getId(); // 任务id
String busData = cmd.getBusData(); // 表单数据json
String bussnessKey = cmd.getBusinessKey(); // 表单数据,也就是业务数据的主键
String url = "http://192.168.3.230:15100/ibps/business/v3/form/def/query";
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("aa", "bb");
StringEntity s = new StringEntity("{\"parameters\":[],\"requestPage\":{\"limit\":20,\"needPage\":true,\"pageNo\":1},\"sorts\":[]}");
s.setContentEncoding("UTF-8");
s.setContentType("application/json");//发送json数据需要设置contentType
httpPost.setEntity(s);
CloseableHttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
// TODO 处理返回逻辑
EntityUtils.consume(entity);注:
如果在脚本里面使用jdbcScript修改了数据需要手动调用缓存清除方法清除缓存com.lc.ibps.base.framework.utils.J2CacheUtil.flushAll()。多个脚本修改数据,只需在最后一个脚本写上清除缓存脚本即可。
如果当前节点执行后会跳过下一个节点需要同步修改线程上下文中的数据对象数据[业务对象编码].set("字段名",值)
通过当前用户ID获取指定扩展属性
从当前用户执行上下文获取员工 ID,通过调用内部 RPC 接口employee/load查询员工扩展属性,最终提取并返回扩展属性Key(属性 Key:查询ibps_party_attr表Key字段,一般为属性名称的拼音小写缩写) 对应的属性值。
下方以职工身份为例。
示例脚本如下:
import com.lc.ibps.cloud.entity.APIResult
import com.lc.ibps.base.core.util.AppUtil
import com.lc.ibps.org.api.IPartyEmployeeService
import com.lc.ibps.base.web.context.ContextUtil
import com.lc.ibps.org.party.persistence.entity.PartyEmployeePo
/**
* 调用employee/load接口,通过zgsf(职工身份)属性Key获取对应值
* 异常场景均返回null,正常场景返回zgsf属性值
* 员工ID从当前上下文获取 | 目标属性Key:zgsf
*/
// 1. 定义目标属性Key(职工身份)
def targetAttrKey = "zgsf"
// 2. 从上下文获取当前员工ID
String targetEmployeeId = ContextUtil.getCurrentUserId();
// 3. 获取服务实例
IPartyEmployeeService partyEmployeeService = AppUtil.getBean(IPartyEmployeeService.class)
// 4. RPC调用load接口,接口调用失败则返回null
APIResult<PartyEmployeePo> employeeResult = partyEmployeeService.load(targetEmployeeId)
if (!employeeResult.isSuccess()) {
return null
}
// 5. 获取variables对象,判空则返回null
Map<String, Object> variables = employeeResult.getVariables()
if (!variables) {
return null
}
// 6. 获取扩展属性集合,空/空集合则返回null
List<Map<String, Object>> partyAttrs = (List<Map<String, Object>>) variables.get("partyAttrs")
if (!partyAttrs || partyAttrs.isEmpty()) {
return null
}
// 7. 查找zgsf对应的属性Map,未找到则返回null
Map<String, Object> zgsfAttrMap = partyAttrs.find { attrMap ->
targetAttrKey.equals(attrMap.get("key"))
}
if (!zgsfAttrMap) {
return null
}
// 8. 获取属性值集合,空/空集合则返回null
List<Map<String, Object>> zgsfValues = (List<Map<String, Object>>) zgsfAttrMap.get("values")
if (zgsfValues == null || zgsfValues.isEmpty()) {
return null
}
// 9. 正常场景:提取单值属性的第一个值并返回
String zgsfValue = zgsfValues.get(0).get("value")
return zgsfValue核心执行流程
脚本按从上下文获取标识→调用 RPC 接口→逐层解析返回结果→提取目标属性的逻辑执行,共 9 个核心步骤,步骤间为串行依赖,任意一步不满足则直接返回null:
1、定义固定属性 Key:初始化目标属性键值targetAttrKey = “zgsf”,指定需提取的职工身份属性标识;
2、获取上下文员工 ID:通过ContextUtil.getCurrentUserId()获取当前登录员工 ID;
3、获取服务接口实例:通过AppUtil.getBean从容器中获取IPartyEmployeeService服务实例,为 RPC 调用做准备;
4、调用 RPC 查询接口:调用服务实例的load方法,传入员工 ID 执行 RPC 调用,返回APIResult
5、解析 variables 映射:从接口结果中提取变量对象variables,若对象为空返回null;
6、获取扩展属性集合:从variables中提取员工扩展属性集合partyAttrs,若集合为null或空集合,返回null;
7、查找 zgsf 属性映射:遍历partyAttrs集合,查找key值等于zgsf的属性映射对象,未找到则返回null;
8、提取属性值集合:从 zgsf 属性映射中提取值集合List<Map<String, Object>> zgsfValues,若集合为null或空集合,返回null;
9、返回目标属性值:提取zgsfValues集合中第一个元素的value值,作为最终结果返回(正常场景唯一返回值)。
脚本使用RPC说明
1、架构前提:IPartyEmployeeService属于 platform 公共服务,脚本运行在 business 业务服务,二者是独立部署的微服务。
2、必须用 RPC 调用:RPC 是微服务间标准化通信方式,通过调用 platform 暴露的IPartyEmployeeService RPC 接口,使用员工数据接口,实现服务松耦合,适配跨服务访问需求。
3、无法直接 getBean:getBean仅能获取当前服务(business) Spring 容器内的 Bean,而IPartyEmployeeService的 Bean 实例仅存在于 platform 的容器中,跨服务无法通过该方式获取。


