1.谐波普测流程相关调整

This commit is contained in:
cdf
2024-04-02 16:23:10 +08:00
parent 40bc9e57f3
commit 8a416bab4d
26 changed files with 2364 additions and 75 deletions

View File

@@ -34,7 +34,7 @@ import java.util.stream.Stream;
@Slf4j
public class XssRequestWrapper extends HttpServletRequestWrapper {
private final static String[] WHITE_PARAMETER_NAME = {"password", "mxContent", "docContent", "bgImage","fileContent"};
private final static String[] WHITE_PARAMETER_NAME = {"password", "mxContent", "docContent", "bgImage","fileContent","flowableXml"};
public XssRequestWrapper(HttpServletRequest request) {

View File

@@ -0,0 +1,38 @@
package com.njcn.process.enums;
import lombok.Getter;
/**
* pqs
*
* @author cdf
* @date 2024/4/2
*/
@Getter
public enum AuditProcessEnum {
New(0,"新建"),
WaitAudit(1,"待审核"),
AuditPass(2,"审核通过"),
AuditRefuse(3,"审核未通过"),
Release(4,"已发布"),
Finish(5,"已完成")
;
private final Integer status;
private final String statusDes;
AuditProcessEnum(Integer status,String statusDes){
this.status = status;
this.statusDes = statusDes;
}
}

View File

@@ -0,0 +1,43 @@
package com.njcn.process.enums;
/**
* 流程意见类型
*
* @author Tony
* @date 2021/4/19
*/
public enum FlowComment {
/**
* 说明
*/
NORMAL("1", "正常意见"),
REBACK("2", "退回意见"),
REJECT("3", "驳回意见"),
DELEGATE("4", "委派意见"),
ASSIGN("5", "转办意见"),
STOP("6", "终止流程");
/**
* 类型
*/
private final String type;
/**
* 说明
*/
private final String remark;
FlowComment(String type, String remark) {
this.type = type;
this.remark = remark;
}
public String getType() {
return type;
}
public String getRemark() {
return remark;
}
}

View File

@@ -0,0 +1,25 @@
package com.njcn.process.pojo.dto.flowable;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
/**
* @author Tony
* @date 2021/3/28 15:50
*/
@Data
@Builder
public class FlowCommentDto implements Serializable {
/**
* 意见类别 0 正常意见 1 退回意见 2 驳回意见
*/
private String type;
/**
* 意见内容
*/
private String comment;
}

View File

@@ -0,0 +1,56 @@
package com.njcn.process.pojo.dto.flowable;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* <p>流程定义<p>
*
* @author Tony
* @date 2021-04-03
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("流程定义")
public class FlowProcDefDto implements Serializable {
@ApiModelProperty("流程id")
private String id;
@ApiModelProperty("流程名称")
private String name;
@ApiModelProperty("流程key")
private String flowKey;
@ApiModelProperty("流程分类")
private String category;
@ApiModelProperty("配置表单名称")
private String formName;
@ApiModelProperty("配置表单id")
private Long formId;
@ApiModelProperty("版本")
private int version;
@ApiModelProperty("部署ID")
private String deploymentId;
@ApiModelProperty("流程定义状态: 1:激活 , 2:中止")
private int suspensionState;
@ApiModelProperty("部署时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date deploymentTime;
}

View File

@@ -0,0 +1,28 @@
package com.njcn.process.pojo.dto.flowable;
import lombok.Data;
import java.io.Serializable;
/**
* @author Tony
* @date 2021/3/28 19:48
*/
@Data
public class FlowSaveXmlVo implements Serializable {
/**
* 流程名称
*/
private String name;
/**
* 流程分类
*/
private String category;
/**
* xml 文件
*/
private String flowableXml;
}

View File

@@ -0,0 +1,103 @@
package com.njcn.process.pojo.dto.flowable;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.Date;
/**
* <p>工作流任务<p>
*
* @author Tony
* @date 2021-04-03
*/
@Getter
@Setter
@ApiModel("工作流任务相关-返回参数")
public class FlowTaskDto implements Serializable {
@ApiModelProperty("任务编号")
private String taskId;
@ApiModelProperty("任务执行编号")
private String executionId;
@ApiModelProperty("任务名称")
private String taskName;
@ApiModelProperty("任务Key")
private String taskDefKey;
@ApiModelProperty("任务执行人Id")
private String assigneeId;
@ApiModelProperty("部门名称")
private String deptName;
@ApiModelProperty("流程发起人部门名称")
private String startDeptName;
@ApiModelProperty("任务执行人名称")
private String assigneeName;
@ApiModelProperty("任务执行人部门")
private String assigneeDeptName;;
@ApiModelProperty("流程发起人Id")
private String startUserId;
@ApiModelProperty("流程发起人名称")
private String startUserName;
@ApiModelProperty("流程类型")
private String category;
@ApiModelProperty("流程变量信息")
private Object procVars;
@ApiModelProperty("局部变量信息")
private Object taskLocalVars;
@ApiModelProperty("流程部署编号")
private String deployId;
@ApiModelProperty("流程ID")
private String procDefId;
@ApiModelProperty("流程key")
private String procDefKey;
@ApiModelProperty("流程定义名称")
private String procDefName;
@ApiModelProperty("流程定义内置使用版本")
private int procDefVersion;
@ApiModelProperty("流程实例ID")
private String procInsId;
@ApiModelProperty("历史流程实例ID")
private String hisProcInsId;
@ApiModelProperty("任务耗时")
private String duration;
@ApiModelProperty("任务意见")
private FlowCommentDto comment;
@ApiModelProperty("候选执行人")
private String candidate;
@ApiModelProperty("任务创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@ApiModelProperty("任务完成时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date finishTime;
}

View File

@@ -0,0 +1,26 @@
package com.njcn.process.pojo.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.List;
/**
* pqs
*
* @author cdf
* @date 2024/3/29
*/
@Data
public class RGeneralSurveyPlanAuditUserParam {
@ApiModelProperty(name = "planIds",value = "提交的计划编号id集合")
@NotEmpty(message = "计划编号不可为空")
private List<String> planIds;
@ApiModelProperty(name = "auditUser",value = "审核人id")
@NotBlank(message = "请选择审核人")
private String auditUser;
}

View File

@@ -0,0 +1,23 @@
package com.njcn.process.pojo.po;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* pqs
*
* @author cdf
* @date 2024/4/2
*/
@Data
@TableName(value = "flow_form_ass")
public class FlowFormAss {
@TableId
private String id;
private String definitionId;
private String formId;
}

View File

@@ -10,7 +10,7 @@ import java.util.Date;
import java.util.List;
/**
* Description:
* Description: 普测只针对电站层级
* 接口文档访问地址http://serverIP:port/swagger-ui.html
* Date: 2022/11/11 15:20【需求编号】
*
@@ -66,12 +66,23 @@ public class RGeneralSurveyPlanVO {
@ApiModelProperty(value="审核人")
private String checkPerson;
@ApiModelProperty(value="审核人中文名称")
private String checkPersonName;
/**
* 审核意见
*/
@ApiModelProperty(value="审核意见")
private String checkComment;
@ApiModelProperty(value="创建人")
private String createPerson;
@ApiModelProperty(value="创建人名称")
private String createPersonName;
/**
* 上传时间
*/

View File

@@ -133,7 +133,7 @@ public class RGeneralSurveyPlanController extends BaseController {
@ApiImplicitParam(name = "rGeneralSurveyPlanQueryParm", value = "普测计划查询参数", required = true)
public HttpResult<IPage<RGeneralSurveyPlanVO>> queryPlan(@Validated @RequestBody RGeneralSurveyPlanQueryParm rGeneralSurveyPlanQueryParm) {
String methodDescribe = getMethodDescribe("queryPlan");
IPage<RGeneralSurveyPlanVO> rGeneralSurveyPlanVOS = rGeneralSurveyPlanPOService.query(rGeneralSurveyPlanQueryParm, Stream.of("0", "1", "2", "3").collect(Collectors.toList()), "1");
IPage<RGeneralSurveyPlanVO> rGeneralSurveyPlanVOS = rGeneralSurveyPlanPOService.query(rGeneralSurveyPlanQueryParm, Stream.of("0", "1", "2", "3","4","5").collect(Collectors.toList()), "1");
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, rGeneralSurveyPlanVOS, methodDescribe);
}
@@ -266,4 +266,19 @@ public class RGeneralSurveyPlanController extends BaseController {
}
/**
* 提交审核
* @author cdf
* @date 2024/3/29
*/
@ApiOperation("提交审核")
@PostMapping(value = "submitAuditUser")
@ApiImplicitParam(name = "rGeneralSurveyPlanAuditUserParam", value = "实体参数", required = true)
public HttpResult<Boolean> submitAuditUser(@RequestBody @Validated RGeneralSurveyPlanAuditUserParam rGeneralSurveyPlanAuditUserParam) {
String methodDescribe = getMethodDescribe("submitAuditUser");
rGeneralSurveyPlanPOService.submitAuditUser(rGeneralSurveyPlanAuditUserParam);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
}
}

View File

@@ -1,8 +1,11 @@
package com.njcn.process.controller.flowable;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.HttpResultUtil;
import com.njcn.process.pojo.dto.flowable.FlowProcDefDto;
import com.njcn.process.pojo.dto.flowable.FlowSaveXmlVo;
import com.njcn.process.service.flowable.IFlowDefinitionService;
import com.njcn.process.service.flowable.IFlowTaskService;
import com.njcn.web.controller.BaseController;
@@ -11,6 +14,7 @@ import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
@@ -23,12 +27,20 @@ import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static cn.hutool.core.util.CharsetUtil.UTF_8;
/**
* pqs
* 工作流
*
* @author cdf
* @date 2023/4/10
*/
@@ -44,27 +56,17 @@ public class FlowDefinitionController extends BaseController {
private final RepositoryService repositoryService;
private final RuntimeService runtimeService;
private final TaskService taskService;
private final HistoryService historyService;
private final IFlowTaskService flowTaskService;
@GetMapping("deployment")
@ApiOperation(value = "工作流_部署流程")
public void createDeployment() {
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("gaojing.bpmn20.xml")
.name("技术监督告警流程").category("gaojing")
.addClasspathResource("puce.bpmn20.xml")
.name("谐波普测管理").category("xbpc")
.deploy();
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
repositoryService.setProcessDefinitionCategory(definition.getId(), "gaojing");
repositoryService.setProcessDefinitionCategory(definition.getId(), "xbpc");
System.out.println(deployment.getId());
}
@@ -84,12 +86,79 @@ public class FlowDefinitionController extends BaseController {
@ApiOperation(value = "工作流_定义删除")
@DeleteMapping(value = "delete/{deployIds}")
public HttpResult<Object> delete(@PathVariable String[] deployIds) {
@PostMapping(value = "delete")
public HttpResult<Object> delete(@RequestBody List<String> deployIds) {
String methodDescribe = getMethodDescribe("delete");
for (String deployId : deployIds) {
flowDefinitionService.delete(deployId);
}
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
}
@GetMapping(value = "/list")
@ApiOperation(value = "流程定义列表")
public HttpResult<Page<FlowProcDefDto>> list(@ApiParam(value = "当前页码", required = true) @RequestParam("pageNum") Integer pageNum,
@ApiParam(value = "每页条数", required = true) @RequestParam("pageSize") Integer pageSize,
@ApiParam(value = "流程名称") @RequestParam(value = "name", required = false) String name) {
String methodDescribe = getMethodDescribe("list");
Page<FlowProcDefDto> pageResult = flowDefinitionService.list(name, pageNum, pageSize);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, pageResult, methodDescribe);
}
@ApiOperation(value = "保存流程设计器内的xml文件")
@PostMapping("/save")
public HttpResult save(@RequestBody FlowSaveXmlVo vo) {
String methodDescribe = getMethodDescribe("save");
InputStream in = null;
try {
in = new ByteArrayInputStream(vo.getFlowableXml().getBytes(StandardCharsets.UTF_8));
flowDefinitionService.importFile(vo.getName(), vo.getCategory(), in);
} catch (Exception e) {
log.error("导入失败:", e);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, e.getMessage(), methodDescribe);
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
log.error("关闭输入流出错", e);
}
}
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, "导入成功", methodDescribe);
}
@ApiOperation(value = "读取xml文件")
@GetMapping("/readXml")
public HttpResult<String> readXml(@ApiParam(value = "流程定义id") @RequestParam(value = "deployId") String deployId) {
String methodDescribe = getMethodDescribe("readXml");
try {
String result = flowDefinitionService.readXml(deployId);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
} catch (Exception e) {
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, "加载xml文件异常", methodDescribe);
}
}
/**
* 挂载表单
*
* @author cdf
* @date 2024/4/2
*/
@ApiOperation(value = "挂载表单")
@GetMapping("/assFormWithDeploy")
public HttpResult<Boolean> assFormWithDeploy(@ApiParam(value = "流程定义id") @RequestParam(value = "deployId") String deployId, @ApiParam(value = "表单功能id") @RequestParam(value = "formId") String formId) {
String methodDescribe = getMethodDescribe("assFormWithDeploy");
flowDefinitionService.assFormWithDeploy(deployId, formId);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
}
}

