This commit is contained in:
kk
2026-07-08 17:57:11 +08:00
commit f1e6599892
743 changed files with 159169 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow;
import org.springblade.core.cloud.client.BladeCloudApplication;
import org.springblade.core.launch.BladeApplication;
import org.springblade.core.launch.constant.AppConstant;
/**
* Flowable启动器
*
* @author Chill
*/
//@SeataCloudApplication
@BladeCloudApplication
public class FlowApplication {
public static void main(String[] args) {
BladeApplication.run(AppConstant.APPLICATION_FLOW_NAME, FlowApplication.class, args);
}
}

View File

@@ -0,0 +1,155 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.business.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.flowable.engine.TaskService;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.tool.api.R;
import org.springblade.flow.business.service.FlowBusinessService;
import org.springblade.flow.core.pojo.entity.BladeFlow;
import org.springblade.flow.core.utils.TaskUtil;
import org.springblade.flow.engine.entity.FlowProcess;
import org.springblade.flow.engine.service.FlowEngineService;
import org.springframework.web.bind.annotation.*;
/**
* 流程事务通用接口
*
* @author Chill
*/
@RestController
@AllArgsConstructor
@RequestMapping("/work")
@Tag(name = "流程事务通用接口", description = "流程事务通用接口")
public class WorkController {
private final TaskService taskService;
private final FlowEngineService flowEngineService;
private final FlowBusinessService flowBusinessService;
/**
* 发起事务列表页
*/
@GetMapping("start-list")
@ApiOperationSupport(order = 1)
@Operation(summary = "发起事务列表页", description = "传入流程类型")
public R<IPage<FlowProcess>> startList(@Parameter(description = "流程类型") String category, Query query, @RequestParam(required = false, defaultValue = "1") Integer mode) {
IPage<FlowProcess> pages = flowEngineService.selectProcessPage(Condition.getPage(query), category, mode);
return R.data(pages);
}
/**
* 待签事务列表页
*/
@GetMapping("claim-list")
@ApiOperationSupport(order = 2)
@Operation(summary = "待签事务列表页", description = "传入流程信息")
public R<IPage<BladeFlow>> claimList(@Parameter(description = "流程信息") BladeFlow bladeFlow, Query query) {
IPage<BladeFlow> pages = flowBusinessService.selectClaimPage(Condition.getPage(query), bladeFlow);
return R.data(pages);
}
/**
* 待办事务列表页
*/
@GetMapping("todo-list")
@ApiOperationSupport(order = 3)
@Operation(summary = "待办事务列表页", description = "传入流程信息")
public R<IPage<BladeFlow>> todoList(@Parameter(description = "流程信息") BladeFlow bladeFlow, Query query) {
IPage<BladeFlow> pages = flowBusinessService.selectTodoPage(Condition.getPage(query), bladeFlow);
return R.data(pages);
}
/**
* 已发事务列表页
*/
@GetMapping("send-list")
@ApiOperationSupport(order = 4)
@Operation(summary = "已发事务列表页", description = "传入流程信息")
public R<IPage<BladeFlow>> sendList(@Parameter(description = "流程信息") BladeFlow bladeFlow, Query query) {
IPage<BladeFlow> pages = flowBusinessService.selectSendPage(Condition.getPage(query), bladeFlow);
return R.data(pages);
}
/**
* 办结事务列表页
*/
@GetMapping("done-list")
@ApiOperationSupport(order = 5)
@Operation(summary = "办结事务列表页", description = "传入流程信息")
public R<IPage<BladeFlow>> doneList(@Parameter(description = "流程信息") BladeFlow bladeFlow, Query query) {
IPage<BladeFlow> pages = flowBusinessService.selectDonePage(Condition.getPage(query), bladeFlow);
return R.data(pages);
}
/**
* 签收事务
*
* @param taskId 任务id
*/
@PostMapping("claim-task")
@ApiOperationSupport(order = 6)
@Operation(summary = "签收事务", description = "传入流程信息")
public R claimTask(@Parameter(description = "任务id") String taskId) {
taskService.claim(taskId, TaskUtil.getTaskUser());
return R.success("签收事务成功");
}
/**
* 完成任务
*
* @param flow 请假信息
*/
@PostMapping("complete-task")
@ApiOperationSupport(order = 7)
@Operation(summary = "完成任务", description = "传入流程信息")
public R completeTask(@Parameter(description = "任务信息") @RequestBody BladeFlow flow) {
return R.status(flowBusinessService.completeTask(flow));
}
/**
* 删除任务
*
* @param taskId 任务id
* @param reason 删除原因
*/
@PostMapping("delete-task")
@ApiOperationSupport(order = 8)
@Operation(summary = "删除任务", description = "传入流程信息")
public R deleteTask(@Parameter(description = "任务id") String taskId, @Parameter(description = "删除原因") String reason) {
taskService.deleteTask(taskId, reason);
return R.success("删除任务成功");
}
}

View File

@@ -0,0 +1,116 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.business.feign;
import lombok.AllArgsConstructor;
import org.flowable.engine.IdentityService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ProcessInstance;
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.flow.core.pojo.entity.BladeFlow;
import org.springblade.flow.core.feign.IFlowClient;
import org.springblade.flow.core.utils.TaskUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* 流程远程调用实现类
*
* @author Chill
*/
@NonDS
@RestController
@AllArgsConstructor
public class FlowClient implements IFlowClient {
private final RuntimeService runtimeService;
private final IdentityService identityService;
private final TaskService taskService;
@Override
@PostMapping(START_PROCESS_INSTANCE_BY_ID)
public R<BladeFlow> startProcessInstanceById(String processDefinitionId, String businessKey, @RequestBody Map<String, Object> variables) {
// 设置流程启动用户
identityService.setAuthenticatedUserId(TaskUtil.getTaskUser());
// 开启流程
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId, businessKey, variables);
// 组装流程通用类
BladeFlow flow = new BladeFlow();
flow.setProcessInstanceId(processInstance.getId());
return R.data(flow);
}
@Override
@PostMapping(START_PROCESS_INSTANCE_BY_KEY)
public R<BladeFlow> startProcessInstanceByKey(String processDefinitionKey, String businessKey, @RequestBody Map<String, Object> variables) {
// 设置流程启动用户
identityService.setAuthenticatedUserId(TaskUtil.getTaskUser());
// 开启流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
// 组装流程通用类
BladeFlow flow = new BladeFlow();
flow.setProcessInstanceId(processInstance.getId());
return R.data(flow);
}
@Override
@PostMapping(COMPLETE_TASK)
public R completeTask(String taskId, String processInstanceId, String comment, @RequestBody Map<String, Object> variables) {
// 增加评论
if (StringUtil.isNoneBlank(processInstanceId, comment)) {
taskService.addComment(taskId, processInstanceId, comment);
}
// 非空判断
if (Func.isEmpty(variables)) {
variables = Kv.create();
}
// 完成任务
taskService.complete(taskId, variables);
return R.success("流程提交成功");
}
@Override
@GetMapping(TASK_VARIABLE)
public R<Object> taskVariable(String taskId, String variableName) {
return R.data(taskService.getVariable(taskId, variableName));
}
@Override
@GetMapping(TASK_VARIABLES)
public R<Map<String, Object>> taskVariables(String taskId) {
return R.data(taskService.getVariables(taskId));
}
}

View File

