完整的笔记可以参考这个专栏,写的挺详细的:云尚办公课件笔记,come on boy
form-create前端组件
formProps记录了表单有哪些表单项,分别是哪些类型(下拉,单选,输入框)
formOptions记录了表单的一些配置项,比如label-width等
oa_process_type 审批类型
oa_process_template 审批模板
oa_prcess 审批列表(关联审批流流程实例id)
为指定的审批模板上传流程定义文件,将该文件保存到指定的目录下,并保存到审批模板的流程定义path字段,流程定义文件的文件名(不包括后缀名)作为审批模板的流程定义key字段
发布该审批模板,就是将此审批模板流程定义path字段所指定的路径所对应的文件读取成流,部署到activiti中,修改审批模板为已发布状态
发起审批,首先获取审批模板对应的表单,申请人填写表单内容,表单内容会转换为json数据传给后台,后台根据审批模板将数据保存到业务审批表,申请人填写的表单内容就保存在业务审批表的form_values字段中,这样就可以得到业务id了,然后activiti使用审批模板所保存的流程定义key启动流程实例,并传入业务id,同时传入将申请人填写的表单内容json数据转为的map存入到key为data的map中,这样流程实例就启动了,并且和业务id绑定了,并且也有了申请人填写的表单内容作为流程变量。
在流程实例启动后,立即查询当前流程实例的下一个节点的审批人(有可能有多个),取到这些审批人的assignee,给这些审批人推送消息以便于提醒他们尽快去审批。
推送完消息之后,将流程实例id保存到业务审批表的流程实例id字段。
//启动流程
@Override
public void startUp(ProcessFormVo processFormVo) {
//1 根据当前用户id获取用户信息
SysUser sysUser = sysUserService.getById(LoginUserInfoHelper.getUserId());
//2 根据审批模板id把模板信息查询
ProcessTemplate processTemplate = processTemplateService.getById(processFormVo.getProcessTemplateId());
//3 保存提交审批信息到业务表,oa_process
Process process = new Process();
//processFormVo复制到process对象里面
BeanUtils.copyProperties(processFormVo,process);
//其他值
process.setStatus(1); //审批中
String workNo = System.currentTimeMillis() + "";
process.setProcessCode(workNo);
process.setUserId(LoginUserInfoHelper.getUserId());
process.setFormValues(processFormVo.getFormValues());
process.setTitle(sysUser.getName() + "发起" + processTemplate.getName() + "申请");
baseMapper.insert(process);
//4 启动流程实例 - RuntimeService
//4.1 流程定义key
String processDefinitionKey = processTemplate.getProcessDefinitionKey();
//4.2 业务key processId
String businessKey = String.valueOf(process.getId());
//4.3 流程参数 form表单json数据,转换map集合
String formValues = processFormVo.getFormValues();
//formData
JSONObject jsonObject = JSON.parseObject(formValues);
JSONObject formData = jsonObject.getJSONObject("formData");
//遍历formData得到内容,封装map集合
Map<String,Object> map = new HashMap<>();
for(Map.Entry<String,Object> entry:formData.entrySet()) {
map.put(entry.getKey(),entry.getValue());
}
Map<String,Object> variables = new HashMap<>();
variables.put("data",map);
//启动流程实例
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey,
businessKey, variables);
//5 查询下一个审批人
//审批人可能多个
List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();
List<String> nameList = new ArrayList<>();
for(Task task : taskList) {
String assigneeName = task.getAssignee();
SysUser user = sysUserService.getUserByUserName(assigneeName);
String name = user.getName();
nameList.add(name);
//推送消息
messageService.pushPendingMessage(process.getId(),user.getId(),task.getId());
}
process.setProcessInstanceId(processInstance.getId());
process.setDescription("等待"+ StringUtils.join(nameList.toArray(), ",")+"审批");
//7 业务和流程关联 更新oa_process数据
baseMapper.updateById(process);
//记录操作审批信息记录
processRecordService.record(process.getId(),1,"发起申请");
}
用来记录1个审批所走过的轨迹,首先添加审批记录表,如下
在开启流程实例的方法中,生成一条审批记录存入到审批记录表当中(调用处在上面)
@Override
public void record(Long processId, Integer status, String description) {
Long userId = LoginUserInfoHelper.getUserId();
SysUser sysUser = sysUserService.getById(userId);
ProcessRecord processRecord = new ProcessRecord();
processRecord.setProcessId(processId);
processRecord.setStatus(status);
processRecord.setDescription(description);
processRecord.setOperateUser(sysUser.getName());
processRecord.setOperateUserId(userId);
baseMapper.insert(processRecord);
}
使用activiti工作流提供的taskService根据当前用户作为assignee查询当前用户的的任务列表(并且使用了分页),遍历这个任务列表,可以拿到每个任务对应的流程实例,然后拿到流程实例对应的业务key(其实,通过task就可以直接拿到businessKey),拿到businessKey之后,就可以查询业务审批表,来获取到申请人所填写的申请表单内容了。这样当前审批人就可以根据申请人提交的表单内容做审批了。注意,工作流的taskId也带上去了。
//查询待处理任务列表
@Override
public IPage<ProcessVo> findfindPending(Page<Process> pageParam) {
//1 封装查询条件,根据当前登录的用户名称
TaskQuery query = taskService.createTaskQuery()
.taskAssignee(LoginUserInfoHelper.getUsername())
.orderByTaskCreateTime()
.desc();
//2 调用方法分页条件查询,返回list集合,待办任务集合
//listPage方法有两个参数
//第一个参数:开始位置 第二个参数:每页显示记录数
int begin = (int)((pageParam.getCurrent()-1)*pageParam.getSize());
int size = (int)pageParam.getSize();
List<Task> taskList = query.listPage(begin, size);
long totalCount = query.count();
//3 封装返回list集合数据 到 List里面
//List -- List
List<ProcessVo> processVoList = new ArrayList<>();
for(Task task : taskList) {
//从task获取流程实例id
String processInstanceId = task.getProcessInstanceId();
//根据流程实例id获取实例对象
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
//从流程实例对象获取业务key---processId
String businessKey = processInstance.getBusinessKey();
if(businessKey == null) {
continue;
}
//根据业务key获取Process对象
long processId = Long.parseLong(businessKey);
Process process = baseMapper.selectById(processId);
//Process对象 复制 ProcessVo对象
ProcessVo processVo = new ProcessVo();
BeanUtils.copyProperties(process,processVo);
processVo.setTaskId(task.getId());
//放到最终list集合processVoList
processVoList.add(processVo);
}
//4 封装返回IPage对象
IPage<ProcessVo> page = new Page<ProcessVo>(pageParam.getCurrent(),
pageParam.getSize(),totalCount);
page.setRecords(processVoList);
return page;
}
测试步骤如下,
先添加审批模板,然后发布,使用admin填写审批表单发起1个审批,然后下一节点审批人zhangsan查看自己的待办任务
张三查看自己的待办任务列表如下
前面张三查看到了自己的待办任务列表,待办任务列表数据的id是业务表的id,因此前端将此id传进来,来查询审批详情信息(比如用户什么时间提的申请,填写的表单内容)。
接着,继续使用业务表的id查询审批记录表中的审批记录(前面发起审批时,记录了,当前自己审批时,也须往这个表中记录)
接着,根据审批业务表的id得到审批模板信息
接着,查询当前人是否可以审批该审批流程实例(这里要推敲下)
下面这个代码感觉有点问题:上面查询待办的时候,拿的就是当前人作为assignee查询的任务,那么查到的审批单对应的审批人是一定包括当前人的(可能有多个审批人),并且,也是有查询到对应的taskId的,我觉得这里应该让前端把taskId传过来,就可以得到这个taskId对应的assignee是不是当前人。但是好好想想,他这样做也没啥问题,他其实就是在判断当前人是否是这个流程实例的当前所在节点的审批人,如果是的话,那就可以审批,不是的话,就不可以审批
问:假设某个节点是个会签节点的话,那么这个会签节点的这个审批记录该如何查询封装呢?如何展示呢?是放到同1个节点中并标名这是个会签节点,还是不管它是会签节点还是非会签节点就特么按时间来排序组成审批轨迹。很显然,下面这种就是第二种情况。
//查看审批详情信息
@Override
public Map<String, Object> show(Long id) {
//1 根据流程id获取流程信息Process
Process process = baseMapper.selectById(id);
//2 根据流程id获取流程记录信息
LambdaQueryWrapper<ProcessRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ProcessRecord::getProcessId,id);
List<ProcessRecord> processRecordList = processRecordService.list(wrapper);
//3 根据模板id查询模板信息
ProcessTemplate processTemplate = processTemplateService.getById(process.getProcessTemplateId());
//4 判断当前用户是否可以审批
//可以看到信息不一定能审批,不能重复审批
boolean isApprove = false;
List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
for(Task task : taskList) {
//判断任务审批人是否是当前用户
String username = LoginUserInfoHelper.getUsername();
if(task.getAssignee().equals(username)) {
isApprove = true;
}
}
//5 查询数据封装到map集合,返回
Map<String,Object> map = new HashMap<>();
map.put("process", process);
map.put("processRecordList", processRecordList);
map.put("processTemplate", processTemplate);
map.put("isApprove", isApprove);
return map;
}
张三点击审批详情后,进入到如下页面
最重要的一步来了,审批人对当前审批单进行审批(同意or驳回)。驳回即审批不通过,流程结束。
如果当前审批人 对 此审批单 审批通过,那么流程引擎会自动将任务流转到下1个审批人那里(如果还有下一个任务节点的话),并通知下一个审批人(可能是多个人),下一个人就可以查询到自己的待办任务。
如果当前审批人 对 此审批单 驳回,那么流程结束。结束流程的代码是固定模式的。
不管当前审批人 对 此审批单 是审批通过 还是驳回,都要记录到审批记录表,并且更新审批的业务表。
//审批
@Override
public void approve(ApprovalVo approvalVo) {
//1 从approvalVo获取任务id,根据任务id获取流程变量
String taskId = approvalVo.getTaskId();
Map<String, Object> variables = taskService.getVariables(taskId);
for(Map.Entry<String,Object> entry:variables.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
//2 判断审批状态值
if(approvalVo.getStatus() == 1) {
//2.1 状态值 =1 审批通过
Map<String, Object> variable = new HashMap<>();
taskService.complete(taskId,variable);
} else {
//2.2 状态值 = -1 驳回,流程直接结束
this.endTask(taskId);
}
//3 记录审批相关过程信息 oa_process_record
String description = approvalVo.getStatus().intValue() ==1 ? "已通过" : "驳回";
processRecordService.record(approvalVo.getProcessId(),approvalVo.getStatus(),description);
//4 查询下一个审批人,更新流程表记录 process表记录
Process process = baseMapper.selectById(approvalVo.getProcessId());
//查询任务
List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
if(!CollectionUtils.isEmpty(taskList)) {
List<String> assignList = new ArrayList<>();
for(Task task : taskList) {
String assignee = task.getAssignee();
SysUser sysUser = sysUserService.getUserByUserName(assignee);
assignList.add(sysUser.getName());
//TODO 公众号消息推送
}
//更新process流程信息
process.setDescription("等待" + StringUtils.join(assignList.toArray(), ",") + "审批");
process.setStatus(1);
} else {
if(approvalVo.getStatus().intValue() == 1) {
process.setDescription("审批完成(通过)");
process.setStatus(2);
} else {
process.setDescription("审批完成(驳回)");
process.setStatus(-1);
}
}
baseMapper.updateById(process);
}
驳回就是手动创建从当前节点到结束节点之间的流向,然后完成当前task,此时,流程引擎就会结束当前流程实例,流程处于完结状态了
//结束流程
private void endTask(String taskId) {
//1 根据任务id获取任务对象 Task
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
//2 获取流程定义模型 BpmnModel
BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
//3 获取结束流向节点
List<EndEvent> endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
if(CollectionUtils.isEmpty(endEventList)) {
return;
}
FlowNode endFlowNode = (FlowNode)endEventList.get(0);
//4 当前流向节点
FlowNode currentFlowNode = (FlowNode)bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
// 临时保存当前活动的原始方向
List originalSequenceFlowList = new ArrayList<>();
originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
//5 清理当前流动方向
currentFlowNode.getOutgoingFlows().clear();
//6 创建新流向
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newSequenceFlow");
newSequenceFlow.setSourceFlowElement(currentFlowNode);
newSequenceFlow.setTargetFlowElement(endFlowNode);
//7 当前节点指向新方向
List newSequenceFlowList = new ArrayList();
newSequenceFlowList.add(newSequenceFlow);
currentFlowNode.setOutgoingFlows(newSequenceFlowList);
//8 完成当前任务
taskService.complete(task.getId());
}
当前用户查询自己审批过的任务(注明:通过或驳回的都算,但用户发起审批不算1个已处理的任务)
此处的实现,使用的是工作流提供的HistoryService根据taskAssignee是当前人,并且是已完成的历史任务实例。
//已处理
@Override
public IPage<ProcessVo> findProcessed(Page<Process> pageParam) {
//封装查询条件
HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
.taskAssignee(LoginUserInfoHelper.getUsername())
.finished().orderByTaskCreateTime().desc();
//调用方法条件分页查询,返回list集合
// 开始位置 和 每页显示记录数
int begin = (int)((pageParam.getCurrent()-1)*pageParam.getSize());
int size = (int)pageParam.getSize();
List<HistoricTaskInstance> list = query.listPage(begin, size);
long totalCount = query.count();
//遍历返回list集合,封装List
List<ProcessVo> processVoList = new ArrayList<>();
for(HistoricTaskInstance item : list) {
//流程实例id
String processInstanceId = item.getProcessInstanceId();
//根据流程实例id查询获取process信息
LambdaQueryWrapper<Process> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Process::getProcessInstanceId,processInstanceId);
Process process = baseMapper.selectOne(wrapper);
// process -- processVo
ProcessVo processVo = new ProcessVo();
BeanUtils.copyProperties(process,processVo);
processVo.setTaskId("0");
//放到list
processVoList.add(processVo);
}
//IPage封装分页查询所有数据,返回
IPage<ProcessVo> pageModel =
new Page<ProcessVo>(pageParam.getCurrent(),pageParam.getSize(),
totalCount);
pageModel.setRecords(processVoList);
return pageModel;
}
查询当前用户发起的审批任务,其实就记录在oa_process表,根据发起人用户id查询即可
//已发起
@Override
public IPage<ProcessVo> findStarted(Page<ProcessVo> pageParam) {
ProcessQueryVo processQueryVo = new ProcessQueryVo();
processQueryVo.setUserId(LoginUserInfoHelper.getUserId());
IPage<ProcessVo> pageModel = baseMapper.selectPage(pageParam, processQueryVo);
for (ProcessVo item : pageModel.getRecords()) {
item.setTaskId("0");
}
return pageModel;
}