View File

@@ -0,0 +1,80 @@
package com.njcn.process.controller.flowable;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.HttpResultUtil;
import com.njcn.process.pojo.vo.flowable.FlowTaskVo;
import com.njcn.process.service.flowable.IFlowInstanceService;
import com.njcn.web.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* <p>工作流流程实例管理<p>
*
* @author Tony
* @date 2021-04-03
*/
@Slf4j
@Api(tags = "工作流流程实例管理")
@RestController
@RequestMapping("/flowable/instance")
public class FlowInstanceController extends BaseController {
@Autowired
private IFlowInstanceService flowInstanceService;
@ApiOperation(value = "根据流程定义id启动流程实例")
@PostMapping("/startBy/{procDefId}")
public HttpResult startById(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId,
@ApiParam(value = "变量集合,json对象") @RequestBody Map<String, Object> variables) {
String methodDescribe = getMethodDescribe("startById");
String result = flowInstanceService.startProcessInstanceById(procDefId, variables);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
}
@ApiOperation(value = "激活或挂起流程实例")
@PostMapping(value = "/updateState")
public HttpResult updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state,
@ApiParam(value = "流程实例ID", required = true) @RequestParam String instanceId) {
String methodDescribe = getMethodDescribe("updateState");
flowInstanceService.updateState(state,instanceId);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
}
@ApiOperation("结束流程实例")
@PostMapping(value = "/stopProcessInstance")
public HttpResult stopProcessInstance(@RequestBody FlowTaskVo flowTaskVo) {
String methodDescribe = getMethodDescribe("stopProcessInstance");
flowInstanceService.stopProcessInstance(flowTaskVo);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
}
@ApiOperation(value = "删除流程实例")
@GetMapping(value = "/delete")
public HttpResult delete(@ApiParam(value = "流程实例ID", required = true) @RequestParam String instanceIds) {
String methodDescribe = getMethodDescribe("delete");
flowInstanceService.delete(instanceIds,"测试删除666");
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
}
}

View File

@@ -1,9 +1,11 @@
package com.njcn.process.controller.flowable;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.HttpResultUtil;
import com.njcn.process.pojo.dto.flowable.FlowTaskDto;
import com.njcn.process.pojo.vo.flowable.FlowQueryVo;
import com.njcn.process.pojo.vo.flowable.FlowTaskVo;
import com.njcn.process.service.flowable.IFlowTaskService;
@@ -70,5 +72,65 @@ public class FlowTaskController extends BaseController {
}
@ApiOperation(value = "我发起的流程", response = HttpResult.class)
@GetMapping(value = "/myProcess")
public HttpResult<Page<FlowTaskDto>> myProcess(FlowQueryVo queryVo) {
String methodDescribe = getMethodDescribe("myProcess");
Page<FlowTaskDto> page = flowTaskService.myProcess(queryVo);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, page, methodDescribe);
}
@ApiOperation(value = "获取已办任务", response = FlowTaskDto.class)
@GetMapping(value = "/finishedList")
public HttpResult<Page<FlowTaskDto>> finishedList(FlowQueryVo queryVo) {
String methodDescribe = getMethodDescribe("finishedList");
Page<FlowTaskDto> page = flowTaskService.finishedList(queryVo);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, page, methodDescribe);
}
@ApiOperation(value = "获取待办列表", response = FlowTaskDto.class)
@GetMapping(value = "/todoList")
public HttpResult<Page<FlowTaskDto>> todoList(FlowQueryVo queryVo) {
String methodDescribe = getMethodDescribe("todoList");
Page<FlowTaskDto> page = flowTaskService.todoList(queryVo);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, page, methodDescribe);
}
@ApiOperation(value = "获取流程变量", response = FlowTaskDto.class)
@GetMapping(value = "/processVariables/{taskId}")
public HttpResult<Map<String,Object>> processVariables(@ApiParam(value = "流程任务Id") @PathVariable(value = "taskId") String taskId) {
String methodDescribe = getMethodDescribe("processVariables");
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, flowTaskService.processVariables(taskId), methodDescribe);
}
@ApiOperation(value = "流程历史流转记录", response = FlowTaskDto.class)
@GetMapping(value = "/flowRecord")
public HttpResult<Map<String, Object>> flowRecord(String procInsId, String deployId) {
String methodDescribe = getMethodDescribe("flowRecord");
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, flowTaskService.flowRecord(procInsId, deployId), methodDescribe);
}
@ApiOperation(value = "取消申请", response = FlowTaskDto.class)
@PostMapping(value = "/stopProcess")
public HttpResult<Object> stopProcess(@RequestBody FlowTaskVo flowTaskVo) {
String methodDescribe = getMethodDescribe("stopProcess");
flowTaskService.stopProcess(flowTaskVo);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,true, methodDescribe);
}
@ApiOperation(value = "驳回任务")
@PostMapping(value = "/reject")
public HttpResult taskReject(@RequestBody FlowTaskVo flowTaskVo) {
String methodDescribe = getMethodDescribe("taskReject");
flowTaskService.taskReject(flowTaskVo);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,true, methodDescribe);
}
}

View File

@@ -0,0 +1,24 @@
package com.njcn.process.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.process.pojo.dto.flowable.FlowProcDefDto;
import com.njcn.process.pojo.po.FlowFormAss;
import com.njcn.process.pojo.po.FlowableAss;
import org.apache.ibatis.annotations.Param;
/**
* <p>
* Mapper 接口
* </p>
*
* @author hongawen
* @since 2023-04-13
*/
public interface FlowFormAssMapper extends BaseMapper<FlowFormAss> {
}

View File

@@ -2,7 +2,12 @@ package com.njcn.process.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.process.pojo.dto.flowable.FlowProcDefDto;
import com.njcn.process.pojo.po.FlowableAss;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
@@ -14,4 +19,12 @@ import com.njcn.process.pojo.po.FlowableAss;
*/
public interface FlowableAssMapper extends BaseMapper<FlowableAss> {
/**
* 流程定义列表
* @param name
* @return
*/
Page<FlowProcDefDto> selectDeployList(Page<FlowProcDefDto> page, @Param("name") String name);
}

View File

@@ -0,0 +1,27 @@
<?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.njcn.process.mapper.FlowableAssMapper">
<select id="selectDeployList" resultType="com.njcn.process.pojo.dto.flowable.FlowProcDefDto">
SELECT
rp.id_ as id,
rp.deployment_id_ as deploymentId,
rd.name_ as name,
rd.category_ as category,
rp.key_ as flowKey,
rp.version_ as version,
rp.suspension_state_ as suspensionState,
rd.deploy_time_ as deploymentTime
FROM
act_re_procdef rp
LEFT JOIN act_re_deployment rd ON rp.deployment_id_ = rd.id_
<where>
<if test="name != null and name != ''">
and rd.name_ like concat('%', #{name}, '%')
</if>
</where>
order by rd.deploy_time_ desc
</select>
</mapper>

View File

@@ -103,4 +103,7 @@ public interface RGeneralSurveyPlanPOService extends IMppService<RGeneralSurveyP
* @Date: 2023/3/15
*/
RSurveyCycleVO addPlanCycle(Integer cycleNum);
Boolean submitAuditUser(RGeneralSurveyPlanAuditUserParam rGeneralSurveyPlanAuditUserParam);
}

View File

@@ -2,10 +2,14 @@ package com.njcn.process.service.flowable;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.process.pojo.dto.flowable.FlowProcDefDto;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
@@ -51,4 +55,36 @@ public interface IFlowDefinitionService {
HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId);
/**
* 流程定义列表
*
* @param pageNum 当前页码
* @param pageSize 每页条数
* @return 流程定义分页列表数据
*/
Page<FlowProcDefDto> list(String name, Integer pageNum, Integer pageSize);
/**
* 导入流程文件
* 当每个key的流程第一次部署时指定版本为1。对其后所有使用相同key的流程定义
* 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义
* @param name
* @param category
* @param in
*/
void importFile(String name, String category, InputStream in);
/**
* 读取xml
* @param deployId
* @return
*/
String readXml(String deployId) throws IOException;
Boolean assFormWithDeploy(String deployId,String formId);
}

View File