@@ -0,0 +1,81 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.business.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.flow.core.pojo.entity.BladeFlow;
/**
* 流程业务类
*
* @author Chill
*/
public interface FlowBusinessService {
/**
* 流程待签列表
*
* @param page 分页工具
* @param bladeFlow 流程类
* @return
*/
IPage<BladeFlow> selectClaimPage(IPage<BladeFlow> page, BladeFlow bladeFlow);
/**
* 流程待办列表
*
* @param page 分页工具
* @param bladeFlow 流程类
* @return
*/
IPage<BladeFlow> selectTodoPage(IPage<BladeFlow> page, BladeFlow bladeFlow);
/**
* 流程已发列表
*
* @param page 分页工具
* @param bladeFlow 流程类
* @return
*/
IPage<BladeFlow> selectSendPage(IPage<BladeFlow> page, BladeFlow bladeFlow);
/**
* 流程办结列表
*
* @param page 分页工具
* @param bladeFlow 流程类
* @return
*/
IPage<BladeFlow> selectDonePage(IPage<BladeFlow> page, BladeFlow bladeFlow);
/**
* 完成任务
*
* @param leave 请假信息
* @return boolean
*/
boolean completeTask(BladeFlow leave);
}

View File

@@ -0,0 +1,342 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.business.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.AllArgsConstructor;
import org.flowable.engine.HistoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.flow.business.service.FlowBusinessService;
import org.springblade.flow.core.constant.ProcessConstant;
import org.springblade.flow.core.pojo.entity.BladeFlow;
import org.springblade.flow.core.utils.TaskUtil;
import org.springblade.flow.engine.constant.FlowEngineConstant;
import org.springblade.flow.engine.entity.FlowProcess;
import org.springblade.flow.engine.utils.FlowCache;
import org.springframework.stereotype.Service;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* 流程业务实现类
*
* @author Chill
*/
@Service
@AllArgsConstructor
public class FlowBusinessServiceImpl implements FlowBusinessService {
private final TaskService taskService;
private final HistoryService historyService;
@Override
public IPage<BladeFlow> selectClaimPage(IPage<BladeFlow> page, BladeFlow bladeFlow) {
String taskUser = TaskUtil.getTaskUser();
String taskGroup = TaskUtil.getCandidateGroup();
List<BladeFlow> flowList = new LinkedList<>();
// 个人等待签收的任务
TaskQuery claimUserQuery = taskService.createTaskQuery().taskCandidateUser(taskUser)
.includeProcessVariables().active().orderByTaskCreateTime().desc();
// 定制流程等待签收的任务
TaskQuery claimRoleWithTenantIdQuery = taskService.createTaskQuery().taskTenantId(AuthUtil.getTenantId()).taskCandidateGroupIn(Func.toStrList(taskGroup))
.includeProcessVariables().active().orderByTaskCreateTime().desc();
// 通用流程等待签收的任务
TaskQuery claimRoleWithoutTenantIdQuery = taskService.createTaskQuery().taskWithoutTenantId().taskCandidateGroupIn(Func.toStrList(taskGroup))
.includeProcessVariables().active().orderByTaskCreateTime().desc();
// 构建列表数据
buildFlowTaskList(bladeFlow, flowList, claimUserQuery, FlowEngineConstant.STATUS_CLAIM);
buildFlowTaskList(bladeFlow, flowList, claimRoleWithTenantIdQuery, FlowEngineConstant.STATUS_CLAIM);
buildFlowTaskList(bladeFlow, flowList, claimRoleWithoutTenantIdQuery, FlowEngineConstant.STATUS_CLAIM);
// 计算总数
long count = claimUserQuery.count() + claimRoleWithTenantIdQuery.count() + claimRoleWithoutTenantIdQuery.count();
// 设置页数
page.setSize(count);
// 设置总数
page.setTotal(count);
// 设置数据
page.setRecords(flowList);
return page;
}
@Override
public IPage<BladeFlow> selectTodoPage(IPage<BladeFlow> page, BladeFlow bladeFlow) {
String taskUser = TaskUtil.getTaskUser();
List<BladeFlow> flowList = new LinkedList<>();
// 已签收的任务
TaskQuery todoQuery = taskService.createTaskQuery().taskAssignee(taskUser).active()
.includeProcessVariables().orderByTaskCreateTime().desc();
// 构建列表数据
buildFlowTaskList(bladeFlow, flowList, todoQuery, FlowEngineConstant.STATUS_TODO);
// 计算总数
long count = todoQuery.count();
// 设置页数
page.setSize(count);
// 设置总数
page.setTotal(count);
// 设置数据
page.setRecords(flowList);
return page;
}
@Override
public IPage<BladeFlow> selectSendPage(IPage<BladeFlow> page, BladeFlow bladeFlow) {
String taskUser = TaskUtil.getTaskUser();
List<BladeFlow> flowList = new LinkedList<>();
HistoricProcessInstanceQuery historyQuery = historyService.createHistoricProcessInstanceQuery().startedBy(taskUser).orderByProcessInstanceStartTime().desc();
if (bladeFlow.getCategory() != null) {
historyQuery.processDefinitionCategory(bladeFlow.getCategory());
}
if (bladeFlow.getProcessDefinitionName() != null) {
historyQuery.processDefinitionName(bladeFlow.getProcessDefinitionName());
}
if (bladeFlow.getBeginDate() != null) {
historyQuery.startedAfter(bladeFlow.getBeginDate());
}
if (bladeFlow.getEndDate() != null) {
historyQuery.startedBefore(bladeFlow.getEndDate());
}
// 查询列表
List<HistoricProcessInstance> historyList = historyQuery.listPage(Func.toInt((page.getCurrent() - 1) * page.getSize()), Func.toInt(page.getSize()));
historyList.forEach(historicProcessInstance -> {
BladeFlow flow = new BladeFlow();
// historicProcessInstance
flow.setCreateTime(historicProcessInstance.getStartTime());
flow.setEndTime(historicProcessInstance.getEndTime());
flow.setVariables(historicProcessInstance.getProcessVariables());
String[] businessKey = Func.toStrArray(StringPool.COLON, historicProcessInstance.getBusinessKey());
if (businessKey.length > 1) {
flow.setBusinessTable(businessKey[0]);
flow.setBusinessId(businessKey[1]);
}
flow.setHistoryActivityName(historicProcessInstance.getName());
flow.setProcessInstanceId(historicProcessInstance.getId());
flow.setHistoryProcessInstanceId(historicProcessInstance.getId());
// ProcessDefinition
FlowProcess processDefinition = FlowCache.getProcessDefinition(historicProcessInstance.getProcessDefinitionId());
flow.setProcessDefinitionId(processDefinition.getId());
flow.setProcessDefinitionName(processDefinition.getName());
flow.setProcessDefinitionVersion(processDefinition.getVersion());
flow.setProcessDefinitionKey(processDefinition.getKey());
flow.setCategory(processDefinition.getCategory());
flow.setCategoryName(FlowCache.getCategoryName(processDefinition.getCategory()));
flow.setProcessInstanceId(historicProcessInstance.getId());
// HistoricTaskInstance
List<HistoricTaskInstance> historyTasks = historyService.createHistoricTaskInstanceQuery().processInstanceId(historicProcessInstance.getId()).orderByHistoricTaskInstanceEndTime().desc().list();
if (Func.isNotEmpty(historyTasks)) {
HistoricTaskInstance historyTask = historyTasks.iterator().next();
flow.setTaskId(historyTask.getId());
flow.setTaskName(historyTask.getName());
flow.setTaskDefinitionKey(historyTask.getTaskDefinitionKey());
}
// Status
if (historicProcessInstance.getEndActivityId() != null) {
flow.setProcessIsFinished(FlowEngineConstant.STATUS_FINISHED);
} else {
flow.setProcessIsFinished(FlowEngineConstant.STATUS_UNFINISHED);
}
flow.setStatus(FlowEngineConstant.STATUS_FINISH);
flowList.add(flow);
});
// 计算总数
long count = historyQuery.count();
// 设置总数
page.setTotal(count);
page.setRecords(flowList);
return page;
}
@Override
public IPage<BladeFlow> selectDonePage(IPage<BladeFlow> page, BladeFlow bladeFlow) {
String taskUser = TaskUtil.getTaskUser();
List<BladeFlow> flowList = new LinkedList<>();
HistoricTaskInstanceQuery doneQuery = historyService.createHistoricTaskInstanceQuery().taskAssignee(taskUser).finished()
.includeProcessVariables().orderByHistoricTaskInstanceEndTime().desc();
if (bladeFlow.getCategory() != null) {
doneQuery.processCategoryIn(Func.toStrList(bladeFlow.getCategory()));
}
if (bladeFlow.getProcessDefinitionName() != null) {
doneQuery.processDefinitionName(bladeFlow.getProcessDefinitionName());
}
if (bladeFlow.getBeginDate() != null) {
doneQuery.taskCompletedAfter(bladeFlow.getBeginDate());
}
if (bladeFlow.getEndDate() != null) {
doneQuery.taskCompletedBefore(bladeFlow.getEndDate());
}
// 查询列表
List<HistoricTaskInstance> doneList = doneQuery.listPage(Func.toInt((page.getCurrent() - 1) * page.getSize()), Func.toInt(page.getSize()));
doneList.forEach(historicTaskInstance -> {
BladeFlow flow = new BladeFlow();
flow.setTaskId(historicTaskInstance.getId());
flow.setTaskDefinitionKey(historicTaskInstance.getTaskDefinitionKey());
flow.setTaskName(historicTaskInstance.getName());
flow.setAssignee(historicTaskInstance.getAssignee());
flow.setCreateTime(historicTaskInstance.getCreateTime());
flow.setExecutionId(historicTaskInstance.getExecutionId());
flow.setHistoryTaskEndTime(historicTaskInstance.getEndTime());
flow.setVariables(historicTaskInstance.getProcessVariables());
FlowProcess processDefinition = FlowCache.getProcessDefinition(historicTaskInstance.getProcessDefinitionId());
flow.setProcessDefinitionId(processDefinition.getId());
flow.setProcessDefinitionName(processDefinition.getName());
flow.setProcessDefinitionKey(processDefinition.getKey());
flow.setProcessDefinitionVersion(processDefinition.getVersion());
flow.setCategory(processDefinition.getCategory());
flow.setCategoryName(FlowCache.getCategoryName(processDefinition.getCategory()));
flow.setProcessInstanceId(historicTaskInstance.getProcessInstanceId());
flow.setHistoryProcessInstanceId(historicTaskInstance.getProcessInstanceId());
HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance((historicTaskInstance.getProcessInstanceId()));
if (Func.isNotEmpty(historicProcessInstance)) {
String[] businessKey = Func.toStrArray(StringPool.COLON, historicProcessInstance.getBusinessKey());
flow.setBusinessTable(businessKey[0]);
flow.setBusinessId(businessKey[1]);
if (historicProcessInstance.getEndActivityId() != null) {
flow.setProcessIsFinished(FlowEngineConstant.STATUS_FINISHED);
} else {
flow.setProcessIsFinished(FlowEngineConstant.STATUS_UNFINISHED);
}
}
flow.setStatus(FlowEngineConstant.STATUS_FINISH);
flowList.add(flow);
});
// 计算总数
long count = doneQuery.count();
// 设置总数
page.setTotal(count);
page.setRecords(flowList);
return page;
}
@Override
public boolean completeTask(BladeFlow flow) {
String taskId = flow.getTaskId();
String processInstanceId = flow.getProcessInstanceId();
String comment = Func.toStr(flow.getComment(), ProcessConstant.PASS_COMMENT);
// 增加评论
if (StringUtil.isNoneBlank(processInstanceId, comment)) {
taskService.addComment(taskId, processInstanceId, comment);
}
// 创建变量
Map<String, Object> variables = flow.getVariables();
if (variables == null) {
variables = Kv.create();
}
variables.put(ProcessConstant.PASS_KEY, flow.isPass());
// 完成任务
taskService.complete(taskId, variables);
return true;
}
/**
* 构建流程
*
* @param bladeFlow 流程通用类
* @param flowList 流程列表
* @param taskQuery 任务查询类
* @param status 状态
*/
private void buildFlowTaskList(BladeFlow bladeFlow, List<BladeFlow> flowList, TaskQuery taskQuery, String status) {
if (bladeFlow.getCategory() != null) {
taskQuery.processCategoryIn(Func.toStrList(bladeFlow.getCategory()));
}
if (bladeFlow.getProcessDefinitionName() != null) {
taskQuery.processDefinitionName(bladeFlow.getProcessDefinitionName());
}
if (bladeFlow.getBeginDate() != null) {
taskQuery.taskCreatedAfter(bladeFlow.getBeginDate());
}
if (bladeFlow.getEndDate() != null) {
taskQuery.taskCreatedBefore(bladeFlow.getEndDate());
}
taskQuery.list().forEach(task -> {
BladeFlow flow = new BladeFlow();
flow.setTaskId(task.getId());
flow.setTaskDefinitionKey(task.getTaskDefinitionKey());
flow.setTaskName(task.getName());
flow.setAssignee(task.getAssignee());
flow.setCreateTime(task.getCreateTime());
flow.setClaimTime(task.getClaimTime());
flow.setExecutionId(task.getExecutionId());
flow.setVariables(task.getProcessVariables());
HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId());
if (Func.isNotEmpty(historicProcessInstance)) {
String[] businessKey = Func.toStrArray(StringPool.COLON, historicProcessInstance.getBusinessKey());
flow.setBusinessTable(businessKey[0]);
flow.setBusinessId(businessKey[1]);
}
FlowProcess processDefinition = FlowCache.getProcessDefinition(task.getProcessDefinitionId());
flow.setCategory(processDefinition.getCategory());
flow.setCategoryName(FlowCache.getCategoryName(processDefinition.getCategory()));
flow.setProcessDefinitionId(processDefinition.getId());
flow.setProcessDefinitionName(processDefinition.getName());
flow.setProcessDefinitionKey(processDefinition.getKey());
flow.setProcessDefinitionVersion(processDefinition.getVersion());
flow.setProcessInstanceId(task.getProcessInstanceId());
flow.setStatus(status);
flowList.add(flow);
});
}
/**
* 获取历史流程
*
* @param processInstanceId 流程实例id
* @return HistoricProcessInstance
*/
private HistoricProcessInstance getHistoricProcessInstance(String processInstanceId) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
}
}

