工作流模块提交
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.enums.BpmModelFormTypeEnum;
|
||||
import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO;
|
||||
import com.njcn.bpm.pojo.dto.PageResult;
|
||||
import com.njcn.bpm.pojo.param.BpmModelParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import com.njcn.bpm.pojo.vo.BpmModelRespVO;
|
||||
import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO;
|
||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 流程模型 Convert
|
||||
*
|
||||
* @author yunlongn
|
||||
*/
|
||||
@Mapper
|
||||
public interface BpmModelConvert {
|
||||
|
||||
BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class);
|
||||
|
||||
default Page<BpmModelRespVO> buildModelPage(Page<Model> pageResult,
|
||||
Map<String, BpmForm> formMap,
|
||||
Map<String, BpmCategory> categoryMap, Map<String, Deployment> deploymentMap,
|
||||
Map<String, ProcessDefinition> processDefinitionMap) {
|
||||
List<BpmModelRespVO> list = CollectionUtils.convertList(pageResult.getRecords(), model -> {
|
||||
BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model);
|
||||
BpmForm form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null;
|
||||
BpmCategory category = categoryMap.get(model.getCategory());
|
||||
Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null;
|
||||
ProcessDefinition processDefinition = model.getDeploymentId() != null ? processDefinitionMap.get(model.getDeploymentId()) : null;
|
||||
return buildModel0(model, metaInfo, form, category, deployment, processDefinition);
|
||||
});
|
||||
Page<BpmModelRespVO> voPage = new Page<>();
|
||||
voPage.setRecords(list);
|
||||
voPage.setTotal(pageResult.getTotal());
|
||||
voPage.setSize(pageResult.getSize());
|
||||
voPage.setCurrent(pageResult.getCurrent());
|
||||
return voPage;
|
||||
}
|
||||
|
||||
default BpmModelRespVO buildModel(Model model,
|
||||
byte[] bpmnBytes) {
|
||||
BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model);
|
||||
BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null);
|
||||
if (ArrayUtil.isNotEmpty(bpmnBytes)) {
|
||||
modelVO.setBpmnXml(new String(bpmnBytes));
|
||||
}
|
||||
return modelVO;
|
||||
}
|
||||
|
||||
default BpmModelRespVO buildModel0(Model model,
|
||||
BpmModelMetaInfoRespDTO metaInfo, BpmForm form, BpmCategory category,
|
||||
Deployment deployment, ProcessDefinition processDefinition) {
|
||||
BpmModelRespVO modelRespVO = new BpmModelRespVO();
|
||||
modelRespVO.setId(model.getId());
|
||||
modelRespVO.setName(model.getName());
|
||||
modelRespVO.setKey(model.getKey());
|
||||
modelRespVO.setCategory(model.getCategory());
|
||||
modelRespVO.setCreateTime(DateUtils.of(model.getCreateTime()));
|
||||
// Form
|
||||
if (metaInfo != null) {
|
||||
modelRespVO.setFormType(metaInfo.getFormType());
|
||||
modelRespVO.setFormId(metaInfo.getFormId());
|
||||
modelRespVO.setFormCustomCreatePath(metaInfo.getFormCustomCreatePath());
|
||||
modelRespVO.setFormCustomViewPath(metaInfo.getFormCustomViewPath());
|
||||
modelRespVO.setIcon(metaInfo.getIcon());
|
||||
modelRespVO.setDescription(metaInfo.getDescription());
|
||||
}
|
||||
if (form != null) {
|
||||
modelRespVO.setFormId(form.getId());
|
||||
modelRespVO.setFormName(form.getName());
|
||||
}
|
||||
// Category
|
||||
if (category != null) {
|
||||
modelRespVO.setCategoryName(category.getName());
|
||||
}
|
||||
// ProcessDefinition
|
||||
if (processDefinition != null) {
|
||||
modelRespVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionInfoVO.class));
|
||||
modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ?
|
||||
SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
|
||||
if (deployment != null) {
|
||||
modelRespVO.getProcessDefinition().setDeploymentTime(DateUtils.of(deployment.getDeploymentTime()));
|
||||
}
|
||||
}
|
||||
return modelRespVO;
|
||||
}
|
||||
|
||||
default void copyToCreateModel(Model model, BpmModelParam bean) {
|
||||
model.setName(bean.getName());
|
||||
model.setKey(bean.getKey());
|
||||
model.setCategory(bean.getCategory());
|
||||
// model.setMetaInfo(buildMetaInfoStr(null,
|
||||
// null, bean.getDescription(),
|
||||
// null, null, null, null));
|
||||
//类型暂时写死为表单 todo...
|
||||
model.setMetaInfo(buildMetaInfoStr(buildMetaInfo(model),
|
||||
bean.getIcon(), bean.getDescription(),
|
||||
BpmModelFormTypeEnum.NORMAL.getType(), bean.getFormId(), bean.getFormCustomCreatePath(), bean.getFormCustomViewPath()));
|
||||
}
|
||||
|
||||
default void copyToUpdateModel(Model model, BpmModelParam.BpmModelUpdateParam bean) {
|
||||
model.setName(bean.getName());
|
||||
model.setCategory(bean.getCategory());
|
||||
model.setMetaInfo(buildMetaInfoStr(buildMetaInfo(model),
|
||||
bean.getIcon(), bean.getDescription(),
|
||||
bean.getFormType(), bean.getFormId(), bean.getFormCustomCreatePath(), bean.getFormCustomViewPath()));
|
||||
}
|
||||
|
||||
default String buildMetaInfoStr(BpmModelMetaInfoRespDTO metaInfo,
|
||||
String icon, String description,
|
||||
Integer formType, String formId, String formCustomCreatePath, String formCustomViewPath) {
|
||||
if (metaInfo == null) {
|
||||
metaInfo = new BpmModelMetaInfoRespDTO();
|
||||
}
|
||||
// 只有非空,才进行设置,避免更新时的覆盖
|
||||
if (StrUtil.isNotEmpty(icon)) {
|
||||
metaInfo.setIcon(icon);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(description)) {
|
||||
metaInfo.setDescription(description);
|
||||
}
|
||||
if (Objects.nonNull(formType)) {
|
||||
metaInfo.setFormType(formType);
|
||||
metaInfo.setFormId(formId);
|
||||
metaInfo.setFormCustomCreatePath(formCustomCreatePath);
|
||||
metaInfo.setFormCustomViewPath(formCustomViewPath);
|
||||
}
|
||||
return JsonUtils.toJsonString(metaInfo);
|
||||
}
|
||||
|
||||
default BpmModelMetaInfoRespDTO buildMetaInfo(Model model) {
|
||||
return JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Bpm 流程定义的 Convert
|
||||
*
|
||||
* @author yunlong.li
|
||||
*/
|
||||
@Mapper
|
||||
public interface BpmProcessDefinitionConvert {
|
||||
|
||||
BpmProcessDefinitionConvert INSTANCE = Mappers.getMapper(BpmProcessDefinitionConvert.class);
|
||||
|
||||
default Page<BpmProcessDefinitionInfoVO> buildProcessDefinitionPage(Page<ProcessDefinition> page,
|
||||
Map<String, Deployment> deploymentMap,
|
||||
Map<String, BpmProcessDefinitionInfo> processDefinitionInfoMap,
|
||||
Map<String, BpmForm> formMap,
|
||||
Map<String, BpmCategory> categoryMap) {
|
||||
List<BpmProcessDefinitionInfoVO> list = buildProcessDefinitionList(page.getRecords(), deploymentMap, processDefinitionInfoMap, formMap, categoryMap);
|
||||
Page<BpmProcessDefinitionInfoVO> voPage = new Page<>();
|
||||
voPage.setRecords(list);
|
||||
voPage.setTotal(page.getTotal());
|
||||
voPage.setSize(page.getSize());
|
||||
voPage.setCurrent(page.getCurrent());
|
||||
return voPage;
|
||||
}
|
||||
|
||||
default List<BpmProcessDefinitionInfoVO> buildProcessDefinitionList(List<ProcessDefinition> list,
|
||||
Map<String, Deployment> deploymentMap,
|
||||
Map<String, BpmProcessDefinitionInfo> processDefinitionInfoMap,
|
||||
Map<String, BpmForm> formMap,
|
||||
Map<String, BpmCategory> categoryMap) {
|
||||
return CollectionUtils.convertList(list, definition -> {
|
||||
Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class);
|
||||
BpmProcessDefinitionInfo processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfo.class);
|
||||
BpmForm form = null;
|
||||
if (processDefinitionInfo != null) {
|
||||
form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmForm.class);
|
||||
}
|
||||
BpmCategory category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategory.class);
|
||||
return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null, null);
|
||||
});
|
||||
}
|
||||
|
||||
default BpmProcessDefinitionInfoVO buildProcessDefinition(ProcessDefinition definition,
|
||||
Deployment deployment,
|
||||
BpmProcessDefinitionInfo processDefinitionInfo,
|
||||
BpmForm form,
|
||||
BpmCategory category,
|
||||
BpmnModel bpmnModel,
|
||||
List<UserTask> startUserSelectUserTaskList) {
|
||||
BpmProcessDefinitionInfoVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionInfoVO.class);
|
||||
respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
|
||||
// Deployment
|
||||
if (deployment != null) {
|
||||
respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime()));
|
||||
}
|
||||
// BpmProcessDefinitionInfoDO
|
||||
if (processDefinitionInfo != null) {
|
||||
copyTo(processDefinitionInfo, respVO);
|
||||
// Form
|
||||
if (form != null) {
|
||||
respVO.setFormName(form.getName());
|
||||
}
|
||||
}
|
||||
// Category
|
||||
if (category != null) {
|
||||
respVO.setCategoryName(category.getName());
|
||||
}
|
||||
// BpmnModel
|
||||
if (bpmnModel != null) {
|
||||
respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));
|
||||
respVO.setStartUserSelectTasks(BeanUtils.toBean(startUserSelectUserTaskList, BpmProcessDefinitionInfoVO.UserTask.class));
|
||||
}
|
||||
return respVO;
|
||||
}
|
||||
|
||||
@Mapping(source = "from.id", target = "to.id", ignore = true)
|
||||
void copyTo(BpmProcessDefinitionInfo from, @MappingTarget BpmProcessDefinitionInfoVO to);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.njcn.bpm.enums.BpmnModelConstants;
|
||||
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
||||
import org.flowable.bpmn.model.Process;
|
||||
import org.flowable.bpmn.model.*;
|
||||
import org.flowable.common.engine.impl.util.io.BytesStreamSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 流程模型转操作工具类
|
||||
*/
|
||||
public class BpmnModelUtils {
|
||||
|
||||
public static Integer parseCandidateStrategy(FlowElement userTask) {
|
||||
return NumberUtils.parseInt(userTask.getAttributeValue(
|
||||
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
|
||||
}
|
||||
|
||||
public static String parseCandidateParam(FlowElement userTask) {
|
||||
return userTask.getAttributeValue(
|
||||
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点,获取入口连线
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @return 入口连线列表
|
||||
*/
|
||||
public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {
|
||||
if (source instanceof FlowNode) {
|
||||
return ((FlowNode) source).getIncomingFlows();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点,获取出口连线
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @return 出口连线列表
|
||||
*/
|
||||
public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {
|
||||
if (source instanceof FlowNode) {
|
||||
return ((FlowNode) source).getOutgoingFlows();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程元素信息
|
||||
*
|
||||
* @param model bpmnModel 对象
|
||||
* @param flowElementId 元素 ID
|
||||
* @return 元素信息
|
||||
*/
|
||||
public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) {
|
||||
Process process = model.getMainProcess();
|
||||
return process.getFlowElement(flowElementId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 BPMN 流程中,指定的元素们
|
||||
*
|
||||
* @param model 模型
|
||||
* @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等
|
||||
* @return 元素们
|
||||
*/
|
||||
public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
|
||||
List<T> result = new ArrayList<>();
|
||||
model.getProcesses().forEach(process -> {
|
||||
process.getFlowElements().forEach(flowElement -> {
|
||||
if (flowElement.getClass().isAssignableFrom(clazz)) {
|
||||
result.add((T) flowElement);
|
||||
}
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public static StartEvent getStartEvent(BpmnModel model) {
|
||||
Process process = model.getMainProcess();
|
||||
// 从 initialFlowElement 找
|
||||
FlowElement startElement = process.getInitialFlowElement();
|
||||
if (startElement instanceof StartEvent) {
|
||||
return (StartEvent) startElement;
|
||||
}
|
||||
// 从 flowElementList 找
|
||||
return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent);
|
||||
}
|
||||
|
||||
public static BpmnModel getBpmnModel(byte[] bpmnBytes) {
|
||||
if (ArrayUtil.isEmpty(bpmnBytes)) {
|
||||
return null;
|
||||
}
|
||||
BpmnXMLConverter converter = new BpmnXMLConverter();
|
||||
// 补充说明:由于在 Flowable 中自定义了属性,所以 validateSchema 传递 false
|
||||
return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false);
|
||||
}
|
||||
|
||||
public static String getBpmnXml(BpmnModel model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
BpmnXMLConverter converter = new BpmnXMLConverter();
|
||||
return new String(converter.convertToXML(model));
|
||||
}
|
||||
|
||||
// ========== 遍历相关的方法 ==========
|
||||
|
||||
/**
|
||||
* 找到 source 节点之前的所有用户任务节点
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 已找到的用户任务节点
|
||||
* @return 用户任务节点 数组
|
||||
*/
|
||||
public static List<UserTask> getPreviousUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof StartEvent && source.getSubProcess() != null) {
|
||||
userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
|
||||
// 根据类型,获取入口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
|
||||
if (sequenceFlows == null) {
|
||||
return userTaskList;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 类型为用户节点,则新增父级节点
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement());
|
||||
}
|
||||
// 类型为子流程,则添加子流程开始节点出口处相连的节点
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
|
||||
// 获取子流程用户任务节点
|
||||
List<UserTask> childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (CollUtil.isNotEmpty(childUserTaskList)) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 迭代获取子流程用户任务节点
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 需要撤回的用户任务列表
|
||||
* @return 用户任务节点
|
||||
*/
|
||||
public static List<UserTask> findChildProcessUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
|
||||
// 根据类型,获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
if (sequenceFlows == null) {
|
||||
return userTaskList;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof UserTask) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
|
||||
continue;
|
||||
}
|
||||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
|
||||
List<UserTask> childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (CollUtil.isNotEmpty(childUserTaskList)) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行
|
||||
* 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param target 目标节点
|
||||
* @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) {
|
||||
visitedElements = visitedElements == null ? new HashSet<>() : visitedElements;
|
||||
// 不能是开始事件和子流程
|
||||
if (source instanceof StartEvent && isInEventSubprocess(source)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 根据类型,获取入口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
|
||||
if (CollUtil.isEmpty(sequenceFlows)) {
|
||||
return true;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (visitedElements.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
visitedElements.add(sequenceFlow.getId());
|
||||
// 这条线路存在目标节点,这条线路完成,进入下个线路
|
||||
FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement();
|
||||
if (target.getId().equals(sourceFlowElement.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 如果目标节点为并行网关,则不继续
|
||||
if (sourceFlowElement instanceof ParallelGateway) {
|
||||
return false;
|
||||
}
|
||||
// 否则就继续迭代
|
||||
if (!isSequentialReachable(sourceFlowElement, target, visitedElements)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前节点是否属于不同的子流程
|
||||
*
|
||||
* @param flowElement 被判断的节点
|
||||
* @return true 表示属于子流程
|
||||
*/
|
||||
private static boolean isInEventSubprocess(FlowElement flowElement) {
|
||||
FlowElementsContainer flowElementsContainer = flowElement.getParentContainer();
|
||||
while (flowElementsContainer != null) {
|
||||
if (flowElementsContainer instanceof EventSubProcess) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flowElementsContainer instanceof FlowElement) {
|
||||
flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();
|
||||
} else {
|
||||
flowElementsContainer = null;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param runTaskKeyList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 需要撤回的用户任务列表
|
||||
* @return 子级任务节点列表
|
||||
*/
|
||||
public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList,
|
||||
Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof StartEvent && source.getSubProcess() != null) {
|
||||
userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList);
|
||||
}
|
||||
|
||||
// 根据类型,获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
if (sequenceFlows == null) {
|
||||
return userTaskList;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
|
||||
continue;
|
||||
}
|
||||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
|
||||
List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (CollUtil.isNotEmpty(childUserTaskList)) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList);
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import com.njcn.bpm.enums.BpmConstants;
|
||||
import org.flowable.common.engine.api.delegate.Expression;
|
||||
import org.flowable.common.engine.api.variable.VariableContainer;
|
||||
import org.flowable.common.engine.impl.el.ExpressionManager;
|
||||
import org.flowable.common.engine.impl.identity.Authentication;
|
||||
import org.flowable.engine.ProcessEngineConfiguration;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
|
||||
import org.flowable.engine.impl.util.CommandContextUtil;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.TaskInfo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Flowable 相关的工具方法
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class FlowableUtils {
|
||||
|
||||
// ========== User 相关的工具方法 ==========
|
||||
|
||||
public static void setAuthenticatedUserId(Long userId) {
|
||||
Authentication.setAuthenticatedUserId(String.valueOf(userId));
|
||||
}
|
||||
|
||||
public static void clearAuthenticatedUserId() {
|
||||
Authentication.setAuthenticatedUserId(null);
|
||||
}
|
||||
|
||||
public static String getTenantId() {
|
||||
return ProcessEngineConfiguration.NO_TENANT_ID;
|
||||
}
|
||||
|
||||
// ========== Execution 相关的工具方法 ==========
|
||||
|
||||
/**
|
||||
* 格式化多实例(并签、或签)的 collectionVariable 变量(多实例对应的多审批人列表)
|
||||
*
|
||||
* @param activityId 活动编号
|
||||
* @return collectionVariable 变量
|
||||
*/
|
||||
public static String formatExecutionCollectionVariable(String activityId) {
|
||||
return activityId + "_assignees";
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化多实例(并签、或签)的 collectionElementVariable 变量(当前实例对应的一个审批人)
|
||||
*
|
||||
* @param activityId 活动编号
|
||||
* @return collectionElementVariable 变量
|
||||
*/
|
||||
public static String formatExecutionCollectionElementVariable(String activityId) {
|
||||
return activityId + "_assignee";
|
||||
}
|
||||
|
||||
// ========== ProcessInstance 相关的工具方法 ==========
|
||||
|
||||
public static Integer getProcessInstanceStatus(ProcessInstance processInstance) {
|
||||
return getProcessInstanceStatus(processInstance.getProcessVariables());
|
||||
}
|
||||
|
||||
public static Integer getProcessInstanceStatus(HistoricProcessInstance processInstance) {
|
||||
return getProcessInstanceStatus(processInstance.getProcessVariables());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程实例的状态
|
||||
*
|
||||
* @param processVariables 流程实例的 variables
|
||||
* @return 状态
|
||||
*/
|
||||
private static Integer getProcessInstanceStatus(Map<String, Object> processVariables) {
|
||||
return (Integer) processVariables.get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程实例的表单
|
||||
*
|
||||
* @param processInstance 流程实例
|
||||
* @return 表单
|
||||
*/
|
||||
public static Map<String, Object> getProcessInstanceFormVariable(HistoricProcessInstance processInstance) {
|
||||
Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables());
|
||||
filterProcessInstanceFormVariable(formVariables);
|
||||
return formVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤流程实例的表单
|
||||
*
|
||||
* 为什么要过滤?目前使用 processVariables 存储所有流程实例的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示
|
||||
*
|
||||
* @param processVariables 流程实例的 variables
|
||||
* @return 过滤后的表单
|
||||
*/
|
||||
public static Map<String, Object> filterProcessInstanceFormVariable(Map<String, Object> processVariables) {
|
||||
processVariables.remove(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
|
||||
return processVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程实例的发起用户选择的审批人 Map
|
||||
*
|
||||
* @param processInstance 流程实例
|
||||
* @return 发起用户选择的审批人 Map
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<String, List<String>> getStartUserSelectAssignees(ProcessInstance processInstance) {
|
||||
return (Map<String, List<String>>) processInstance.getProcessVariables().get(
|
||||
BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
|
||||
}
|
||||
|
||||
// ========== Task 相关的工具方法 ==========
|
||||
|
||||
/**
|
||||
* 获得任务的状态
|
||||
*
|
||||
* @param task 任务
|
||||
* @return 状态
|
||||
*/
|
||||
public static Integer getTaskStatus(TaskInfo task) {
|
||||
return (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得任务的审批原因
|
||||
*
|
||||
* @param task 任务
|
||||
* @return 审批原因
|
||||
*/
|
||||
public static String getTaskReason(TaskInfo task) {
|
||||
return (String) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_REASON);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得任务的表单
|
||||
*
|
||||
* @param task 任务
|
||||
* @return 表单
|
||||
*/
|
||||
public static Map<String, Object> getTaskFormVariable(TaskInfo task) {
|
||||
Map<String, Object> formVariables = new HashMap<>(task.getTaskLocalVariables());
|
||||
filterTaskFormVariable(formVariables);
|
||||
return formVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤任务的表单
|
||||
*
|
||||
* 为什么要过滤?目前使用 taskLocalVariables 存储所有任务的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示
|
||||
*
|
||||
* @param taskLocalVariables 任务的 taskLocalVariables
|
||||
* @return 过滤后的表单
|
||||
*/
|
||||
public static Map<String, Object> filterTaskFormVariable(Map<String, Object> taskLocalVariables) {
|
||||
taskLocalVariables.remove(BpmConstants.TASK_VARIABLE_STATUS);
|
||||
taskLocalVariables.remove(BpmConstants.TASK_VARIABLE_REASON);
|
||||
return taskLocalVariables;
|
||||
}
|
||||
|
||||
// ========== Expression 相关的工具方法 ==========
|
||||
|
||||
public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) {
|
||||
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
|
||||
assert processEngineConfiguration != null;
|
||||
ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
|
||||
assert expressionManager != null;
|
||||
Expression expression = expressionManager.createExpression(expressionString);
|
||||
return expression.getValue(variableContainer);
|
||||
}
|
||||
|
||||
}
|
||||
202
pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/JsonUtils.java
Normal file
202
pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/JsonUtils.java
Normal file
@@ -0,0 +1,202 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JSON 工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
public class JsonUtils {
|
||||
|
||||
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
static {
|
||||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值
|
||||
objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 objectMapper 属性
|
||||
* <p>
|
||||
* 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean
|
||||
*
|
||||
* @param objectMapper ObjectMapper 对象
|
||||
*/
|
||||
public static void init(ObjectMapper objectMapper) {
|
||||
JsonUtils.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String toJsonString(Object object) {
|
||||
return objectMapper.writeValueAsString(object);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static byte[] toJsonByte(Object object) {
|
||||
return objectMapper.writeValueAsBytes(object);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String toJsonPrettyString(Object object) {
|
||||
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, clazz);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, String path, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JsonNode treeNode = objectMapper.readTree(text);
|
||||
JsonNode pathNode = treeNode.path(path);
|
||||
return objectMapper.readValue(pathNode.toString(), clazz);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, Type type) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type));
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串解析成指定类型的对象
|
||||
* 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下,
|
||||
* 如果 text 没有 class 属性,则会报错。此时,使用这个方法,可以解决。
|
||||
*
|
||||
* @param text 字符串
|
||||
* @param clazz 类型
|
||||
* @return 对象
|
||||
*/
|
||||
public static <T> T parseObject2(String text, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
return JSONUtil.toBean(text, clazz);
|
||||
}
|
||||
|
||||
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
|
||||
if (ArrayUtil.isEmpty(bytes)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(bytes, clazz);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", bytes, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return objectMapper.readValue(text, typeReference);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 JSON 字符串成指定类型的对象,如果解析失败,则返回 null
|
||||
*
|
||||
* @param text 字符串
|
||||
* @param typeReference 类型引用
|
||||
* @return 指定类型的对象
|
||||
*/
|
||||
public static <T> T parseObjectQuietly(String text, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return objectMapper.readValue(text, typeReference);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> List<T> parseArray(String text, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> List<T> parseArray(String text, String path, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JsonNode treeNode = objectMapper.readTree(text);
|
||||
JsonNode pathNode = treeNode.path(path);
|
||||
return objectMapper.readValue(pathNode.toString(), objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonNode parseTree(String text) {
|
||||
try {
|
||||
return objectMapper.readTree(text);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonNode parseTree(byte[] text) {
|
||||
try {
|
||||
return objectMapper.readTree(text);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isJson(String text) {
|
||||
return JSONUtil.isTypeJSON(text);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.text.StrPool;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class StrUtils {
|
||||
|
||||
public static String maxLength(CharSequence str, int maxLength) {
|
||||
return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定字符串是否以任何一个字符串开始
|
||||
* 给定字符串和数组为空都返回 false
|
||||
*
|
||||
* @param str 给定字符串
|
||||
* @param prefixes 需要检测的开始字符串
|
||||
* @since 3.0.6
|
||||
*/
|
||||
public static boolean startWithAny(String str, Collection<String> prefixes) {
|
||||
if (StrUtil.isEmpty(str) || ArrayUtil.isEmpty(prefixes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (CharSequence suffix : prefixes) {
|
||||
if (StrUtil.startWith(str, suffix, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<Long> splitToLong(String value, CharSequence separator) {
|
||||
long[] longs = StrUtil.splitToLong(value, separator);
|
||||
return Arrays.stream(longs).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static Set<Long> splitToLongSet(String value) {
|
||||
return splitToLongSet(value, StrPool.COMMA);
|
||||
}
|
||||
|
||||
public static Set<Long> splitToLongSet(String value, CharSequence separator) {
|
||||
long[] longs = StrUtil.splitToLong(value, separator);
|
||||
return Arrays.stream(longs).boxed().collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static List<Integer> splitToInteger(String value, CharSequence separator) {
|
||||
int[] integers = StrUtil.splitToInt(value, separator);
|
||||
return Arrays.stream(integers).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除字符串中,包含指定字符串的行
|
||||
*
|
||||
* @param content 字符串
|
||||
* @param sequence 包含的字符串
|
||||
* @return 移除后的字符串
|
||||
*/
|
||||
public static String removeLineContains(String content, String sequence) {
|
||||
if (StrUtil.isEmpty(content) || StrUtil.isEmpty(sequence)) {
|
||||
return content;
|
||||
}
|
||||
return Arrays.stream(content.split("\n"))
|
||||
.filter(line -> !line.contains(sequence))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
public static List<String> splitToStringList(String value) {
|
||||
return StrUtil.split(value, StrPool.COMMA);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user