@@ -0,0 +1,55 @@
package com.njcn.process.service.flowable;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.process.pojo.vo.flowable.FlowTaskVo;
import org.flowable.engine.history.HistoricProcessInstance;
import java.util.Map;
/**
* @author Tony
* @date 2021-04-03 14:40
*/
public interface IFlowInstanceService {
/**
* 结束流程实例
*
* @param vo
*/
void stopProcessInstance(FlowTaskVo vo);
/**
* 激活或挂起流程实例
*
* @param state 状态
* @param instanceId 流程实例ID
*/
void updateState(Integer state, String instanceId);
/**
* 删除流程实例ID
*
* @param instanceId 流程实例ID
* @param deleteReason 删除原因
*/
void delete(String instanceId, String deleteReason);
/**
* 根据实例ID查询历史实例数据
*
* @param processInstanceId
* @return
*/
HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId);
/**
* 根据流程定义ID启动流程实例
*
* @param procDefId 流程定义Id
* @param variables 流程变量
* @return
*/
String startProcessInstanceById(String procDefId, Map<String, Object> variables);
}

View File

@@ -2,7 +2,10 @@ package com.njcn.process.service.flowable;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.process.pojo.dto.flowable.FlowTaskDto;
import com.njcn.process.pojo.vo.flowable.FlowQueryVo;
import com.njcn.process.pojo.vo.flowable.FlowTaskVo;
import org.flowable.task.api.Task;
@@ -36,6 +39,14 @@ public interface IFlowTaskService {
Boolean complete(FlowTaskVo taskVo);
/**
* 给下一个任务指定执行人员
* @author cdf
* @date 2024/3/29
*/
Task toNextTaskUser(FlowTaskVo taskVo);
/**
* 获取任务
* @author cdf
@@ -44,5 +55,64 @@ public interface IFlowTaskService {
Task getTask(String proIndex);
/**
* 我发起的流程
* @param queryVo 请求参数
* @return
*/
Page<FlowTaskDto> myProcess(FlowQueryVo queryVo);
/**
* 已办任务列表
*
* @param queryVo 请求参数
* @return
*/
Page<FlowTaskDto> finishedList(FlowQueryVo queryVo);
/**
* 代办任务列表
*
* @param queryVo 请求参数
* @return
*/
Page<FlowTaskDto> todoList(FlowQueryVo queryVo);
/**
* 获取流程变量
* @param taskId
* @return
*/
Map<String, Object> processVariables(String taskId);
/**
* 流程历史流转记录
*
* @param procInsId 流程实例Id
* @return
*/
Map<String, Object> flowRecord(String procInsId,String deployId);
/**
* 取消申请
* 目前实现方式: 直接将当前流程变更为已完成
* @param flowTaskVo
* @return
*/
Boolean stopProcess(FlowTaskVo flowTaskVo);
/**
* 驳回任务
*
* @param flowTaskVo
*/
void taskReject(FlowTaskVo flowTaskVo);
}

View File

