2024-11-29 11:14:35 +08:00

397 lines
16 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
}
}