1. 前言
上一篇博客【SpringBoot整合Flowable工作流-1(畫流程定義) 】介紹用 Flowable-ui 畫了一個(gè)簡(jiǎn)單的流程圖创橄。
這篇博客將介紹代碼整合部分,主要內(nèi)容有:【發(fā)布流程定義】、【開啟流程任務(wù)】术瓮、【獲取用戶任務(wù)】溶弟、【用戶審批任務(wù)】沙廉、【添加審批意見】扶叉、【獲取流程圖】嗓节、【獲取我的待辦任務(wù)】荧缘、【獲取我發(fā)起的流程】、【我審批過的流程】...
2. 代碼添加依賴
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.4.2</version>
</dependency>
3. 創(chuàng)建數(shù)據(jù)庫(kù)
創(chuàng)建數(shù)據(jù)庫(kù)可以使用通過 Flowable 提供的 sql 實(shí)現(xiàn)拦宣,也可以通過程序自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)實(shí)現(xiàn)
3.1 Flowable 提供的 sql
下載文件 Flowable 相關(guān)的資源截粗,進(jìn)入 https://flowable.com/open-source/downloads,然后點(diǎn)擊 【Download Flowable v6.x.x】鸵隧,下載下來是一個(gè)壓縮包绸罗,解壓后會(huì)看到如下目錄結(jié)構(gòu)
└─database # 數(shù)據(jù)庫(kù)文件
└─create
└─all
└─flowable.mysql.all.create.sql
找到 $/database/create/database/create/flowable.mysql.all.create.sql 文件,導(dǎo)入mysql數(shù)據(jù)庫(kù)即可
3.2 應(yīng)用程序自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)(推薦)
需要在 jdbc 的 url 中添加一個(gè)參數(shù)值豆瘫,nullCatalogMeansCurrent=true
如下:
jdbc:mysql://127.0.0.1:3306/flowable?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&nullCatalogMeansCurrent=true
4. 代碼部分
4.1 常用的幾個(gè)Service類
/** 運(yùn)行時(shí)Service(用于運(yùn)行時(shí)流程實(shí)例珊蟀、流程變量、流程節(jié)點(diǎn)) */
@Autowired
private RuntimeService runtimeService;
/** 資源存儲(chǔ)Service(用于模型外驱、流程定義) */
@Autowired
private RepositoryService repositoryService;
/** 流程引擎Service */
@Qualifier("processEngine")
@Autowired
private ProcessEngine processEngine;
/** 任務(wù)Service(用于運(yùn)行時(shí)的用戶任務(wù)育灸、審批日志) */
@Autowired
private TaskService taskService;
@Autowired
protected ManagementService managementService;
/** 歷史Service(用于歷史記錄腻窒,可以找到歷史的流程實(shí)例、流程節(jié)點(diǎn)) */
@Autowired
protected HistoryService historyService;
4.2 流程定義相關(guān)代碼
4.2.1 發(fā)布流程定義
上一篇博客【SpringBoot整合Flowable工作流-1(畫流程定義) 】畫好了流程磅崭,然后下載下來是一個(gè) “請(qǐng)假流程1.bpmn20.xml” 的xml文件定页,下載就可以通過代碼把這個(gè)流程發(fā)布到流程定義中了。
代碼如下
@Override
public boolean importProcessDefinition(MultipartFile file) {
try {
Deployment deployment = repositoryService.createDeployment()
// .key()
// .name(name)
// .category(category)
// .tenantId()
// 通過壓縮包的形式一次行多個(gè)發(fā)布
// .addZipInputStream()
// 通過InputStream的形式發(fā)布
.addInputStream(file.getOriginalFilename(), file.getInputStream())
// 通過存放在classpath目錄下的文件進(jìn)行發(fā)布
// .addClasspathResource("p1.bpmn20.xml")
// 通過xml字符串的形式
// .addString()
.deploy();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
if (processDefinition == null) {
return false;
}
} catch (Exception e) {
throw new BusinessException("導(dǎo)入流程定義失斦莱稀:" + e.getMessage());
}
return true;
}
基于 SpringBoot 發(fā)布流程定義,還有一種巧妙的形式杭煎,那就是在 resources 目錄下建立一個(gè)文件夾 processes 恩够,然后把對(duì)應(yīng)的流程文件發(fā)到這個(gè)文件夾下即可,啟動(dòng) SpringBoot 項(xiàng)目的時(shí)候羡铲,通過觀察日志就會(huì)發(fā)現(xiàn)該流程就自動(dòng)發(fā)布了蜂桶。(不推薦)
workflow-server
└─src
└─main
├─java
└─resources
├─mapper
└─processes
└─請(qǐng)假流程1.bpmn20.xml
4.2.2 查詢流程定義
// 創(chuàng)建 ProcessDefinitionQuery
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
// 根據(jù)流程定義ID
// processDefinitionQuery.processDefinitionId(requestDTO.getId());
// 根據(jù)流程定義Key
// processDefinitionQuery.processDefinitionKeyLike("%" + requestDTO.getKey().trim() + "%");
// 根據(jù)流程定義名稱
// processDefinitionQuery.processDefinitionNameLike("%" + requestDTO.getName().trim() + "%");
//
// 獲取總數(shù)
long count = processDefinitionQuery.count();
// 獲取單個(gè)
ProcessDefinition processDefinition = processDefinitionQuery.singleResult();
// 獲取列表
List<ProcessDefinition> processDefinitions = processDefinitionQuery.list();
// 獲取分頁(yè)
List<ProcessDefinition> processDefinitions = processDefinitionQuery.listPage(0, 10)
4.2.3 獲取流程定義xml
public String getXmlResource(String id) {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
processDefinition.getResourceName());
try {
return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new BusinessException("獲取資源失敗:" + e.getMessage());
} finally {
try {
IOUtils.close(inputStream, null);
} catch (IOException ignored) {
}
}
}
4.2.4 獲取流程定義圖片
public String getDiagramImageResource(String id) {
// 理論上我用這種形式也是行的也切,但是我獲取出來會(huì)有亂碼扑媚,我也比較奇怪,所以換了通過 bpmnModel 的方式
// ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
// InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
// processDefinition.getDiagramResourceName());
// 獲取bpmnModel對(duì)象
BpmnModel bpmnModel = repositoryService.getBpmnModel(id);
// 生成圖片流
ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = configuration.getProcessDiagramGenerator();
InputStream inputStream = diagramGenerator.generateDiagram(bpmnModel, "png", Collections.emptyList(),
Collections.emptyList(), "宋體", "宋體", "宋體",
this.getClass().getClassLoader(), 1.0, true);
try {
return "data:image/png;base64," + Base64.encode(inputStream);
} catch (Exception e) {
throw new BusinessException("獲取資源失斃资选:" + e.getMessage());
} finally {
try {
IOUtils.close(inputStream, null);
} catch (IOException ignored) {
}
}
}
4.2.5 刪除流程定義
public void deleteByIds(List<String> ids) {
List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().processDefinitionIds(new HashSet<>(ids)).list();
// repositoryService.deleteDeployment(String deploymentId, boolean cascade)
// 刪除給定的部署和級(jí)聯(lián)刪除流程實(shí)例疆股、歷史流程實(shí)例和作業(yè)。
processDefinitions.forEach(v -> repositoryService.deleteDeployment(v.getDeploymentId(), true));
}
4.3 流程實(shí)例相關(guān)代碼
4.3.1 添加流程實(shí)例審批意見
起到類似于記錄流程的操作記錄的作用倒槐,我這里是自己封裝了一層旬痹,我封裝了自己的業(yè)務(wù)用戶ID、用戶名讨越、執(zhí)行類型两残、意見內(nèi)容...
@Data
@ApiModel("流程實(shí)例-審批意見請(qǐng)求參數(shù)")
public class ProcessInstanceCommentRequestDTO implements Serializable {
@ApiModelProperty(value = "流程定義Key")
private String processInstanceId;
@ApiModelProperty(value = "任務(wù)ID(缺省)")
private String taskId;
@ApiModelProperty(value = "類型 CommentEntity: event/comment")
private String type;
@ApiModelProperty(value = "用戶ID")
private String userId;
@ApiModelProperty(value = "用戶昵稱")
private String nickname;
@ApiModelProperty(value = "執(zhí)行類型")
private String executeType;
@ApiModelProperty(value = "執(zhí)行類型(參考ExecuteTypeEnum)SUBMIT-提交;YES-同意;NO-拒絕;STOP-流程終止;DELETE-流程刪除")
private String executeTypeValue;
@ApiModelProperty(value = "內(nèi)容")
private String content;
@ApiModelProperty(value = "額外攜帶的內(nèi)容")
private String ext;
}
添加流程實(shí)例審批意見
// 添加流程實(shí)例審批記錄
public void addProcessInstanceComment(ProcessInstanceCommentRequestDTO requestDTO) {
CommentWrapper wrapper = new CommentWrapper();
wrapper.setUserId(requestDTO.getUserId());
wrapper.setNickname(requestDTO.getNickname());
wrapper.setExecuteType(requestDTO.getExecuteType());
wrapper.setExecuteTypeValue(requestDTO.getExecuteTypeValue());
wrapper.setContent(requestDTO.getContent());
wrapper.setExt(requestDTO.getExt());
String message = JSON.toJSONString(wrapper);
// 使用 taskService 添加一條審批意見
taskService.addComment(requestDTO.getTaskId(), requestDTO.getProcessInstanceId(), requestDTO.getType(), message);
}
4.3.2 啟動(dòng)流程實(shí)例
@Data
@ApiModel("流程實(shí)例-啟動(dòng)請(qǐng)求參數(shù)")
public class ProcessInstanceStartRequestDTO implements Serializable {
@ApiModelProperty(value = "流程定義Key")
@NotEmpty(message = "流程定義Key 不可以為空")
private String processDefinitionKey;
@ApiModelProperty(value = "流程實(shí)例名稱")
@NotEmpty(message = "流程實(shí)例名稱 不可以為空")
private String name;
@ApiModelProperty(value = "項(xiàng)目ID")
@NotEmpty(message = "項(xiàng)目ID 不可以為空")
private String communityId;
@ApiModelProperty(value = "全局變量")
private Map<String, Object> variables;
}
啟動(dòng)流程實(shí)例
@Transactional(rollbackFor = Exception.class)
public ProcessInstanceStartResponseDTO startProcessInstance(ProcessInstanceStartRequestDTO requestDTO) {
ValidatorUtils.validate(requestDTO);
//
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
processDefinitionQuery.processDefinitionKey(requestDTO.getProcessDefinitionKey());
ProcessDefinition processDefinition = processDefinitionQuery.latestVersion().singleResult();
AssertUtils.notEmpty(processDefinition, "找不到流程定義");
// 啟動(dòng)流程
ProcessInstanceBuilder builder = runtimeService.createProcessInstanceBuilder();
builder.processDefinitionKey(requestDTO.getProcessDefinitionKey());
builder.name(requestDTO.getName());
// variables("name", "value")
// 可以添加流程過程的變量把跨,比如這里我添加了我不少業(yè)務(wù)變量進(jìn)來人弓,方面流程流轉(zhuǎn)的時(shí)候處理業(yè)務(wù)
builder.variables(requestDTO.getVariables());
builder.variable(VAR_COMMUNITY_ID, VAR_COMMUNITY_ID_EQ + requestDTO.getCommunityId());
builder.variable(VAR_PROCESS_INSTANCE_NAME, VAR_PROCESS_INSTANCE_NAME_EQ + requestDTO.getName());
builder.variable(VAR_CREATE_USER_ID, VAR_CREATE_USER_ID_EQ + SecurityUser.getUserId());
builder.variable(VAR_CREATE_USER_NICKNAME, VAR_CREATE_USER_NICKNAME_EQ + SecurityUser.get().getName());
builder.variable(VAR_PROCESS_DEFINITION_NAME, VAR_PROCESS_DEFINITION_NAME_EQ + processDefinition.getName());
builder.transientVariables(requestDTO.getTransientVariables());
// builder.tenantId("101");
ProcessInstance processInstance = builder.start();
// 添加審批意見
ProcessInstanceCommentRequestDTO commentRequestDTO = new ProcessInstanceCommentRequestDTO();
commentRequestDTO.setProcessInstanceId(processInstance.getProcessInstanceId());
commentRequestDTO.setTaskId(null);
commentRequestDTO.setUserId(SecurityUser.getUserId());
commentRequestDTO.setNickname(SecurityUser.get().getName());
commentRequestDTO.setExecuteType(ExecuteTypeEnum.SUBMIT.name());
commentRequestDTO.setExecuteTypeValue(ExecuteTypeEnum.SUBMIT.getValue());
commentRequestDTO.setContent(requestDTO.getName() + " 提交流程");
commentRequestDTO.setExt("");
this.addProcessInstanceComment(commentRequestDTO);
// 構(gòu)建 ResponseDTO
ProcessInstanceStartResponseDTO responseDTO = new ProcessInstanceStartResponseDTO();
responseDTO.setProcessInstanceId(processInstance.getProcessInstanceId());
responseDTO.setProcessDefinitionId(processInstance.getProcessDefinitionId());
responseDTO.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
responseDTO.setProcessDefinitionName(processInstance.getProcessDefinitionName());
responseDTO.setName(processInstance.getName());
responseDTO.setBusinessKey(processInstance.getBusinessKey());
responseDTO.setDescription(processInstance.getDescription());
//
return responseDTO;
}
4.3.3 獲取流程進(jìn)度圖片
public String getProcessImage(String processInstanceId) {
// 1.獲取當(dāng)前的流程實(shí)例
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
String processDefinitionId;
List<String> activeActivityIds = new ArrayList<>();
List<String> highLightedFlows = new ArrayList<>();
// 2.獲取所有的歷史軌跡線對(duì)象
List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processInstanceId).activityType(BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW).list();
historicActivityInstances.forEach(historicActivityInstance -> highLightedFlows.add(historicActivityInstance.getActivityId()));
// 3. 獲取流程定義id和高亮的節(jié)點(diǎn)id
if (processInstance != null) {
// 3.1 正在運(yùn)行的流程實(shí)例
processDefinitionId = processInstance.getProcessDefinitionId();
activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
} else {
// 3.2 已經(jīng)結(jié)束的流程實(shí)例
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstanceId).singleResult();
processDefinitionId = historicProcessInstance.getProcessDefinitionId();
// 3.3 獲取結(jié)束節(jié)點(diǎn)列表
List<HistoricActivityInstance> historicEnds = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processInstanceId).activityType(BpmnXMLConstants.ELEMENT_EVENT_END).list();
List<String> finalActiveActivityIds = activeActivityIds;
historicEnds.forEach(historicActivityInstance -> finalActiveActivityIds.add(historicActivityInstance.getActivityId()));
}
// 4. 獲取bpmnModel對(duì)象
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
// 5. 生成圖片流
ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = configuration.getProcessDiagramGenerator();
InputStream inputStream = diagramGenerator.generateDiagram(bpmnModel, "png", activeActivityIds,
highLightedFlows, "宋體", "宋體", "宋體",
this.getClass().getClassLoader(), 1.0, true);
// 6. 轉(zhuǎn)化成Base64網(wǎng)絡(luò)傳輸
return "data:image/png;base64," + Base64.encode(inputStream);
}
4.3.4 獲取流程審批意見
@Override
public List<ProcessInstanceCommentResponseDTO> findProcessInstanceCommentList(String processInstanceId) {
List<Comment> processInstanceComments = taskService.getProcessInstanceComments(processInstanceId);
List<ProcessInstanceCommentResponseDTO> list = processInstanceComments.stream()
.map(this::convertComment)
.filter(Objects::nonNull).collect(Collectors.toList());
return list;
}
private ProcessInstanceCommentResponseDTO convertComment(Comment v) {
String fullMessage = v.getFullMessage();
if (StringUtils.startsWith(fullMessage, "{")) {
CommentWrapper wrapper = JSON.parseObject(fullMessage, CommentWrapper.class);
ProcessInstanceCommentResponseDTO responseDTO = new ProcessInstanceCommentResponseDTO();
responseDTO.setProcessInstanceId(v.getProcessInstanceId());
responseDTO.setType(v.getType());
responseDTO.setTaskId(v.getTaskId());
responseDTO.setTime(v.getTime());
responseDTO.setUserId(wrapper.getUserId());
responseDTO.setNickname(wrapper.getNickname());
responseDTO.setExecuteType(wrapper.getExecuteType());
responseDTO.setExecuteTypeValue(wrapper.getExecuteTypeValue());
responseDTO.setContent(wrapper.getContent());
responseDTO.setExt(wrapper.getExt());
return responseDTO;
}
return null;
}
4.3.5 流程實(shí)例執(zhí)行下一步
@Data
@ApiModel("流程實(shí)例-執(zhí)行下一步請(qǐng)求參數(shù)")
public class ProcessInstanceExecuteNextStepRequestDTO implements Serializable {
@ApiModelProperty(value = "流程實(shí)例ID")
@NotEmpty(message = "流程實(shí)例ID 不可以為空")
private String processInstanceId;
@ApiModelProperty(value = "任務(wù)ID")
private String taskId;
@ApiModelProperty(value = "ExecuteTypeEnum 執(zhí)行類型")
@NotEmpty(message = "執(zhí)行類型 不可以為空")
private String executeType;
@ApiModelProperty(value = "審批意見")
@NotEmpty(message = "審批意見 不可以為空")
private String commentContent;
@ApiModelProperty(value = "變量參數(shù)")
private HashMap<String, Object> variables;
}
@Transactional(rollbackFor = Exception.class)
public void executeNextStep(ProcessInstanceExecuteNextStepRequestDTO requestDTO) {
ValidatorUtils.validate(requestDTO);
//
if (ExecuteTypeEnum.of(requestDTO.getExecuteType()).isNone()) {
throw new BusinessException("未知執(zhí)行狀態(tài)");
}
TaskQuery taskQuery = taskService.createTaskQuery();
taskQuery.taskId(requestDTO.getTaskId());
Task task = taskQuery.singleResult();
AssertUtils.notEmpty(task, "找不到任務(wù)");
//
// 添加審批意見
ProcessInstanceCommentRequestDTO commentRequestDTO = new ProcessInstanceCommentRequestDTO();
commentRequestDTO.setProcessInstanceId(task.getProcessInstanceId());
commentRequestDTO.setTaskId(task.getId());
// commentRequestDTO.setType("event");
commentRequestDTO.setUserId(SecurityUser.getUserId());
commentRequestDTO.setNickname(SecurityUser.get().getName());
commentRequestDTO.setExecuteType(requestDTO.getExecuteType());
commentRequestDTO.setExecuteTypeValue(ExecuteTypeEnum.of(requestDTO.getExecuteType()).getValue());
commentRequestDTO.setContent(task.getName() + ":" + requestDTO.getCommentContent());
commentRequestDTO.setExt("");
//
this.addProcessInstanceComment(commentRequestDTO);
// 處理流程審批
HashMap<String, Object> variables = requestDTO.getVariables();
if (variables == null) {
variables = new HashMap<>(8);
}
// 這里會(huì)put一個(gè)執(zhí)行變量executeType,也就是流程定義xml中的那個(gè)變量名稱,這個(gè)變量決定了流程再走向
variables.put(WorkflowConstants.EXECUTE_TYPE, requestDTO.getExecuteType());
// 添加執(zhí)行人
variables.put("_execute_user_id=" + SecurityUser.getUserId(), "_execute_user_id=" + SecurityUser.getUserId());
taskService.complete(task.getId(), variables);
}
4.3.6 終止流程實(shí)例
@Data
@ApiModel("流程實(shí)例-分頁(yè)請(qǐng)求參數(shù)")
public class ProcessInstanceStopRequestDTO implements Serializable {
@ApiModelProperty(value = "流程實(shí)例ID")
@NotEmpty(message = "流程實(shí)例ID 不可以為空")
private String processInstanceId;
@ApiModelProperty(value = "審批意見")
@NotEmpty(message = "審批意見 不可以為空")
private String commentContent;
}
public void stopProcessInstance(ProcessInstanceStopRequestDTO requestDTO) {
ValidatorUtils.validate(requestDTO);
// 修改流轉(zhuǎn)執(zhí)行狀態(tài)
runtimeService.setVariable(requestDTO.getProcessInstanceId(), WorkflowConstants.EXECUTE_TYPE, ExecuteTypeEnum.STOP.name());
//
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(requestDTO.getProcessInstanceId()).singleResult();
// 添加一條審批記錄
ProcessInstanceCommentRequestDTO commentRequestDTO = new ProcessInstanceCommentRequestDTO();
commentRequestDTO.setProcessInstanceId(processInstance.getProcessInstanceId());
commentRequestDTO.setTaskId(null);
commentRequestDTO.setUserId(SecurityUser.getUserId());
commentRequestDTO.setNickname(SecurityUser.get().getName());
commentRequestDTO.setExecuteType(ExecuteTypeEnum.STOP.name());
commentRequestDTO.setExecuteTypeValue(ExecuteTypeEnum.STOP.getValue());
commentRequestDTO.setContent(StringUtils.defaultString(requestDTO.getCommentContent(), "終止流程"));
commentRequestDTO.setExt("");
this.addProcessInstanceComment(commentRequestDTO);
/// 執(zhí)行終止
List<Execution> executions = runtimeService.createExecutionQuery().parentId(requestDTO.getProcessInstanceId()).list();
List<String> executionIds = executions.stream().map(v -> v.getId()).collect(Collectors.toList());
// 獲取流程結(jié)束點(diǎn)
BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
Process process = bpmnModel.getMainProcess();
List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class);
String endId = endNodes.get(endNodes.size() - 1).getId();
// 執(zhí)行跳轉(zhuǎn)
runtimeService.createChangeActivityStateBuilder()
.moveExecutionsToSingleActivityId(executionIds, endId)
.changeState();
}
4.3.7 批量獲取流程實(shí)例變量列表
這個(gè)方法是自定義實(shí)現(xiàn)的着逐,因?yàn)?Flowable 沒有對(duì)應(yīng)的根據(jù)流程實(shí)例ID列表獲取批量的流程變量
/**
* 實(shí)例映射 Flowable 的 act_hi_varinst 表
*/
@Data
public class HistoryVariable implements Serializable {
public static final HistoryVariable EMPTY = new HistoryVariable();
private String id;
private String rev;
private String processInstanceId;
private String executionId;
private String taskId;
private String name;
private String varType;
private String scopeId;
private String subScopeId;
private String scopeType;
private String bytearrayId;
private Double doubleValue;
private Long longValue;
private String text;
private String text2;
private Date createTime;
private Date lastUpdatedTime;
/*
act_hi_varinst 字段列表:
ID_
REV_
PROC_INST_ID_
EXECUTION_ID_
TASK_ID_
NAME_
VAR_TYPE_
SCOPE_ID_
SUB_SCOPE_ID_
SCOPE_TYPE_
BYTEARRAY_ID_
DOUBLE_
LONG_
TEXT_
TEXT2_
CREATE_TIME_
LAST_UPDATED_TIME_
*/
}
Service
@Override
public List<HistoryVariable> findHistoryVariableList(Collection<String> processInstanceIds) {
if (CollectionUtils.isEmpty(processInstanceIds)) {
return Collections.emptyList();
}
QueryWrapper<HistoryVariable> ew = new QueryWrapper<>();
ew.in("t.PROC_INST_ID_", processInstanceIds);
return this.baseMapper.findHistoryVariableList(ew);
}
Dao
List<HistoryVariable> findHistoryVariableList(@Param("ew") QueryWrapper<HistoryVariable> ew);
xml
<select id="findHistoryVariableList" resultType="cn.leadersheep.xz.workflow.server.entity.flowable.HistoryVariable">
SELECT
t.ID_ AS id,
t.REV_ AS rev,
t.PROC_INST_ID_ AS process_instance_id,
t.EXECUTION_ID_ AS execution_id,
t.TASK_ID_ AS task_id,
t.NAME_ AS name,
t.VAR_TYPE_ AS var_type,
t.SCOPE_ID_ AS scope_id,
t.SUB_SCOPE_ID_ AS sub_scope_id,
t.SCOPE_TYPE_ AS scope_type,
t.BYTEARRAY_ID_ AS bytearray_id,
t.DOUBLE_ AS double_value,
t.LONG_ AS long_value,
t.TEXT_ AS text,
t.TEXT2_ AS text2,
t.CREATE_TIME_ AS create_time,
t.LAST_UPDATED_TIME_ AS last_update_time
FROM act_hi_varinst t
<where>
${ew.sqlSegment}
</where>
</select>
4.3.8 獲取流程實(shí)例分頁(yè)
獲取流程實(shí)例分頁(yè)崔赌,因?yàn)檫@里涉及到
數(shù)據(jù)權(quán)限過濾
、以及待辦
滨嘱、已辦
峰鄙、歷史記錄
、我發(fā)起
等等太雨,邏輯還是挺復(fù)雜的吟榴,因此簡(jiǎn)單通過Flowable的API可能不是一個(gè)很好的選擇了,因此這里自定義實(shí)現(xiàn)囊扳,通過查找Flowable相關(guān)的數(shù)據(jù)庫(kù)表找出符合記錄
1.如果流程實(shí)例還在進(jìn)行中數(shù)據(jù)是保存在
act_ru_*
這幾張表中吩翻,如果流程實(shí)例結(jié)束了數(shù)據(jù)是保存在act_hi_*
這幾張表中兜看,因此查詢的時(shí)候需要根據(jù)不用場(chǎng)景查詢不用的表;
2.流程定義圖中定義的分配的用戶組狭瞎,保存在act_ru_identitylink
表中细移;
流程實(shí)例-分頁(yè)請(qǐng)求參數(shù)
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("流程實(shí)例-分頁(yè)請(qǐng)求參數(shù)")
public class ProcessInstancePageRequestDTO extends PageParamsEntity {
@ApiModelProperty(value = "項(xiàng)目ID")
private String communityId;
@ApiModelProperty(value = "查找范圍:MY_TODO-我的待辦;MY_DONE-我的已辦熊锭;MY_SCOPE-我的范圍弧轧;MY_CREATE-我的創(chuàng)建;")
private String searchScope;
@ApiModelProperty(value = "任務(wù)名稱")
private String processInstanceName;
}
流程定義-分頁(yè)響應(yīng)結(jié)果
@Data
@ApiModel("流程定義-分頁(yè)響應(yīng)結(jié)果")
public class ProcessInstancePageResponseDTO implements Serializable {
@ApiModelProperty(value = "項(xiàng)目ID")
private String communityId;
@ApiModelProperty(value = "項(xiàng)目名稱")
private String communityName;
@ApiModelProperty(value = "流程實(shí)例ID")
private String processInstanceId;
@ApiModelProperty(value = "流程實(shí)例名稱")
private String processInstanceName;
@ApiModelProperty(value = "流程定義ID")
private String processDefinitionId;
@ApiModelProperty(value = "流程定義名稱")
private String processDefinitionName;
@ApiModelProperty(value = "任務(wù)ID")
private String taskId;
@ApiModelProperty(value = "任務(wù)名稱")
private String taskName;
@ApiModelProperty(value = "開始時(shí)間")
private Date startTime;
@ApiModelProperty(value = "結(jié)束時(shí)間")
private Date endTime;
@ApiModelProperty(value = "持續(xù)時(shí)間")
private String duration;
@ApiModelProperty(value = "創(chuàng)建人ID")
private String createId;
@ApiModelProperty(value = "創(chuàng)建人名稱")
private String createName;
}
Service
public PageDTO<ProcessInstancePageResponseDTO> findPage(ProcessInstancePageRequestDTO requestDTO) {
// 在這里實(shí)現(xiàn)數(shù)據(jù)過濾
String filterSql = StringUtils.defaultString(processFilterSQL("TEXT_", requestDTO.getCommunityId(), true), "");
filterSql = filterSql.replace("('", "('" + WorkflowConstants.VAR_COMMUNITY_ID_EQ);
filterSql = filterSql.replace(", '", ", '" + WorkflowConstants.VAR_COMMUNITY_ID_EQ);
// TEXT_ IN ('_community_id=1545645315843546', '_community_id=15456453158436521')
// System.out.println("filterSQL = " + filterSQL);
//
PageDTO<ProcessInstancePageResponseDTO> page = this.buildPage(requestDTO);
if ("MY_TODO".equals(requestDTO.getSearchScope())) {
// 我的待辦
QueryWrapper<ProcessInstancePageResponseDTO> ew = buildEmptyQueryWrapper();
ew.like(StringUtils.isNotEmpty(requestDTO.getProcessInstanceName()), "t4.NAME_", requestDTO.getProcessInstanceName());
ew.eq("1", 1);
ew.apply(CoreUtil.isNotEmpty(filterSql), "t3." + filterSql);
ew.orderByDesc("t.CREATE_TIME_");
ew.groupBy("t.PROC_INST_ID_");
//
if (!AuthHelper.isRoleSuperAdmin(SecurityUser.current().getRoleCodeSet())) {
// 不是超級(jí)管理員,需要過濾
ew.and(and -> and.in("t2.GROUP_ID_", SecurityUser.current().getRoleCodeSet()).or(or -> {
or.eq("t2.USER_ID_", SecurityUser.getUserId());
}));
}
page = this.baseMapper.findMyTodo(page, ew);
} else if ("MY_DONE".equals(requestDTO.getSearchScope())) {
// 我的已辦
QueryWrapper<ProcessInstancePageResponseDTO> ew = buildEmptyQueryWrapper();
ew.like(StringUtils.isNotEmpty(requestDTO.getProcessInstanceName()), "t.NAME_", requestDTO.getProcessInstanceName());
ew.eq("t2.TEXT_", "_execute_user_id=" + SecurityUser.getUserId());
ew.apply(filterSql.length() > 0, "t3." + filterSql);
ew.orderByDesc("t.START_TIME_");
ew.groupBy("t.ID_");
//
page = this.baseMapper.findMyDonePage(page, ew);
} else if ("MY_SCOPE".equals(requestDTO.getSearchScope())) {
// 我的范圍
QueryWrapper<ProcessInstancePageResponseDTO> ew = buildEmptyQueryWrapper();
ew.like(StringUtils.isNotEmpty(requestDTO.getProcessInstanceName()), "t.NAME_", requestDTO.getProcessInstanceName());
ew.eq("1", 1);
ew.apply(filterSql.length() > 0, "t4." + filterSql);
ew.orderByDesc("t.START_TIME_");
ew.groupBy("t.ID_");
//
if (!AuthHelper.isRoleSuperAdmin(SecurityUser.current().getRoleCodeSet())) {
// 不是超級(jí)管理員,需要過濾
ew.and(and -> and.in("t3.GROUP_ID_", SecurityUser.current().getRoleCodeSet()).or(or -> {
or.eq("t3.USER_ID_", SecurityUser.getUserId());
}));
}
page = this.baseMapper.findMyScopePage(page, ew);
} else if ("MY_CREATE".equals(requestDTO.getSearchScope())) {
// 我發(fā)起的
QueryWrapper<ProcessInstancePageResponseDTO> ew = buildEmptyQueryWrapper();
ew.like(StringUtils.isNotEmpty(requestDTO.getProcessInstanceName()), "t.NAME_", requestDTO.getProcessInstanceName());
ew.eq("t2.TEXT_", "_create_user_id=" + SecurityUser.getUserId());
ew.apply(filterSql.length() > 0, "t3." + filterSql);
ew.orderByDesc("t.START_TIME_");
ew.groupBy("t.ID_");
//
page = this.baseMapper.findMyDonePage(page, ew);
}
//
if (CollectionUtils.isNotEmpty(page.getRecords())) {
// 填充其他屬性
List<String> processInstanceIds = page.getRecords().stream().map(v -> v.getProcessInstanceId()).distinct().collect(Collectors.toList());
List<HistoryVariable> variableList = this.findHistoryVariableList(processInstanceIds);
Map<String, HistoryVariable> variableMap = variableList.stream()
.collect(Collectors.toMap(v -> v.getProcessInstanceId() + "_" + v.getName(), v -> v));
page.getRecords().forEach(v -> {
String prefix = v.getProcessInstanceId() + "_";
//
HistoryVariable variable = variableMap.getOrDefault(prefix + VAR_COMMUNITY_ID, HistoryVariable.EMPTY);
String text = Optional.ofNullable(variable.getText()).map(t -> t.replace(VAR_COMMUNITY_ID_EQ, "")).orElse(null);
v.setCommunityId(text);
//
// variable = variableMap.getOrDefault(prefix + VAR_PROCESS_INSTANCE_NAME, HistoryVariable.EMPTY);
// text = Optional.ofNullable(variable.getText()).map(t -> t.replace(VAR_PROCESS_INSTANCE_NAME_EQ, "")).orElse(null);
// v.setProcessInstanceName(text);
//
variable = variableMap.getOrDefault(prefix + VAR_CREATE_USER_ID, HistoryVariable.EMPTY);
text = Optional.ofNullable(variable.getText()).map(t -> t.replace(VAR_CREATE_USER_ID_EQ, "")).orElse(null);
v.setCreateId(text);
//
variable = variableMap.getOrDefault(prefix + VAR_CREATE_USER_NICKNAME, HistoryVariable.EMPTY);
text = Optional.ofNullable(variable.getText()).map(t -> t.replace(VAR_CREATE_USER_NICKNAME_EQ, "")).orElse(null);
v.setCreateName(text);
//
variable = variableMap.getOrDefault(prefix + VAR_PROCESS_DEFINITION_NAME, HistoryVariable.EMPTY);
text = Optional.ofNullable(variable.getText()).map(t -> t.replace(VAR_PROCESS_DEFINITION_NAME_EQ, "")).orElse(null);
v.setProcessDefinitionName(text);
});
sysOrganizationRemote.fillOrganization(page.getRecords(), v -> v.getCommunityId(), (v, c) -> v.setCommunityName(c.getName()));
}
return page;
}
Dao
/**
* 獲取我的待辦任務(wù)
*
* @author houyu for.houyu@qq.com <br>
* @param page 分頁(yè)
* @param ew 參數(shù)包裝器
* @return PageDTO<ProcessInstancePageResponseDTO>
*/
PageDTO<ProcessInstancePageResponseDTO> findMyTodo(@Param("page") PageDTO<ProcessInstancePageResponseDTO> page, @Param("ew") QueryWrapper<ProcessInstancePageResponseDTO> ew);
/**
* 獲取我的范圍內(nèi)的任務(wù)
*
* @author houyu for.houyu@qq.com <br>
* @param page 分頁(yè)
* @param ew 參數(shù)包裝器
* @return PageDTO<ProcessInstancePageResponseDTO>
*/
PageDTO<ProcessInstancePageResponseDTO> findMyScopePage(@Param("page") PageDTO<ProcessInstancePageResponseDTO> page, @Param("ew") QueryWrapper<ProcessInstancePageResponseDTO> ew);
/**
* 獲取我的已辦(我的發(fā)起 / 我審批的)
*
* @author houyu for.houyu@qq.com <br>
* @param page 分頁(yè)
* @param ew 參數(shù)包裝器
* @return PageDTO<ProcessInstancePageResponseDTO>
*/
PageDTO<ProcessInstancePageResponseDTO> findMyDonePage(@Param("page") PageDTO<ProcessInstancePageResponseDTO> page, @Param("ew") QueryWrapper<ProcessInstancePageResponseDTO> ew);
Xml
<select id="findMyTodo" resultType="cn.leadersheep.xz.workflow.client.dto.flowable.response.ProcessInstancePageResponseDTO">
SELECT
t.ID_ AS task_id,
t.PROC_INST_ID_ AS process_instance_id,
t4.NAME_ AS process_instance_name,
t.NAME_ AS task_name,
t.PROC_DEF_ID_ as process_definition_id,
t.CREATE_TIME_ as start_time,
NULL as end_time
FROM act_ru_task t
LEFT JOIN act_ru_identitylink t2 ON t2.TASK_ID_ = t.ID_
LEFT JOIN act_ru_variable t3 ON t3.PROC_INST_ID_ = t.PROC_INST_ID_
LEFT JOIN act_hi_procinst t4 ON t4.PROC_INST_ID_ = t.PROC_INST_ID_
<where>
${ew.sqlSegment}
</where>
</select>
<select id="findMyScopePage" resultType="cn.leadersheep.xz.workflow.client.dto.flowable.response.ProcessInstancePageResponseDTO">
SELECT
t.PROC_INST_ID_ AS process_instance_id,
t.NAME_ AS process_instance_name,
t.PROC_DEF_ID_ as process_definition_id,
t.START_TIME_ as start_time,
t.END_TIME_ as end_time
FROM act_hi_procinst t
LEFT JOIN act_hi_taskinst t2 ON t2.PROC_INST_ID_ = t.ID_
LEFT JOIN act_hi_identitylink t3 ON t3.TASK_ID_ = t2.ID_
LEFT JOIN act_hi_varinst t4 ON t4.PROC_INST_ID_ = t.ID_
<where>
${ew.sqlSegment}
</where>
</select>
<select id="findMyDonePage" resultType="cn.leadersheep.xz.workflow.client.dto.flowable.response.ProcessInstancePageResponseDTO">
SELECT
t.PROC_INST_ID_ AS process_instance_id,
t.NAME_ AS process_instance_name,
t.PROC_DEF_ID_ as process_definition_id,
t.START_TIME_ as start_time,
t.END_TIME_ as end_time
FROM act_hi_procinst t
LEFT JOIN act_hi_varinst t2 ON t2.PROC_INST_ID_ = t.ID_
LEFT JOIN act_hi_varinst t3 ON t3.PROC_INST_ID_ = t.ID_
<where>
${ew.sqlSegment}
</where>
</select>
5. Linux部署流程圖文字亂碼
簡(jiǎn)單描述一下我遇到的情況碗殷,我當(dāng)時(shí)是在Windows上開發(fā)的精绎,設(shè)置流程圖的字體為宋體,沒有出現(xiàn)亂碼的情況锌妻,但是我部署到Linux服務(wù)器上查看流程圖的時(shí)候文字出現(xiàn)了亂碼代乃,然后我大概能猜到是因?yàn)槿鄙僮煮w的問題(因?yàn)橹坝辛私膺^activiti流程圖亂碼是缺少字體的問題),所以我這次也是按照相同套路給Linux添加宋體字體就解決了仿粹。
5.1 復(fù)制Windows上的字體
進(jìn)入目錄:C:\Windows\Fonts 復(fù)制 “宋體 常規(guī)”到桌面上備用
5.2 Linux找出 java 位置
執(zhí)行命令
whereis java
我的是在 /var/lib/jdk/jdk1.8.0_211/bin/java
5.3 復(fù)制字體文件到 jre/lib/fonts
找出了java的位置搁吓,jre 的位置在 java 的前幾級(jí)目錄下
java:
/var/lib/jdk/jdk1.8.0_211/bin/java
jre 字體目錄(復(fù)制到這里):
/var/lib/jdk/jdk1.8.0_211/jre/lib/fonts
5.4 復(fù)制字體文件到系統(tǒng)字體目錄中(/usr/share/fonts)
這個(gè)目錄可能不存在,如果不存在則自己創(chuàng)建出來
/usr/share/fonts
5.5 重啟系統(tǒng)
reboot
重啟系統(tǒng)之后吭历,啟動(dòng)應(yīng)用程序堕仔,即不會(huì)出現(xiàn)亂碼的情況了,祝你好運(yùn)~
上一篇博客:SpringBoot整合Flowable工作流-1(畫流程定義)
基于 flowable-spring-boot-starter 整合的代碼基本完成晌区,但是感覺還是少了一點(diǎn)東西贮预,流程一步一步執(zhí)行下去了,什么時(shí)候執(zhí)行完契讲?現(xiàn)在到什么環(huán)節(jié)了仿吞?貌似我們都不太清楚,執(zhí)行完了業(yè)務(wù)要怎么操作捡偏,這就要介紹一下 flowable 全局事件監(jiān)聽器了唤冈,下一篇博客將介紹 flowable 全局事件監(jiān)聽器,結(jié)合監(jiān)聽器實(shí)現(xiàn)業(yè)務(wù)的通知業(yè)務(wù)银伟。
- 公眾號(hào):IT加載中(it_loading)
- CSDN:https://blog.csdn.net/JinglongSource
- 博客:https://ihouyu.cn/
- 郵箱:for.houyu@qq.com