1.导包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>activiti_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mysql.version>5.1.29</mysql.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M3.1</version>
</dependency>
<!--解决activiti坐标导致的security注入问题-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.新建启动类,配置如下:
注:启动类上的exclude = {}一定要有,不然会报错
package com.wanglj;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Author 955
* @Date 2022-06-21 13:47
* @Description
*/
@SpringBootApplication(exclude = {
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class,
org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration.class
})
public class ActivitiApplication {
public static void main(String[] args) {
SpringApplication.run(ActivitiApplication.class, args);
}
}
3.idea下载bpmn画图插件Activiti BPMN visualizer
,安装重启idea(actiBPM插件早就不适配2021的idea了,想用可以降级idea版本或者用eclipse画,我相信没人想用这两个方法,推荐使用下文官方画图工具,Activiti BPMN visualizer
这个插件只能有一些简单的功能)
4.在resources目录下新建processes文件夹,并点击右键选择bpmn文件新建(注意:流程名字随便取,但文件必须是xx.bpmn20.xml
格式)
5.填写相应参数
6.打开新建的xml文件,点击右键打开流程图工具
7.画流程图(以user task为例,其他功能自行参考官网),点击右键新建开始–>Activities–>User task,注意每个步骤写好流程名称
注:流转下一步人名称得写上(这里提交请假申请-->worker,部门经理审批-->leader,财务审批-->finance)
拖动箭头使流程连接
最终效果
8.保存流程图到项目,右键Save to PNG,选择路径为xx.bpmn20.xml同路径
完整结构如图
9.新建activiti配置,也可使用yml方式springbean注入,这里使用xml方式配置(注:必须叫activiti.cfg.xml
且必须放resources根目录下,否则需要在代码配置放置路径,内容如下)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:mysql://xxxxxxx:3306/activiti_demo" />
<property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="root" />
<property name="databaseSchemaUpdate" value="true" />
</bean>
</beans>
10.编写测试代码
注:其中开始流程的key值为新建流程文件时的名称,如果不清楚可打开流程图面板,左键点击空白处,可显示id,对应key值
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.List;
/**
* @Author 955
* @Date 2022-06-21 15:01
* @Description
*/
@Slf4j
public class test {
/**
* 初始化流程部署
*/
@Test
public void testDeployment() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment().addClasspathResource("processes/process_955.bpmn20.xml").addClasspathResource("processes/process_955.png").name("请假申请流程").deploy();
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
/**
* 开始流程
*/
@Test
public void testStartProcess() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("process_955");
System.out.println("流程部署id:" + processInstance.getId());
System.out.println("流程部署名称:" + processInstance.getName());
System.out.println("processInstanceId:" + processInstance.getProcessInstanceId());
}
/**
* 查询流转到该所属角色的任务
*/
@Test
public void testFindPersonalTaskList() {
//对应各流程节点流转下一步人名称,这里第一步从worker开始
//调用下方completTask方法可通过审批,再查询下一个名称leader,以此类推直到结束,因为流程图没有不通过的情况所以暂不考虑
String assignee = "worker";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery().processDefinitionKey("process_955").taskAssignee(assignee).list();
for (Task task : list) {
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
/**
* 完成任务
*/
@Test
public void completTask() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
//根据流程key和任务的负责人 查询任务
//返回一个任务对象
//对应各流程节点流转下一步人名称,这里第一步从worker开始,分别为worker-->leader-->finance
Task task = taskService.createTaskQuery().processDefinitionKey("process_955").taskAssignee("worker").singleResult();
//完成任务,参数:任务id
taskService.complete(task.getId());
}
/**
* 查询出当前所有的流程定义
*/
@Test
public void queryProcessDefinition() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//查询出当前所有的流程定义
List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey("process_955").orderByProcessDefinitionVersion().desc().list();
//输出流程定义信息
for (ProcessDefinition processDefinition : list) {
System.out.println("流程定义 id=" + processDefinition.getId());
System.out.println("流程定义 name=" + processDefinition.getName());
System.out.println("流程定义 key=" + processDefinition.getKey());
System.out.println("流程部署id =" + processDefinition.getDeploymentId());
System.out.println("<=========================================>");
}
}
/**
* 删除流程
*/
@Test
public void deleteDeployment(){
String deploymentId = "1";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//通过流程引擎获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//删除流程定义,如果该流程定义已有流程实例启动则删除报错
repositoryService.deleteDeployment(deploymentId);
//设置为true,则有流程实例在启动也可以强制删除
// repositoryService.deleteDeployment(deploymentId,true);
}
/**
* 输出流程文件和流程图到文件夹
* @throws Exception
*/
@Test
public void queryBpmnFile()throws Exception{
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("process_955").singleResult();
//通过流程定义信息,得到部署id
String deploymentId = processDefinition.getDeploymentId();
//得到png图片流
InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
//得到bpmn文件流
InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());
File file_png = new File("C:\\Users\\HYGK\\Desktop\\bpmn\\process_955.png");
File file_bpmn = new File("C:\\Users\\HYGK\\Desktop\\bpmn\\process_955.bpmn");
FileOutputStream pngOut = new FileOutputStream(file_png);
FileOutputStream bpmnOut = new FileOutputStream(file_bpmn);
IOUtils.copy(pngInput,pngOut);
IOUtils.copy(bpmnInput,bpmnOut);
pngOut.close();
bpmnOut.close();
}
/**
* 根据instanceId查询整个流程
*/
@Test
public void findHistoryInfo(){
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
HistoryService historyService = processEngine.getHistoryService();
//获取actinst表的查询对象
HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
//根据instanceId查询整个流程
instanceQuery.processInstanceId("5001").orderByHistoricActivityInstanceStartTime().asc();
List<HistoricActivityInstance> list = instanceQuery.list();
for (HistoricActivityInstance historicActivityInstance : list) {
System.out.println(historicActivityInstance.getActivityId());
System.out.println(historicActivityInstance.getActivityName());
System.out.println(historicActivityInstance.getProcessDefinitionId());
System.out.println(historicActivityInstance.getCalledProcessInstanceId());
System.out.println("<=========================================>");
}
}
}
==============================================================================
进阶使用:activiti-adit使用
要使用监听器最好使用其他的画图工具,idea的画图工具支持不是很好,这里推荐官方的工具
https://download.csdn.net/download/m0_49605579/87255395
(0积分下载即可,资源内含3个war包,需要放到tomcat的webapps目录下,运行tomcat服务即可)
<1>成功运行后访问http://localhost:8080/activiti-app
(默认账号密码为:admin/test)
<2>登录成功后点击Kickstart App,并新建Process
<3>填写模块名
<4>画流程图
注:
Execution listeners为执行监听器,可以查看执行中的一些流程信息等。
Task listeners为任务监听器,可以当任务流转到某一步骤时,如动态设置受理人等。
Assignments为指定用户/用户组,可以指定当前步骤由谁执行。
流程指向箭头:
在流程指向箭头中,可以设置el表达式来设定满足某些条件时指定流程走向,具体用法为${逻辑},此外流程箭头可以点击工具类上方拆分按钮
使连线拆分,满足强迫症患者。
执行/任务监听器设定:
点击:Execution listeners/Task listeners,添加监听器,监听器配置为项目相对路径
保存,下载流程图文件:
画好流程图后,点击左上角保存按钮保存退出编辑页面,在点击此流程图,进入预览页面,点击下载按钮,保存bpmn文件到本地。
流程文件放入项目中:
将生成好的流程文件放到项目src/main/resources/processes
目录下,此通过idea进入xx.bpmn20.xml文件中,点击右键选择View BPMN (Activiti) Diagram
,可在idea中查看/编辑流程图,并保存png格式的流程图图片(这里想在idea中打开流程图需要下载上方的Activiti BPMN visualizer
插件方可使用)。
<5>编写监听类(以经理审批任务监听类为例,执行监听器和任务监听器的代码区别就是分别实现ExecutionListener
和TaskListener
,重写notify方法的参数DelegateTask
和DelegateExecution
不同):
注:想了解执行/任务监听器的设计区别,请参考这一文章:https://blog.csdn.net/yabushandaxue/article/details/119297080
package com.wanglj.listener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
/**
* @Author 955
* @Date 2022-10-14 14:34
* @Description 经理审批任务监听器
*/
public class ManagerTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
//动态配置用户组
delegateTask.addCandidateGroup("jl1,jl2,jl3");
动态配置执行人
delegateTask.setAssignee("s");
}
}
/**
* @Author 955
* @Date 2022-10-14 10:10
* @Description 经理审批执行监听器
*/
public class ManagerExecutionListener implements ExecutionListener {
@Override
public void notify(DelegateExecution delegateExecution) {
System.out.println("经理审批监听");
}
}
<6>提供一些service方法:
注:如果是新建项目未建表的,在springboot-test类执行此方法,配置好流程文件路径即可
@Test
public void testDeployment1() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment().addClasspathResource("processes/leave.bpmn20.xml").addClasspathResource("processes/leave.png").name("借款申请流程").deploy();
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
(1)act业务父类
FinshBpVo
package com.wanglj.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Map;
/**
* @Author 955
* @Date 2022-10-14 11:15
* @Description
*/
@Data
public class FinshBpVo implements Serializable {
@ApiModelProperty(value = "任务id")
private String taskId;
@ApiModelProperty(value = "下一步需要的参数,可为空")
private Map<String, Object> variables;
@ApiModelProperty(value = "用户")
private String userId;
@ApiModelProperty(value = "完成时的批注内容")
private String comment;
}
package com.wanglj.service.impl;
import com.wanglj.util.R;
import com.wanglj.vo.EndProcessVo;
import com.wanglj.vo.StartProcessInstanceVo;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.engine.*;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* act业务父类
* @Author 955
* @Date 2022-07-04 10:21
* @Description
*/
@Service
public class BaseActService {
@Autowired
protected ManagementService managementService;
@Autowired
protected TaskService taskService;
@Autowired
protected RuntimeService runtimeService;
@Autowired
protected RepositoryService repositoryService;
@Autowired
protected HistoryService historyService;
/**
* 初始化流程部署
*/
public void testDeployment() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment().addClasspathResource("processes/leave.bpmn20.xml").addClasspathResource("processes/leave.png").name("借款申请流程").deploy();
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
public R<String> startProcessInstance(StartProcessInstanceVo vo) {
testDeployment();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(vo.getProcessDefinitionKey())
.latestVersion().singleResult();
if (processDefinition != null && processDefinition.isSuspended()) {
return R.fail("该流程已存在");
}
ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
.processDefinitionKey(vo.getProcessDefinitionKey().trim())
.businessKey(vo.getBusinessKey().trim())
.variables(vo.getVariables())
.start();
String processInstanceId = processInstance.getProcessInstanceId();
return R.ok(processInstanceId);
}
public R<String> stopProcessInstanceById(EndProcessVo endVo) {
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(endVo.getProcessInstanceId()).singleResult();
if (processInstance != null) {
//1、添加审批记录
// flowableCommentService.addComment(null,endVo.getUserId(),endVo.getUserName(),endVo.getDeptId(), endVo.getProcessInstanceId(), TaskStatusEnum.QZOVER.getStatus(),
// endVo.getMessage());
// List endNodes = flowableBpmnModelService.findEndFlowElement(processInstance.getProcessDefinitionId());
// String endId = endNodes.get(0).getId();
String processInstanceId = endVo.getProcessInstanceId();
//2、执行终止
List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstanceId).list();
List<String> executionIds = new ArrayList<>();
executions.forEach(execution -> executionIds.add(execution.getId()));
// this.moveExecutionsToSingleActivityId(executionIds, endId);
return R.ok("终止成功!");
} else {
return R.fail("不存在运行的流程实例,请确认!");
}
}
public List<FlowNode> findFlowNodes(String processDefId) {
List<FlowNode> flowNodes = new ArrayList<>();
BpmnModel bpmnModel = this.getBpmnModelByProcessDefId(processDefId);
org.activiti.bpmn.model.Process process = bpmnModel.getMainProcess();
Collection<FlowElement> list = process.getFlowElements();
list.forEach(flowElement -> {
if (flowElement instanceof FlowNode) {
flowNodes.add((FlowNode) flowElement);
}
});
return flowNodes;
}
public BpmnModel getBpmnModelByProcessDefId(String processDefId) {
return repositoryService.getBpmnModel(processDefId);
}
public void testFindPersonalTaskList() {
String assignee = "xmfzr1";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
// List list = taskService.createTaskQuery().processDefinitionKey("order_ex_flow").taskAssignee(assignee).list();
List<Task> list = taskService.createTaskQuery().processDefinitionKey("order_ex_flow").taskCandidateUser(assignee).list();
for (Task task : list) {
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("ProcessDefinitionId:" + task.getProcessDefinitionId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
System.out.println("任务名称:" + task.getCreateTime());
}
}
}
(2)具体业务实现类:
package com.wanglj.service.impl;
import com.wanglj.service.ActivitiService;
import com.wanglj.util.Kit;
import com.wanglj.util.R;
import com.wanglj.vo.FinshBpVo;
import com.wanglj.vo.StartProcessInstanceVo;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.ActivitiTaskAlreadyClaimedException;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestParam;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @Author 955
* @Date 2022-10-09 15:38
* @Description
*/
@Service
@Slf4j
public class ActivitiServiceImpl extends BaseActService implements ActivitiService {
@Override
public R<String> startApplyProcess(StartProcessInstanceVo vo) {
return startProcessInstance(vo);
}
/**
* 获取最新版本的所有的流程定义列表
*
* @return
*/
@Override
public List<ProcessDefinition> getLastVersionProcessDefinitions() {
//我们需要的是最新的版本
return repositoryService.createProcessDefinitionQuery().latestVersion().list();
}
/**
* 根据流程定义id 获取到对应的模板节点信息 节点名称,节点id,监听事件也可以获取
*
* @param processDefinitionId
* @return
*/
@Override
public List<FlowElement> getFlowElementByProcessDefId(String processDefinitionId) {
log.info("根据流程定义获取节点信息入参:{}", processDefinitionId);
//流程定义中的模板信息
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
//模板中的所有流程
List<Process> processes = bpmnModel.getProcesses();
log.info("流程定义id:{}的流程条数大小:{}", processDefinitionId, processes.size());
//取得流程的节点包括 连线实体
Process process = processes.get(0);
//把所有节点 用map装好。
Map<String, FlowElement> flowElements = process.getFlowElementMap();
List<FlowElement> flowElementList = new ArrayList<>();
if (CollectionUtils.isEmpty(flowElements)) {
return flowElementList;
}
//转换一下 成为list
for (Map.Entry<String, FlowElement> flowElement : flowElements.entrySet()) {
flowElementList.add(flowElement.getValue());
}
return flowElementList;
}
/**
* 根据用户id 或者该用户的所有 有权限审核的流程task
*
* @param userId
* @return
*/
@Override
public List<Task> getAuthorityTaskByUserId(String userId) {
return taskService
.createTaskQuery()
.taskCandidateUser(userId)
.taskUnassigned()
.list();
}
/**
* 用户认领task,如果该用户有权限并且该task未被认领
*
* @param taskId
* @param userId
*/
@Override
public R<Boolean> claimTaskByUserId(String taskId, String userId) {
try {
taskService.claim(taskId, userId);
} catch (ActivitiTaskAlreadyClaimedException e) {
log.info("该任务已经被认领taskId:{},userId:{}", taskId, userId);
R.fail("该任务已经被其他的人认领");
} catch (Exception e) {
log.info("无权限认领该任务:{},userId:{}", taskId, userId);
}
return R.ok(true);
}
/**
* 根据用户id 获取需要立即审核的流程task
*
* @param userId
* @return
*/
@Override
public List<Task> getAssignedTaskByUserId(String userId) {
return taskService.createTaskQuery().taskAssignee(userId).list();
}
/**
* 完成当前节点 进入到下一步。
*
* @param taskId finshBpVo
* @throws Exception
*/
@Override
public R<Boolean> completeTask(FinshBpVo finshBpVo) {
if (Kit.isNotEmpty(finshBpVo)) {
String taskId = finshBpVo.getTaskId();
String comment = finshBpVo.getComment();
String userId = finshBpVo.getUserId();
Map<String, Object> variables = finshBpVo.getVariables();
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (Objects.isNull(task)) {
log.info("该任务taskId:{},已被完成或者不存在", taskId);
return R.fail("任务已被完成或者不存在");
}
if (Objects.nonNull(task) && !userId.equals(task.getAssignee())) {
log.info("该用户没有权限完成处理该任务taskId:{},userId:{},yUserId:{}", taskId, userId, task.getAssignee());
return R.fail("该用户没有权限完成处理该任务");
}
//如果批注信息不为空,新增批注信息内容
if (StringUtils.isNotBlank(comment)) {
taskService.addComment(taskId, task.getProcessInstanceId(), comment);
}
taskService.complete(taskId, variables);
return R.ok(true);
}
return R.ok(false);
}
/**
* 根据实例id 获取已经完成的节点
*
* @param processInstanceId
* @return
*/
@Override
public List<HistoricTaskInstance> getHistoryTaskByProcessInstanceId(String processInstanceId) {
return historyService
.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.finished().orderByTaskCreateTime().desc().list();
}
/**
* 根据用户id获取到 用户处理过的所有任务信息
*
* @param userId
* @return
*/
@Override
public List<HistoricTaskInstance> getHistoryTaskByUserId(String userId) {
return historyService
.createHistoricTaskInstanceQuery()
.taskAssignee(userId).finished()
.orderByTaskCreateTime().desc().list();
}
/**
* 根据流程实例id 结束任务 任何时候都可以结束
*
* @param processId
* @return
*/
@Override
public boolean finishProcess(String processId) {
runtimeService.deleteProcessInstance(processId, "结束");
Task task = taskService.createTaskQuery().processInstanceId(processId).singleResult();
if (null == task) {
return true;
}
return false;
}
/**
* 根据taskId 获取批注消息
*
* @param taskId 任务id
* @return
*/
@Override
public Comment getComment(@RequestParam("taskId") String taskId) {
return taskService.getComment(taskId);
}
/**
* 根据instanceId查询整个流程
*/
@Override
public List<HistoricActivityInstance> findHistoryInfo(String processInstanceId) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
HistoryService historyService = processEngine.getHistoryService();
//获取actinst表的查询对象
HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
//根据instanceId查询整个流程
instanceQuery.processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc();
List<HistoricActivityInstance> list = instanceQuery.list();
return list;
}
/**
* 撤回或者驳回任务, 撤回是当前处理人 处理任务后后悔了,在下一个处理人还未处理的情况可以撤回
* 驳回:当前处理人 查看任务后发现上一步处理人未处理到位,可以驳回到上一个人。
*
* @param processInstanceId 流程实例id
* @param userId 当前用户id
* @param setUserId 撤回或者驳回后的 设置用户处理人id
* @param recallOrRebutFlag 是撤回还是驳回标识 1代表撤回 2代表驳回
* @param comment 撤回或者驳回时的批注内容
* @throws Exception
*/
@Override
public R<Boolean> cancel(String processInstanceId, String userId, String setUserId, Integer recallOrRebutFlag, String comment) {
log.info("撤回或者驳回的任务的参数processInstanceId:{}, userId:{},serUserId:{}, recallOrRebutFlag:{}",
processInstanceId, userId, setUserId, recallOrRebutFlag);
//参数验证
if (StringUtils.isBlank(processInstanceId)) {
return R.fail("流程实例id不能为空");
}
if (StringUtils.isBlank(userId)) {
return R.fail("当前用户id不能为空");
}
if (StringUtils.isBlank(setUserId)) {
return R.fail("撤回或者驳回后的处理人id不能为空");
}
if (Objects.isNull(recallOrRebutFlag)) {
return R.fail("撤回或这驳回类型不能为空");
}
//获取当前的task
Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
if (task == null) {
log.info("任务节点已完成或者未启动,无法撤回processInstanceId:{}", processInstanceId);
return R.fail("任务节点已完成或者未启动");
}
//根据时间的正序 输出,最后的一个就是正在进行中的节点任务。
List<HistoricTaskInstance> historicTaskInstances = historyService
.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.orderByTaskCreateTime().asc().list();
//撤回的应该是 正在进行中的上一个节点
HistoricTaskInstance myTask = null;
for (int i = 0; i < historicTaskInstances.size(); i++) {
if (historicTaskInstances.get(i).getId().equals(task.getId()) && i > 0) {
myTask = historicTaskInstances.get(i - 1);
break;
}
}
if (myTask == null) {
log.info("流程实例id,{},上一步任务节点为空");
return R.fail("上一步任务节点为空,无法撤回或驳回");
}
log.info("流程实例id,{}的上一个节点的id:{},节点处理人:{}", myTask.getId(), myTask.getAssignee());
//权限校验
if (recallOrRebutFlag.equals(1)) {
if (!userId.equals(myTask.getAssignee())) {
log.info("流程实例id:{},用户id:{}无权限,需要用户id为:{}处理撤回", processInstanceId, userId, myTask.getAssignee());
return R.fail("无权限撤回");
}
} else if (recallOrRebutFlag.equals(2)) {
if (!userId.equals(task.getAssignee())) {
log.info("流程实例id:{},用户id:{}无权限,需要用户id为:{}处理驳回", processInstanceId, userId, myTask.getAssignee());
return R.fail("无权限驳回");
}
} else {
log.info("流程实例id:{},类型标识:{},不是撤回也不是驳回类型", processInstanceId, recallOrRebutFlag);
return R.fail("类型标识无法识别");
}
//获取到当前节点的上一个节点的id
String myTaskId = myTask.getId();
String processDefinitionId = myTask.getProcessDefinitionId();
//获取上一个节点流程定义
ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService
.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
//获取到 历史的activity 节点
List<HistoricActivityInstance> haiList = historyService
.createHistoricActivityInstanceQuery()
.executionId(myTask.getExecutionId())
.finished().list();
String myActivityId = null;
for (HistoricActivityInstance hai : haiList) {
if (myTaskId.equals(hai.getTaskId())) {
myActivityId = hai.getActivityId();
break;
}
}
FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);
Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
//获取当前节点的 活动节点
String activityId = execution.getActivityId();
log.info("流程实例id:{},需要撤回的活动id:{},当前活动id:{}", processInstanceId, myActivityId, activityId);
//获取当前的 node
FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
//记录当前task的节点活动方向
List<SequenceFlow> oriSequenceFlows = new ArrayList<>();
oriSequenceFlows.addAll(flowNode.getOutgoingFlows());
//清理当前的活动方向
flowNode.getOutgoingFlows().clear();
//建立新的方向
List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newSequenceFlowId");
newSequenceFlow.setSourceFlowElement(flowNode);
newSequenceFlow.setTargetFlowElement(myFlowNode);
newSequenceFlowList.add(newSequenceFlow);
flowNode.setOutgoingFlows(newSequenceFlowList);
taskService.addComment(task.getId(), task.getProcessInstanceId(), comment);
taskService.complete(task.getId());
//设置回原来的 方向
flowNode.setOutgoingFlows(oriSequenceFlows);
//再次获取到当前的 活动节点就已经是 撤销后的节点了
Task cuTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
//设置受理人
taskService.setAssignee(cuTask.getId(), setUserId);
return R.ok(true);
}
}
<7>swagger测试:
1.启动流程:
注:其他几个参数都不重要,processDefinitionKey
参数一定得填对,值为流程图创建时输入的model_key
,执行成功会返回processInstanceId
2.根据processInstanceId
查询任务进度
3.完成当前节点,进入下一步
注:comment
参数为批注,可不填,taskId
为第2步任务进度最后一步的taskid,userId
为流程图设置的userid,variables
参数为流程指向线的条件等
4.其他业务方法自行研究即可
返回实体:
import java.io.Serializable;
import java.util.List;
import com.xxxx.common.core.constant.CommonConstants;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
/**
* 响应信息体
* @author:955
* @date:2022年5月9日 下午9:53:56
*/
@Builder
@ToString
@Accessors(chain = true)
@ApiModel(description = "响应信息主体")
@AllArgsConstructor
public class R<T> implements Serializable
{
private static final long serialVersionUID = 1L;
//@Builder.Default
@Getter
@Setter
@ApiModelProperty(value = "返回标记:成功标记=0,失败标记=1")
private int code = CommonConstants.SUCCESS;
//@Builder.Default
@Getter
@Setter
@ApiModelProperty(value = "返回信息")
private String msg = "success";
@Getter
@Setter
@ApiModelProperty(value = "返回错误集合信息")
private List<ErrorMessage> errorMessage;
@Getter
@Setter
@ApiModelProperty(value = "数据")
private T data;
@Getter
@Setter
@ApiModelProperty(value = "是否成功")
private boolean success;
public R()
{
super();
}
public R(T data)
{
super();
this.data = data;
}
public R(T data, String msg)
{
super();
this.data = data;
this.msg = msg;
}
public R(Throwable e)
{
super();
this.msg = e.getMessage();
this.code = CommonConstants.FAIL;
}
public static <T> R<T> ok()
{
return R.restResult(null, CommonConstants.SUCCESS, null);
}
public static <T> R<T> ok(T data)
{
return R.restResult(data, CommonConstants.SUCCESS, null);
}
public static <T> R<T> ok(T data, String msg)
{
return R.restResult(data, CommonConstants.SUCCESS, msg);
}
public static <T> R<T> fail()
{
return R.restResult(null, CommonConstants.FAIL, null);
}
public static <T> R<T> fail(int code)
{
return R.restResult(null, code, null);
}
public static <T> R<T> fail(String msg)
{
return R.restResult(null, CommonConstants.FAIL, msg);
}
public static <T> R<T> fail(int code, String msg)
{
return R.restResult(null, code, msg);
}
public static <T> R<T> fail(List<ErrorMessage> errorMessages)
{
return R.restErrorMessageResult(null, CommonConstants.FAIL, errorMessages);
}
public static <T> R<T> fail(T data)
{
return R.restResult(data, CommonConstants.FAIL, null);
}
public static <T> R<T> fail(T data, String msg)
{
return R.restResult(data, CommonConstants.FAIL, msg);
}
private static <T> R<T> restResult(T data, int code, String msg)
{
R<T> apiResult = new R<>();
apiResult.setCode(code);
if(code==CommonConstants.SUCCESS) {
apiResult.setSuccess(true);
}else if(code==CommonConstants.FAIL) {
apiResult.setSuccess(false);
}
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}
private static <T> R<T> restErrorMessageResult(T data, int code, List<ErrorMessage> errorMessages)
{
R<T> apiResult = new R<>();
apiResult.setCode(code);
if(code==CommonConstants.SUCCESS) {
apiResult.setSuccess(true);
}else if(code==CommonConstants.FAIL) {
apiResult.setSuccess(false);
}
apiResult.setData(data);
apiResult.setErrorMessage(errorMessages);
return apiResult;
}
/**
* @param localizedMessage
* @return
*/
public static R<?> failed(String localizedMessage) {
return R.restResult(null, CommonConstants.FAIL, localizedMessage);
}
}
错误实体:
@Builder
@Data
@ApiModel(description = "响应错误列表")
@AllArgsConstructor
public class ErrorMessage implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String field;
private String message;
}
公共属性抽象类:
public interface CommonConstants
{
/**
* 编码
*/
String UTF8 = "UTF-8";
/**
* 成功标记
*/
Integer SUCCESS = 0;
/**
* 失败标记
*/
Integer FAIL = 1;
String AC_TASK_MSG ="msg";
String AC_TASK_USER= "taskUser";
String AC_TASK_USERNAME= "taskUserName";
String AC_TASK_STATUS= "taskStatus";
String ACT_TASK_FLAG = "flag";
String AC_TASK_DEPT = "taskDept";
String AC_AGENT = "agent";
String AC_TASK_ID = "taskUnId";
}
工具类:
package com.txqc.mlk.util;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DateUtil;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.web.multipart.MultipartFile;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 工具方法类
*/
public class Kit {
/**
* 15位身份证号
*/
private static final Integer FIFTEEN_ID_CARD = 15;
/**
* 18位身份证号
*/
private static final Integer EIGHTEEN_ID_CARD = 18;
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static boolean isValidCard18(String idcard) {
if (EIGHTEEN_ID_CARD != idcard.length()) {
return false;
}
return true;
}
/**
* 验证15位身份编码是否合法
*
* @param idcard 身份编码
* @return 是否合法
*/
public static boolean isValidCard15(String idcard) {
if (FIFTEEN_ID_CARD != idcard.length()) {
return false;
}
return true;
}
private static <T> void setFieldValue(T rowData, Field field, Object value) throws IllegalAccessException {
if (field.getType() == int.class || field.getType() == Integer.class) {
field.set(rowData, value);
} else if (field.getType() == long.class || field.getType() == Long.class) {
field.set(rowData, value);
} else if (field.getType() == double.class || field.getType() == Double.class) {
field.set(rowData, value);
} else if (field.getType() == String.class) {
field.set(rowData, String.valueOf(value));
} else if (field.getType() == LocalDateTime.class) {
field.set(rowData, LocalDateTime.parse(String.valueOf(value), dateTimeFormatter));
}
}
private static Object getCellValue(Cell cell) {
CellType cellType = cell.getCellType();
Object cellValue = null;
if (cellType == CellType._NONE) {
} else if (cellType == CellType.NUMERIC) {
// 数值型
if (DateUtil.isCellDateFormatted(cell)) {
// 日期类型
Date d = cell.getDateCellValue();
cellValue = dateTimeFormatter.format(LocalDateTime.ofInstant(d.toInstant(), ZoneId.systemDefault()));
} else {
double numericCellValue = cell.getNumericCellValue();
BigDecimal bdVal = new BigDecimal(numericCellValue);
if ((bdVal + ".0").equals(Double.toString(numericCellValue))) {
// 整型
cellValue = bdVal;
} else if (String.valueOf(numericCellValue).contains("E10")) {
// 科学记数法
cellValue = new BigDecimal(numericCellValue).toPlainString();
} else {
// 浮点型
cellValue = numericCellValue;
}
}
} else if (cellType == CellType.STRING) {
// 字符串型
cellValue = cell.getStringCellValue();
} else if (cellType == CellType.FORMULA) {
// 公式型
} else if (cellType == CellType.BLANK) {
// 空值
} else if (cellType == CellType.BOOLEAN) {
// 布尔型
cellValue = cell.getBooleanCellValue();
} else if (cellType == CellType.ERROR) {
// 错误
cellValue = cell.getErrorCellValue();
}
return cellValue;
}
/**
* 根据身份编号获取年龄
*
* @param idCard 身份编号
* @return 年龄
*/
public static int getAgeByIdCard(String idCard) {
int iAge = 0;
Calendar cal = Calendar.getInstance();
String year = idCard.substring(6, 10);
int iCurrYear = cal.get(Calendar.YEAR);
iAge = iCurrYear - Integer.valueOf(year);
return iAge;
}
/**
* 获取出生日期 yyyy年MM月dd日
*
* @param IDCard
* @return
*/
public static String getBirthByIdCard(String IDCard) {
String birthday = "";
String year = "";
String month = "";
String day = "";
if (isNotEmpty(IDCard)) {
//15位身份证号
if (IDCard.length() == FIFTEEN_ID_CARD) {
// 身份证上的年份(15位身份证为1980年前的)
year = "19" + IDCard.substring(6, 8);
//身份证上的月份
month = IDCard.substring(8, 10);
//身份证上的日期
day = IDCard.substring(10, 12);
//18位身份证号
} else if (IDCard.length() == EIGHTEEN_ID_CARD) {
// 身份证上的年份
year = IDCard.substring(6).substring(0, 4);
// 身份证上的月份
month = IDCard.substring(10).substring(0, 2);
//身份证上的日期
day = IDCard.substring(12).substring(0, 2);
}
birthday = year + "-" + month + "-" + day;
}
return birthday;
}
/**
* 根据身份编号获取生日年
*
* @param idCard 身份编号
* @return 生日(yyyy)
*/
public static Short getYearByIdCard(String idCard) {
return Short.valueOf(idCard.substring(6, 10));
}
/**
* 根据身份编号获取生日月
*
* @param idCard 身份编号
* @return 生日(MM)
*/
public static Short getMonthByIdCard(String idCard) {
return Short.valueOf(idCard.substring(10, 12));
}
/**
* 根据身份编号获取生日天
*
* @param idCard 身份编号
* @return 生日(dd)
*/
public static Short getDateByIdCard(String idCard) {
return Short.valueOf(idCard.substring(12, 14));
}
/**
* 根据身份编号获取性别
*
* @param idCard 身份编号
* @return 性别(M - 男 , F - 女 , N - 未知)
*/
public static String getGenderByIdCard(String idCard) {
String sGender = "未知";
String sCardNum = idCard.substring(16, 17);
if (Integer.parseInt(sCardNum) % 2 != 0) {
sGender = "1";//男
} else {
sGender = "2";//女
}
return sGender;
}
/**
* 去掉null
*
* @param source
* @return
*/
public static String[] getNullPropertyNames(Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<String>();
for (java.beans.PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue != null) {
emptyNames.add(pd.getName());
}
}
String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}
/**
* 判空方法
*
* @param obj
* @return
*/
public static boolean isEmpty(Object obj) {
if (obj != null && !"".equals(obj.toString())) {
if (obj instanceof List) {
if (((List<?>) obj).size() == 0) {
return true;
}
}
return false;
}
return true;
}
/**
* 判不为空方法
*
* @param obj
* @return
*/
public static boolean isNotEmpty(Object obj) {
if (obj != null && !"".equals(obj.toString())) {
if (obj instanceof List) {
if (((List<?>) obj).size() == 0) {
return false;
}
}
return true;
}
return false;
}
/**
* 验证文件后缀是否符合要求
*
* @param fileType 后缀类型数组(如:xlsx/pdf/docx)
* @param file
* @return
*/
public static R verifyFileLegal(String[] fileType, MultipartFile file) {
if (isEmpty(file)) {
return R.fail("未获取到文件");
} else if (isNotEmpty(file)) {
String fileName = file.getResource().getFilename();
if (isNotEmpty(fileName) && isNotEmpty(fileType)) {
String[] fileNames = fileName.split("\\.");
if (isNotEmpty(fileNames) && fileNames.length > 0) {
if (!Arrays.asList(fileType).contains(fileNames[1])) {
return R.fail("文件类型不匹配");
}
}
}
}
return R.ok();
}
/**
* String转换为LocalDateTime
*
* @param timeString
* @return
*/
public static LocalDateTime getStringToLocalDateTime(String timeString) {
if (isNotEmpty(timeString)) {
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDateTime.parse(timeString, df);
}
return null;
}
/**
* String转换为LocalDate
*
* @param timeString
* @return
*/
public static LocalDate getStringToLocalDate(String timeString) {
if (isNotEmpty(timeString)) {
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDate.parse(timeString, df);
}
return null;
}
public static Map<String, Object> objectToMap(Object obj) throws IllegalAccessException {
Map<String, Object> map = new HashMap<String, Object>();
Class<?> clazz = obj.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
String fieldName = field.getName();
Object value = field.get(obj);
if (value == null) {
value = "";
}
map.put(fieldName, value);
}
return map;
}
private static String[] parsePatterns = {"yyyy-MM-dd", "yyyy年MM月dd日",
"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd",
"yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyyMMdd",
"yyyy.MM.dd", "yyyy.MM.dd HH:mm", "yyyy.MM.dd HH:mm:ss"};
/**
* 校验输入的字符串是否为日期格式
*
* @param str
* @return
*/
public static boolean verifyDate(String str) {
boolean convertSuccess = true;
try {
// 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期,比如2007/02/29会被接受,并转换成2007/03/01
DateUtils.parseDate(str, parsePatterns);
} catch (ParseException e) {
// e.printStackTrace();
// 如果throw java.text.ParseException或者NullPointerException,就说明格式不对
convertSuccess = false;
}
return convertSuccess;
}
/**
* 求差集
* 求List1中有的但是List2中没有的元素
*/
public static List<String> subList2(List<String> list1, List<String> list2) {
Map<String, String> tempMap = list2.parallelStream().collect(Collectors.toMap(Function.identity(), Function.identity(), (oldData, newData) -> newData));
return list1.parallelStream().filter(str -> {
return !tempMap.containsKey(str);
}).collect(Collectors.toList());
}
public static boolean isBlank(String temp) {
boolean returnValue = true;
if (temp != null) {
returnValue = temp.matches("\\s*");
}
return returnValue;
}
public static String nullToStr(String temp) {
if (temp == null) {
temp = "";
}
return temp;
}
/**
* sql注入
*
* @param str
* @return
*/
public static boolean sqlValidate(String str) {
str = str.toLowerCase();//统一转为小写
String badStr = "'|and|exec|execute|insert|drop|" +
"char|declare|sitename|net user|xp_cmdshell|or|like'|create|" +
"table|from|grant|group_concat|column_name|" +
"information_schema.columns|table_schema|union|where|select|delete|update|order|by|count|" +
"chr|mid|master|truncate|";//过滤掉的sql关键字,可以手动添加
String[] badStrs = badStr.split("\\|");
for (int i = 0; i < badStrs.length; i++) {
if (str.indexOf(badStrs[i]) >= 0) {
return false;
}
}
return true;
}
/**
* Date转LocalDate
*
* @param date
* @return
*/
public static LocalDate date2LocalDate(Date date) {
if (null == date) {
return null;
}
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
/**
* LocalDate转Date
*
* @param localDate
* @return
*/
public static Date localDate2Date(LocalDate localDate) {
if (null == localDate) {
return null;
}
ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
return Date.from(zonedDateTime.toInstant());
}
}