弄(抄)了近乎一个星期,终于把完整流程走通了,记下来,之后还要做好几个
controller层
controller层主要是产生对象,是前端传数据的接口,我把功能单独列出来方便看(抄)
提交审核,权限路径为monthlyPlan:monthlyPlan:verify
,前端通用请求为“/verify”
,通用结构都是一样的,
屌用service层的verify方法来进行结果判定,成功提交审核失败是捕获异常、打印异常堆栈跟踪信息,并将异常消息作为错误结果返回给调用方
/**
* 提交审核
*
* @param id
* @return
*/
@AutoLog(value = "月度计划产值-编辑")
@ApiOperation(value="月度计划产值-编辑", notes="月度计划产值-编辑")
@RequiresPermissions("monthlyPlan:monthlyPlan:verify")
@GetMapping(value = "/verify")
public Result<String> verify(@RequestParam("id") String id) {
try {
monthlyPlanService.verify(id);
return Result.OK("已提交审核!");
} catch (Exception e) {
e.printStackTrace();
return Result.error(e.getMessage());
}
}
该结构用list封装了想要查询的几个值,其中有默认值,初始化一个querywapper对象,构建查询条件,然后创建page对象指定当前页码和每页数量,调用service和调取项目和用户信息的方法进行分页查询,用page和querywarapper进行传参
/**
* 月度计划产值查询
*
* @param monthlyPlan
* @param pageNo
* @param pageSize
* @param req
* @return
*/
@ApiOperation(value="月度计划产值-分页列表查询", notes="月度计划产值-分页列表查询")
@GetMapping(value = "/monthlyPlan/list")
// @RequiresPermissions("warehosueMgr:warehosue:audit")
public Result<IPage<MonthlyPlanVO>> monthlyPlanList(MonthlyPlanVO monthlyPlan,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<MonthlyPlanVO> queryWrapper = QueryGenerator.initQueryWrapper(monthlyPlan, req.getParameterMap());
Page<MonthlyPlanVO> page = new Page<>(pageNo, pageSize);
IPage<MonthlyPlanVO> pageList = monthlyPlanService.pageVerifyWithProjectAndSysuser(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 月度计划审核
*
* @param vo
*/
@AutoLog(value = "月度计划产值-审核")
@ApiOperation(value="月度计划产值-审核", notes="月度计划产值-审核")
@RequiresPermissions("monthlyPlan:monthPlan:audit") //月度产值审核
@PostMapping(value = "/monthlyPlan/audit")
public Result<String> monthlyPlanAudit(@RequestBody AuditVO<MonthlyPlan> vo) {
monthlyPlanService.verifyStep(vo);
return Result.OK("入库审批完成!");
}
/**
* 月度计划审核详情
* @param id
*/
@AutoLog(value = "月度计划产值-审核详情")
@ApiOperation(value="月度计划产值-审核详情", notes="月度计划产值-审核详情")
@GetMapping(value = "/monthlyPlan/verifyQuery")
public Result<AuditVO> monthlyPlanVerifyQuery(@RequestParam("id") String id) {
AuditVO vo = monthlyPlanService.monthlyPlanVerifyQuery(id);
return Result.OK(vo);
}
构造一个list对象来传递service中过程密钥的,这里面分别有id,字符串类型,index,整形,和type,字符转类型,如果
list的长度小于等于0,那么结果返回error,没有审批历史,
在ipage中构造page对象,page进行记录,将查询到信息写入list’中,同时记录list的长度
/**
* 流程审批节点历史信息详情列表
*
* @param id
*/
@AutoLog(value = "月度计划产值管理-审核")
@ApiOperation(value="月度计划产值管理-审核", notes="月度计划产值管理-入库审核")
@GetMapping(value = "/monthlyPlan/verify/historyQuery")
public Result<IPage<AuditHistroyVO>> monthlyPlanVerifyHistoryQuery(@RequestParam("id") String id, @RequestParam("index") Integer index, @RequestParam("type") String type) {
List<AuditHistroyVO> list = monthlyPlanService.auditHistoryListByProcessKey(id,index,type);
if(list.size() <=0){
return Result.error(1001,"该节点没有审批历史!");
}
IPage<AuditHistroyVO> page = new Page<>();
page.setRecords(list);
page.setTotal(list.size());
return Result.OK(page);
}
同上,当时这里查询的是queryhistorulist中的id返回数量和信息
/**
* 流程审批全流程详情
* @param id
*/
@AutoLog(value = "月度计划产值-审核")
@ApiOperation(value="月度计划产值-审核", notes="月度计划产值-审核")
@GetMapping(value = "/monthlyPlan/verify/historyList")
public Result<IPage<AuditHistroyVO>> historyList(@RequestParam("id") String id) {
List<AuditHistroyVO> list = monthlyPlanService.queryhistoryList(id);
if(list.size() <=0){
return Result.error(1001,"没有审批历史!");
}
IPage<AuditHistroyVO> page = new Page<>();
page.setRecords(list);
page.setTotal(list.size());
return Result.OK(page);
}
service&implements层
这个地方是对象和方法实现类,尽可能讲清楚,方便回来看,
先说一下我们审核的逻辑,是从表单开始,表单有项目经历提交审核,然就发油经营部审核,然后经营部副总进行总审批,然后总经理审批,这样流程结束,每个环节出现驳回都将返回到最开始的一级,即由项目经理填写
private String[] monthlyPlanAuditTasks= {"start","form","step1","step2","step3","end"};
private String[] taskNodeNames = {"流程开始","项目经理提审","经营部审核","经营部副总审批","总经理审核","审批结束"};
以上两个一个审核任务节点,下面的是节点名称
@Override
public IPage<MonthlyPlanVO> pageVerifyWithProjectAndSysuser(Page<MonthlyPlanVO> page, QueryWrapper<MonthlyPlanVO> queryWrapper) {
System.out.println(monthlyPlanEndListener);
//获取登录用户信息
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
//获取用户角色
List<String> userRoles = iSysBaseAPI.getRolesByUsername(sysUser.getUsername());
//获取用户拥有的项目部权限
//List<ProjectMgr> projects = this.getBaseMapper().queryProjectByTeamUserId(sysUser.getId());
//List<String> projectIds = projects.stream().map(ProjectMgr::getId).collect(Collectors.toList());
List<String> insIds = new ArrayList<>();
// step1审核权限角色(部门负责人)
if(userRoles.indexOf("monthlyPlan_step1")!=-1){
List<String> insId1 = taskService.createTaskQuery()
.processDefinitionKey("monthlyPlan")
.taskName("step1")
.list()//这个地方不要分页
.stream()
.map(Task::getProcessInstanceId)
.collect(Collectors.toList());
insIds.addAll(insId1);
}
// step2审核权限角色(副总经理)
if(userRoles.indexOf("monthlyPlan_step2")!=-1){
List<String> insId2 = taskService.createTaskQuery()
.processDefinitionKey("monthlyPlan")
.taskName("step2")
.list() //这个地方不要分页
.stream()
.map(Task::getProcessInstanceId)
.collect(Collectors.toList());
insIds.addAll(insId2);
}
// step3审核权限角色(总经理)
if(userRoles.indexOf("monthlyPlan_step3")!=-1){
List<String> insId2 = taskService.createTaskQuery()
.processDefinitionKey("monthlyPlan")
.taskName("step3")
.list() //这个地方不要分页
.stream()
.map(Task::getProcessInstanceId)
.collect(Collectors.toList());
insIds.addAll(insId2);
}
//必须满足 审核中的流程 && 且项目是当前用户所在项目组
if(insIds.size()>=1 /**&& projectIds.size()>=1*/){
queryWrapper.lambda()
.in(MonthlyPlanVO::getActInstanceId,insIds); //符合条件的流程Id
// .in(WarehosueVO::getProjectId,projectIds); //所在项目部
queryWrapper
.orderByDesc("verify_status") //根据出入库状态排序
.orderByDesc("create_time"); //根据时间排序
IPage<MonthlyPlanVO> pageList = this.getBaseMapper().pageVerifyWithProjectAndSysuser(page,queryWrapper);
return pageList;
}
//如果没有流程则返回空对象
return new Page<>(page.getCurrent(), page.getSize(), 0);
}
这个地方就是之前审核要进行调用的实现类,构造一个monthlyPlan的对象,这个对象是一个查取的方法,利用id去查取数据库的对象,如果其中monthlyPlan这个对象查到的状态等于1或者状态等于二,相当于审核未通过
如果查到的这个叫审核状态等于0,相当于第一次提交被驳回了们开始重新提交,verifyStart就是审核流程开始的方法,方法的参数为该对象,每当流程重新开始就更新数据库更逊查询结果
如果违背驳回,那么就钩爪一个审核信息对象,该对象可以对mongthlyPlan这个对象写入记录,获取登录用户的信息了解其权限然后得到结果,完成结点
//提交审核
@Override
public void verify(String id) throws Exception {
MonthlyPlan monthlyPlan = this.getBaseMapper().selectById(id);
if (monthlyPlan.getVerifyStatus() == 1 || monthlyPlan.getVerifyStatus() == 2) {
//当前状态为审核中或审核通过,不允许提交流程
log.error("当前状态为审核中或审核通过,不允许提交流程, id={}", monthlyPlan.getId());
return;
}
//入库审核
//判断时第一次提交还是驳回后再次提交
if(monthlyPlan.getVerifyStatus() == 0){
//第一次提交
verifyStart(monthlyPlan); //开启一个新流程
this.getBaseMapper().updateById(monthlyPlan); //更新数据库
}else {
//再次提交
//构造一个审核信息对象
AuditVO<MonthlyPlan> vo = new AuditVO<>();
vo.setRecord(monthlyPlan);
//获取登录用户信息
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
vo.setAuditUserId(sysUser.getId());
vo.setAuditResult(2);
//完成节点
verifyStep(vo);
}
}
这就是流程开始方法,首先获取登录着的信息
根据提供的代码片段,这段代码的作用是查询定义为 “monthlyPlan” 的流程定义,并获取最新版本的流程定义对象。
首先,通过 repositoryService(流程存储服务)调用 createProcessDefinitionQuery() 方法创建一个流程定义查询对象。然后使用 processDefinitionKey(“monthlyPlan”) 方法设置查询条件,筛选出流程定义的 key 为 “monthlyPlan” 的流程。
接下来,使用 orderByProcessDefinitionVersion().desc() 方法按照流程定义版本进行降序排列,以确保获取最新的版本。
然后,通过 list() 方法获取查询结果列表,并使用 get(0) 获取列表中的第一个流程定义对象,即最新版本的流程定义对象。
最终,将获取到的流程定义赋值给 processDefinition 变量,以便后续使用。
这段代码的目的是获取指定 key 的流程定义中最新的版本流程定义对象,以便使用该对象执行后续的流程操作。
之后在注释写的很清楚了
private void verifyStart(MonthlyPlan monthlyPlan) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
//获取流程定义
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("monthlyPlan")
.orderByProcessDefinitionVersion().desc() //如果多次部署时要获取最新的版本
.list().get(0);
//获取流程实例(启动新流程)通过id进行绑定,绑定流程图和表
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(),monthlyPlan.getId()); //流程定义ID,业务ID
//设置任务完成 (从from变成step1了)
Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); //form
//System.out.println("当前任务节点:"+task.getName()); //form
//form直接在这里完成
taskService.complete(task.getId());
//System.out.println("当前任务节点(完成后):"+taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult().getName());// step1
//流程全局属性,用于存放每个节点的审批信息
{
List<AuditHistroyVO> formVariables = new ArrayList<AuditHistroyVO>();
AuditHistroyVO vo = new AuditHistroyVO()
.setAuditType("月度计划审核")
.setTaskId(task.getId())
.setAuditCreateTime( new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(task.getCreateTime()))
.setAuditEndTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()))
.setTaskName("form")
.setTaskDescr("项目经理填写表单")
.setAuditUserId(sysUser.getId()); //审批人,这里表示提交人
formVariables.add(vo);
runtimeService.setVariable(processInstance.getId(),"form",formVariables);
List<AuditHistroyVO> step1Variables = new ArrayList<AuditHistroyVO>();
runtimeService.setVariable(processInstance.getId(),"step1",step1Variables);
List<AuditHistroyVO> step2Variables = new ArrayList<AuditHistroyVO>();
runtimeService.setVariable(processInstance.getId(),"step2",step2Variables);
List<AuditHistroyVO> step3Variables = new ArrayList<AuditHistroyVO>();
runtimeService.setVariable(processInstance.getId(),"step3",step3Variables);
}
monthlyPlan.setVerifyStatus(1);//设置状态为提交审核
monthlyPlan.setActInstanceId(processInstance.getId()); //记录入库流程ID
}
太晚了,先弄到这里,剩下的明天再来
@Override
public void verifyStep(AuditVO<MonthlyPlan> auditVO) {
MonthlyPlan monthlyPlan = auditVO.getRecord();
String processInstanceId = monthlyPlan.getActInstanceId();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
Optional.of( taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult()).ifPresent(task->{
String taskName = task.getName();
List<AuditHistroyVO> stepVariable = (List<AuditHistroyVO>) runtimeService.getVariable(processInstanceId,taskName);
Date createTime = task.getCreateTime();
String createTimeString = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(createTime);
Date endTime = new Date();
String endTimeString = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(endTime);
stepVariable.add(new AuditHistroyVO()
.setAuditCreateTime(createTimeString)
.setAuditEndTime(endTimeString)
.setAuditType("月度计划产值审核")
.setAuditUserId(auditVO.getAuditUserId())
.setAuditContent(auditVO.getAuditContent())
.setAuditResult(auditVO.getAuditResult())
.setTaskName(task.getName())
.setTaskDescr(task.getDescription())
.setTaskId(task.getId())
);
runtimeService.setVariable(processInstanceId,taskName,stepVariable); //重新set一遍更新属性
//设置网关条件
//是否驳回 true驳回,false通过 字典:2通过 3驳回
runtimeService.setVariable(processInstanceId,"isReject",auditVO.getAuditResult() == 3);
//更新业务审核状态
this.getBaseMapper().updateById(
new MonthlyPlan()
.setId(monthlyPlan.getId())
.setVerifyStatus(
auditVO.getAuditResult() == 3 ?3:1 //如果是驳回设置3(已驳回) 否则设置1(审核中)
));
//任务完成
taskService.complete(task.getId());
});
}
@Override
public AuditVO monthlyPlanVerifyQuery(String id) {
AuditVO vo = new AuditVO();
//查询出实体类
MonthlyPlanVO monthlyPlan = this.getBaseMapper().selectVOById(id);
//根据流程id查询流程信息
Optional.of(taskService.createTaskQuery().processInstanceId(monthlyPlan.getActInstanceId()).singleResult()).ifPresent(task->{
//当前处于哪个节点
int index = IntStream.range(0, monthlyPlanAuditTasks.length)
.filter(i -> monthlyPlanAuditTasks[i].equals(task.getName()))
.findFirst()
.orElse(-1);
vo.setVerifyIndex(index);
vo.setRecord(monthlyPlan);
vo.setAuditType("月度计划产值审核");
vo.setLastTaskNodeName(getLastTaskName(index));
vo.setNextTaskNodeName(getNextTaskName(index));
vo.setCurrTaskNodeName(getCurrTaskName(index));
JSONObject json = new JSONObject();
json.put("name",task.getName());
json.put("id",task.getId());
json.put("claimTime",task.getClaimTime());
json.put("createTime",task.getCreateTime());
json.put("taskDefinitionKey",task.getTaskDefinitionKey());
json.put("processInstanceId",task.getProcessInstanceId());
json.put("taskDefinitionKey",task.getTaskDefinitionKey());
json.put("dueDate",task.getDueDate());
// task.getId();
// task.getCategory(); //命名空间、类别
// task.getFormKey();
// task.getClaimTime();//获取任务被签收的时间
// task.getCreateTime();
// task.getDescription();
// task.getAssignee(); //受理人 (审批人)
// task.getOwner();
// task.getProcessDefinitionId();
// task.getProcessInstanceId();
// task.getTaskDefinitionKey();
// task.getPriority(); //获取任务的优先级
// task.getExecutionId();
// task.getDueDate(); //获取任务的截止日期
vo.setActiviti(json);
});
return vo;
}
private String getCurrTaskName(int index) {
return taskNodeNames[index];
}
private String getNextTaskName(int index) {
return taskNodeNames[index+1];
}
private String getLastTaskName(int index) {
return taskNodeNames[index-1];
}
@Override
public List<AuditHistroyVO> auditHistoryListByProcessKey(String id, Integer index, String type) {
if(("monthlyPlan").equals(type)){
//注意前端index =1,2
String processKey = "monthlyPlan"; //前端传递的是,这里转换成流程定义KEY
String taskKey = monthlyPlanAuditTasks[index+1];
return auditMonthlyPlanHistoryListByProcessKey(id,processKey,taskKey);
}
// if(("outbound").equals(type)){
// String processKey = "out_bound";//前端传递的是出入库类型,这里转换成流程定义KEY
// String taskKey = outBoundAuditTasks[index];
// return auditOutboundHistoryListByProcessKey(id,processKey,taskKey);
// }
log.error("错误的type或index,type={},index={}",type,index);
return null;
}
private List<AuditHistroyVO> auditMonthlyPlanHistoryListByProcessKey(String id, String processKey, String taskKey) {
MonthlyPlan monthlyPlan = this.getBaseMapper().selectById(id);
List<AuditHistroyVO> stepVariable = (List<AuditHistroyVO>) runtimeService.getVariable(monthlyPlan.getActInstanceId(),taskKey);
return stepVariable;
//return null;
}
@Override
public List<AuditHistroyVO> queryhistoryList(String id) {
MonthlyPlan monthlyPlan = this.getBaseMapper().selectById(id);
List<AuditHistroyVO> list = new ArrayList<>(); //返回结果
List<HistoricTaskInstance> historyTaskList = new ArrayList<>(); //临时保存任务节点流
Map<String,AuditHistroyVO> temp = new HashMap<>(); //临时保存vo对象,key为taskId
//入库审核历史
Optional.ofNullable(monthlyPlan.getActInstanceId()).ifPresent(processInstanceId->{
if(oConvertUtils.isEmpty(processInstanceId)){
return;
}
//已经结束的任务查不到task
// List<Task> task = taskService.createTaskQuery()
// .processDefinitionKey("in_bound")
// .processDefinitionId(processInstanceId)
// .list();
setKeyMapAuditHistroyVO(temp,processInstanceId,"form");//form数据
setKeyMapAuditHistroyVO(temp,processInstanceId,"step1");//节点1数据
setKeyMapAuditHistroyVO(temp,processInstanceId,"step2");//节点2数据
setKeyMapAuditHistroyVO(temp,processInstanceId,"step3");//节点2数据
//将流程任务顺序存入list中
historyTaskList.addAll(historyService.createHistoricTaskInstanceQuery()
.processDefinitionKey("monthlyPlan")
.processInstanceId(processInstanceId)
.list());
});
//变量流程任务节点,按顺序存入list中,并匹配vo属性
//@BUG 流程没结束时,最后一个任务没有vo,list添加了一个空元素
historyTaskList.stream().forEach(htask->{
AuditHistroyVO vo = temp.get(htask.getId()); //根据taskId 获取对应的vo
if(vo != null && oConvertUtils.isNotEmpty(vo.getTaskId())){
list.add(vo);
}
});
return list;
}
@Override
public void updateProcessResult(String processInstanceId, String processInstanceBusinessKey, int verifyStatus) {
MonthlyPlan monthlyPlan = new MonthlyPlan();
monthlyPlan.setId(processInstanceBusinessKey);
monthlyPlan.setVerifyStatus(2);
this.getBaseMapper().updateById(monthlyPlan); //更新审核状态
}
private void setKeyMapAuditHistroyVO(Map<String,AuditHistroyVO> temp, String processInstanceId, String taskKey){
HistoricVariableInstance hisInstance = historyService
.createHistoricVariableInstanceQuery()
.processInstanceId(processInstanceId)
.variableName(taskKey)
.singleResult();
List<AuditHistroyVO> vo = (List<AuditHistroyVO>) hisInstance.getValue();
vo.stream().forEach(v->{
temp.put(v.getTaskId(),v);
});
}
}
注意流程图
流程图注意一个是每个节点的注释,和最后审批完成节点的监听器,当后端出现
这样的问题时去查看end节点的事件是否路径正确,路径是endlistenner的包路径
package org.jeecg.modules.demo.monthlyAchieve.listener;
更改完之后记得重新部署