View File

@@ -0,0 +1,53 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.config;
import lombok.AllArgsConstructor;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.flowable.spring.boot.FlowableProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* Flowable配置类
*
* @author Chill
*/
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@EnableConfigurationProperties(FlowableProperties.class)
public class FlowableConfiguration implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
private final FlowableProperties flowableProperties;
@Override
public void configure(SpringProcessEngineConfiguration engineConfiguration) {
engineConfiguration.setActivityFontName(flowableProperties.getActivityFontName());
engineConfiguration.setLabelFontName(flowableProperties.getLabelFontName());
engineConfiguration.setAnnotationFontName(flowableProperties.getAnnotationFontName());
}
}

View File

@@ -0,0 +1,61 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.constant;
/**
* 流程常量.
*
* @author zhuangqian
*/
public interface FlowEngineConstant {
String FLOWABLE_BASE_PACKAGES = "org.flowable.ui";
String SUFFIX = ".bpmn20.xml";
String ACTIVE = "active";
String SUSPEND = "suspend";
String STATUS_TODO = "todo";
String STATUS_CLAIM = "claim";
String STATUS_SEND = "send";
String STATUS_DONE = "done";
String STATUS_FINISHED = "finished";
String STATUS_UNFINISHED = "unfinished";
String STATUS_FINISH = "finish";
String START_EVENT = "startEvent";
String END_EVENT = "endEvent";
}

View File