@@ -1,8 +1,10 @@
package com.njcn.process.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -15,22 +17,28 @@ import com.njcn.minioss.bo.MinIoUploadResDTO;
import com.njcn.oss.constant.OssPath;
import com.njcn.oss.enums.OssResponseEnum;
import com.njcn.oss.utils.FileStorageUtil;
import com.njcn.process.mapper.RGeneralSurveyPlanDetailMapper;
import com.njcn.process.mapper.RGeneralSurveyPlanPOMapper;
import com.njcn.process.mapper.RSurveyCycleMapper;
import com.njcn.process.enums.AuditProcessEnum;
import com.njcn.process.enums.FlowComment;
import com.njcn.process.mapper.*;
import com.njcn.process.pojo.param.*;
import com.njcn.process.pojo.po.RGeneralSurveyPlanDetail;
import com.njcn.process.pojo.po.RGeneralSurveyPlanPO;
import com.njcn.process.pojo.po.RSurveyCyclePO;
import com.njcn.process.pojo.po.RSurveyPlanConfigPO;
import com.njcn.process.pojo.po.*;
import com.njcn.process.pojo.vo.*;
import com.njcn.process.pojo.vo.flowable.FlowTaskVo;
import com.njcn.process.service.RGeneralSurveyPlanDetailService;
import com.njcn.process.service.RGeneralSurveyPlanPOService;
import com.njcn.process.service.flowable.IFlowDefinitionService;
import com.njcn.process.service.flowable.IFlowTaskService;
import com.njcn.process.service.impl.flowable.FlowDefinitionServiceImpl;
import com.njcn.user.api.DeptFeignClient;
import com.njcn.user.api.UserFeignClient;
import com.njcn.user.pojo.po.Dept;
import com.njcn.user.pojo.po.User;
import com.njcn.user.pojo.vo.PvTerminalTreeVO;
import com.njcn.web.utils.RequestUtil;
import liquibase.pro.packaged.S;
import lombok.RequiredArgsConstructor;
import org.flowable.task.api.Task;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -40,6 +48,7 @@ import org.springframework.util.StringUtils;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -64,6 +73,16 @@ public class RGeneralSurveyPlanPOServiceImpl extends MppServiceImpl<RGeneralSurv
private final RSurveyCycleMapper rSurveyCycleMapper;
private final RSurveyPlanConfigService rSurveyPlanConfigService;
private final IFlowDefinitionService iFlowDefinitionService;
private final IFlowTaskService iFlowTaskService;
private final UserFeignClient userFeignClient;
private final FlowableAssMapper flowableAssMapper;
private final FlowFormAssMapper flowFormAssMapper;
/**
* @param rGeneralSurveyPlanAddParm
@@ -88,20 +107,23 @@ public class RGeneralSurveyPlanPOServiceImpl extends MppServiceImpl<RGeneralSurv
RSurveyCyclePO rSurveyCyclePO = rSurveyCycleMapper.selectOne(rSurveyCyclePOQueryWrapper);
rGeneralSurveyPlanPO.setCycleId(rSurveyCyclePO.getId());
String deptIndex = RequestUtil.getDeptIndex();
Dept data = deptFeignClient.getDeptById(deptIndex).getData();
rGeneralSurveyPlanPO.setCheckPerson(data.getPid());
rGeneralSurveyPlanPO.setCreatePerson(RequestUtil.getUserIndex());
int count = this.count(new LambdaQueryWrapper<RGeneralSurveyPlanPO>()
.eq(RGeneralSurveyPlanPO::getPlanNo, rGeneralSurveyPlanAddParm.getPlanNo())
);
boolean b;
if (count > 0) {
b = this.updateByMultiId(rGeneralSurveyPlanPO);
RGeneralSurveyPlanPO generalSurveyPlanPO = this.getOne(new LambdaQueryWrapper<RGeneralSurveyPlanPO>()
.eq(RGeneralSurveyPlanPO::getPlanNo, rGeneralSurveyPlanAddParm.getPlanNo()));
rGeneralSurveyPlanPO.setStatus(AuditProcessEnum.New.getStatus());
if (Objects.isNull(generalSurveyPlanPO)) {
this.save(rGeneralSurveyPlanPO);
} else {
/*todo 后期与工作流绑定*/
rGeneralSurveyPlanPO.setStatus(0);
b = this.save(rGeneralSurveyPlanPO);
if(rGeneralSurveyPlanPO.getStatus() == AuditProcessEnum.New.getStatus() || rGeneralSurveyPlanPO.getStatus() == AuditProcessEnum.AuditRefuse.getStatus()){
this.updateByMultiId(rGeneralSurveyPlanPO);
}else {
throw new BusinessException("存在相同普测计划编号,请勿重复新建");
}
}
QueryWrapper<RGeneralSurveyPlanDetail> queryWrapper = new QueryWrapper();
@@ -128,8 +150,9 @@ public class RGeneralSurveyPlanPOServiceImpl extends MppServiceImpl<RGeneralSurv
rGeneralSurveyPlanDetail.setVoltageLevel(stat.getVoltageLevel());
rGeneralSurveyPlanDetailList.add(rGeneralSurveyPlanDetail);
}
boolean b1 = rGeneralSurveyPlanDetailService.saveOrUpdateBatchByMultiId(rGeneralSurveyPlanDetailList, 500);
return b && b1;
rGeneralSurveyPlanDetailService.saveOrUpdateBatchByMultiId(rGeneralSurveyPlanDetailList, 500);
return true;
}
/**
@@ -145,13 +168,24 @@ public class RGeneralSurveyPlanPOServiceImpl extends MppServiceImpl<RGeneralSurv
IPage<RGeneralSurveyPlanPO> page = new Page<>(rGeneralSurveyPlanQueryParm.getCurrentPage(), rGeneralSurveyPlanQueryParm.getPageSize());
IPage<RGeneralSurveyPlanVO> returnpage = new Page<>(rGeneralSurveyPlanQueryParm.getCurrentPage(), rGeneralSurveyPlanQueryParm.getPageSize());
String loginUserDept = RequestUtil.getDeptIndex();
String loginUsrId = RequestUtil.getUserIndex();
List<String> childrenDeptIds = deptFeignClient.getDepSonIdtByDeptId(loginUserDept).getData();
childrenDeptIds = childrenDeptIds.stream().filter(item -> !Objects.equals(item, loginUserDept)).distinct().collect(Collectors.toList());
List<User> userList = userFeignClient.getUserInfoByDeptIds(childrenDeptIds).getData();
User my = new User();
my.setId(loginUsrId);
my.setName(RequestUtil.getUserNickname());
userList.add(my);
List<String> userIds = userList.stream().map(User::getId).collect(Collectors.toList());
LambdaQueryWrapper<RGeneralSurveyPlanPO> queryWrapper = new LambdaQueryWrapper<>();
/*type=1新建页面查看自己负责的计划type=2审核页面审核者==当前用户;结果页面:查看自己负责的计划*/
if (type == "1" || type == "3") {
queryWrapper.eq(RGeneralSurveyPlanPO::getCreatePerson, RequestUtil.getUserIndex());
queryWrapper.in(RGeneralSurveyPlanPO::getCreatePerson, userIds);
}
if (type == "2") {
queryWrapper.eq(RGeneralSurveyPlanPO::getCheckPerson, RequestUtil.getDeptIndex());
queryWrapper.eq(RGeneralSurveyPlanPO::getCheckPerson, loginUsrId);
}
if (!StringUtils.isEmpty(rGeneralSurveyPlanQueryParm.getOrgNo())) {
List<String> data = deptFeignClient.getDepSonIdtByDeptId(rGeneralSurveyPlanQueryParm.getOrgNo()).getData();
@@ -180,10 +214,7 @@ public class RGeneralSurveyPlanPOServiceImpl extends MppServiceImpl<RGeneralSurv
//部门处理根据部门code取名称
List<PvTerminalTreeVO> dept = deptFeignClient.allDeptList().getData();
Map<String, String> pvTerminalTreeVOMap = dept.stream().
collect(Collectors.
toMap(PvTerminalTreeVO::getId,
PvTerminalTreeVO::getName));
Map<String, String> pvTerminalTreeVOMap = dept.stream().collect(Collectors.toMap(PvTerminalTreeVO::getId, PvTerminalTreeVO::getName));
List<String> collect = rGeneralSurveyPlanPOS.stream().map(RGeneralSurveyPlanPO::getPlanNo).collect(Collectors.toList());
@@ -191,6 +222,12 @@ public class RGeneralSurveyPlanPOServiceImpl extends MppServiceImpl<RGeneralSurv
lambdaQueryWrapper.in(RGeneralSurveyPlanDetail::getPlanNo, collect);
List<RGeneralSurveyPlanDetail> rGeneralSurveyPlanDetails = rGeneralSurveyPlanDetailMapper.selectList(lambdaQueryWrapper);
List<RGeneralSurveyPlanVO> rGeneralSurveyPlanVOList = new ArrayList<>();
Map<String, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));
List<String> checkIds = rGeneralSurveyPlanPOS.stream().map(RGeneralSurveyPlanPO::getCheckPerson).filter(Objects::nonNull).collect(Collectors.toList());
List<User> checkUserList = userFeignClient.getUserByIdList(checkIds).getData();
Map<String, User> checkMap = checkUserList.stream().collect(Collectors.toMap(User::getId, Function.identity()));
rGeneralSurveyPlanPOS.forEach(temp -> {
RGeneralSurveyPlanVO rGeneralSurveyPlanVO = new RGeneralSurveyPlanVO();
BeanUtils.copyProperties(temp, rGeneralSurveyPlanVO);
@@ -200,21 +237,13 @@ public class RGeneralSurveyPlanPOServiceImpl extends MppServiceImpl<RGeneralSurv
rGeneralSurveyPlanVO.setSubCount(collect1.size());
rGeneralSurveyPlanVO.setSubIds(collect1);
rGeneralSurveyPlanVO.setIsFileUpload(ObjectUtil.isNull(temp.getIsFileUpload()) ? 0 : temp.getIsFileUpload());
/*
long Subcount = rGeneralSurveyPlanDetails.stream ( ).
filter (surveyPlanDetail -> Objects.equals (surveyPlanDetail.getPlanNo ( ), temp.getPlanNo ( ))).
map (RGeneralSurveyPlanDetail::getSubId).distinct ( ).count ( );*/
// rGeneralSurveyPlanVO.setBusCount (Busbarcount);
// List<RGeneralSurveyPlanVO.RGeneralSurveyPlanDetailVO> collect1 = rGeneralSurveyPlanDetails.stream ( ).
// filter (surveyPlanDetail -> Objects.equals (surveyPlanDetail.getPlanNo ( ), temp.getPlanNo ( ))).
// map (surveyPlanDetail -> {
// RGeneralSurveyPlanVO.RGeneralSurveyPlanDetailVO rGeneralSurveyPlanDetailVO = new RGeneralSurveyPlanVO.RGeneralSurveyPlanDetailVO ( );
// BeanUtils.copyProperties (surveyPlanDetail, rGeneralSurveyPlanDetailVO);
// return rGeneralSurveyPlanDetailVO;
// }).collect (Collectors.toList ( ));
rGeneralSurveyPlanVO.setOrgName(pvTerminalTreeVOMap.get(temp.getOrgNo())); //单位名称
//
// rGeneralSurveyPlanVO.setRGeneralSurveyPlanDetailVOList (collect1);
rGeneralSurveyPlanVO.setCreatePersonName(userMap.get(temp.getCreatePerson()).getName());
if (StrUtil.isNotBlank(temp.getCheckPerson())) {
rGeneralSurveyPlanVO.setCheckPerson(checkMap.get(temp.getCheckPerson()).getName());
}
rGeneralSurveyPlanVOList.add(rGeneralSurveyPlanVO);
});
@@ -249,12 +278,13 @@ public class RGeneralSurveyPlanPOServiceImpl extends MppServiceImpl<RGeneralSurv
filePath = filePath + fileStoragePath + "##" + OriginalFilename + ";";
fileCount++;
}
rGeneralSurveyPlanPO.setStatus(4);
rGeneralSurveyPlanPO.setFileCount(fileCount);
rGeneralSurveyPlanPO.setFilePath(filePath);
rGeneralSurveyPlanPO.setIsFileUpload(1);
rGeneralSurveyPlanPO.setUploadTime(new Date());
this.saveOrUpdateByMultiId(rGeneralSurveyPlanPO);
return result;
}
@@ -339,13 +369,13 @@ public class RGeneralSurveyPlanPOServiceImpl extends MppServiceImpl<RGeneralSurv
case 1:
result = "待审核";
break;
case 2:
case 3:
result = "审核未通过";
break;
case 3:
case 4:
result = "已发布";
break;
case 4:
case 5:
result = "已完成";
break;
default:
@@ -401,13 +431,50 @@ public class RGeneralSurveyPlanPOServiceImpl extends MppServiceImpl<RGeneralSurv
@Override
@Transactional(rollbackFor = {Exception.class})
public Boolean checkPlanAudit(RGeneralSurveyPlanChcekParm rGeneralSurveyPlanChcekParm) {
RGeneralSurveyPlanPO rGeneralSurveyPlanPO = this.lambdaQuery().eq(RGeneralSurveyPlanPO::getPlanNo,rGeneralSurveyPlanChcekParm.getPlanNo()).one();
if(rGeneralSurveyPlanPO.getStatus() != AuditProcessEnum.WaitAudit.getStatus()){
throw new BusinessException("当前流程非待审核状态");
}
boolean result = true;
UpdateWrapper<RGeneralSurveyPlanPO> updateWrapper = new UpdateWrapper();
updateWrapper.eq("plan_no", rGeneralSurveyPlanChcekParm.getPlanNo());
updateWrapper.set("check_comment", rGeneralSurveyPlanChcekParm.getCheckComment());
// updateWrapper.set ("check_person", rGeneralSurveyPlanChcekParm.getCheckPerson ( ));
updateWrapper.set("status", Objects.equals("1", rGeneralSurveyPlanChcekParm.getCheckResult()) ? 3 : 2);
updateWrapper.set("status", Objects.equals("1", rGeneralSurveyPlanChcekParm.getCheckResult()) ? AuditProcessEnum.Release.getStatus() : AuditProcessEnum.AuditRefuse.getStatus());
result = this.update(updateWrapper);
//处理流程
FlowableAss flowableAss = flowableAssMapper.selectOne(new LambdaQueryWrapper<FlowableAss>().eq(FlowableAss::getThsIndex,rGeneralSurveyPlanChcekParm.getPlanNo()));
String auditResult = Objects.equals("1", rGeneralSurveyPlanChcekParm.getCheckResult()) ? "同意" : "拒绝";
if(Objects.equals("0", rGeneralSurveyPlanChcekParm.getCheckResult())){
//拒绝,则回退到申请步骤
Task task = iFlowTaskService.getTask(flowableAss.getExecIndex());
FlowTaskVo flowTaskVo = new FlowTaskVo();
flowTaskVo.setTaskId(task.getId());
flowTaskVo.setComment(rGeneralSurveyPlanChcekParm.getCheckComment());
iFlowTaskService.taskReject(flowTaskVo);
}else {
Task task = iFlowTaskService.getTask(flowableAss.getExecIndex());
FlowTaskVo flowTaskVo = new FlowTaskVo();
flowTaskVo.setTaskId(task.getId());
flowTaskVo.setInstanceId(flowableAss.getExecIndex());
flowTaskVo.setComment(RequestUtil.getUserNickname() + auditResult+"编号为"+rGeneralSurveyPlanChcekParm.getPlanNo()+"的计划,结论:"+rGeneralSurveyPlanChcekParm.getCheckComment());
Map<String,Object> map = new HashMap<>();
map.put("auditFlag",1);
flowTaskVo.setVariables(map);
iFlowTaskService.complete(flowTaskVo);
}
return result;
}
@@ -603,6 +670,70 @@ public class RGeneralSurveyPlanPOServiceImpl extends MppServiceImpl<RGeneralSurv
return rSurveyCycleVO;
}
@Override
public Boolean submitAuditUser(RGeneralSurveyPlanAuditUserParam rGeneralSurveyPlanAuditUserParam) {
String userId = RequestUtil.getUserIndex();
List<String> planIds = rGeneralSurveyPlanAuditUserParam.getPlanIds();
Integer count = this.lambdaQuery().in(RGeneralSurveyPlanPO::getPlanNo, planIds).eq(RGeneralSurveyPlanPO::getCreatePerson, userId).count();
if (count != planIds.size()) {
throw new BusinessException("只可以操作自己创建的计划!");
}
this.update(new LambdaUpdateWrapper<RGeneralSurveyPlanPO>()
.in(RGeneralSurveyPlanPO::getPlanNo, planIds)
.set(RGeneralSurveyPlanPO::getStatus, AuditProcessEnum.WaitAudit.getStatus()).set(RGeneralSurveyPlanPO::getCheckPerson, rGeneralSurveyPlanAuditUserParam.getAuditUser())
);
//绑定工作流
Map<String, Object> map = new HashMap<>();
//map.put("applyUser",userId);
for (String planId : planIds) {
//需要判断那些流程已经启动,已经启动的则为驳回后的再次提交审核
FlowableAss flowableAss = flowableAssMapper.selectOne(new LambdaQueryWrapper<FlowableAss>().eq(FlowableAss::getThsIndex,planId));
if(Objects.nonNull(flowableAss)){
//不为空则认为是驳回后的重新发起
Task task = iFlowTaskService.getTask(flowableAss.getExecIndex());
FlowTaskVo flowTaskVo = new FlowTaskVo();
flowTaskVo.setTaskId(task.getId());
flowTaskVo.setAssignee(userId);
flowTaskVo.setComment(RequestUtil.getUserNickname() + "重新发起普测计划申请");
iFlowTaskService.complete(flowTaskVo);
Task taskNext = iFlowTaskService.getTask(flowableAss.getExecIndex());
FlowTaskVo flowTaskVoNext = new FlowTaskVo();
flowTaskVoNext.setTaskId(taskNext.getId());
flowTaskVoNext.setAssignee(rGeneralSurveyPlanAuditUserParam.getAuditUser());
iFlowTaskService.toNextTaskUser(flowTaskVoNext);
}else {
//开始流程
FlowFormAss flowFormAss = flowFormAssMapper.selectOne(new LambdaQueryWrapper<FlowFormAss>().eq(FlowFormAss::getFormId,1));
if(Objects.isNull(flowFormAss)){
throw new BusinessException("当前功能未绑定流程,请先绑定流程");
}
String processId = iFlowDefinitionService.startProcessInstanceById(flowFormAss.getDefinitionId(), planId, map);
Task task = iFlowTaskService.getTask(processId);
FlowTaskVo flowTaskVo = new FlowTaskVo();
flowTaskVo.setTaskId(task.getId());
flowTaskVo.setInstanceId(processId);
flowTaskVo.setAssignee(RequestUtil.getUserIndex());
flowTaskVo.setComment(RequestUtil.getUserNickname() + "发起普测计划申请");
iFlowTaskService.complete(flowTaskVo);
Task taskNext = iFlowTaskService.getTask(processId);
FlowTaskVo flowTaskVoNext = new FlowTaskVo();
flowTaskVoNext.setTaskId(taskNext.getId());
flowTaskVoNext.setAssignee(rGeneralSurveyPlanAuditUserParam.getAuditUser());
iFlowTaskService.toNextTaskUser(flowTaskVoNext);
}
}
return true;
}
public List<DeptSubstationVO> recursion(DeptSubstationVO result, String orgdid) {
List<DeptSubstationVO> deptSubstationVOList = new ArrayList<>();
if (Objects.equals(result.getId(), orgdid)) {

View File

@@ -8,7 +8,10 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.utils.PubUtils;
import com.njcn.process.factory.FlowServiceFactory;
import com.njcn.process.mapper.FlowFormAssMapper;
import com.njcn.process.mapper.FlowableAssMapper;
import com.njcn.process.pojo.dto.flowable.FlowProcDefDto;
import com.njcn.process.pojo.po.FlowFormAss;
import com.njcn.process.pojo.po.FlowableAss;
import com.njcn.process.service.flowable.IFlowDefinitionService;
import com.njcn.web.utils.RequestUtil;
@@ -53,6 +56,8 @@ public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFl
private final FlowableAssMapper flowableAssMapper;
private final FlowFormAssMapper flowFormAssMapper;
@@ -75,7 +80,7 @@ public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFl
// 设置流程发起人Id到流程中
identityService.setAuthenticatedUserId(RequestUtil.getUserIndex());
variables.put("INITIATOR", RequestUtil.getUserIndex());
//variables.put("applyUser", RequestUtil.getUserIndex());
ProcessInstance res = runtimeService.startProcessInstanceById(procDefId, variables);
FlowableAss flowableAss = new FlowableAss();
@@ -84,7 +89,7 @@ public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFl
flowableAss.setExecIndex(res.getProcessInstanceId());
flowableAssMapper.insert(flowableAss);
return res.getCallbackId();
return res.getProcessInstanceId();
} catch (Exception e) {
e.printStackTrace();
throw new BusinessException("开始流程出错!");
@@ -146,4 +151,103 @@ public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFl
return historicProcessInstance;
}
/**
* 流程定义列表
*
* @param pageNum 当前页码
* @param pageSize 每页条数
* @return 流程定义分页列表数据
*/
@Override
public Page<FlowProcDefDto> list(String name, Integer pageNum, Integer pageSize) {
// // 流程定义列表数据查询
// final ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
// if (StringUtils.isNotEmpty(name)) {
// processDefinitionQuery.processDefinitionNameLike(name);
// }
//// processDefinitionQuery.orderByProcessDefinitionKey().asc();
// page.setTotal(processDefinitionQuery.count());
// List<ProcessDefinition> processDefinitionList = processDefinitionQuery.listPage(pageSize * (pageNum - 1), pageSize);
//
// List<FlowProcDefDto> dataList = new ArrayList<>();
// for (ProcessDefinition processDefinition : processDefinitionList) {
// String deploymentId = processDefinition.getDeploymentId();
// Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
// FlowProcDefDto reProcDef = new FlowProcDefDto();
// BeanUtils.copyProperties(processDefinition, reProcDef);
// SysForm sysForm = sysDeployFormService.selectSysDeployFormByDeployId(deploymentId);
// if (Objects.nonNull(sysForm)) {
// reProcDef.setFormName(sysForm.getFormName());
// reProcDef.setFormId(sysForm.getFormId());
// }
// // 流程定义时间
// reProcDef.setDeploymentTime(deployment.getDeploymentTime());
// dataList.add(reProcDef);
// }
Page<FlowProcDefDto> pageResult = flowableAssMapper.selectDeployList(new Page<>(pageNum,pageSize),name);
// 加载挂表单
/* for (FlowProcDefDto procDef : dataList) {
SysForm sysForm = sysDeployFormService.selectSysDeployFormByDeployId(procDef.getDeploymentId());
if (Objects.nonNull(sysForm)) {
procDef.setFormName(sysForm.getFormName());
procDef.setFormId(sysForm.getFormId());
}
}*/
return pageResult;
}
/**
* 导入流程文件
*
* 当每个key的流程第一次部署时指定版本为1。对其后所有使用相同key的流程定义
* 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义
* @param name
* @param category
* @param in
*/
@Override
public void importFile(String name, String category, InputStream in) {
Deployment deploy = repositoryService.createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in).name(name).category(category).deploy();
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
repositoryService.setProcessDefinitionCategory(definition.getId(), category);
}
/**
* 读取xml
*
* @param deployId
* @return
*/
@Override
public String readXml(String deployId) throws IOException {
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName());
String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
return result;
}
@Override
public Boolean assFormWithDeploy(String deployId, String formId) {
LambdaQueryWrapper<FlowFormAss> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(FlowFormAss::getDefinitionId,deployId);
FlowFormAss flowFormAss = flowFormAssMapper.selectOne(lambdaQueryWrapper);
FlowFormAss po = new FlowFormAss();
po.setDefinitionId(deployId);
po.setFormId(formId);
if(Objects.isNull(flowFormAss)){
flowFormAssMapper.insert(po);
}else {
flowFormAssMapper.update(po,new LambdaQueryWrapper<FlowFormAss>().eq(FlowFormAss::getDefinitionId,deployId));
}
return true;
}
}

