397 lines
16 KiB
Java
397 lines
16 KiB
Java
package com.wflow.workflow;
|
||
|
||
import cn.hutool.core.bean.BeanUtil;
|
||
import cn.hutool.core.collection.CollectionUtil;
|
||
import cn.hutool.core.util.ArrayUtil;
|
||
import cn.hutool.core.util.NumberUtil;
|
||
import com.wflow.bean.do_.UserDeptDo;
|
||
import com.wflow.service.OrgRepositoryService;
|
||
import com.wflow.workflow.bean.process.HttpDefinition;
|
||
import com.wflow.workflow.bean.process.OrgUser;
|
||
import com.wflow.workflow.bean.process.props.ConditionProps;
|
||
import com.wflow.workflow.bean.process.props.DelayProps;
|
||
import com.wflow.workflow.config.WflowGlobalVarDef;
|
||
import com.wflow.workflow.execute.ElExecute;
|
||
import com.wflow.workflow.execute.HttpExecute;
|
||
import com.wflow.workflow.execute.JsExecute;
|
||
import com.wflow.workflow.service.UserDeptOrLeaderService;
|
||
import com.zhgd.xmgl.tenant.TenantContextHolder;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.flowable.engine.HistoryService;
|
||
import org.flowable.engine.RepositoryService;
|
||
import org.flowable.engine.RuntimeService;
|
||
import org.flowable.engine.TaskService;
|
||
import org.flowable.engine.delegate.DelegateExecution;
|
||
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
|
||
import org.flowable.engine.repository.ProcessDefinition;
|
||
import org.flowable.engine.runtime.ProcessInstance;
|
||
import org.flowable.engine.task.Comment;
|
||
import org.flowable.task.api.history.HistoricTaskInstance;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.beans.factory.annotation.Qualifier;
|
||
import org.springframework.stereotype.Component;
|
||
|
||
import java.time.LocalDateTime;
|
||
import java.time.format.DateTimeFormatter;
|
||
import java.util.*;
|
||
import java.util.concurrent.ExecutorService;
|
||
import java.util.stream.Collectors;
|
||
|
||
/**
|
||
* @author : willian fu
|
||
* @date : 2022/7/15
|
||
*/
|
||
@Slf4j
|
||
@Component("uelTools")
|
||
public class UELTools {
|
||
|
||
@Autowired
|
||
private UserDeptOrLeaderService userDeptOrLeaderService;
|
||
|
||
@Autowired
|
||
@Qualifier("wflowThreadPool")
|
||
private ExecutorService executorService;
|
||
|
||
@Autowired
|
||
private RepositoryService repositoryService;
|
||
|
||
@Autowired
|
||
private RuntimeService runtimeService;
|
||
|
||
@Autowired
|
||
private TaskService taskService;
|
||
|
||
@Autowired
|
||
private HistoryService historyService;
|
||
|
||
@Autowired
|
||
private OrgRepositoryService orgRepositoryService;
|
||
|
||
/**
|
||
* 判断集合是否包含某元素
|
||
*
|
||
* @param collection 集合
|
||
* @param val 目标元素
|
||
* @return 匹配结果
|
||
*/
|
||
public boolean contains(Collection<?> collection, Object val) {
|
||
return CollectionUtil.contains(collection, val);
|
||
}
|
||
|
||
//判断,集合是否包含某元素
|
||
public boolean contains(String list, Object val) {
|
||
return ArrayUtil.contains(list.split(","), val);
|
||
}
|
||
|
||
/**
|
||
* 判断用户是否属于某些用户或者部门内
|
||
*
|
||
* @param orgId 用户/部门ID
|
||
* @param userAndDepts 比较的
|
||
* @return 结果
|
||
*/
|
||
public boolean orgContains(String orgId, List<OrgUser> userAndDepts) {
|
||
List<String> users = userAndDepts.stream().filter(v -> "user".equals(v.getType()))
|
||
.map(OrgUser::getId).collect(Collectors.toList());
|
||
if (users.contains(orgId)) {
|
||
return true;
|
||
}
|
||
List<String> collect = userAndDepts.stream().filter(v -> "dept".equals(v.getType()))
|
||
.map(OrgUser::getId).collect(Collectors.toList());
|
||
for (String dept : collect) {
|
||
if (userDeptOrLeaderService.userIsBelongToDept(orgId, dept)) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 判断节点是否结束,被flowable调用
|
||
*
|
||
* @param execution 上下文
|
||
* @param type 节点类型
|
||
* @return 是否结束
|
||
*/
|
||
public boolean nodeIsComplete(ExecutionEntity execution, String type) {
|
||
//读取当前节点内3个变量: 已完成的任务数、任务总数、未处理的任务数
|
||
Number nrOfCompletedInstances = execution.getVariable("nrOfCompletedInstances", Number.class);
|
||
Number nrOfActiveInstances = execution.getVariable("nrOfActiveInstances", Number.class);
|
||
if (Objects.isNull(nrOfCompletedInstances)) {
|
||
nrOfCompletedInstances = historyService.createHistoricTaskInstanceQuery()
|
||
.processInstanceId(execution.getProcessInstanceId())
|
||
.taskDefinitionKey(execution.getActivityId())
|
||
.finished().count();
|
||
}
|
||
switch (type) {
|
||
case "OR":
|
||
return nrOfCompletedInstances.longValue() >= 0;
|
||
case "AND":
|
||
case "NEXT":
|
||
if (Objects.nonNull(nrOfActiveInstances)) {
|
||
return nrOfActiveInstances.longValue() == 0;
|
||
} else {
|
||
Number nrOfInstances = execution.getVariable("nrOfInstances", Number.class);
|
||
if (Objects.nonNull(nrOfInstances)) {
|
||
return nrOfCompletedInstances.equals(nrOfInstances);
|
||
} else {
|
||
//nrOfInstances和 nrOfActiveInstances 变量都不存在
|
||
nrOfActiveInstances = taskService.createTaskQuery().active().count();
|
||
return nrOfActiveInstances.longValue() == 0;
|
||
}
|
||
}
|
||
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
/**
|
||
* 动态获取延时节点延时时长
|
||
*
|
||
* @param execution 上下文
|
||
* @return 延时表达式
|
||
*/
|
||
public String getDelayDuration(ExecutionEntity execution) {
|
||
try {
|
||
Map variable = execution.getVariable(WflowGlobalVarDef.WFLOW_NODE_PROPS, Map.class);
|
||
DelayProps props = (DelayProps) variable.get(execution.getActivityId());
|
||
String date = null;
|
||
if (DelayProps.Type.AUTO.equals(props.getType())) {
|
||
date = LocalDateTime.now().format(DateTimeFormatter.ISO_DATE) + "T" + props.getDateTime().trim();
|
||
} else if (DelayProps.Type.PRECISE.equals(props.getType())) {
|
||
date = LocalDateTime.parse(props.getDateTime().trim(),
|
||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||
.format(DateTimeFormatter.ISO_DATE_TIME);
|
||
}
|
||
return date;
|
||
} catch (Exception e) {
|
||
return LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 条件表达式判断
|
||
*
|
||
* @param execution 上下文
|
||
* @return 对比结果
|
||
*/
|
||
public boolean conditionCompare(String conditionId, ExecutionEntity execution) {
|
||
Map variable = execution.getVariable(WflowGlobalVarDef.WFLOW_NODE_PROPS, Map.class);
|
||
ConditionProps props = (ConditionProps) variable.get(conditionId);
|
||
Map<String, Object> variables = execution.getVariables();
|
||
variables.put("root", variables.get(WflowGlobalVarDef.INITIATOR));
|
||
return conditionCompare(props, variables);
|
||
}
|
||
|
||
/**
|
||
* 校验条件公共函数
|
||
*
|
||
* @param props 条件设置
|
||
* @param ctx 变量
|
||
* @return 校验结果
|
||
*/
|
||
public Boolean conditionCompare(ConditionProps props, Map<String, Object> ctx) {
|
||
//拿到前端的条件节点props设置项后开始校验条件
|
||
switch (props.getMode()) {
|
||
case SIMPLE:
|
||
return validateSimpleCd(props, ctx);
|
||
case UEL:
|
||
return new ElExecute().execute(props.getExpression(), ctx, Boolean.class);
|
||
case JS:
|
||
return new JsExecute(executorService)
|
||
.execute("compare", "function compare(ctx){ \n" + props.getJs() + "\n}",
|
||
Boolean.class, ctx);
|
||
case HTTP:
|
||
return new HttpExecute().execute(BeanUtil.mapToBean(props.getHttp(), HttpDefinition.class, true), executorService, Boolean.class, ctx);
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 校验简单模式条件设置
|
||
*
|
||
* @param ctx 系统变量
|
||
* @param props 条件设置项
|
||
* @return 校验结果
|
||
*/
|
||
private Boolean validateSimpleCd(ConditionProps props, Map<String, Object> ctx) {
|
||
int groupConditionSuccess = 0;
|
||
for (ConditionProps.Group group : props.getGroups()) {
|
||
int subConditionSuccess = 0;
|
||
for (ConditionProps.Group.Condition condition : group.getConditions()) {
|
||
if (subConditionCompare(condition, ctx.get(condition.getId()))) {
|
||
subConditionSuccess++;
|
||
if ("OR".equals(group.getGroupType())) {
|
||
//或的关系,那么结束循环,组++
|
||
groupConditionSuccess++;
|
||
break;
|
||
}
|
||
//全部满足条件也结束循环
|
||
if (subConditionSuccess == group.getConditions().size()) {
|
||
groupConditionSuccess++;
|
||
}
|
||
}
|
||
}
|
||
//判断组对比结果
|
||
if (("OR".equals(props.getGroupsType()) && groupConditionSuccess > 0)
|
||
|| groupConditionSuccess == props.getGroups().size()) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 子条件校验
|
||
*
|
||
* @param condition 子条件配置
|
||
* @param compareVal 子条件比较值
|
||
* @return 校验结果
|
||
*/
|
||
private boolean subConditionCompare(ConditionProps.Group.Condition condition, Object compareVal) {
|
||
try {
|
||
List<Object> values = condition.getValue();
|
||
double val = 0;
|
||
switch (condition.getCompare()) {
|
||
case ">":
|
||
return toDouble(compareVal) > toDouble(values.get(0));
|
||
case "<":
|
||
return toDouble(compareVal) < toDouble(values.get(0));
|
||
case ">=":
|
||
return toDouble(compareVal) >= toDouble(values.get(0));
|
||
case "<=":
|
||
return toDouble(compareVal) <= toDouble(values.get(0));
|
||
case "=":
|
||
return compareVal.toString().equals(String.valueOf(values.get(0)));
|
||
case "!=":
|
||
return !compareVal.toString().equals(String.valueOf(values.get(0)));
|
||
case "B":
|
||
val = toDouble(compareVal);
|
||
return toDouble(values.get(0)) < val && val < toDouble(values.get(1));
|
||
case "AB":
|
||
val = toDouble(compareVal);
|
||
return toDouble(values.get(0)) <= val && val < toDouble(values.get(1));
|
||
case "BA":
|
||
val = toDouble(compareVal);
|
||
return toDouble(values.get(0)) < val && val <= toDouble(values.get(1));
|
||
case "ABA":
|
||
val = toDouble(compareVal);
|
||
return toDouble(values.get(0)) <= val && val <= toDouble(values.get(1));
|
||
case "IN":
|
||
return values.contains(compareVal.toString());
|
||
case "DEPT":
|
||
if (compareVal instanceof List) {
|
||
List<String> ids = ((List<Map>) compareVal).stream().map(v -> String.valueOf(v.get("id"))).collect(Collectors.toList());
|
||
List<String> pids = values.stream().map(v -> String.valueOf(((Map) v).get("id"))).collect(Collectors.toList());
|
||
for (String sid : ids) {
|
||
boolean result = false;
|
||
for (String pid : pids) {
|
||
if (sid.equals(pid) || userDeptOrLeaderService.deptIsBelongToDept(sid, pid)) {
|
||
result = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!result) {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
case "ORG":
|
||
List<OrgUser> orgs = values.stream().map(v -> {
|
||
if (v instanceof OrgUser) {
|
||
return (OrgUser) v;
|
||
} else if (v instanceof Map) {
|
||
Map<String, Object> valMap = (Map<String, Object>) v;
|
||
return OrgUser.builder().id(valMap.get("id").toString())
|
||
.type(valMap.get("type").toString()).build();
|
||
}
|
||
return null;
|
||
}).collect(Collectors.toList());
|
||
if (compareVal instanceof List) {
|
||
List<String> ids = ((List<Map>) compareVal).stream().map(v -> String.valueOf(v.get("id"))).collect(Collectors.toList());
|
||
for (String id : ids) {
|
||
if (orgContains(id, orgs)) {
|
||
return true;
|
||
}
|
||
}
|
||
} else {
|
||
return orgContains(String.valueOf(compareVal), orgs);
|
||
}
|
||
return false;
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("条件判断异常[{}]", condition);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 获取流程实例上下文变量
|
||
*
|
||
* @param execution 上下文
|
||
* @return 流程实例上下文变量
|
||
*/
|
||
public Map<String, Object> getContextVar(DelegateExecution execution) {
|
||
Map<String, Object> variables = execution.getVariables();
|
||
return loadCtxVar(execution.getProcessInstanceId(), execution.getProcessDefinitionId(), variables);
|
||
}
|
||
|
||
/**
|
||
* 获取流程实例上下文变量
|
||
*
|
||
* @param instanceId 实例ID
|
||
* @param defId 流程定义ID
|
||
* @return 流程实例上下文变量
|
||
*/
|
||
public Map<String, Object> getContextVar(String instanceId, String defId) {
|
||
Map<String, Object> variables = runtimeService.getVariables(instanceId);
|
||
return loadCtxVar(instanceId, defId, variables);
|
||
}
|
||
|
||
/**
|
||
* 组装流程实例上下文变量
|
||
*
|
||
* @param instanceId 实例ID
|
||
* @param defId 流程定义ID
|
||
* @param variables 流程实例上下文变量
|
||
* @return 流程实例上下文变量
|
||
*/
|
||
private Map<String, Object> loadCtxVar(String instanceId, String defId, Map<String, Object> variables) {
|
||
List<String> opUserIds = new ArrayList<>();
|
||
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(instanceId).list();
|
||
if (list != null) {
|
||
for (HistoricTaskInstance historicTaskInstance : list) {
|
||
opUserIds.add(historicTaskInstance.getAssignee());
|
||
}
|
||
}
|
||
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().processDefinitionId(defId).singleResult();
|
||
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
|
||
List<Comment> processInstanceComments = taskService.getProcessInstanceComments(instanceId);
|
||
variables.put("formName", definition.getName());
|
||
variables.put("instanceId", instanceId);
|
||
//获取流程发起人信息
|
||
String startUser = String.valueOf(variables.get(WflowGlobalVarDef.INITIATOR));
|
||
// String startDept = String.valueOf(variables.getOrDefault("startDept",
|
||
// ((ProcessInstanceOwnerDto) variables.get("owner")).getOwnerDeptId()));
|
||
String startDept = String.valueOf(variables.get("startDept"));
|
||
Map<String, UserDeptDo> infoMap = orgRepositoryService.getUserDeptInfos(CollectionUtil.newArrayList(startUser + "_" + startDept));
|
||
UserDeptDo userDeptDo = infoMap.get(startUser);
|
||
variables.put("owner.id", startUser);
|
||
variables.put("owner.name", userDeptDo.getUserName());
|
||
variables.put("owner.deptId", startDept);
|
||
variables.put("owner.deptName", userDeptDo.getDeptName());
|
||
variables.put("tenantId", TenantContextHolder.getTenantId());
|
||
variables.put("op.user", opUserIds);
|
||
variables.put("startTime", processInstance.getStartTime());
|
||
variables.put("fullMessages", processInstanceComments.stream().map(comment -> comment.getFullMessage()).collect(Collectors.toList()));
|
||
return variables;
|
||
}
|
||
|
||
private double toDouble(Object val) {
|
||
return NumberUtil.parseNumber(val.toString()).doubleValue();
|
||
}
|
||
}
|