@@ -0,0 +1,80 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import lombok.AllArgsConstructor;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.secure.annotation.IsAdministrator;
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.api.R;
import org.springblade.flow.engine.entity.FlowExecution;
import org.springblade.flow.engine.service.FlowEngineService;
import org.springframework.web.bind.annotation.*;
/**
* 流程状态控制器
*
* @author Chill
*/
@NonDS
@RestController
@RequestMapping("/follow")
@AllArgsConstructor
@IsAdministrator
@Hidden
public class FlowFollowController {
private final FlowEngineService flowEngineService;
/**
* 流程状态列表
*/
@GetMapping("list")
@ApiOperationSupport(order = 1)
@Operation(summary = "分页", description = "传入notice")
public R<IPage<FlowExecution>> list(Query query, @Parameter(description = "流程实例id") String processInstanceId, @Parameter(description = "流程key") String processDefinitionKey) {
IPage<FlowExecution> pages = flowEngineService.selectFollowPage(Condition.getPage(query), processInstanceId, processDefinitionKey);
return R.data(pages);
}
/**
* 删除流程实例
*/
@PostMapping("delete-process-instance")
@ApiOperationSupport(order = 2)
@Operation(summary = "删除", description = "传入主键集合")
public R deleteProcessInstance(@Parameter(description = "流程实例id") @RequestParam String processInstanceId, @Parameter(description = "删除原因") @RequestParam String deleteReason) {
boolean temp = flowEngineService.deleteProcessInstance(processInstanceId, deleteReason);
return R.status(temp);
}
}

View File

@@ -0,0 +1,132 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.secure.annotation.IsAdministrator;
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.Func;
import org.springblade.flow.engine.constant.FlowEngineConstant;
import org.springblade.flow.engine.entity.FlowProcess;
import org.springblade.flow.engine.service.FlowEngineService;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Objects;
/**
* 流程管理接口
*
* @author Chill
*/
@NonDS
@RestController
@RequestMapping("/manager")
@AllArgsConstructor
@Tag(name = "流程管理接口", description = "流程管理接口")
@IsAdministrator
@Hidden
public class FlowManagerController {
private final FlowEngineService flowEngineService;
/**
* 分页
*/
@GetMapping("list")
@ApiOperationSupport(order = 1)
@Operation(summary = "分页", description = "传入流程类型")
public R<IPage<FlowProcess>> list(@Parameter(description = "流程类型") String category, Query query, @RequestParam(required = false, defaultValue = "1") Integer mode) {
IPage<FlowProcess> pages = flowEngineService.selectProcessPage(Condition.getPage(query), category, mode);
return R.data(pages);
}
/**
* 变更流程状态
*
* @param state 状态
* @param processId 流程id
*/
@PostMapping("change-state")
@ApiOperationSupport(order = 2)
@Operation(summary = "变更流程状态", description = "传入state,processId")
public R changeState(@RequestParam String state, @RequestParam String processId) {
String msg = flowEngineService.changeState(state, processId);
return R.success(msg);
}
/**
* 删除部署流程
*
* @param deploymentIds 部署流程id集合
*/
@PostMapping("delete-deployment")
@ApiOperationSupport(order = 3)
@Operation(summary = "删除部署流程", description = "部署流程id集合")
public R deleteDeployment(String deploymentIds) {
return R.status(flowEngineService.deleteDeployment(deploymentIds));
}
/**
* 检查流程文件格式
*
* @param file 流程文件
*/
@PostMapping("check-upload")
@ApiOperationSupport(order = 4)
@Operation(summary = "上传部署流程文件", description = "传入文件")
public R checkUpload(@RequestParam MultipartFile file) {
boolean temp = Objects.requireNonNull(file.getOriginalFilename()).endsWith(FlowEngineConstant.SUFFIX);
return R.data(Kv.create().set("name", file.getOriginalFilename()).set("success", temp));
}
/**
* 上传部署流程文件
*
* @param files 流程文件
* @param category 类型
*/
@PostMapping("deploy-upload")
@ApiOperationSupport(order = 5)
@Operation(summary = "上传部署流程文件", description = "传入文件")
public R deployUpload(@RequestParam List<MultipartFile> files,
@RequestParam String category,
@RequestParam(required = false, defaultValue = "") String tenantIds) {
return R.status(flowEngineService.deployUpload(files, category, Func.toStrList(tenantIds)));
}
}

View File

@@ -0,0 +1,131 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.secure.annotation.IsAdministrator;
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.xss.annotation.XssIgnore;
import org.springblade.flow.engine.entity.FlowModel;
import org.springblade.flow.engine.service.FlowEngineService;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 流程模型控制器
*
* @author Chill
*/
@NonDS
@RestController
@RequestMapping("/model")
@AllArgsConstructor
@IsAdministrator
@Hidden
public class FlowModelController {
private final FlowEngineService flowEngineService;
/**
* 分页
*/
@GetMapping("/list")
@Parameters({
@Parameter(name = "modelKey", description = "模型标识", in = ParameterIn.QUERY, schema = @Schema(type = "string")),
@Parameter(name = "name", description = "模型名称", in = ParameterIn.QUERY, schema = @Schema(type = "string"))
})
@ApiOperationSupport(order = 1)
@Operation(summary = "分页", description = "传入notice")
public R<IPage<FlowModel>> list(@Parameter(hidden = true) @RequestParam Map<String, Object> flow, Query query) {
IPage<FlowModel> pages = flowEngineService.page(Condition.getPage(query), Condition.getQueryWrapper(flow, FlowModel.class)
.select("id,model_key modelKey,name,description,version,created,last_updated lastUpdated")
.orderByDesc("last_updated"));
return R.data(pages);
}
/**
* 删除
*/
@PostMapping("/remove")
@ApiOperationSupport(order = 2)
@Operation(summary = "删除", description = "传入主键集合")
public R remove(@Parameter(description = "主键集合") @RequestParam String ids) {
boolean temp = flowEngineService.removeByIds(Func.toStrList(ids));
return R.status(temp);
}
/**
* 部署
*/
@PostMapping("/deploy")
@ApiOperationSupport(order = 3)
@Operation(summary = "部署", description = "传入模型id和分类")
public R deploy(@Parameter(description = "模型id") @RequestParam String modelId,
@Parameter(description = "工作流分类") @RequestParam String category,
@Parameter(description = "租户ID") @RequestParam(required = false, defaultValue = "") String tenantIds) {
boolean temp = flowEngineService.deployModel(modelId, category, Func.toStrList(tenantIds));
return R.status(temp);
}
@XssIgnore
@PostMapping("submit")
@ApiOperationSupport(order = 4)
@Operation(summary = "保存/编辑")
@Parameters({
@Parameter(name = "id", description = "模型id"),
@Parameter(name = "name", description = "模型名称", required = true),
@Parameter(name = "modelKey", description = "模型key", required = true),
@Parameter(name = "description", description = "模型描述"),
@Parameter(name = "xml", description = "模型xml", required = true),
})
public R<FlowModel> submit(@RequestBody @Parameter(hidden = true) FlowModel model) {
return R.data(flowEngineService.submitModel(model));
}
@GetMapping("detail")
@Operation(summary = "详情")
@ApiOperationSupport(order = 5)
@Parameters({
@Parameter(name = "id", description = "模型id", required = true),
})
public R<FlowModel> detail(String id) {
return R.data(flowEngineService.getById(id));
}
}

View File