View File

@@ -0,0 +1,121 @@
package com.njcn.process.service.impl.flowable;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.process.factory.FlowServiceFactory;
import com.njcn.process.pojo.vo.flowable.FlowTaskVo;
import com.njcn.process.service.flowable.IFlowInstanceService;
import com.njcn.web.utils.RequestUtil;
import liquibase.pro.packaged.S;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.history.HistoricProcessInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Map;
import java.util.Objects;
/**
* <p>工作流流程实例管理<p>
*
* @author Tony
* @date 2021-04-03
*/
@Service
@Slf4j
public class FlowInstanceServiceImpl extends FlowServiceFactory implements IFlowInstanceService {
/**
* 结束流程实例
*
* @param vo
*/
@Override
public void stopProcessInstance(FlowTaskVo vo) {
String taskId = vo.getTaskId();
}
/**
* 激活或挂起流程实例
*
* @param state 状态
* @param instanceId 流程实例ID
*/
@Override
public void updateState(Integer state, String instanceId) {
// 激活
if (state == 1) {
runtimeService.activateProcessInstanceById(instanceId);
}
// 挂起
if (state == 2) {
runtimeService.suspendProcessInstanceById(instanceId);
}
}
/**
* 删除流程实例ID
*
* @param instanceId 流程实例ID
* @param deleteReason 删除原因
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(String instanceId, String deleteReason) {
// 查询历史数据
HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(instanceId);
if (historicProcessInstance.getEndTime() != null) {
historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());
return;
}
// 删除流程实例
runtimeService.deleteProcessInstance(instanceId, deleteReason);
// 删除历史流程实例
historyService.deleteHistoricProcessInstance(instanceId);
}
/**
* 根据实例ID查询历史实例数据
*
* @param processInstanceId
* @return
*/
@Override
public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) {
HistoricProcessInstance historicProcessInstance =
historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
if (Objects.isNull(historicProcessInstance)) {
throw new FlowableObjectNotFoundException("流程实例不存在: " + processInstanceId);
}
return historicProcessInstance;
}
/**
* 根据流程定义ID启动流程实例
*
* @param procDefId 流程定义Id
* @param variables 流程变量
* @return
*/
@Override
public String startProcessInstanceById(String procDefId, Map<String, Object> variables) {
try {
// 设置流程发起人Id到流程中
String userId = RequestUtil.getUserIndex();
// identityService.setAuthenticatedUserId(userId.toString());
variables.put("initiator",userId);
variables.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", true);
runtimeService.startProcessInstanceById(procDefId, variables);
return "流程启动成功";
} catch (Exception e) {
e.printStackTrace();
return "流程启动错误";
}
}
}

View File

