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 userAndDepts) { List users = userAndDepts.stream().filter(v -> "user".equals(v.getType())) .map(OrgUser::getId).collect(Collectors.toList()); if (users.contains(orgId)) { return true; } List 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 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 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 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 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 ids = ((List) compareVal).stream().map(v -> String.valueOf(v.get("id"))).collect(Collectors.toList()); List 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 orgs = values.stream().map(v -> { if (v instanceof OrgUser) { return (OrgUser) v; } else if (v instanceof Map) { Map valMap = (Map) 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 ids = ((List) 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 getContextVar(DelegateExecution execution) { Map variables = execution.getVariables(); return loadCtxVar(execution.getProcessInstanceId(), execution.getProcessDefinitionId(), variables); } /** * 获取流程实例上下文变量 * * @param instanceId 实例ID * @param defId 流程定义ID * @return 流程实例上下文变量 */ public Map getContextVar(String instanceId, String defId) { Map variables = runtimeService.getVariables(instanceId); return loadCtxVar(instanceId, defId, variables); } /** * 组装流程实例上下文变量 * * @param instanceId 实例ID * @param defId 流程定义ID * @param variables 流程实例上下文变量 * @return 流程实例上下文变量 */ private Map loadCtxVar(String instanceId, String defId, Map variables) { List opUserIds = new ArrayList<>(); List 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 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 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(); } }