@@ -0,0 +1,107 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.controller;
import io.swagger.v3.oas.annotations.Hidden;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.api.R;
import org.springblade.flow.core.pojo.entity.BladeFlow;
import org.springblade.flow.engine.service.FlowEngineService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 流程通用控制器
*
* @author Chill
*/
@NonDS
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/process")
@Hidden
public class FlowProcessController {
private static final String IMAGE_NAME = "image";
private final FlowEngineService flowEngineService;
/**
* 获取流转历史列表
*
* @param processInstanceId 流程实例id
* @param startActivityId 开始节点id
* @param endActivityId 结束节点id
*/
@GetMapping(value = "history-flow-list")
public R<List<BladeFlow>> historyFlowList(@RequestParam String processInstanceId, String startActivityId, String endActivityId) {
return R.data(flowEngineService.historyFlowList(processInstanceId, startActivityId, endActivityId));
}
/**
* 流程节点进程图
*
* @param processDefinitionId 流程id
* @param processInstanceId 流程实例id
*/
@GetMapping(value = "model-view")
public R modelView(String processDefinitionId, String processInstanceId) {
return R.data(flowEngineService.modelView(processDefinitionId, processInstanceId));
}
/**
* 流程节点进程图
*
* @param processInstanceId 流程实例id
* @param httpServletResponse http响应
*/
@GetMapping(value = "diagram-view")
public void diagramView(String processInstanceId, HttpServletResponse httpServletResponse) {
flowEngineService.diagramView(processInstanceId, httpServletResponse);
}
/**
* 流程图展示
*
* @param processDefinitionId 流程id
* @param processInstanceId 实例id
* @param resourceType 资源类型
* @param response 响应
*/
@GetMapping("resource-view")
public void resourceView(@RequestParam String processDefinitionId, String processInstanceId, @RequestParam(defaultValue = IMAGE_NAME) String resourceType, HttpServletResponse response) {
flowEngineService.resourceView(processDefinitionId, processInstanceId, resourceType, response);
}
}

View File

@@ -0,0 +1,61 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.entity;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 运行实体类
*
* @author Chill
*/
@Data
public class FlowExecution implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String id;
private String name;
private String startUserId;
private String startUser;
private Date startTime;
private String taskDefinitionId;
private String taskDefinitionKey;
private String category;
private String categoryName;
private String processInstanceId;
private String processDefinitionId;
private String processDefinitionKey;
private String activityId;
private int suspensionState;
private String executionId;
}

View File

@@ -0,0 +1,69 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 流程模型
*
* @author Chill
*/
@Data
@TableName("ACT_DE_MODEL")
public class FlowModel implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
public static final int MODEL_TYPE_BPMN = 0;
public static final int MODEL_TYPE_FORM = 2;
public static final int MODEL_TYPE_APP = 3;
public static final int MODEL_TYPE_DECISION_TABLE = 4;
public static final int MODEL_TYPE_CMMN = 5;
private String id;
private String name;
private String modelKey;
private String description;
private Date created;
private Date lastUpdated;
private String createdBy;
private String lastUpdatedBy;
private Integer version;
private String modelEditorJson;
private String modelComment;
private Integer modelType;
private String tenantId;
private byte[] thumbnail;
private String modelEditorXml;
}

View File

@@ -0,0 +1,74 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntityImpl;
import org.springblade.flow.engine.utils.FlowCache;
import java.io.Serializable;
import java.util.Date;
/**
* FlowProcess
*
* @author Chill
*/
@Data
@NoArgsConstructor
public class FlowProcess implements Serializable {
private String id;
private String tenantId;
private String name;
private String key;
private String category;
private String categoryName;
private Integer version;
private String deploymentId;
private String resourceName;
private String diagramResourceName;
private Integer suspensionState;
private Date deploymentTime;
public FlowProcess(ProcessDefinitionEntityImpl entity) {
if (entity != null) {
this.id = entity.getId();
this.tenantId = entity.getTenantId();
this.name = entity.getName();
this.key = entity.getKey();
this.category = entity.getCategory();
this.categoryName = FlowCache.getCategoryName(entity.getCategory());
this.version = entity.getVersion();
this.deploymentId = entity.getDeploymentId();
this.resourceName = entity.getResourceName();
this.diagramResourceName = entity.getDiagramResourceName();
this.suspensionState = entity.getSuspensionState();
}
}
}

View File

@@ -0,0 +1,55 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.flow.engine.entity.FlowModel;
import java.util.List;
/**
* FlowMapper.
*
* @author Chill
*/
public interface FlowMapper extends BaseMapper<FlowModel> {
/**
* 自定义分页
* @param page
* @param flowModel
* @return
*/
List<FlowModel> selectFlowPage(IPage page, FlowModel flowModel);
/**
* 获取模型
* @param parentModelId
* @return
*/
List<FlowModel> findByParentModelId(String parentModelId);
}

View File

@@ -0,0 +1,53 @@
<?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="org.springblade.flow.engine.mapper.FlowMapper">
<!-- 通用查询映射结果 -->
<resultMap id="flowModelResultMap" type="org.springblade.flow.engine.entity.FlowModel">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="model_key" property="modelKey"/>
<result column="description" property="description"/>
<result column="model_comment" property="modelComment"/>
<result column="created" property="created"/>
<result column="created_by" property="createdBy"/>
<result column="last_updated" property="lastUpdated"/>
<result column="last_updated_by" property="lastUpdatedBy"/>
<result column="version" property="version"/>
<result column="model_editor_json" property="modelEditorJson"/>
<result column="thumbnail" property="thumbnail"/>
<result column="model_type" property="modelType"/>
<result column="tenant_id" property="tenantId"/>
</resultMap>
<select id="selectFlowPage" resultMap="flowModelResultMap">
SELECT
a.id,
a.name,
a.model_key,
a.description,
a.model_comment,
a.created,
a.created_by,
a.last_updated,
a.last_updated_by,
a.version,
a.model_editor_json,
a.thumbnail,
a.model_type,
a.tenant_id
FROM
ACT_DE_MODEL a
WHERE
1 = 1
ORDER BY
a.created DESC
</select>
<select id="findByParentModelId" parameterType="string" resultMap="flowModelResultMap">
select model.* from ACT_DE_MODEL_RELATION modelrelation
inner join ACT_DE_MODEL model on modelrelation.model_id = model.id
where modelrelation.parent_model_id = #{_parameter}
</select>
</mapper>

View File

@@ -0,0 +1,174 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletResponse;
import org.springblade.flow.core.pojo.entity.BladeFlow;
import org.springblade.flow.engine.entity.FlowExecution;
import org.springblade.flow.engine.entity.FlowModel;
import org.springblade.flow.engine.entity.FlowProcess;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
/**
* FlowEngineService
*
* @author Chill
*/
public interface FlowEngineService extends IService<FlowModel> {
/**
* 自定义分页
*
* @param page 分页工具
* @param flowModel 流程模型
* @return
*/
IPage<FlowModel> selectFlowPage(IPage<FlowModel> page, FlowModel flowModel);
/**
* 流程管理列表
*
* @param page 分页工具
* @param category 分类
* @param mode 形态
* @return
*/
IPage<FlowProcess> selectProcessPage(IPage<FlowProcess> page, String category, Integer mode);
/**
* 流程管理列表
*
* @param page 分页工具
* @param processInstanceId 流程实例id
* @param processDefinitionKey 流程key
* @return
*/
IPage<FlowExecution> selectFollowPage(IPage<FlowExecution> page, String processInstanceId, String processDefinitionKey);
/**
* 获取流转历史列表
*
* @param processInstanceId 流程实例id
* @param startActivityId 开始节点id
* @param endActivityId 结束节点id
* @return
*/
List<BladeFlow> historyFlowList(String processInstanceId, String startActivityId, String endActivityId);
/**
* 变更流程状态
*
* @param state 状态
* @param processId 流程ID
* @return
*/
String changeState(String state, String processId);
/**
* 删除部署流程
*
* @param deploymentIds 部署流程id集合
* @return
*/
boolean deleteDeployment(String deploymentIds);
/**
* 上传部署流程
*
* @param files 流程配置文件
* @param category 流程分类
* @param tenantIdList 租户id集合
* @return
*/
boolean deployUpload(List<MultipartFile> files, String category, List<String> tenantIdList);
/**
* 部署流程
*
* @param modelId 模型id
* @param category 分类
* @param tenantIdList 租户id集合
* @return
*/
boolean deployModel(String modelId, String category, List<String> tenantIdList);
/**
* 删除流程实例
*
* @param processInstanceId 流程实例id
* @param deleteReason 删除原因
* @return
*/
boolean deleteProcessInstance(String processInstanceId, String deleteReason);
/**
* 保存/更新模型
*
* @param model 模型
* @return 模型
*/
FlowModel submitModel(FlowModel model);
/**
* 流程节点进程图
*
* @param processDefinitionId
* @param processInstanceId
* @return
*/
Map<String, Object> modelView(String processDefinitionId, String processInstanceId);
/**
* 流程节点进程图
*
* @param processInstanceId
* @param httpServletResponse
*/
void diagramView(String processInstanceId, HttpServletResponse httpServletResponse);
/**
* 流程图展示
*
* @param processDefinitionId
* @param processInstanceId
* @param resourceType
* @param response
*/
void resourceView(String processDefinitionId, String processInstanceId, String resourceType, HttpServletResponse response);
/**
* 获取XML
*
* @param model
* @return
*/
byte[] getModelEditorXML(FlowModel model);
}