@@ -2,13 +2,26 @@ package com.njcn.process.service.impl.flowable;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.process.enums.FlowComment;
import com.njcn.process.factory.FlowServiceFactory;
import com.njcn.process.pojo.dto.FlowViewerDto;
import com.njcn.process.pojo.dto.flowable.FlowCommentDto;
import com.njcn.process.pojo.dto.flowable.FlowTaskDto;
import com.njcn.process.pojo.vo.flowable.FlowQueryVo;
import com.njcn.process.pojo.vo.flowable.FlowTaskVo;
import com.njcn.process.service.flowable.IFlowTaskService;
import com.njcn.process.utils.FlowableUtils;
import com.njcn.user.api.UserFeignClient;
import com.njcn.user.pojo.po.User;
import com.njcn.user.pojo.vo.UserVO;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
@@ -46,6 +59,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author Tony
@@ -53,8 +67,11 @@ import java.util.stream.Collectors;
**/
@Service
@Slf4j
@RequiredArgsConstructor
public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTaskService {
private final UserFeignClient userFeignClient;
@Override
public Boolean getNextFlowNodeByStart(FlowTaskVo flowTaskVo) {
@@ -124,16 +141,28 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
}
if (DelegationState.PENDING.equals(task.getDelegationState())) {
//taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.DELEGATE.getType(), taskVo.getComment());
//taskService.resolveTask(taskVo.getTaskId(), taskVo.getVariables());
taskService.resolveTask(taskVo.getTaskId(), taskVo.getVariables());
} else {
//taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment());
taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment());
//Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
taskService.setAssignee(taskVo.getTaskId(), "1");
if(StrUtil.isNotBlank(taskVo.getAssignee())){
taskService.setAssignee(taskVo.getTaskId(), taskVo.getAssignee());
}
taskService.complete(taskVo.getTaskId(), taskVo.getVariables());
}
return true;
}
@Override
public Task toNextTaskUser(FlowTaskVo taskVo) {
Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult();
if (Objects.isNull(task)) {
throw new BusinessException("任务不存在");
}
taskService.setAssignee(taskVo.getTaskId(), taskVo.getAssignee());
return task;
}
@Override
public Task getTask(String proIndex) {
return taskService.createTaskQuery()
@@ -141,4 +170,512 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
}
/**
* 我发起的流程
*
* @param queryVo 请求参数
* @return
*/
@Override
public Page<FlowTaskDto> myProcess(FlowQueryVo queryVo) {
Page<FlowTaskDto> page = new Page<>();
String userId = RequestUtil.getUserIndex();
HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
.startedBy(userId)
.orderByProcessInstanceStartTime()
.desc();
List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize());
page.setTotal(historicProcessInstanceQuery.count());
List<FlowTaskDto> flowList = new ArrayList<>();
for (HistoricProcessInstance hisIns : historicProcessInstances) {
FlowTaskDto flowTask = new FlowTaskDto();
flowTask.setCreateTime(hisIns.getStartTime());
flowTask.setFinishTime(hisIns.getEndTime());
flowTask.setProcInsId(hisIns.getId());
// 计算耗时
if (Objects.nonNull(hisIns.getEndTime())) {
long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime();
flowTask.setDuration(getDate(time));
} else {
long time = System.currentTimeMillis() - hisIns.getStartTime().getTime();
flowTask.setDuration(getDate(time));
}
// 流程定义信息
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
.processDefinitionId(hisIns.getProcessDefinitionId())
.singleResult();
flowTask.setDeployId(pd.getDeploymentId());
flowTask.setProcDefName(pd.getName());
flowTask.setProcDefVersion(pd.getVersion());
flowTask.setCategory(pd.getCategory());
flowTask.setProcDefVersion(pd.getVersion());
// 当前所处流程
List<Task> taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list();
if (CollectionUtils.isNotEmpty(taskList)) {
flowTask.setTaskId(taskList.get(0).getId());
flowTask.setTaskName(taskList.get(0).getName());
if (StringUtils.isNotBlank(taskList.get(0).getAssignee())) {
// 当前任务节点办理人信息
List<User> userList = userFeignClient.getUserByIdList(Stream.of(taskList.get(0).getAssignee()).collect(Collectors.toList())).getData();
if (CollectionUtil.isNotEmpty(userList)) {
flowTask.setAssigneeId(userList.get(0).getId());
flowTask.setAssigneeName(userList.get(0).getName());
flowTask.setAssigneeDeptName(Objects.nonNull(userList.get(0).getDeptId()) ? userList.get(0).getDeptId() : "");
}
}
} else {
List<HistoricTaskInstance> historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list();
flowTask.setTaskId(historicTaskInstance.get(0).getId());
flowTask.setTaskName(historicTaskInstance.get(0).getName());
if (StringUtils.isNotBlank(historicTaskInstance.get(0).getAssignee())) {
// 当前任务节点办理人信息
UserVO userVO = userFeignClient.getUserById(historicTaskInstance.get(0).getAssignee()).getData();
if (Objects.nonNull(userVO)) {
flowTask.setAssigneeId(userVO.getId());
flowTask.setAssigneeName(userVO.getName());
flowTask.setAssigneeDeptName(Objects.nonNull(userVO.getDeptName()) ? userVO.getDeptName() : "");
}
}
}
flowList.add(flowTask);
}
page.setRecords(flowList);
return page;
}
/**
* 已办任务列表
*
* @param queryVo 请求参数
* @return
*/
@Override
public Page<FlowTaskDto> finishedList(FlowQueryVo queryVo) {
Page<FlowTaskDto> page = new Page<>();
String userId = RequestUtil.getUserIndex();
HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
.includeProcessVariables()
.finished()
.taskAssignee(userId)
.orderByHistoricTaskInstanceEndTime()
.desc();
List<HistoricTaskInstance> historicTaskInstanceList = taskInstanceQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize());
List<FlowTaskDto> hisTaskList = new ArrayList<>();
for (HistoricTaskInstance histTask : historicTaskInstanceList) {
FlowTaskDto flowTask = new FlowTaskDto();
// 当前流程信息
flowTask.setTaskId(histTask.getId());
// 审批人员信息
flowTask.setCreateTime(histTask.getCreateTime());
flowTask.setFinishTime(histTask.getEndTime());
flowTask.setDuration(getDate(histTask.getDurationInMillis()));
flowTask.setProcDefId(histTask.getProcessDefinitionId());
flowTask.setTaskDefKey(histTask.getTaskDefinitionKey());
flowTask.setTaskName(histTask.getName());
// 流程定义信息
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
.processDefinitionId(histTask.getProcessDefinitionId())
.singleResult();
flowTask.setDeployId(pd.getDeploymentId());
flowTask.setProcDefName(pd.getName());
flowTask.setProcDefVersion(pd.getVersion());
flowTask.setProcInsId(histTask.getProcessInstanceId());
flowTask.setHisProcInsId(histTask.getProcessInstanceId());
// 流程发起人信息
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(histTask.getProcessInstanceId())
.singleResult();
List<User> userList = userFeignClient.getUserByIdList(Stream.of(historicProcessInstance.getStartUserId()).collect(Collectors.toList())).getData();
flowTask.setStartUserId(userList.get(0).getLoginName());
flowTask.setStartUserName(userList.get(0).getName());
flowTask.setStartDeptName(userList.get(0).getDeptId());
hisTaskList.add(flowTask);
}
page.setTotal(taskInstanceQuery.count());
page.setRecords(hisTaskList);
return page;
}
/**
* 代办任务列表
*
* @param queryVo 请求参数
* @return
*/
@Override
public Page<FlowTaskDto> todoList(FlowQueryVo queryVo) {
Page<FlowTaskDto> page = new Page<>();
// 只查看自己的数据
String userId = RequestUtil.getUserIndex();
UserVO sysUser = userFeignClient.getUserById(userId).getData();
TaskQuery taskQuery = taskService.createTaskQuery()
.active()
.includeProcessVariables()
.taskCandidateGroupIn(sysUser.getRoleList())
.taskCandidateOrAssigned(sysUser.getId())
.orderByTaskCreateTime().desc();
// TODO 传入名称查询不到数据?
// if (StringUtils.isNotBlank(queryVo.getName())){
// taskQuery.processDefinitionNameLike(queryVo.getName());
// }
page.setTotal(taskQuery.count());
List<Task> taskList = taskQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize());
List<FlowTaskDto> flowList = new ArrayList<>();
for (Task task : taskList) {
FlowTaskDto flowTask = new FlowTaskDto();
// 当前流程信息
flowTask.setTaskId(task.getId());
flowTask.setTaskDefKey(task.getTaskDefinitionKey());
flowTask.setCreateTime(task.getCreateTime());
flowTask.setProcDefId(task.getProcessDefinitionId());
flowTask.setExecutionId(task.getExecutionId());
flowTask.setTaskName(task.getName());
// 流程定义信息
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
.processDefinitionId(task.getProcessDefinitionId())
.singleResult();
flowTask.setDeployId(pd.getDeploymentId());
flowTask.setProcDefName(pd.getName());
flowTask.setProcDefVersion(pd.getVersion());
flowTask.setProcInsId(task.getProcessInstanceId());
// 流程发起人信息
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(task.getProcessInstanceId())
.singleResult();
UserVO startUser = userFeignClient.getUserById(historicProcessInstance.getStartUserId()).getData();
flowTask.setStartUserId(startUser.getId());
flowTask.setStartUserName(startUser.getName());
flowTask.setStartDeptName(Objects.nonNull(startUser.getDeptName()) ? startUser.getDeptName() : "");
flowList.add(flowTask);
}
page.setRecords(flowList);
return page;
}
/**
* 获取流程变量
*
* @param taskId
* @return
*/
@Override
public Map<String, Object> processVariables(String taskId) {
// HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
// .processInstanceId(task.getProcessInstanceId())
// .singleResult();
// SysUser startUser = sysUserService.selectUserById(Long.parseLong(historicProcessInstance.getStartUserId()));
// 流程变量
HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().includeProcessVariables().finished().taskId(taskId).singleResult();
if (Objects.nonNull(historicTaskInstance)) {
return historicTaskInstance.getProcessVariables();
} else {
Map<String, Object> variables = taskService.getVariables(taskId);
return variables;
}
}
/**
* 流程历史流转记录
*
* @param procInsId 流程实例Id
* @return
*/
@Override
public Map<String, Object> flowRecord(String procInsId, String deployId) {
Map<String, Object> map = new HashMap<String, Object>();
if (StringUtils.isNotBlank(procInsId)) {
List<HistoricActivityInstance> list = historyService
.createHistoricActivityInstanceQuery()
.processInstanceId(procInsId)
.orderByHistoricActivityInstanceStartTime()
.desc().list();
List<FlowTaskDto> hisFlowList = new ArrayList<>();
for (HistoricActivityInstance histIns : list) {
// 展示开始节点
// if ("startEvent".equals(histIns.getActivityType())) {
// FlowTaskDto flowTask = new FlowTaskDto();
// // 流程发起人信息
// HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
// .processInstanceId(histIns.getProcessInstanceId())
// .singleResult();
// SysUser startUser = sysUserService.selectUserById(Long.parseLong(historicProcessInstance.getStartUserId()));
// flowTask.setTaskName(startUser.getNickName() + "(" + startUser.getDept().getDeptName() + ")发起申请");
// flowTask.setFinishTime(histIns.getEndTime());
// hisFlowList.add(flowTask);
// } else if ("endEvent".equals(histIns.getActivityType())) {
// FlowTaskDto flowTask = new FlowTaskDto();
// flowTask.setTaskName(StringUtils.isNotBlank(histIns.getActivityName()) ? histIns.getActivityName() : "结束");
// flowTask.setFinishTime(histIns.getEndTime());
// hisFlowList.add(flowTask);
// } else
if (StringUtils.isNotBlank(histIns.getTaskId())) {
FlowTaskDto flowTask = new FlowTaskDto();
flowTask.setTaskId(histIns.getTaskId());
flowTask.setTaskName(histIns.getActivityName());
flowTask.setCreateTime(histIns.getStartTime());
flowTask.setFinishTime(histIns.getEndTime());
if (StringUtils.isNotBlank(histIns.getAssignee())) {
UserVO sysUser = userFeignClient.getUserById(histIns.getAssignee()).getData();
flowTask.setAssigneeId(sysUser.getId());
flowTask.setAssigneeName(sysUser.getName());
flowTask.setDeptName(Objects.nonNull(sysUser.getDeptName()) ? sysUser.getDeptName() : "");
}
// 展示审批人员
List<HistoricIdentityLink> linksForTask = historyService.getHistoricIdentityLinksForTask(histIns.getTaskId());
StringBuilder stringBuilder = new StringBuilder();
for (HistoricIdentityLink identityLink : linksForTask) {
// 获选人,候选组/角色(多个)
if ("candidate".equals(identityLink.getType())) {
if (StringUtils.isNotBlank(identityLink.getUserId())) {
UserVO sysUser = userFeignClient.getUserById(identityLink.getUserId()).getData();
stringBuilder.append(sysUser.getName()).append(",");
}
if (StringUtils.isNotBlank(identityLink.getGroupId())) {
/* UserVO sysUser = userFeignClient.getUserById(identityLink.getUserId()).getData();
SysRole sysRole = sysRoleService.selectRoleById(Long.parseLong(identityLink.getGroupId()));
stringBuilder.append(sysRole.getRoleName()).append(",");*/
}
}
}
if (StringUtils.isNotBlank(stringBuilder)) {
flowTask.setCandidate(stringBuilder.substring(0, stringBuilder.length() - 1));
}
flowTask.setDuration(histIns.getDurationInMillis() == null || histIns.getDurationInMillis() == 0 ? null : getDate(histIns.getDurationInMillis()));
// 获取意见评论内容
List<Comment> commentList = taskService.getProcessInstanceComments(histIns.getProcessInstanceId());
commentList.forEach(comment -> {
if (histIns.getTaskId().equals(comment.getTaskId())) {
flowTask.setComment(FlowCommentDto.builder().type(comment.getType()).comment(comment.getFullMessage()).build());
}
});
hisFlowList.add(flowTask);
}
}
map.put("flowList", hisFlowList);
}
// 第一次申请获取初始化表单
if (StringUtils.isNotBlank(deployId)) {
/* SysForm sysForm = sysInstanceFormService.selectSysDeployFormByDeployId(deployId);
if (Objects.isNull(sysForm)) {
return AjaxResult.error("请先配置流程表单");
}
map.put("formData", JSONObject.parseObject(sysForm.getFormContent()));*/
}
return map;
}
/**
* 取消申请
* 目前实现方式: 直接将当前流程变更为已完成
*
* @param flowTaskVo
* @return
*/
@Override
public Boolean stopProcess(FlowTaskVo flowTaskVo) {
List<Task> task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).list();
if (CollectionUtils.isEmpty(task)) {
throw new BusinessException("流程未启动或已执行完成,取消申请失败");
}
// 获取当前流程实例
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(flowTaskVo.getInstanceId())
.singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
if (Objects.nonNull(bpmnModel)) {
Process process = bpmnModel.getMainProcess();
List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
if (CollectionUtils.isNotEmpty(endNodes)) {
// TODO 取消流程为什么要设置流程发起人?
// SysUser loginUser = SecurityUtils.getLoginUser().getUser();
// Authentication.setAuthenticatedUserId(loginUser.getUserId().toString());
// taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(),
// StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment());
// 获取当前流程最后一个节点
String endId = endNodes.get(0).getId();
List<Execution> executions = runtimeService.createExecutionQuery()
.parentId(processInstance.getProcessInstanceId()).list();
List<String> executionIds = new ArrayList<>();
executions.forEach(execution -> executionIds.add(execution.getId()));
// 变更流程为已结束状态
runtimeService.createChangeActivityStateBuilder()
.moveExecutionsToSingleActivityId(executionIds, endId).changeState();
}
}
return true;
}
/**
* 驳回任务
*
* @param flowTaskVo
*/
@Override
public void taskReject(FlowTaskVo flowTaskVo) {
if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) {
throw new BusinessException("任务处于挂起状态!");
}
// 当前任务 task
Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
// 获取流程定义信息
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
// 获取所有节点信息
Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
// 获取全部节点列表,包含子节点
Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
// 获取当前任务节点元素
FlowElement source = null;
if (allElements != null) {
for (FlowElement flowElement : allElements) {
// 类型为用户节点
if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
// 获取节点信息
source = flowElement;
}
}
}
// 目的获取所有跳转到的节点 targetIds
// 获取当前节点的所有父级用户任务节点
// 深度优先算法思想:延边迭代深入
List<UserTask> parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null);
if (parentUserTaskList == null || parentUserTaskList.size() == 0) {
throw new BusinessException("当前节点为初始任务节点,不能驳回");
}
// 获取活动 ID 即节点 Key
List<String> parentUserTaskKeyList = new ArrayList<>();
parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId()));
// 获取全部历史节点活动实例,即已经走过的节点历史,数据采用开始时间升序
List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list();
// 数据清洗,将回滚导致的脏数据清洗掉
List<String> lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList);
// 此时历史任务实例为倒序,获取最后走的节点
List<String> targetIds = new ArrayList<>();
// 循环结束标识,遇到当前目标节点的次数
int number = 0;
StringBuilder parentHistoricTaskKey = new StringBuilder();
for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
// 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过
if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
continue;
}
parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
number++;
}
// 在数据清洗后,历史节点就是唯一一条从起始到当前节点的历史记录,理论上每个点只会出现一次
// 在流程中如果出现循环,那么每次循环中间的点也只会出现一次,再出现就是下次循环
// number == 1第一次遇到当前节点
// number == 2第二次遇到代表最后一次的循环范围
if (number == 2) {
break;
}
// 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点
if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) {
targetIds.add(historicTaskInstanceKey);
}
}
// 目的获取所有需要被跳转的节点 currentIds
// 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路
UserTask oneUserTask = parentUserTaskList.get(0);
// 获取所有正常进行的任务节点 Key这些任务不能直接使用需要找出其中需要撤回的任务
List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
List<String> runTaskKeyList = new ArrayList<>();
runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
// 需驳回任务列表
List<String> currentIds = new ArrayList<>();
// 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务
List<UserTask> currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null);
currentUserTaskList.forEach(item -> currentIds.add(item.getId()));
// 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况
if (targetIds.size() > 1 && currentIds.size() > 1) {
throw new BusinessException("任务出现多对多情况,无法撤回");
}
// 循环获取那些需要被撤回的节点的ID用来设置驳回原因
List<String> currentTaskIds = new ArrayList<>();
currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
if (currentId.equals(runTask.getTaskDefinitionKey())) {
currentTaskIds.add(runTask.getId());
}
}));
// 设置驳回意见
currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskVo.getComment()));
try {
// 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况
if (targetIds.size() > 1) {
// 1 对 多任务跳转currentIds 当前节点(1)targetIds 跳转到的节点(多)
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId()).
moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState();
}
// 如果父级任务只有一个,因此当前任务可能为网关中的任务
if (targetIds.size() == 1) {
// 1 对 1 或 多 对 1 情况currentIds 当前要跳转的节点列表(1或多)targetIds.get(0) 跳转到的节点(1)
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState();
}
} catch (FlowableObjectNotFoundException e) {
throw new BusinessException("未找到流程实例,流程可能已发生变化");
} catch (FlowableException e) {
throw new BusinessException("无法取消或开始活动");
}
}
/**
* 流程完成时间处理
*
* @param ms
* @return
*/
private String getDate(long ms) {
long day = ms / (24 * 60 * 60 * 1000);
long hour = (ms / (60 * 60 * 1000) - day * 24);
long minute = ((ms / (60 * 1000)) - day * 24 * 60 - hour * 60);
long second = (ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60);
if (day > 0) {
return day + "" + hour + "小时" + minute + "分钟";
}
if (hour > 0) {
return hour + "小时" + minute + "分钟";
}
if (minute > 0) {
return minute + "分钟";
}
if (second > 0) {
return second + "";
} else {
return 0 + "";
}
}
}

