创建历史审批表
CREATE TABLE `tb_his_approval` (
`id` varchar(50) NOT NULL COMMENT '主键',
`business_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '业务表主键',
`approval_name` varchar(50) DEFAULT NULL COMMENT '审批人姓名',
`approval_time` datetime DEFAULT NULL COMMENT '审批时间',
`approval_comments` text COMMENT '审批意见',
`approval_attachment_url` varchar(255) DEFAULT NULL COMMENT '审批附件的地址,可以看到当时上传的文件,这是属于你的扩展功能,可以在这里自己扩展',
`task_name` varchar(50) DEFAULT NULL COMMENT '任务节点',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='历史审批表';
创建实体类
package com.dmg.entity;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 历史审批表
*/
@Data
@TableName("tb_his_approval")
public class HisApproval {
/**
* 主键
*/
private String id;
/**
* 业务表主键
*/
private String businessId;
/**
* 审批人姓名
*/
private String approvalName;
/**
* 审批时间
*/
private Date approvalTime;
/**
* 审批意见
*/
private String approvalComments;
/**
* 审批附件的地址,可以看到当时上传的文件,这是属于你的扩展功能,可以在这里自己扩展
*/
private String approvalAttachmentUrl;
/**
* 任务节点
*/
private String taskName;
}
创建mapper
package com.dmg.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dmg.entity.HisApproval;
public interface HisApprovalMapper extends BaseMapper<HisApproval> {
}
先在请假表造一条数据
INSERT INTO `flowable`.`tb_qj` (`id`, `name`, `day`, `create_by`, `create_by_name`, `instance_id`, `approval_status`, `create_time`) VALUES ('001', '张三-请假单', 1, 'zhangsan', '张三', NULL, '1', '2022-11-25 17:26:52');
创建请求类
package com.dmg.vo.req;
import lombok.Data;
@Data
public class Req {
/**
* 主键
*/
private String id;
/**
* 审批意见
*/
private String approvalComments;
/**
* 通过:true 驳回false
*/
private String flag;
/**
* 审批人
*/
private String approver;
}
创建请假service
package com.dmg.service;
import com.dmg.vo.req.QjReq;
public interface QjService {
public boolean submitApplication(Req req);
}
创建请假实现类
package com.dmg.service.impl;
import com.dmg.entity.Qj;
import com.dmg.mapper.QjMapper;
import com.dmg.service.FlowService;
import com.dmg.service.QjService;
import com.dmg.vo.req.QjReq;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.Map;
@Service
public class QjServiceImpl implements QjService {
@Autowired
private QjMapper qjMapper;
@Autowired
private FlowService flowService;
/**
* 提交请假单
* @param req
*/
@Transactional
@Override
public boolean submitApplication(Req req) {
//从数据库查询当前信息
Qj qj=qjMapper.selectById(req.getId());
if(qj==null){
throw new RuntimeException("请假单不存在");
}
if("3".equals(qj.getApprovalStatus())){
throw new RuntimeException("已审批通过,不能提交");
}
//业务key 用于待办已办 扩展字段 展示使用
//我这里以 流程定义key + 主键 + 申请单名称 的方式命名
String businessKey="qj."+qj.getId()+"."+qj.getName();
//参数 分支条件
Map<String, Object> map=new HashMap<>();
// >=3走一级审批 <3走 三级审批
map.put("num",qj.getDay());
//如果流程实例为空 启动一个新的流程实例
String instanceId = flowService.startProcessInstance(businessKey, map,qj.getInstanceId());
//保存instanceId到数据库
qj.setInstanceId(instanceId);
//审批状态改为 2:审批中
qj.setApprovalStatus("2");
qjMapper.updateById(qj);
return true;
}
}
然后在流程服务和流程服务实现类加入对应的方法
public interface FlowService {
public String startProcessInstance(String businessKey, Map<String, Object> map,String instanceId);
}
package com.dmg.service.impl;
/**
* 流程定义服务实现类
*/
@Service
public class FlowServiceImpl extends FlowServiceFactory implements FlowService {
@Autowired
private HisApprovalMapper hisApprovalMapper;
/**
* 启动流程实例,返回流程实例id 完成第一个任务
* @param map 分支条件参数
* @param businessKey 业务表拼接内容 流程定义key+业务表主键+申请单名称
* @param instanceId 流程实例id 如果不为空表示还没有走完这个流程 不需要发起
*/
@Override
public String startProcessInstance(String businessKey, Map<String, Object> map,
String instanceId){
//截取业务key
String []res=businessKey.split("\\.");
//流程定义key
String processDefinitionKey=res[0];
//业务表主键
String businessId=res[1];
//我这里写死 你要改成 申请单传入过来的账号
String userId="zhangsan";
if(StringUtils.isEmpty(instanceId)){
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey,businessKey, map);
if(processInstance==null){
throw new RuntimeException("启动流程实例失败");
}
instanceId=processInstance.getId();
}
//启动流程实例之后 完成第一个任务 就是提交申请单这一个节点 这样才能走到领导审批的节点
//根据流程实例id 获取任务
Task task = taskService.createTaskQuery()
.processInstanceId(instanceId)
.singleResult();
//先认领任务
taskService.claim(task.getId(),userId);
//在完成任务
taskService.complete(task.getId(),map);
//添加历史审批
insertHisApproval( "", userId, businessId, task.getName());
//返回流程实例id
return instanceId;
}
/**
* 添加历史审批
* @param approvalComments 审批意见
* @param assignee 审批人
* @param businessId 业务表主键
* @param taskName 节点名称
*/
public void insertHisApproval(String approvalComments,String assignee,String businessId,String taskName){
HisApproval hisApproval=new HisApproval();
hisApproval.setId(UUID.randomUUID().toString().replaceAll("-",""));
hisApproval.setApprovalComments(approvalComments);
hisApproval.setApprovalName(assignee);
hisApproval.setBusinessId(businessId);
//任务节点
hisApproval.setTaskName(taskName);
hisApproval.setApprovalTime(new Date());
hisApprovalMapper.insert(hisApproval);
}
}
然后在请假控制层加入
package com.dmg.controller;
@RestController
@RequestMapping("/qj")
public class QjController {
@Autowired
private QjService qjService;
/***
* 提交请假单
* @param req
* @return
*/
@PostMapping("submitApplication")
public Result submitApplication(@RequestBody Req req){
return Result.success(qjService.submitApplication(req));
}
}
http://localhost:8080/qj/submitApplication
{
"id":"001"
}
从数据库找一个id, 提交请假单
可以看到数据库已经发生了变化
接下来 我们看下所有任务信息
先创建分页vo
package com.dmg.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dmg.entity.Qj;
import com.dmg.vo.TaskVo;
public interface QjMapper extends BaseMapper<Qj> {
/**
* 分页查询任务
* @param page
* @return
*/
IPage<TaskVo>getTaskPage(Page<?>page);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dmg.mapper.QjMapper">
<select id="getTaskPage" resultType="com.dmg.vo.TaskVo">
SELECT
ID_ taskId,
PROC_INST_ID_ procInsId,
PROC_DEF_ID_ procDefId,
NAME_ taskName,
CREATE_TIME_ createTime,
SUSPENSION_STATE_ suspensionState,
EXECUTION_ID_ executionId,
ASSIGNEE_ assigneeName
FROM
act_ru_task
order by CREATE_TIME_ desc
</select>
</mapper>
package com.dmg.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.dmg.vo.TaskVo;
import com.dmg.vo.req.QjReq;
public interface QjService {
public IPage<TaskVo> getTaskPage(int current,int size);
}
package com.dmg.service.impl;
@Service
public class QjServiceImpl implements QjService {
@Autowired
private QjMapper qjMapper;
/**
* 查询任务信息
* @param current
* @param size
* @return
*/
@Override
public IPage<TaskVo> getTaskPage(int current, int size) {
Page<?>page=new Page<>();
page.setCurrent(current);
page.setSize(size);
IPage<TaskVo> taskPage = qjMapper.getTaskPage(page);
return taskPage;
}
}
package com.dmg.controller;
/**
* 任务控制层
*/
@RestController
public class TaskController {
@Autowired
private QjService qjService;
/**
* 分页查询任务信息
*/
@PostMapping("getTaskPage")
public Result getTaskPage(@RequestBody PageReq req){
IPage<TaskVo> page = qjService.getTaskPage(req.getCurrent()
, req.getSize());
return Result.success(page);
}
}
package com.dmg.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
/**
* 任务
*/
@Data
public class TaskVo {
// 任务编号
private String taskId;
//任务执行编号
private String executionId;
// 任务名称
private String taskName;
//任务执行人名称
private String assigneeName;
//流程定义ID
private String procDefId;
//流程定义key
private String procDefKey;
//流程实例ID
private String procInsId;
//任务创建时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 是否激活 1正常 2挂起
*/
private String suspensionState;
}
接下来我们看下结果
http://localhost:8080/getTaskPage
{
"current":"1",
"size": 2
}
我们可以看到assigneeName还没有人认领,那么我们先找一个人去认领这个任务
package com.dmg.service;
public interface FlowService {
public void claim(String taskId,String account);
}
package com.dmg.service.impl;
/**
* 流程定义服务实现类
*/
@Service
public class FlowServiceImpl extends FlowServiceFactory implements FlowService {
/**
* 认领 审批单
* @param taskId
* @param account
*/
@Override
public void claim(String taskId, String account) {
//吧act_ru_task表的ASSIGNEE_ 设置为account 来审批
taskService.claim(taskId,account);
}
}
package com.dmg.controller;
/**
* 流程定义控制层
*/
@RestController
public class ProcessDefinitionController {
@Autowired
private FlowService flowService;
/**
* 认领
*/
@PostMapping("claim")
public Result claim(@RequestBody FlowReq req){
flowService.claim(req.getTaskId(),req.getAccount());
return Result.success();
}
}
我们来看下效果
{
"taskId":"5da9023a-6cb9-11ed-b720-005056c00008",
"account":"lisi"
}
可以看到act_ru_task运行时任务表的ASSIGNEE_字段已经有值了,说明认领了,可以审批了
接下来,我们使用lisi来进行审批驳回,看看是什么效果
package com.dmg.controller;
@RestController
@RequestMapping("/qj")
public class QjController {
@Autowired
private QjService qjService;
/***
* 审批请假单
* @param req
* @return
*/
@PostMapping("approval")
public Result approval(@RequestBody Req req){
return Result.success(qjService.approval(req));
}
}
package com.dmg.service;
public interface QjService {
public boolean approval(Req req);
}
package com.dmg.service.impl;
@Service
public class QjServiceImpl implements QjService {
@Autowired
private QjMapper qjMapper;
@Autowired
private FlowService flowService;
/**
* 审批请假单
* @param req
*/
@Transactional
@Override
public boolean approval(Req req) {
//从数据库查询当前信息
Qj qj=qjMapper.selectById(req.getId());
if(qj==null){
throw new RuntimeException("请假单不存在");
}
//2:审批中
if(!"2".equals(qj.getApprovalStatus())){
throw new RuntimeException("状态不是审批中,不能审批");
}
//参数 分支条件
Map<String, Object> map=new HashMap<>();
//通过:true 驳回false
map.put("flag",req.getFlag());
String res = flowService.complete(qj.getInstanceId(), map, qj.getId(), req.getApprovalComments(), req.getApprover());
if("end".equals(res)){
//如果流程已经走完了 那么吧审批状态修改为 3:审批通过
qj.setApprovalStatus("3");
qjMapper.updateById(qj);
}
if("false".equals(req.getFlag())){
//如果是驳回 那么修改状态为 未提交
qj.setApprovalStatus("1");
qjMapper.updateById(qj);
}
return true;
}
}
package com.dmg.service;
public interface FlowService {
public String complete(String instanceId,Map<String,Object>map,String businessKey,String approvalComments,String assignee);
}
package com.dmg.service.impl;
/**
* 流程定义服务实现类
*/
@Service
public class FlowServiceImpl extends FlowServiceFactory implements FlowService {
@Autowired
private HisApprovalMapper hisApprovalMapper;
@Autowired
private QjMapper qjMapper;
/**
* 审批
* @param businessId 业务表主键
* @param map 分支条件
* @param approvalComments 审批意见
* @param instanceId 流程实例id
* @param assignee 审批人
*/
@Override
public String complete(String instanceId,Map<String,Object>map,
String businessId,String approvalComments,
String assignee){
//根据流程实例id 获取任务
Task task = taskService.createTaskQuery()
.processInstanceId(instanceId)
.taskAssignee(assignee)
.singleResult();
if(task==null){
throw new RuntimeException("任务不存在,或者不是当前任务的审批人");
}
//完成任务
taskService.complete(task.getId(),map);
//审批完成后 再次查询任务是否还有数据
long count = taskService.createTaskQuery()
.processInstanceId(instanceId)
.count();
//添加历史审批
insertHisApproval( approvalComments, assignee, businessId, task.getName());
if(count==0){
//返回结束 修改表单的状态
return "end";
}
return "ok";
}
/**
* 添加历史审批
* @param approvalComments 审批意见
* @param assignee 审批人
* @param businessId 业务表主键
* @param taskName 节点名称
*/
public void insertHisApproval(String approvalComments,String assignee,String businessId,String taskName){
HisApproval hisApproval=new HisApproval();
hisApproval.setId(UUID.randomUUID().toString().replaceAll("-",""));
hisApproval.setApprovalComments(approvalComments);
hisApproval.setApprovalName(assignee);
hisApproval.setBusinessId(businessId);
//任务节点
hisApproval.setTaskName(taskName);
hisApproval.setApprovalTime(new Date());
hisApprovalMapper.insert(hisApproval);
}
}
我们来看下结果
http://localhost:8080/qj/approval
{
"id":"001",
"approvalComments":"我不同意",
"flag":"false",
"approver":"lisi"
}
我们可以看到状态回到了未提交,任务也回到了提交的节点
然后我们一路审批通过,flag设置为true,走完这个流程
这个时候我们可以看到,请假表已经更改了状态为审批通过,运行时任务表也走完了整个流程