1 工作流简介
1.1 工作流的定义
百度百科定义
工作流(Workflow),就是“业务过程的部分或整体在计算机应用环境下的自动化”,它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档、信息或任务的过程自动进行,从而实现某个预期的业务目标,或者促使此目标的实现”
我的理解是工作流就是将业务流程用xml文件描述,然后交给机器去执行,从而实现业务的自动进行
Activiti是有Alfresco软件在2010年5月17日发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理、工作流等领域的一个开源的、灵活的、易扩展的可执行流程语言框架
1.2 BPMN 规范
BPMN规范详细文档戳这里 传送门
BPMN的概念
BPMN是用于表示流程的规范,他本质上是一个XML文件如下图
这里只需要了解BPMN规范的结构,实际开发中有专门的工具画图不需要手写XML
BPMN2.0结构
- 事件
- 顺序流
- 网关
- 任务
- 子节点和调用节点
- 事务和并发
- 流程实例授权
- 数据对象
常见BPMN元素介绍
开始事件
开始事件用来指明流程在哪里开始。开始事件的类型(流程在接收事件时启动, 还是在指定时间启动,等等),定义了流程如何启动, 这通过事件中不同的小图表来展示。 在XML中,这些类型是通过声明不同的子元素来区分的。
开始事件都是捕获事件:最终这些事件都是一直等待着,知道对应的触发时机出现
在开始事件中可以设置如下的activiti属性
initiator:当流程启动时,把当前登录的用户保存到哪个变量名中。 示例如下:
initiator 指定当前启动流程的人,可以再流程变量中看到这个值
<startEvent id="request" activiti:initiator="initiator" />
登录的用户必须使用IdentityService.setAuthenticatedUserId(String)方法设置, 并像这样包含在try-finally代码中
try {
identityService.setAuthenticatedUserId("bono");
runtimeService.startProcessInstanceByKey("someProcessKey");
} finally {
identityService.setAuthenticatedUserId(null);
}
顺序流(连接点与点之间的线)
描述:
连接两个流程节点的连线。流程执行完一个节点后,会沿着节点的所有外出顺序流继续执行
XML内容
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />
条件顺序流
描述
可以为顺序流定义一个条件。离开一个BPMN 2.0节点时, 默认会计算外出顺序流的条件。 如果条件结果为true, 就会选择外出顺序流继续执行。当多条顺序流被选中时, 就会创建多条分支, 流程会继续以并行方式继续执行。
XML内容
<sequenceFlow id="flow" sourceRef="theStart" targetRef="theTask">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${order.price > 100 && order.price < 250}]]>
</conditionExpression>
</sequenceFlow>
默认顺序流
描述:
所有的BPMN 2.0任务和网关都可以设置一个默认顺序流。 只有在节点的其他外出顺序流不能被选中是,才会使用它作为外出顺序流继续执行。 默认顺序流的条件设置不会生效。
xml内容
<exclusiveGateway id="exclusiveGw" name="Exclusive Gateway" default="flow2" />
<sequenceFlow id="flow1" sourceRef="exclusiveGw" targetRef="task1">
<conditionExpression xsi:type="tFormalExpression">${conditionA}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow2" sourceRef="exclusiveGw" targetRef="task2"/>
<sequenceFlow id="flow3" sourceRef="exclusiveGw" targetRef="task3">
<conditionExpression xsi:type="tFormalExpression">${conditionB}</conditionExpression>
</sequenceFlow>
网关
网关用来控制流程的走向
- 排他网关
- 并行网关
- 包含网关
- 基于事件网关
任务
- 用户任务 (常用)
- 脚本任务
- Java服务任务 (常用)
- WebService任务
- 邮件任务
- Mule任务
- Camel任务
- 手工任务
- Java接收任务
- Shell任务
- 排他任务
空结束事件
描述:空结束事件意味着到达事件时不会指定抛出的结果。 这样,引擎会直接结束当前执行的分支,不会做其他事情。
XML内容
<endEvent id="end" name="my end event" />
2 工作流开发工具
这里使用官方的应用进行演示
官方war包下载戳我
解压后在wars文件夹下可以看到三个war包 这三个war包都互相独立
- activiti-app.war //app应用包含流程设计器,表单设计器,等等
- activiti-rest.war //rest 接口示例
- activiti-admin.war //管理流程的工具
用tomcat部署后的访问地址和默认的用户名密码
其中只有activiti-app 和 activiti-admin有界面,URL和密码可以看下面的表格
URL | 用户名 | 密码 |
---|---|---|
localhost:8080/activiti-app | admin | test |
localhost:8080/activiti-admin | admin | admin |
具体使用可以参考这篇博客 传送门
3 Acitivit表结构和API
3.1 activiti 数据库结构规律
- ACT_RE_*: 'RE’表示repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
- ACT_RU_*: 'RU’表示runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
- ACT_ID_*: 'ID’表示identity。 这些表包含身份信息,比如用户,组等等。
- ACT_HI_*: 'HI’表示history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
- ACT_GE_*: 通用数据, 用于不同场景下,如存放资源文件。
3.1.1 资源库流程规则表
- act_re_deployment 部署信息表
- act_re_model 流程设计模型部署表
- act_re_procdef 流程定义数据表
3.1.2 运行时数据库表
- act_ru_execution 运行时流程执行实例表
- act_ru_identitylink 运行时流程人员表,主要存储任务节点与参与者的相关信息
- act_ru_task 运行时任务节点表
- act_ru_variable 运行时流程变量数据表
3.1.3 历史数据库表
- act_hi_actinst 历史节点表
- act_hi_attachment 历史附件表
- act_hi_comment 历史意见表
- act_hi_identitylink 历史流程人员表
- act_hi_detail 历史详情表,提供历史变量的查询
- act_hi_procinst 历史流程实例表
- act_hi_taskinst 历史任务实例表
- act_hi_varinst 历史变量表
3.1.4 组织结构表
- act_id_group 用户组信息表
- act_id_info 用户扩展信息表
- act_id_membership 用户与用户组对应信息表
- act_id_user 用户信息表
3.1.5 通用数据表
- act_ge_bytearray 二进制数据表
- act_ge_property 属性数据表存储整个流程引擎级别的数据
3.2 Activiti相关api
3.2.1 流程引擎对象
Process Engine 流程引擎对象 所有的service都是通过流程引擎对象获得的
我们主要是通过这7大service来操作流程
//获取默认的引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//管理和操作流程的服务
RuntimeService runtimeService = processEngine.getRuntimeService();
//管理资源的服务如流程文件表单文件
RepositoryService repositoryService = processEngine.getRepositoryService();
//任务服务
TaskService taskService = processEngine.getTaskService();
//引擎管理服务
ManagementService managementService = processEngine.getManagementService();
//身份管理,管理用户和组
IdentityService identityService = processEngine.getIdentityService();
//历史服务
HistoryService historyService = processEngine.getHistoryService();
//表单服务(可选)
FormService formService = processEngine.getFormService();
//动态bpmn服务,无需重新部署就可以改变流程
DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();
3.2.2 常用service的简单使用
RepositoryService
是Activiti的仓库服务类。所谓的仓库指流程定义文档的两个文件:bpmn文件和流程图片
//通过仓库服务类部署流程
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deloyment = repositoryService.createDeployment().addClasspathResource("xxx.bpmn20.xml".).deploy();
// 通过api查询流程定义
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
System.out.println(processDefinition.getName());
RuntimeService
是activiti的流程执行服务类。可以从这个服务类中获取很多关于流程执行相关的信息。
RuntimeService runtimeService = processEngine.getRuntimeService();
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employee", employee);
variables.put("nrOfHolidays", nrOfHolidays);
variables.put("description", description);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("process的id值",variables)
TaskService
是activiti的任务服务类。可以从这个类中获取任务的信息。
//查询任务列表
TaskService taskService = processEngine.getTaskService();
//查询manager组的任务
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();
System.out.println("You have " + tasks.size() + " tasks:");
for (int i=0; i<tasks.size(); i++) {
System.out.println((i+1) + ") " + tasks.get(i).getName());
}
HistoryService
是activiti的查询历史信息的类。在一个流程执行完成后,这个对象为我们提供查询历史信息。
//查询历史流程记录
HistoryService historyService = processEngine.getHistoryService();
List<HistoricActivityInstance> activities = historyService
.createHistoricActivityInstanceQuery()//创建历史流程查询对象
.processInstanceId(processInstance.getId())//通过id查询
.finished()//查询已完成的流程
.orderByHistoricActivityInstanceEndTime().asc()//通过endTime排序
.list();//查询记录
for (HistoricActivityInstance activity : activities) {
System.out.println(activity.getActivityId() + " took "
+ activity.getDurationInMillis() + " milliseconds");
}
ProcessDefinition
流程定义类。可以从这里获得资源文件等。
ProcessInstance 对象
代表流程定义的执行实例。如范冰冰请了一天的假,她就必须发出一个流程实例的申请。一个流程实例包括了所有的运行节点。我们可以利用这个对象来了解当前流程实例的进度等信息。流程实例就表示一个流程从开始到结束的最大的流程分支,即一个流程中流程实例只有一个
Execution 对象
Activiti用这个对象去描述流程执行的每一个节点。在没有并发的情况下,Execution就是同ProcessInstance。流程按照流程定义的规则执行一次的过程,就可以表示执行对象Execution
各个service的更多具体方法可以看activiti文档
3.2.3 流程变量
每个流程都需要数据来执行相应的步骤,在activiti中这些数据成为变量,这些变量存储在数据库中。并且可以在表达式中使用变量。
一个流程实例可以具有任意数量的变量,每个变量都储存在ACT_RU_VARIABLE数据表中的一行中
流程变量可以在执行过程中添加/获取,也可用在脚本,表达式,任务监听器。。。中
VariableScope 接口定义了操作流程变量的api
void setVariable(String executionId, String variableName, Object value);
void setVariableLocal(String executionId, String variableName, Object value);
void setVariables(String executionId, Map<String, ? extends Object> variables);
void setVariablesLocal(String executionId, Map<String, ? extends Object> variables);
//--------------------------------------------
Map<String, Object> getVariables(String executionId);
Map<String, Object> getVariablesLocal(String executionId);
Map<String, Object> getVariables(String executionId, Collection<String> variableNames);
Map<String, Object> getVariablesLocal(String executionId, Collection<String> variableNames);
Object getVariable(String executionId, String variableName);
<T> T getVariable(String executionId, String variableName, Class<T> variableClass);
3.2.4 表达式的使用
Activiti使用UEL进行表达式解析。UEL代表统一表达式语言,并且是EE6规范的一部分(有关详细信息,请参阅EE6规范)
表达式可用于Java服务任务,执行监听器,任务监听器和条件序列流等地方。有两种类型的表达式,值表达式和方法表达式
- 值表达式:解析为一个值一般是流程变量,如果和spring整合,可以使用spring中的bean
$ {myVar}
$ {myBean.myProperty}
- 方法表达式:调用一个方法,带或不带参数。当调用一个没有参数的方法时,一定要在方法名后面添加空括号
$ {printer.print()}
$ {myBean.addNewOrder( 'ORDERNAME')}
$ {myBean.doSomething(myVar,execution)}
表达式中默认的对象
- execution:DelegateExecution它包含有关正在进行的执行的更多信息
- task:当前任务
- authenticatedUserId:当前通过身份验证的用户的ID。如果没有用户通过身份验证,则该变量不可用。
3.2.5 查询api说明
activiti有两种方法从引擎查询数据:查询API和sql查询,查询API可以使用链式编程有dsl的风格,实例如下
//这里使用了taskService实例,每个service都有一系列的查询方法
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("kermit")
.processVariableValueEquals("orderId", "0815")
.orderByDueDate().asc()
.list();
sql查询可以自己手写sql语句来处理一些查询API不能做的事,详情可以看javadoc
List<Task> tasks = taskService.createNativeTaskQuery()
.sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}")
.parameter("taskName", "gonzoTask")
.list();
long count = taskService.createNativeTaskQuery()
.sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T1, "
+ managementService.getTableName(VariableInstanceEntity.class) + " V1 WHERE V1.TASK_ID_ = T1.ID_")
.count();
4 项目开发
4.1 Spring Boot整合Activiti开发
1. 添加相关依赖
//activiti
compile "org.activiti:activiti-engine:6.0.0"
compile "org.activiti:activiti-spring:6.0.0"
compile "org.activiti:activiti-rest:6.0.0"
//或者使用starter
compile "org.activiti:activiti-spring-boot-starter-basic:5.21.0"
2.配置Activiti
public class ActivitiConfiguration {
@Autowired
DataSource dataSource;//数据源
@Autowired
PlatformTransactionManager transactionManager;//领域事务管理器
@Autowired
IdGenerator idGenerator;//uuid生成器
@Bean
public SpringProcessEngineConfiguration processEngineConfiguration() {
//和spring 整合使用 SpringProcessEngineConfiguration
SpringProcessEngineConfiguration springProcessEngineConfiguration = new SpringProcessEngineConfiguration();
//设置字符编码,防止生成图片时字体乱码
springProcessEngineConfiguration.setAnnotationFontName("宋体");
springProcessEngineConfiguration.setLabelFontName("宋体");
springProcessEngineConfiguration.setActivityFontName("宋体");
springProcessEngineConfiguration.setDataSource(dataSource);
springProcessEngineConfiguration.setTransactionManager(transactionManager);
springProcessEngineConfiguration.setDbIdentityUsed(false);
springProcessEngineConfiguration.setIdGenerator(idGenerator);
try {
//扫描classpath下的processes文件,读取流程文档并部署
springProcessEngineConfiguration.setDeploymentResources(getResourceByClassPath("processes/"));
} catch (IOException e) {
throw new ActivitiException("can not load process file");
}
return springProcessEngineConfiguration;
}
/**
* 获取指定路径下的资源
*/
public Resource[] getResourceByClassPath(String path) throws IOException {
List<Resource> resourceList = new ArrayList<>();
ClassPathResource classPathResource = new ClassPathResource(path);
File file = classPathResource.getFile();
if(file.isDirectory()){
File[] files= file.listFiles();
resourceList.addAll(Arrays.stream(files).map(fileItem->new FileSystemResource(fileItem)).collect(Collectors.toList()));
}else{
resourceList.add(new FileSystemResource(file));
}
return resourceList.toArray(new Resource[resourceList.size()]);
}
@Bean
public ProcessEngineFactoryBean processEngine() {
//使用工厂类创建流程引擎
ProcessEngineFactoryBean processEngineFactoryBean = new ProcessEngineFactoryBean();
processEngineFactoryBean.setProcessEngineConfiguration(processEngineConfiguration());
return processEngineFactoryBean;
}
@Bean
public RestResponseFactory restResponseFactory() {
return new RestResponseFactory();
}
@Bean
public RuntimeService runtimeService() throws Exception {
return processEngine().getObject().getRuntimeService();
}
@Bean
public TaskService taskService() throws Exception {
return processEngine().getObject().getTaskService();
}
@Bean
public ManagementService managementService() throws Exception {
return processEngine().getObject().getManagementService();
}
@Bean
public IdentityService identityService() throws Exception {
return processEngine().getObject().getIdentityService();
}
@Bean
public HistoryService historyService() throws Exception {
return processEngine().getObject().getHistoryService();
}
@Bean
public FormService formService() throws Exception {
return processEngine().getObject().getFormService();
}
@Bean
public DynamicBpmnService dynamicBpmnService() throws Exception {
return processEngine().getObject().getDynamicBpmnService();
}
@Bean
public RepositoryService repositoryService() throws Exception {
return processEngine().getObject().getRepositoryService();
}
@Bean
public ContentTypeResolver contentTypeResolver() {
return new DefaultContentTypeResolver();
}
}
3.替换activiti的用户模块
因为activiti用户模块过于简单不能直接在项目中应用,这里有三种方法替换activiti的用户模块
- 调用IdentifyService接口完成同步 //把系统中的user数据同步到activiti的表中
- 自定义SessionFactory //重写用户的数据访问接口用自己的类实现
- 创建同名视图 //创建视图于自己的用户表一一对应
- ACT_ID_GROUP 用户组表
- ACT_ID_INFO 用户信息表
- ACT_ID_MEMBERSHIP 用户关系表
- ACT_ID_USER 用户表
本项目用的是创建同名视图的方法解决的
4.设计流程图
这里使用官方的activiti-app应用设计开发
上图中标注的是比较常用的组件,和BPMN规范中定义的组件基本是一样的,有些是activiti对BPMN规范的拓展,所以在使用设计器之前首先要了解一下BPMN规范
服务任务的使用
服务任务是引擎自动执行的不需要人干预的任务,详情可以去看BPMN规范
在activiti中使用服务任务只需要实现JavaDelegate接口并在服务任务中配置Delegate expression属性,一般使用spring管理bean,然后使用表达式引用
@Component("sendMessageDelegate")
public class SendMessageDelegate implements JavaDelegate {
@Autowired
private WxMpService wxMpService;
@Override
public void execute(DelegateExecution execution) {
//这里是从流程变量中获取手机号码然后发送短信
String phone = (String) execution.getVariable("phone");
System.out.println("投诉人短信通知" + phone);
}
}
任务监听器的使用
用于在任务的各个阶段执行自定义Java逻辑
事件类型
- create 创建任务时发生
- assignee 在将任务分配给某人时发生
- complete 在任务完成时发生
- delete 在任务销毁时发生
//基础代码示例
public class MyAssignmentHandler implements TaskListener {
public void notify(DelegateTask delegateTask) {
// Execute custom identity lookups here
// and then for example call following methods:
delegateTask.setAssignee("kermit");
delegateTask.addCandidateUser("fozzie");
delegateTask.addCandidateGroup("management");
...
}
}
编辑器设置
执行监听器的使用
执行侦听器可以在流程执行期间发生某些事件时执行外部Java代码或计算表达式
事件类型
- 流程实例的开始和结束
- 活动的开始和结束
- 网关的开始和结束
- 中间事件的开始和结束
- 结束事件或开始事件
//基础代码示例
public class ExampleExecutionListenerOne implements ExecutionListener {
public void notify(ExecutionListenerExecution execution) throws Exception {
execution.setVariable("variableSetInExecutionListener", "firstValue");
execution.setVariable("eventReceived", execution.getEventName());
}
}
编辑器设置
后续配置于任务监听器一致
候选人设置
用于指派候选人或候选组,可以使用任务监听器在运行的时候动态指定
编辑器设置
5.发布流程
从设计器下载设计好的文档
放在classpath/process目录下,项目启动时会自动部署改目录下的流程文档
5.1 关于流程定义的版本
BPMN没有版本控制的概念,因为可执行的BPMN流程文件可能作为开发项目的一部分存在于版本控制系统(例如Subversion,Git或Mercurial)中。流程定义的版本会在部署期间创建。在部署期间,Activiti在ProcessDefinition存储到Activiti DB之前会为其分配一个版本,所以id就会像下面这样。
pubprocess:1:12508
在act_re_*表中可以看到部署的流程
6.启动流程
activiti 中有很多启动流程的方法,更多可以查阅RuntimeService的javadoc
本次项目中使用的是通过key来启动流程,这种启动方法每次启动的都是最新的流程
ProcessInstance instance = runtimeService.startProcessInstanceByKey(key, bussinessKey, variables);
7.接口编码
7.1任务处理
根据业务需求编写代码,本项目中有三个流程不同流程关联不同的业务实体,流程实例中有个bussiness_key可以储存关联的业务字段,也可以自己建立一张中间表把哪个流程对应哪个业务类型和业务实体对应起来,方便以后的查询
完成任务的基础接口
//variables 是传过来的流程变量 taskId 为任务id可以在act_ru_task 中找到
taskService.complete(taskId, variables);
//完成任务后流程引擎会自动跳转到下一个需要处理的节点,不需要人为干预
注意:在调用完成任务方法之前要判断当前处理人是否在候选人列表中,如果是候选人需要先认领任务然后再完成任务,此次项目中没有认领这一概念所以在完成任务的是否需要判断一下
业务需求中可能不止简单的调用complete接口那么简单,可能有附件评论啥的表单内容 还有对流程的一些复杂的操作,比如自由跳转等,考虑到系统的拓展性可以使用策略模式对任务处理进行拓展,以后如果有新增的业务需求只需要添加策略类就行了不需要修改源代码
策略接口
/**
* 处理策略接口
*/
public interface TaskAction {
void handlerTask(TaskActionConfigure taskActionConfigure);
String getName();
}
抽象基类
public abstract class AbstractTaskAction implements TaskAction{
public abstract void handlerTask(TaskActionConfigure taskActionConfigure,FormData formData);
public abstract String getName();
//这里提供一些实现的公用方法比如权限建议等方法
public void validPermission(Task task){
}
}
具体策略实现类
public class CompleteAction extends AbstractTaskAction{
//用来标注策略类
private static final String name = "complete";
@Autowired
private ProcessEngineConfiguration processEngineConfiguration;
@Override
public void handlerTask(TaskActionConfigure taskActionConfigure, FormData formData) {
//在这里处理任务
TaskService taskService = processEngineConfiguration.getTaskService();
taskService.complete(formData.getTaskId);
}
@Override
public String getName() {
return name;
}
}
策略调度类
public class ActionContext {
//根据策略实现类名字分组策略map
private Map<String,TaskAction> actionMap;
private TaskActionConfigure taskActionConfigure;
public void doAction(String action,FormData formData){
//选择合适的策略类
TaskAction taskAction = actionMap.get(action);
if(taskAction==null){
throw new ActivitiObjectNotFoundException("can not found action with name "+action);
}
taskAction.handlerTask(taskActionConfigure,formData);
}
}
控制器层调用
@Controller
public class TaskResource {
@Autowired
private ActionContext actionContext;
@PostMapping("api/task/{action}/action")
public ResponseEntity handlerTask(String action,FormData formData){
actionContext.doAction(action,formData);
return ResponseEntity.ok().build();
}
}
7.2 关于通用动态表单设计
表单就是用户在处理任务时会看到的页面,这些表单就和html中的表单一样,但是本次项目处理表单是在手机页面上,如果用html实现也可以但是可能有一些文件上传不好做,所以考虑使用json数据来描述表单,由客户端根据json数据渲染表单,这样做不同平台的客户端就必须都要实现根据json数据渲染本地视图的功能,目前项目中的表单是固定在手机端的,以下是对动态表单的一些构想
表单返回格式
{
formDefinition:{//表单元素定义
fieLds:[{//每个对象代表表单的一行
id:"label",
name:"label",
readOnly:false,
required:false,
type:"text"
},...],
key:"xxx",
name:"name",
outcomes:[{//不同处理按钮对应不同的处理情况
name:"已处理",
value:"0",
action:"complete"//处理策略
},...]
},
formId:"1"
}
数据提交格式
api/task/{action}/action //action 对应相应的策略
{
formId:"1",
taskId:"1",
outcome:"0",
values:{//表单数据
key:value,
...
}
}
7.3 代表列表查询
//查询某个用户某个流程的代表任务
对应act_ru_task中的数据
List<Task> task = taskService.createTaskQuery()//创建query对象
.processDefinitionKey(key)//根据流程类型筛选
.taskCandidateOrAssigned(user.getId())//根据候选人筛选
.orderByTaskCreateTime().desc()//排序
.listPage(pageable.getOffset(), pageable.getPageSize()*(pageable.getPageNumber()+1));//分页
//查询某个用户某个流程的已办任务
对应act_hi_taskinst中的数据
List<HistoricTaskInstance> htasks = historyService.createHistoricTaskInstanceQuery()//创建历史查询对象
.processDefinitionKey(key)//根据流程类型筛选
.finished()//筛选已经完成的
.taskAssignee(user.getId())//根据完成的用户id筛选
.orderByHistoricTaskInstanceEndTime().desc()//排序
.listPage(pageable.getOffset(), pageable.getPageSize()*(pageable.getPageNumber()+1));//分页
总结
整个项目大概的处理逻辑是用户查询自己的代表列表,然后获取表单填写或上传相关数据完成任务,然后流程自动流转到下一个节点,再由下一个人处理,在流传流转的过程中使用了任务监听器还有表达式和流程变量来指定任务的处理人,同时任务监听器也可在任务认领前或完成后处理相关的业务逻辑,流程里面使用的元素大部分都是UserTask,有少量的ServerTask用来处理一些需要自动做的事情,比如发送短信等功能
对于一般流程主要就是使用7大service中的一些方法去操作流程包括流程的查询,如果需要在任务处理前或处理后做一些业务可以配合activiti提供的任务监听器和执行监听器等去实现,对于一些需要特殊处理的任务就需要自定义命令类去实现