View File

@@ -0,0 +1,589 @@
package com.njcn.process.utils;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.*;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.flowable.task.api.history.HistoricTaskInstance;
import java.util.*;
/**
* @author Tony
* @date 2021-04-03 23:57
*/
@Slf4j
public class FlowableUtils {
/**
* 根据节点,获取入口连线
* @param source
* @return
*/
public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {
List<SequenceFlow> sequenceFlows = null;
if (source instanceof FlowNode) {
sequenceFlows = ((FlowNode) source).getIncomingFlows();
} else if (source instanceof Gateway) {
sequenceFlows = ((Gateway) source).getIncomingFlows();
} else if (source instanceof SubProcess) {
sequenceFlows = ((SubProcess) source).getIncomingFlows();
} else if (source instanceof StartEvent) {
sequenceFlows = ((StartEvent) source).getIncomingFlows();
} else if (source instanceof EndEvent) {
sequenceFlows = ((EndEvent) source).getIncomingFlows();
}
return sequenceFlows;
}
/**
* 根据节点,获取出口连线
* @param source
* @return
*/
public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {
List<SequenceFlow> sequenceFlows = null;
if (source instanceof FlowNode) {
sequenceFlows = ((FlowNode) source).getOutgoingFlows();
} else if (source instanceof Gateway) {
sequenceFlows = ((Gateway) source).getOutgoingFlows();
} else if (source instanceof SubProcess) {
sequenceFlows = ((SubProcess) source).getOutgoingFlows();
} else if (source instanceof StartEvent) {
sequenceFlows = ((StartEvent) source).getOutgoingFlows();
} else if (source instanceof EndEvent) {
sequenceFlows = ((EndEvent) source).getOutgoingFlows();
}
return sequenceFlows;
}
/**
* 获取全部节点列表,包含子流程节点
* @param flowElements
* @param allElements
* @return
*/
public static Collection<FlowElement> getAllElements(Collection<FlowElement> flowElements, Collection<FlowElement> allElements) {
allElements = allElements == null ? new ArrayList<>() : allElements;
for (FlowElement flowElement : flowElements) {
allElements.add(flowElement);
if (flowElement instanceof SubProcess) {
// 继续深入子流程,进一步获取子流程
allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements);
}
}
return allElements;
}
/**
* 迭代获取父级任务节点列表,向前找
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param userTaskList 已找到的用户任务节点
* @return
*/
public static List<UserTask> iteratorFindParentUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
if (source instanceof StartEvent && source.getSubProcess() != null) {
userTaskList = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList);
}
// 根据类型,获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 类型为用户节点,则新增父级节点
if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement());
continue;
}
// 类型为子流程,则添加子流程开始节点出口处相连的节点
if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
// 获取子流程用户任务节点
List<UserTask> childUserTaskList = findChildProcessUserTasks((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null);
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
if (childUserTaskList != null && childUserTaskList.size() > 0) {
userTaskList.addAll(childUserTaskList);
continue;
}
}
// 继续迭代
userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList);
}
}
return userTaskList;
}
/**
* 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找
* @param source 起始节点(退回节点)
* @param runTaskKeyList 正在运行的任务 Key用于校验任务节点是否是正在运行的节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param userTaskList 需要撤回的用户任务列表
* @return
*/
public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
if (source instanceof EndEvent && source.getSubProcess() != null) {
userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList);
}
// 根据类型,获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) {
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
continue;
}
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null);
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
if (childUserTaskList != null && childUserTaskList.size() > 0) {
userTaskList.addAll(childUserTaskList);
continue;
}
}
// 继续迭代
userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList);
}
}
return userTaskList;
}
/**
* 迭代获取子流程用户任务节点
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param userTaskList 需要撤回的用户任务列表
* @return
*/
public static List<UserTask> findChildProcessUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
// 根据类型,获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
if (sequenceFlow.getTargetFlowElement() instanceof UserTask) {
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
continue;
}
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
List<UserTask> childUserTaskList = findChildProcessUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null);
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
if (childUserTaskList != null && childUserTaskList.size() > 0) {
userTaskList.addAll(childUserTaskList);
continue;
}
}
// 继续迭代
userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList);
}
}
return userTaskList;
}
/**
* 从后向前寻路,获取所有脏线路上的点
* @param source 起始节点
* @param passRoads 已经经过的点集合
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param targets 目标脏线路终点
* @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储
* @return
*/
public static Set<String> iteratorFindDirtyRoads(FlowElement source, List<String> passRoads, Set<String> hasSequenceFlow, List<String> targets, Set<String> dirtyRoads) {
passRoads = passRoads == null ? new ArrayList<>() : passRoads;
dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads;
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
if (source instanceof StartEvent && source.getSubProcess() != null) {
dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, dirtyRoads);
}
// 根据类型,获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 新增经过的路线
passRoads.add(sequenceFlow.getSourceFlowElement().getId());
// 如果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线
if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) {
dirtyRoads.addAll(passRoads);
continue;
}
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
dirtyRoads = findChildProcessAllDirtyRoad((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, dirtyRoads);
// 是否存在子流程上true 是false 否
Boolean isInChildProcess = dirtyTargetInChildProcess((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, targets, null);
if (isInChildProcess) {
// 已在子流程上找到,该路线结束
continue;
}
}
// 继续迭代
dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, targets, dirtyRoads);
}
}
return dirtyRoads;
}
/**
* 迭代获取子流程脏路线
* 说明,假如回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储
* @return
*/
public static Set<String> findChildProcessAllDirtyRoad(FlowElement source, Set<String> hasSequenceFlow, Set<String> dirtyRoads) {
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads;
// 根据类型,获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 添加脏路线
dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId());
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
dirtyRoads = findChildProcessAllDirtyRoad((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, dirtyRoads);
}
// 继续迭代
dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, dirtyRoads);
}
}
return dirtyRoads;
}
/**
* 判断脏路线结束节点是否在子流程上
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止
* @param inChildProcess 是否存在子流程上true 是false 否
* @return
*/
public static Boolean dirtyTargetInChildProcess(FlowElement source, Set<String> hasSequenceFlow, List<String> targets, Boolean inChildProcess) {
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
inChildProcess = inChildProcess != null && inChildProcess;
// 根据类型,获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (sequenceFlows != null && !inChildProcess) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 如果发现目标点在子流程上存在,说明只到子流程为止
if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) {
inChildProcess = true;
break;
}
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
inChildProcess = dirtyTargetInChildProcess((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, targets, inChildProcess);
}
// 继续迭代
inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, targets, inChildProcess);
}
}
return inChildProcess;
}
/**
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行
* 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况
* @param source 起始节点
* @param isSequential 是否串行
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param targetKsy 目标节点
* @return
*/
public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, Set<String> hasSequenceFlow, Boolean isSequential) {
isSequential = isSequential == null || isSequential;
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
if (source instanceof StartEvent && source.getSubProcess() != null) {
isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, isSequential);
}
// 根据类型,获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 如果目标节点已被判断为并行,后面都不需要执行,直接返回
if (!isSequential) {
break;
}
// 这条线路存在目标节点,这条线路完成,进入下个线路
if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) {
continue;
}
if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) {
isSequential = false;
break;
}
// 否则就继续迭代
isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, hasSequenceFlow, isSequential);
}
}
return isSequential;
}
/**
* 从后向前寻路,获取到达节点的所有路线
* 不存在直接回退到子流程,但是存在回退到父级流程的情况
* @param source 起始节点
* @param passRoads 已经经过的点集合
* @param roads 路线
* @return
*/
public static List<List<UserTask>> findRoad(FlowElement source, List<UserTask> passRoads, Set<String> hasSequenceFlow, List<List<UserTask>> roads) {
passRoads = passRoads == null ? new ArrayList<>() : passRoads;
roads = roads == null ? new ArrayList<>() : roads;
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
if (source instanceof StartEvent && source.getSubProcess() != null) {
roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads);
}
// 根据类型,获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (sequenceFlows != null && sequenceFlows.size() != 0) {
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 添加经过路线
if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
passRoads.add((UserTask) sequenceFlow.getSourceFlowElement());
}
// 继续迭代
roads = findRoad(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, roads);
}
} else {
// 添加路线
roads.add(passRoads);
}
return roads;
}
/**
* 历史节点数据清洗,清洗掉又回滚导致的脏数据
* @param allElements 全部节点信息
* @param historicTaskInstanceList 历史任务实例信息,数据采用开始时间升序
* @return
*/
public static List<String> historicTaskInstanceClean(Collection<FlowElement> allElements, List<HistoricTaskInstance> historicTaskInstanceList) {
// 会签节点收集
List<String> multiTask = new ArrayList<>();
allElements.forEach(flowElement -> {
if (flowElement instanceof UserTask) {
// 如果该节点的行为为会签行为,说明该节点为会签节点
if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) {
multiTask.add(flowElement.getId());
}
}
});
// 循环放入栈,栈 LIFO后进先出
Stack<HistoricTaskInstance> stack = new Stack<>();
historicTaskInstanceList.forEach(stack::push);
// 清洗后的历史任务实例
List<String> lastHistoricTaskInstanceList = new ArrayList<>();
// 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗
// 临时用户任务 key
StringBuilder userTaskKey = null;
// 临时被删掉的任务 key存在并行情况
List<String> deleteKeyList = new ArrayList<>();
// 临时脏数据线路
List<Set<String>> dirtyDataLineList = new ArrayList<>();
// 由某个点跳到会签点,此时出现多个会签实例对应 1 个跳转情况,需要把这些连续脏数据都找到
// 会签特殊处理下标
int multiIndex = -1;
// 会签特殊处理 key
StringBuilder multiKey = null;
// 会签特殊处理操作标识
boolean multiOpera = false;
while (!stack.empty()) {
// 从这里开始 userTaskKey 都还是上个栈的 key
// 是否是脏数据线路上的点
final boolean[] isDirtyData = {false};
for (Set<String> oldDirtyDataLine : dirtyDataLineList) {
if (oldDirtyDataLine.contains(stack.peek().getTaskDefinitionKey())) {
isDirtyData[0] = true;
}
}
// 删除原因不为空,说明从这条数据开始回跳或者回退的
// MI_END会签完成后其他未签到节点的删除原因不在处理范围内
if (stack.peek().getDeleteReason() != null && !"MI_END".equals(stack.peek().getDeleteReason())) {
// 可以理解为脏线路起点
String dirtyPoint = "";
if (stack.peek().getDeleteReason().contains("Change activity to ")) {
dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", "");
}
// 会签回退删除原因有点不同
if (stack.peek().getDeleteReason().contains("Change parent activity to ")) {
dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", "");
}
FlowElement dirtyTask = null;
// 获取变更节点的对应的入口处连线
// 如果是网关并行回退情况,会变成两条脏数据路线,效果一样
for (FlowElement flowElement : allElements) {
if (flowElement.getId().equals(stack.peek().getTaskDefinitionKey())) {
dirtyTask = flowElement;
}
}
// 获取脏数据线路
Set<String> dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, Arrays.asList(dirtyPoint.split(",")), null);
// 自己本身也是脏线路上的点,加进去
dirtyDataLine.add(stack.peek().getTaskDefinitionKey());
log.info(stack.peek().getTaskDefinitionKey() + "点脏路线集合:" + dirtyDataLine);
// 是全新的需要添加的脏线路
boolean isNewDirtyData = true;
for (int i = 0; i < dirtyDataLineList.size(); i++) {
// 如果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回
// 这时,都以之前的脏线路节点为标准,只需合并脏线路即可,也就是路线补全
if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) {
isNewDirtyData = false;
dirtyDataLineList.get(i).addAll(dirtyDataLine);
}
}
// 已确定时全新的脏线路
if (isNewDirtyData) {
// deleteKey 单一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey 其实是由多个值组成
// 按照逻辑,回退后立刻生成的实例记录就是回退的记录
// 至于驳回所生成的 Key直接从删除原因中获取因为存在驳回到并行的情况
deleteKeyList.add(dirtyPoint + ",");
dirtyDataLineList.add(dirtyDataLine);
}
// 添加后,现在这个点变成脏线路上的点了
isDirtyData[0] = true;
}
// 如果不是脏线路上的点,说明是有效数据,添加历史实例 Key
if (!isDirtyData[0]) {
lastHistoricTaskInstanceList.add(stack.peek().getTaskDefinitionKey());
}
// 校验脏线路是否结束
for (int i = 0; i < deleteKeyList.size(); i ++) {
// 如果发现脏数据属于会签,记录下下标与对应 Key以备后续比对会签脏数据范畴开始
if (multiKey == null && multiTask.contains(stack.peek().getTaskDefinitionKey())
&& deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) {
multiIndex = i;
multiKey = new StringBuilder(stack.peek().getTaskDefinitionKey());
}
// 会签脏数据处理,节点退回会签清空
// 如果在会签脏数据范畴中发现 Key改变说明会签脏数据在上个节点就结束了可以把会签脏数据删掉
if (multiKey != null && !multiKey.toString().equals(stack.peek().getTaskDefinitionKey())) {
deleteKeyList.set(multiIndex , deleteKeyList.get(multiIndex).replace(stack.peek().getTaskDefinitionKey() + ",", ""));
multiKey = null;
// 结束进行下校验删除
multiOpera = true;
}
// 其他脏数据处理
// 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息
// 脏数据产生的新实例中是否包含这条数据
if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) {
// 删除匹配到的部分
deleteKeyList.set(i , deleteKeyList.get(i).replace(stack.peek().getTaskDefinitionKey() + ",", ""));
}
// 如果每组中的元素都以匹配过,说明脏数据结束
if ("".equals(deleteKeyList.get(i))) {
// 同时删除脏数据
deleteKeyList.remove(i);
dirtyDataLineList.remove(i);
break;
}
}
// 会签数据处理需要在循环外处理,否则可能导致溢出
// 会签的数据肯定是之前放进去的所以理论上不会溢出,但还是校验下
if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) {
// 同时删除脏数据
deleteKeyList.remove(multiIndex);
dirtyDataLineList.remove(multiIndex);
multiIndex = -1;
multiOpera = false;
}
// pop() 方法与 peek() 方法不同,在返回值的同时,会把值从栈中移除
// 保存新的 userTaskKey 在下个循环中使用
userTaskKey = new StringBuilder(stack.pop().getTaskDefinitionKey());
}
log.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList);
return lastHistoricTaskInstanceList;
}
}