View File

@@ -0,0 +1,568 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.impl.util.IoUtil;
import org.flowable.common.engine.impl.util.io.StringStreamSource;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.editor.language.json.converter.BpmnJsonConverterContext;
import org.flowable.editor.language.json.converter.CustomBpmnJsonConverterContext;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntityImpl;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.flowable.engine.task.Comment;
import org.flowable.image.ProcessDiagramGenerator;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.FileUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.flow.core.pojo.entity.BladeFlow;
import org.springblade.flow.core.pojo.enums.FlowModeEnum;
import org.springblade.flow.core.utils.TaskUtil;
import org.springblade.flow.engine.constant.FlowEngineConstant;
import org.springblade.flow.engine.entity.FlowExecution;
import org.springblade.flow.engine.entity.FlowModel;
import org.springblade.flow.engine.entity.FlowProcess;
import org.springblade.flow.engine.mapper.FlowMapper;
import org.springblade.flow.engine.service.FlowEngineService;
import org.springblade.flow.engine.utils.FlowCache;
import org.springblade.system.cache.UserCache;
import org.springblade.system.pojo.entity.User;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
/**
* 工作流服务实现类
*
* @author Chill
*/
@Slf4j
@Service
@AllArgsConstructor
public class FlowEngineServiceImpl extends ServiceImpl<FlowMapper, FlowModel> implements FlowEngineService {
private static final String ALREADY_IN_STATE = "already in state";
private static final String USR_TASK = "userTask";
private static final String IMAGE_NAME = "image";
private static final String XML_NAME = "xml";
private static final Integer INT_1024 = 1024;
private static final BpmnJsonConverter BPMN_JSON_CONVERTER = new BpmnJsonConverter();
private static final BpmnXMLConverter BPMN_XML_CONVERTER = new BpmnXMLConverter();
private final ObjectMapper objectMapper;
private final RepositoryService repositoryService;
private final RuntimeService runtimeService;
private final HistoryService historyService;
private final TaskService taskService;
private final ProcessEngine processEngine;
@Override
public IPage<FlowModel> selectFlowPage(IPage<FlowModel> page, FlowModel flowModel) {
return page.setRecords(baseMapper.selectFlowPage(page, flowModel));
}
@Override
public IPage<FlowProcess> selectProcessPage(IPage<FlowProcess> page, String category, Integer mode) {
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery().latestVersion().orderByProcessDefinitionKey().asc();
// 通用流程
if (mode == FlowModeEnum.COMMON.getMode()) {
processDefinitionQuery.processDefinitionWithoutTenantId();
}
// 定制流程
else if (!AuthUtil.isAdministrator()) {
processDefinitionQuery.processDefinitionTenantId(AuthUtil.getTenantId());
}
if (StringUtils.isNotEmpty(category)) {
processDefinitionQuery.processDefinitionCategory(category);
}
List<ProcessDefinition> processDefinitionList = processDefinitionQuery.listPage(Func.toInt((page.getCurrent() - 1) * page.getSize()), Func.toInt(page.getSize()));
List<FlowProcess> flowProcessList = new ArrayList<>();
processDefinitionList.forEach(processDefinition -> {
String deploymentId = processDefinition.getDeploymentId();
Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
FlowProcess flowProcess = new FlowProcess((ProcessDefinitionEntityImpl) processDefinition);
flowProcess.setDeploymentTime(deployment.getDeploymentTime());
flowProcessList.add(flowProcess);
});
page.setTotal(processDefinitionQuery.count());
page.setRecords(flowProcessList);
return page;
}
@Override
public IPage<FlowExecution> selectFollowPage(IPage<FlowExecution> page, String processInstanceId, String processDefinitionKey) {
ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
if (StringUtil.isNotBlank(processInstanceId)) {
processInstanceQuery.processInstanceId(processInstanceId);
}
if (StringUtil.isNotBlank(processDefinitionKey)) {
processInstanceQuery.processDefinitionKey(processDefinitionKey);
}
List<FlowExecution> flowList = new ArrayList<>();
List<ProcessInstance> procInsList = processInstanceQuery.listPage(Func.toInt((page.getCurrent() - 1) * page.getSize()), Func.toInt(page.getSize()));
procInsList.forEach(processInstance -> {
ExecutionEntityImpl execution = (ExecutionEntityImpl) processInstance;
FlowExecution flowExecution = new FlowExecution();
flowExecution.setId(execution.getId());
flowExecution.setName(execution.getName());
flowExecution.setStartUserId(execution.getStartUserId());
User taskUser = UserCache.getUserByTaskUser(execution.getStartUserId());
if (taskUser != null) {
flowExecution.setStartUser(taskUser.getName());
}
flowExecution.setStartTime(execution.getStartTime());
flowExecution.setExecutionId(execution.getId());
flowExecution.setProcessInstanceId(execution.getProcessInstanceId());
flowExecution.setProcessDefinitionId(execution.getProcessDefinitionId());
flowExecution.setProcessDefinitionKey(execution.getProcessDefinitionKey());
flowExecution.setSuspensionState(execution.getSuspensionState());
FlowProcess processDefinition = FlowCache.getProcessDefinition(execution.getProcessDefinitionId());
flowExecution.setCategory(processDefinition.getCategory());
flowExecution.setCategoryName(FlowCache.getCategoryName(processDefinition.getCategory()));
flowList.add(flowExecution);
});
page.setTotal(processInstanceQuery.count());
page.setRecords(flowList);
return page;
}
@Override
public List<BladeFlow> historyFlowList(String processInstanceId, String startActivityId, String endActivityId) {
List<BladeFlow> flowList = new LinkedList<>();
List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().orderByHistoricActivityInstanceEndTime().asc().list();
boolean start = false;
Map<String, Integer> activityMap = new HashMap<>(16);
for (int i = 0; i < historicActivityInstanceList.size(); i++) {
HistoricActivityInstance historicActivityInstance = historicActivityInstanceList.get(i);
// 过滤开始节点前的节点
if (StringUtil.isNotBlank(startActivityId) && startActivityId.equals(historicActivityInstance.getActivityId())) {
start = true;
}
if (StringUtil.isNotBlank(startActivityId) && !start) {
continue;
}
// 显示开始节点和结束节点,并且执行人不为空的任务
if (StringUtils.equals(USR_TASK, historicActivityInstance.getActivityType())
|| FlowEngineConstant.START_EVENT.equals(historicActivityInstance.getActivityType())
|| FlowEngineConstant.END_EVENT.equals(historicActivityInstance.getActivityType())) {
// 给节点增加序号
activityMap.computeIfAbsent(historicActivityInstance.getActivityId(), k -> activityMap.size());
BladeFlow flow = new BladeFlow();
flow.setHistoryActivityId(historicActivityInstance.getActivityId());
flow.setHistoryActivityName(historicActivityInstance.getActivityName());
flow.setCreateTime(historicActivityInstance.getStartTime());
flow.setEndTime(historicActivityInstance.getEndTime());
String durationTime = DateUtil.secondToTime(Func.toLong(historicActivityInstance.getDurationInMillis(), 0L) / 1000);
flow.setHistoryActivityDurationTime(durationTime);
// 获取流程发起人名称
if (FlowEngineConstant.START_EVENT.equals(historicActivityInstance.getActivityType())) {
List<HistoricProcessInstance> processInstanceList = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).orderByProcessInstanceStartTime().asc().list();
if (!processInstanceList.isEmpty()) {
if (StringUtil.isNotBlank(processInstanceList.get(0).getStartUserId())) {
String taskUser = processInstanceList.get(0).getStartUserId();
User user = UserCache.getUser(TaskUtil.getUserId(taskUser));
if (user != null) {
flow.setAssignee(historicActivityInstance.getAssignee());
flow.setAssigneeName(user.getName());
}
}
}
}
// 获取任务执行人名称
if (StringUtil.isNotBlank(historicActivityInstance.getAssignee())) {
User user = UserCache.getUser(TaskUtil.getUserId(historicActivityInstance.getAssignee()));
if (user != null) {
flow.setAssignee(historicActivityInstance.getAssignee());
flow.setAssigneeName(user.getName());
}
}
// 获取意见评论内容
if (StringUtil.isNotBlank(historicActivityInstance.getTaskId())) {
List<Comment> commentList = taskService.getTaskComments(historicActivityInstance.getTaskId());
if (!commentList.isEmpty()) {
flow.setComment(commentList.get(0).getFullMessage());
}
}
flowList.add(flow);
}
// 过滤结束节点后的节点
if (StringUtils.isNotBlank(endActivityId) && endActivityId.equals(historicActivityInstance.getActivityId())) {
boolean temp = false;
Integer activityNum = activityMap.get(historicActivityInstance.getActivityId());
// 该活动节点,后续节点是否在结束节点之前,在后续节点中是否存在
for (int j = i + 1; j < historicActivityInstanceList.size(); j++) {
HistoricActivityInstance hi = historicActivityInstanceList.get(j);
Integer activityNumA = activityMap.get(hi.getActivityId());
boolean numberTemp = activityNumA != null && activityNumA < activityNum;
boolean equalsTemp = StringUtils.equals(hi.getActivityId(), historicActivityInstance.getActivityId());
if (numberTemp || equalsTemp) {
temp = true;
}
}
if (!temp) {
break;
}
}
}
return flowList;
}
@Override
public String changeState(String state, String processId) {
try {
if (state.equals(FlowEngineConstant.ACTIVE)) {
repositoryService.activateProcessDefinitionById(processId, true, null);
return StringUtil.format("激活ID为 [{}] 的流程成功", processId);
} else if (state.equals(FlowEngineConstant.SUSPEND)) {
repositoryService.suspendProcessDefinitionById(processId, true, null);
return StringUtil.format("挂起ID为 [{}] 的流程成功", processId);
} else {
return "暂无流程变更";
}
} catch (Exception e) {
if (e.getMessage().contains(ALREADY_IN_STATE)) {
return StringUtil.format("ID为 [{}] 的流程已是此状态,无需操作", processId);
}
return e.getMessage();
}
}
@Override
public boolean deleteDeployment(String deploymentIds) {
Func.toStrList(deploymentIds).forEach(deploymentId -> repositoryService.deleteDeployment(deploymentId, true));
return true;
}
@Override
public boolean deployUpload(List<MultipartFile> files, String category, List<String> tenantIdList) {
files.forEach(file -> {
try {
String fileName = file.getOriginalFilename();
InputStream fileInputStream = file.getInputStream();
byte[] bytes = FileUtil.copyToByteArray(fileInputStream);
if (Func.isNotEmpty(tenantIdList)) {
tenantIdList.forEach(tenantId -> {
Deployment deployment = repositoryService.createDeployment().addBytes(fileName, bytes).tenantId(tenantId).deploy();
deploy(deployment, category);
});
} else {
Deployment deployment = repositoryService.createDeployment().addBytes(fileName, bytes).deploy();
deploy(deployment, category);
}
} catch (IOException e) {
e.printStackTrace();
}
});
return true;
}
@Override
public boolean deployModel(String modelId, String category, List<String> tenantIdList) {
FlowModel model = this.getById(modelId);
if (model == null) {
throw new ServiceException("未找到模型 id: " + modelId);
}
byte[] bytes = getBpmnXML(model);
String processName = model.getName();
if (!StringUtil.endsWithIgnoreCase(processName, FlowEngineConstant.SUFFIX)) {
processName += FlowEngineConstant.SUFFIX;
}
String finalProcessName = processName;
if (Func.isNotEmpty(tenantIdList)) {
tenantIdList.forEach(tenantId -> {
Deployment deployment = repositoryService.createDeployment().addBytes(finalProcessName, bytes).name(model.getName()).key(model.getModelKey()).tenantId(tenantId).deploy();
deploy(deployment, category);
});
} else {
Deployment deployment = repositoryService.createDeployment().addBytes(finalProcessName, bytes).name(model.getName()).key(model.getModelKey()).deploy();
deploy(deployment, category);
}
return true;
}
@Override
public boolean deleteProcessInstance(String processInstanceId, String deleteReason) {
runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
return true;
}
private void deploy(Deployment deployment, String category) {
log.debug("流程部署--------deploy: " + deployment + " 分类---------->" + category);
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
StringBuilder logBuilder = new StringBuilder(500);
List<Object> logArgs = new ArrayList<>();
// 设置流程分类
for (ProcessDefinition processDefinition : list) {
if (StringUtil.isNotBlank(category)) {
repositoryService.setProcessDefinitionCategory(processDefinition.getId(), category);
}
logBuilder.append("部署成功,流程ID={} \n");
logArgs.add(processDefinition.getId());
}
if (list.isEmpty()) {
throw new ServiceException("部署失败,未找到流程");
} else {
log.info(logBuilder.toString(), logArgs.toArray());
}
}
@Override
public FlowModel submitModel(FlowModel model) {
FlowModel flowModel = new FlowModel();
flowModel.setId(model.getId());
flowModel.setVersion(Func.toInt(model.getVersion(), 0) + 1);
flowModel.setName(model.getName());
flowModel.setModelKey(model.getModelKey());
flowModel.setModelType(FlowModel.MODEL_TYPE_BPMN);
flowModel.setCreatedBy(TaskUtil.getTaskUser());
flowModel.setDescription(model.getDescription());
flowModel.setLastUpdated(Calendar.getInstance().getTime());
flowModel.setLastUpdatedBy(TaskUtil.getTaskUser());
flowModel.setTenantId(AuthUtil.getTenantId());
flowModel.setModelEditorXml(model.getModelEditorXml());
if (StringUtil.isBlank(model.getId())) {
flowModel.setCreated(Calendar.getInstance().getTime());
}
if (StringUtil.isNotBlank(model.getModelEditorXml())) {
flowModel.setModelEditorJson(getBpmnJson(model.getModelEditorXml()));
}
this.saveOrUpdate(flowModel);
return flowModel;
}
@Override
public Map<String, Object> modelView(String processDefinitionId, String processInstanceId) {
Map<String, Object> result = new HashMap<>();
// 节点标记
if (StringUtil.isNotBlank(processInstanceId)) {
result.put("flow", this.historyFlowList(processInstanceId, null, null));
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
processDefinitionId = processInstance.getProcessDefinitionId();
}
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
// 流程图展示
result.put("xml", new String(new BpmnXMLConverter().convertToXML(bpmnModel)));
return result;
}
@Override
public void diagramView(String processInstanceId, HttpServletResponse httpServletResponse) {
// 获得当前活动的节点
String processDefinitionId;
// 如果流程已经结束,则得到结束节点
if (this.isFinished(processInstanceId)) {
HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
processDefinitionId = pi.getProcessDefinitionId();
} else {
// 如果流程没有结束,则取当前活动节点
// 根据流程实例ID获得当前处于活动状态的ActivityId合集
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
processDefinitionId = pi.getProcessDefinitionId();
}
List<String> highLightedActivities = new ArrayList<>();
// 获得活动的节点
List<HistoricActivityInstance> highLightedActivityList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
for (HistoricActivityInstance tempActivity : highLightedActivityList) {
String activityId = tempActivity.getActivityId();
highLightedActivities.add(activityId);
}
List<String> flows = new ArrayList<>();
// 获取流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
ProcessEngineConfiguration engConf = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = engConf.getProcessDiagramGenerator();
InputStream in = diagramGenerator.generateDiagram(bpmnModel, "bmp", highLightedActivities, flows, engConf.getActivityFontName(),
engConf.getLabelFontName(), engConf.getAnnotationFontName(), engConf.getClassLoader(), 1.0, true);
OutputStream out = null;
byte[] buf = new byte[1024];
int length;
try {
out = httpServletResponse.getOutputStream();
while ((length = in.read(buf)) != -1) {
out.write(buf, 0, length);
}
} catch (IOException e) {
log.error("操作异常", e);
} finally {
IoUtil.closeSilently(out);
IoUtil.closeSilently(in);
}
}
@Override
public void resourceView(String processDefinitionId, String processInstanceId, String resourceType, HttpServletResponse response) {
if (StringUtil.isAllBlank(processDefinitionId, processInstanceId)) {
return;
}
if (StringUtil.isBlank(processDefinitionId)) {
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
processDefinitionId = processInstance.getProcessDefinitionId();
}
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
String resourceName = "";
if (resourceType.equals(IMAGE_NAME)) {
resourceName = processDefinition.getDiagramResourceName();
} else if (resourceType.equals(XML_NAME)) {
resourceName = processDefinition.getResourceName();
}
try {
InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
byte[] b = new byte[1024];
int len;
while ((len = resourceAsStream.read(b, 0, INT_1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
@Override
public byte[] getModelEditorXML(FlowModel model) {
return getBpmnXML(model);
}
/**
* 是否已完结
*
* @param processInstanceId 流程实例id
* @return bool
*/
private boolean isFinished(String processInstanceId) {
return historyService.createHistoricProcessInstanceQuery().finished()
.processInstanceId(processInstanceId).count() > 0;
}
/**
* xml转bpmn json
*
* @param xml xml
* @return json
*/
private String getBpmnJson(String xml) {
return BPMN_JSON_CONVERTER.convertToJson(getBpmnModel(xml)).toString();
}
/**
* xml转bpmnModel
*
* @param xml xml
* @return bpmnModel
*/
private BpmnModel getBpmnModel(String xml) {
return BPMN_XML_CONVERTER.convertToBpmnModel(new StringStreamSource(xml), false, false);
}
private byte[] getBpmnXML(FlowModel model) {
BpmnModel bpmnModel = getBpmnModel(model);
return getBpmnXML(bpmnModel);
}
private byte[] getBpmnXML(BpmnModel bpmnModel) {
for (Process process : bpmnModel.getProcesses()) {
if (StringUtils.isNotEmpty(process.getId())) {
char firstCharacter = process.getId().charAt(0);
if (Character.isDigit(firstCharacter)) {
process.setId("a" + process.getId());
}
}
}
return BPMN_XML_CONVERTER.convertToXML(bpmnModel);
}
private BpmnModel getBpmnModel(FlowModel model) {
BpmnModel bpmnModel;
try {
Map<String, FlowModel> formMap = new HashMap<>(16);
Map<String, FlowModel> decisionTableMap = new HashMap<>(16);
List<FlowModel> referencedModels = baseMapper.findByParentModelId(model.getId());
for (FlowModel childModel : referencedModels) {
if (FlowModel.MODEL_TYPE_FORM == childModel.getModelType()) {
formMap.put(childModel.getId(), childModel);
} else if (FlowModel.MODEL_TYPE_DECISION_TABLE == childModel.getModelType()) {
decisionTableMap.put(childModel.getId(), childModel);
}
}
bpmnModel = getBpmnModel(model, formMap, decisionTableMap);
} catch (Exception e) {
log.error("Could not generate BPMN 2.0 model for {}", model.getId(), e);
throw new ServiceException("Could not generate BPMN 2.0 model");
}
return bpmnModel;
}
private BpmnModel getBpmnModel(FlowModel model, Map<String, FlowModel> formMap, Map<String, FlowModel> decisionTableMap) {
try {
ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(model.getModelEditorJson());
Map<String, String> formKeyMap = new HashMap<>(16);
for (FlowModel formModel : formMap.values()) {
formKeyMap.put(formModel.getId(), formModel.getModelKey());
}
Map<String, String> decisionTableKeyMap = new HashMap<>(16);
for (FlowModel decisionTableModel : decisionTableMap.values()) {
decisionTableKeyMap.put(decisionTableModel.getId(), decisionTableModel.getModelKey());
}
BpmnJsonConverterContext converterContext = new CustomBpmnJsonConverterContext(formKeyMap, decisionTableKeyMap);
return BPMN_JSON_CONVERTER.convertToBpmnModel(editorJsonNode, converterContext);
} catch (Exception e) {
log.error("Could not generate BPMN 2.0 model for {}", model.getId(), e);
throw new ServiceException("Could not generate BPMN 2.0 model");
}
}
}

View File

@@ -0,0 +1,89 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.flow.engine.utils;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntityImpl;
import org.flowable.engine.repository.ProcessDefinition;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.flow.engine.entity.FlowProcess;
import org.springblade.system.cache.DictCache;
/**
* 流程缓存
*
* @author Chill
*/
public class FlowCache {
private static final String FLOW_CACHE = "flow:process";
private static final String FLOW_DEFINITION_ID = "definition:id";
private static RepositoryService repositoryService;
private static RepositoryService getRepositoryService() {
if (repositoryService == null) {
repositoryService = SpringUtil.getBean(RepositoryService.class);
}
return repositoryService;
}
/**
* 获得流程定义对象
*
* @param processDefinitionId 流程对象id
* @return
*/
public static FlowProcess getProcessDefinition(String processDefinitionId) {
return CacheUtil.get(FLOW_CACHE, FLOW_DEFINITION_ID, processDefinitionId, () -> {
ProcessDefinition processDefinition = getRepositoryService().createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
ProcessDefinitionEntityImpl processDefinitionEntity = BeanUtil.copyProperties(processDefinition, ProcessDefinitionEntityImpl.class);
return new FlowProcess(processDefinitionEntity);
});
}
/**
* 获取流程类型名
*
* @param category 流程类型
* @return
*/
public static String getCategoryName(String category) {
if (Func.isEmpty(category)) {
return StringPool.EMPTY;
}
String[] categoryArr = category.split(StringPool.UNDERSCORE);
if (categoryArr.length <= 1) {
return StringPool.EMPTY;
} else {
return DictCache.getValue(category.split(StringPool.UNDERSCORE)[0], Func.toInt(category.split(StringPool.UNDERSCORE)[1]));
}
}
}