初始化添加warm-flow

This commit is contained in:
gssong
2024-08-04 13:47:48 +08:00
parent 782821aeb2
commit efb628be5f
83 changed files with 1642 additions and 7206 deletions

11
pom.xml
View File

@@ -53,7 +53,7 @@
<!-- 面向运行时的D-ORM依赖 -->
<anyline.version>8.7.2-20240722</anyline.version>
<!--工作流配置-->
<flowable.version>7.0.1</flowable.version>
<warm-flow>1.2.3</warm-flow>
<!-- 插件版本 -->
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
@@ -116,13 +116,12 @@
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-bom</artifactId>
<version>${flowable.version}</version>
<type>pom</type>
<scope>import</scope>
<groupId>io.github.minliuhua</groupId>
<artifactId>warm-flow-mybatis-plus-sb-starter</artifactId>
<version>${warm-flow}</version>
</dependency>
<!-- JustAuth 的依赖配置-->
<dependency>
<groupId>me.zhyd.oauth</groupId>

View File

@@ -270,20 +270,21 @@ websocket:
# 设置访问源地址
allowedOrigins: '*'
--- #flowable配置
flowable:
async-executor-activate: false #关闭定时任务JOB
# 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时会自动将数据库表结构升级至新版本。
database-schema-update: true
activity-font-name: 宋体
label-font-name: 宋体
annotation-font-name: 宋体
# 关闭各个模块生成表,目前只使用工作流基础表
idm:
enabled: false
cmmn:
enabled: false
dmn:
enabled: false
app:
enabled: false
--- # warm-flow工作流配置
warm-flow:
# 是否显示banner图默认是
banner: true
# 填充器 (可配置文件注入,也可用@Bean/@Component方式
data-fill-handler-path: com.ruoyi.system.handle.CustomDataFillHandler
# 全局租户处理器(可配置文件注入,也可用@Bean/@Component方式
tenant_handler_path: com.ruoyi.system.handle.CustomTenantHandler
# 是否开启逻辑删除
logic_delete: false
# 逻辑删除字段值开启后默认为2
logic_delete_value: 2
# 逻辑未删除字段开启后默认为0
logic_not_delete_value: 0
# 数据源类型, mybatis模块对orm进一步的封装, 由于各数据库分页语句存在差异,
# 当配置此参数时, 以此参数结果为基准, 未配置时, 取DataSource中数据源类型,
# 兜底为mysql数据库
data_source_type: mysql

View File

@@ -16,7 +16,7 @@ public interface WorkflowService {
* @param businessKeys 业务id
* @return 结果
*/
boolean deleteRunAndHisInstance(List<String> businessKeys);
boolean deleteInstance(List<Long> businessKeys);
/**
* 获取当前流程状态

View File

@@ -18,54 +18,6 @@
<dependencies>
<!--引入flowable依赖-->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-autoconfigure</artifactId>
<exclusions>
<exclusion>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-security</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-configurator</artifactId>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 绘制flowable流程图 -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-image-generator</artifactId>
</dependency>
<!-- flowable json 转换 -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-json-converter</artifactId>
<version>6.8.0</version>
</dependency>
<!-- svg转png图片工具-->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-all</artifactId>
<version>1.10</version>
<exclusions>
<exclusion>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-websocket</artifactId>
@@ -113,6 +65,10 @@
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-security</artifactId>
</dependency>
<dependency>
<groupId>io.github.minliuhua</groupId>
<artifactId>warm-flow-mybatis-plus-sb-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,25 @@
package org.dromara.workflow.config;
import com.warm.flow.core.handler.TenantHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.dromara.common.satoken.utils.LoginHelper;
/**
* 任务状态枚举
*
* @author may
*/
@Configuration
public class WarmFlowConfig {
/**
* 全局租户处理器(可配置文件注入,也可用@Bean/@Component方式
*/
@Bean
public TenantHandler tenantHandler() {
return LoginHelper::getTenantId;
}
}

View File

@@ -1,147 +0,0 @@
package org.dromara.workflow.controller;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.workflow.domain.bo.ModelBo;
import org.dromara.workflow.domain.vo.ModelVo;
import org.dromara.workflow.service.IActModelService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Model;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
/**
* 模型管理 控制层
*
* @author may
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/model")
public class ActModelController extends BaseController {
private final RepositoryService repositoryService;
private final IActModelService actModelService;
/**
* 分页查询模型
*
* @param modelBo 模型参数
*/
@GetMapping("/list")
public TableDataInfo<Model> page(ModelBo modelBo, PageQuery pageQuery) {
return actModelService.page(modelBo, pageQuery);
}
/**
* 新增模型
*
* @param modelBo 模型请求对象
*/
@Log(title = "模型管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/save")
public R<Void> saveNewModel(@Validated(AddGroup.class) @RequestBody ModelBo modelBo) {
return toAjax(actModelService.saveNewModel(modelBo));
}
/**
* 查询模型
*
* @param id 模型id
*/
@GetMapping("/getInfo/{id}")
public R<ModelVo> getInfo(@NotBlank(message = "模型id不能为空") @PathVariable String id) {
return R.ok(actModelService.getInfo(id));
}
/**
* 修改模型信息
*
* @param modelBo 模型数据
*/
@Log(title = "模型管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping(value = "/update")
public R<Void> update(@RequestBody ModelBo modelBo) {
return toAjax(actModelService.update(modelBo));
}
/**
* 编辑XMl模型
*
* @param modelBo 模型数据
*/
@Log(title = "模型管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping(value = "/editModelXml")
public R<Void> editModel(@Validated(EditGroup.class) @RequestBody ModelBo modelBo) {
return toAjax(actModelService.editModelXml(modelBo));
}
/**
* 删除流程模型
*
* @param ids 模型id
*/
@Log(title = "模型管理", businessType = BusinessType.DELETE)
@RepeatSubmit()
@DeleteMapping("/{ids}")
@Transactional(rollbackFor = Exception.class)
public R<Void> delete(@NotEmpty(message = "主键不能为空") @PathVariable String[] ids) {
Arrays.stream(ids).parallel().forEachOrdered(repositoryService::deleteModel);
return R.ok();
}
/**
* 模型部署
*
* @param id 模型id
*/
@Log(title = "模型管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/modelDeploy/{id}")
public R<Void> deploy(@NotBlank(message = "模型id不能为空") @PathVariable("id") String id) {
return toAjax(actModelService.modelDeploy(id));
}
/**
* 导出模型zip压缩包
*
* @param modelIds 模型id
* @param response 相应
*/
@GetMapping("/export/zip/{modelIds}")
public void exportZip(@NotEmpty(message = "模型id不能为空") @PathVariable List<String> modelIds,
HttpServletResponse response) {
actModelService.exportZip(modelIds, response);
}
/**
* 复制模型
*
* @param modelBo 模型数据
*/
@PostMapping("/copyModel")
public R<Void> copyModel(@RequestBody ModelBo modelBo) {
return toAjax(actModelService.copyModel(modelBo));
}
}

View File

@@ -1,147 +0,0 @@
package org.dromara.workflow.controller;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.workflow.domain.bo.ProcessDefinitionBo;
import org.dromara.workflow.domain.vo.ProcessDefinitionVo;
import org.dromara.workflow.service.IActProcessDefinitionService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 流程定义管理 控制层
*
* @author may
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/processDefinition")
public class ActProcessDefinitionController extends BaseController {
private final IActProcessDefinitionService actProcessDefinitionService;
/**
* 分页查询
*
* @param bo 参数
*/
@GetMapping("/list")
public TableDataInfo<ProcessDefinitionVo> page(ProcessDefinitionBo bo, PageQuery pageQuery) {
return actProcessDefinitionService.page(bo, pageQuery);
}
/**
* 查询历史流程定义列表
*
* @param key 流程定义key
*/
@GetMapping("/getListByKey/{key}")
public R<List<ProcessDefinitionVo>> getListByKey(@NotEmpty(message = "流程定义key不能为空") @PathVariable String key) {
return R.ok("操作成功", actProcessDefinitionService.getListByKey(key));
}
/**
* 查看流程定义图片
*
* @param processDefinitionId 流程定义id
*/
@GetMapping("/definitionImage/{processDefinitionId}")
public R<String> definitionImage(@PathVariable String processDefinitionId) {
return R.ok("操作成功", actProcessDefinitionService.definitionImage(processDefinitionId));
}
/**
* 查看流程定义xml文件
*
* @param processDefinitionId 流程定义id
*/
@GetMapping("/definitionXml/{processDefinitionId}")
public R<Map<String, Object>> definitionXml(@NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) {
Map<String, Object> map = new HashMap<>();
String xmlStr = actProcessDefinitionService.definitionXml(processDefinitionId);
map.put("xml", Arrays.asList(xmlStr.split("\n")));
map.put("xmlStr", xmlStr);
return R.ok(map);
}
/**
* 删除流程定义
*
* @param deploymentIds 部署id
* @param processDefinitionIds 流程定义id
*/
@Log(title = "流程定义管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{deploymentIds}/{processDefinitionIds}")
public R<Void> deleteDeployment(@NotNull(message = "流程部署id不能为空") @PathVariable List<String> deploymentIds,
@NotNull(message = "流程定义id不能为空") @PathVariable List<String> processDefinitionIds) {
return toAjax(actProcessDefinitionService.deleteDeployment(deploymentIds, processDefinitionIds));
}
/**
* 激活或者挂起流程定义
*
* @param processDefinitionId 流程定义id
*/
@Log(title = "流程定义管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/updateDefinitionState/{processDefinitionId}")
public R<Void> updateDefinitionState(@NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) {
return toAjax(actProcessDefinitionService.updateDefinitionState(processDefinitionId));
}
/**
* 迁移流程定义
*
* @param currentProcessDefinitionId 当前流程定义id
* @param fromProcessDefinitionId 需要迁移到的流程定义id
*/
@Log(title = "流程定义管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/migrationDefinition/{currentProcessDefinitionId}/{fromProcessDefinitionId}")
public R<Void> migrationDefinition(@NotBlank(message = "当前流程定义id") @PathVariable String currentProcessDefinitionId,
@NotBlank(message = "需要迁移到的流程定义id") @PathVariable String fromProcessDefinitionId) {
return toAjax(actProcessDefinitionService.migrationDefinition(currentProcessDefinitionId, fromProcessDefinitionId));
}
/**
* 流程定义转换为模型
*
* @param processDefinitionId 流程定义id
*/
@Log(title = "流程定义管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/convertToModel/{processDefinitionId}")
public R<Void> convertToModel(@NotEmpty(message = "流程定义id不能为空") @PathVariable String processDefinitionId) {
return toAjax(actProcessDefinitionService.convertToModel(processDefinitionId));
}
/**
* 通过zip或xml部署流程定义
*
* @param file 文件
* @param categoryCode 分类
*/
@Log(title = "流程定义管理", businessType = BusinessType.INSERT)
@PostMapping("/deployByFile")
public void deployByFile(@RequestParam("file") MultipartFile file, @RequestParam("categoryCode") String categoryCode) {
actProcessDefinitionService.deployByFile(file, categoryCode);
}
}

View File

@@ -1,160 +0,0 @@
package org.dromara.workflow.controller;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.workflow.domain.bo.ProcessInstanceBo;
import org.dromara.workflow.domain.bo.ProcessInvalidBo;
import org.dromara.workflow.domain.bo.TaskUrgingBo;
import org.dromara.workflow.domain.vo.ActHistoryInfoVo;
import org.dromara.workflow.domain.vo.ProcessInstanceVo;
import org.dromara.workflow.service.IActProcessInstanceService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* 流程实例管理 控制层
*
* @author may
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/processInstance")
public class ActProcessInstanceController extends BaseController {
private final IActProcessInstanceService actProcessInstanceService;
/**
* 分页查询正在运行的流程实例
*
* @param bo 参数
*/
@GetMapping("/getPageByRunning")
public TableDataInfo<ProcessInstanceVo> getPageByRunning(ProcessInstanceBo bo, PageQuery pageQuery) {
return actProcessInstanceService.getPageByRunning(bo, pageQuery);
}
/**
* 分页查询已结束的流程实例
*
* @param bo 参数
*/
@GetMapping("/getPageByFinish")
public TableDataInfo<ProcessInstanceVo> getPageByFinish(ProcessInstanceBo bo, PageQuery pageQuery) {
return actProcessInstanceService.getPageByFinish(bo, pageQuery);
}
/**
* 通过业务id获取历史流程图
*
* @param businessKey 业务id
*/
@GetMapping("/getHistoryImage/{businessKey}")
public R<String> getHistoryImage(@NotBlank(message = "业务id不能为空") @PathVariable String businessKey) {
return R.ok("操作成功", actProcessInstanceService.getHistoryImage(businessKey));
}
/**
* 通过业务id获取历史流程图运行中历史等节点
*
* @param businessKey 业务id
*/
@GetMapping("/getHistoryList/{businessKey}")
public R<Map<String, Object>> getHistoryList(@NotBlank(message = "业务id不能为空") @PathVariable String businessKey) {
return R.ok("操作成功", actProcessInstanceService.getHistoryList(businessKey));
}
/**
* 获取审批记录
*
* @param businessKey 业务id
*/
@GetMapping("/getHistoryRecord/{businessKey}")
public R<List<ActHistoryInfoVo>> getHistoryRecord(@NotBlank(message = "业务id不能为空") @PathVariable String businessKey) {
return R.ok(actProcessInstanceService.getHistoryRecord(businessKey));
}
/**
* 作废流程实例,不会删除历史记录(删除运行中的实例)
*
* @param processInvalidBo 参数
*/
@Log(title = "流程实例管理", businessType = BusinessType.DELETE)
@RepeatSubmit()
@PostMapping("/deleteRunInstance")
public R<Void> deleteRunInstance(@Validated(AddGroup.class) @RequestBody ProcessInvalidBo processInvalidBo) {
return toAjax(actProcessInstanceService.deleteRunInstance(processInvalidBo));
}
/**
* 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
*
* @param businessKeys 业务id
*/
@Log(title = "流程实例管理", businessType = BusinessType.DELETE)
@RepeatSubmit()
@DeleteMapping("/deleteRunAndHisInstance/{businessKeys}")
public R<Void> deleteRunAndHisInstance(@NotNull(message = "业务id不能为空") @PathVariable String[] businessKeys) {
return toAjax(actProcessInstanceService.deleteRunAndHisInstance(Arrays.asList(businessKeys)));
}
/**
* 已完成的实例 删除程实例,删除历史记录,删除业务与流程关联信息
*
* @param businessKeys 业务id
*/
@Log(title = "流程实例管理", businessType = BusinessType.DELETE)
@RepeatSubmit()
@DeleteMapping("/deleteFinishAndHisInstance/{businessKeys}")
public R<Void> deleteFinishAndHisInstance(@NotNull(message = "业务id不能为空") @PathVariable String[] businessKeys) {
return toAjax(actProcessInstanceService.deleteFinishAndHisInstance(Arrays.asList(businessKeys)));
}
/**
* 撤销流程申请
*
* @param businessKey 业务id
*/
@Log(title = "流程实例管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/cancelProcessApply/{businessKey}")
public R<Void> cancelProcessApply(@NotBlank(message = "业务id不能为空") @PathVariable String businessKey) {
return toAjax(actProcessInstanceService.cancelProcessApply(businessKey));
}
/**
* 分页查询当前登录人单据
*
* @param bo 参数
*/
@GetMapping("/getPageByCurrent")
public TableDataInfo<ProcessInstanceVo> getPageByCurrent(ProcessInstanceBo bo, PageQuery pageQuery) {
return actProcessInstanceService.getPageByCurrent(bo, pageQuery);
}
/**
* 任务催办(给当前任务办理人发送站内信,邮件,短信等)
*
* @param taskUrgingBo 任务催办
*/
@Log(title = "流程实例管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/taskUrging")
public R<Void> taskUrging(@RequestBody TaskUrgingBo taskUrgingBo) {
return toAjax(actProcessInstanceService.taskUrging(taskUrgingBo));
}
}

View File

@@ -1,295 +0,0 @@
package org.dromara.workflow.controller;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.workflow.domain.WfTaskBackNode;
import org.dromara.workflow.domain.bo.*;
import org.dromara.workflow.domain.vo.TaskVo;
import org.dromara.workflow.domain.vo.VariableVo;
import org.dromara.workflow.service.IActTaskService;
import org.dromara.workflow.service.IWfTaskBackNodeService;
import org.dromara.workflow.utils.QueryUtils;
import org.flowable.engine.TaskService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 任务管理 控制层
*
* @author may
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/task")
public class ActTaskController extends BaseController {
private final IActTaskService actTaskService;
private final TaskService taskService;
private final IWfTaskBackNodeService wfTaskBackNodeService;
/**
* 启动任务
*
* @param startProcessBo 启动流程参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/startWorkFlow")
public R<Map<String, Object>> startWorkFlow(@Validated(AddGroup.class) @RequestBody StartProcessBo startProcessBo) {
Map<String, Object> map = actTaskService.startWorkFlow(startProcessBo);
return R.ok("提交成功", map);
}
/**
* 办理任务
*
* @param completeTaskBo 办理任务参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/completeTask")
public R<Void> completeTask(@Validated(AddGroup.class) @RequestBody CompleteTaskBo completeTaskBo) {
return toAjax(actTaskService.completeTask(completeTaskBo));
}
/**
* 查询当前用户的待办任务
*
* @param taskBo 参数
*/
@GetMapping("/getPageByTaskWait")
public TableDataInfo<TaskVo> getPageByTaskWait(TaskBo taskBo, PageQuery pageQuery) {
return actTaskService.getPageByTaskWait(taskBo, pageQuery);
}
/**
* 查询当前租户所有待办任务
*
* @param taskBo 参数
*/
@GetMapping("/getPageByAllTaskWait")
public TableDataInfo<TaskVo> getPageByAllTaskWait(TaskBo taskBo, PageQuery pageQuery) {
return actTaskService.getPageByAllTaskWait(taskBo, pageQuery);
}
/**
* 查询当前用户的已办任务
*
* @param taskBo 参数
*/
@GetMapping("/getPageByTaskFinish")
public TableDataInfo<TaskVo> getPageByTaskFinish(TaskBo taskBo, PageQuery pageQuery) {
return actTaskService.getPageByTaskFinish(taskBo, pageQuery);
}
/**
* 查询当前用户的抄送
*
* @param taskBo 参数
*/
@GetMapping("/getPageByTaskCopy")
public TableDataInfo<TaskVo> getPageByTaskCopy(TaskBo taskBo, PageQuery pageQuery) {
return actTaskService.getPageByTaskCopy(taskBo, pageQuery);
}
/**
* 查询当前租户所有已办任务
*
* @param taskBo 参数
*/
@GetMapping("/getPageByAllTaskFinish")
public TableDataInfo<TaskVo> getPageByAllTaskFinish(TaskBo taskBo, PageQuery pageQuery) {
return actTaskService.getPageByAllTaskFinish(taskBo, pageQuery);
}
/**
* 签收(拾取)任务
*
* @param taskId 任务id
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/claim/{taskId}")
public R<Void> claimTask(@NotBlank(message = "任务id不能为空") @PathVariable String taskId) {
try {
taskService.claim(taskId, Convert.toStr(LoginHelper.getUserId()));
return R.ok();
} catch (Exception e) {
e.printStackTrace();
return R.fail("签收任务失败:" + e.getMessage());
}
}
/**
* 归还(拾取的)任务
*
* @param taskId 任务id
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/returnTask/{taskId}")
public R<Void> returnTask(@NotBlank(message = "任务id不能为空") @PathVariable String taskId) {
try {
taskService.setAssignee(taskId, null);
return R.ok();
} catch (Exception e) {
e.printStackTrace();
return R.fail("归还任务失败:" + e.getMessage());
}
}
/**
* 委派任务
*
* @param delegateBo 参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/delegateTask")
public R<Void> delegateTask(@Validated({AddGroup.class}) @RequestBody DelegateBo delegateBo) {
return toAjax(actTaskService.delegateTask(delegateBo));
}
/**
* 终止任务
*
* @param terminationBo 参数
*/
@Log(title = "任务管理", businessType = BusinessType.DELETE)
@RepeatSubmit()
@PostMapping("/terminationTask")
public R<Void> terminationTask(@RequestBody TerminationBo terminationBo) {
return toAjax(actTaskService.terminationTask(terminationBo));
}
/**
* 转办任务
*
* @param transmitBo 参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/transferTask")
public R<Void> transferTask(@Validated({AddGroup.class}) @RequestBody TransmitBo transmitBo) {
return toAjax(actTaskService.transferTask(transmitBo));
}
/**
* 会签任务加签
*
* @param addMultiBo 参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/addMultiInstanceExecution")
public R<Void> addMultiInstanceExecution(@Validated({AddGroup.class}) @RequestBody AddMultiBo addMultiBo) {
return toAjax(actTaskService.addMultiInstanceExecution(addMultiBo));
}
/**
* 会签任务减签
*
* @param deleteMultiBo 参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/deleteMultiInstanceExecution")
public R<Void> deleteMultiInstanceExecution(@Validated({AddGroup.class}) @RequestBody DeleteMultiBo deleteMultiBo) {
return toAjax(actTaskService.deleteMultiInstanceExecution(deleteMultiBo));
}
/**
* 驳回审批
*
* @param backProcessBo 参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/backProcess")
public R<String> backProcess(@Validated({AddGroup.class}) @RequestBody BackProcessBo backProcessBo) {
return R.ok("操作成功", actTaskService.backProcess(backProcessBo));
}
/**
* 获取当前任务
*
* @param taskId 任务id
*/
@GetMapping("/getTaskById/{taskId}")
public R<TaskVo> getTaskById(@PathVariable String taskId) {
return R.ok(QueryUtils.getTask(taskId));
}
/**
* 修改任务办理人
*
* @param taskIds 任务id
* @param userId 办理人id
*/
@Log(title = "任务管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/updateAssignee/{taskIds}/{userId}")
public R<Void> updateAssignee(@PathVariable String[] taskIds, @PathVariable String userId) {
return toAjax(actTaskService.updateAssignee(taskIds, userId));
}
/**
* 查询流程变量
*
* @param taskId 任务id
*/
@GetMapping("/getInstanceVariable/{taskId}")
public R<List<VariableVo>> getProcessInstVariable(@PathVariable String taskId) {
return R.ok(actTaskService.getInstanceVariable(taskId));
}
/**
* 获取可驳回得任务节点
*
* @param processInstanceId 流程实例id
*/
@GetMapping("/getTaskNodeList/{processInstanceId}")
public R<List<WfTaskBackNode>> getNodeList(@PathVariable String processInstanceId) {
return R.ok(CollUtil.reverse(wfTaskBackNodeService.getListByInstanceId(processInstanceId)));
}
/**
* 查询工作流任务用户选择加签人员
*
* @param taskId 任务id
*/
@GetMapping("/getTaskUserIdsByAddMultiInstance/{taskId}")
public R<String> getTaskUserIdsByAddMultiInstance(@PathVariable String taskId) {
return R.ok("操作成功", actTaskService.getTaskUserIdsByAddMultiInstance(taskId));
}
/**
* 查询工作流选择减签人员
*
* @param taskId 任务id
*/
@GetMapping("/getListByDeleteMultiInstance/{taskId}")
public R<List<TaskVo>> getListByDeleteMultiInstance(@PathVariable String taskId) {
return R.ok(actTaskService.getListByDeleteMultiInstance(taskId));
}
}

View File

@@ -0,0 +1,178 @@
package org.dromara.workflow.controller;
import com.warm.flow.core.entity.Definition;
import com.warm.flow.core.service.DefService;
import com.warm.flow.orm.entity.FlowDefinition;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.workflow.domain.vo.FlowDefinitionVo;
import org.dromara.workflow.service.IFlwDefinitionService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
/**
* 流程定义管理 控制层
*
* @author may
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/definition")
public class FlwDefinitionController extends BaseController {
private final IFlwDefinitionService iFlwDefinitionService;
private final DefService defService;
/**
* 分页查询
*
* @param flowDefinition 参数
*/
@GetMapping("/list")
public TableDataInfo<FlowDefinitionVo> page(FlowDefinition flowDefinition, PageQuery pageQuery) {
return iFlwDefinitionService.page(flowDefinition, pageQuery);
}
/**
* 获取流程定义详细信息
*
* @param id 流程定义id
*/
@GetMapping(value = "/{id}")
public R<Definition> getInfo(@PathVariable Long id) {
return R.ok(defService.getById(id));
}
/**
* 新增流程定义
*
* @param flowDefinition 参数
*/
@Log(title = "流程定义", businessType = BusinessType.INSERT)
@PostMapping
@Transactional(rollbackFor = Exception.class)
public R<Boolean> add(@RequestBody FlowDefinition flowDefinition) {
return R.ok(defService.checkAndSave(flowDefinition));
}
/**
* 修改流程定义
*
* @param flowDefinition 参数
*/
@Log(title = "流程定义", businessType = BusinessType.UPDATE)
@PutMapping
@Transactional(rollbackFor = Exception.class)
public R<Boolean> edit(@RequestBody FlowDefinition flowDefinition) {
return R.ok(defService.updateById(flowDefinition));
}
/**
* 发布流程定义
*
* @param id 流程定义id
*/
@Log(title = "流程定义", businessType = BusinessType.INSERT)
@GetMapping("/publish/{id}")
@Transactional(rollbackFor = Exception.class)
public R<Boolean> publish(@PathVariable Long id) {
return R.ok(defService.publish(id));
}
/**
* 取消发布流程定义
*
* @param id 流程定义id
*/
@Log(title = "流程定义", businessType = BusinessType.INSERT)
@GetMapping("/unPublish/{id}")
@Transactional(rollbackFor = Exception.class)
public R<Void> unPublish(@PathVariable Long id) {
defService.unPublish(id);
return R.ok();
}
/**
* 删除流程定义
*/
@Log(title = "流程定义", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@Transactional(rollbackFor = Exception.class)
public R<Boolean> remove(@PathVariable List<Long> ids) {
return R.ok(defService.removeDef(ids));
}
/**
* 复制流程定义
*
* @param id 流程定义id
*/
@Log(title = "流程定义", businessType = BusinessType.INSERT)
@GetMapping("/copyDef/{id}")
@Transactional(rollbackFor = Exception.class)
public R<Boolean> copyDef(@PathVariable Long id) {
return R.ok(defService.copyDef(id));
}
/**
* 导入流程定义
*
* @param file 文件
* @throws Exception 异常
*/
@Log(title = "流程定义", businessType = BusinessType.IMPORT)
@PostMapping("/importDefinition")
@Transactional(rollbackFor = Exception.class)
public R<Void> importDefinition(MultipartFile file) throws Exception {
defService.importXml(file.getInputStream());
return R.ok();
}
/**
* 导出流程定义
*
* @param id 流程定义id
* @param response 响应
* @throws IOException 异常
*/
@Log(title = "流程定义", businessType = BusinessType.EXPORT)
@PostMapping("/exportDefinition/{id}")
public void exportDefinition(@PathVariable Long id, HttpServletResponse response) throws IOException {
iFlwDefinitionService.exportDefinition(id, response);
}
/**
* 获取流程定义xml字符串
*
* @param id 流程定义id
*/
@GetMapping("/xmlString/{id}")
public R<String> xmlString(@PathVariable Long id) {
return R.ok(defService.xmlString(id));
}
/**
* 查询流程图
*
* @param instanceId 流程实例id
* @throws IOException 异常
*/
@GetMapping("/flowChart/{instanceId}")
public R<String> flowChart(@PathVariable Long instanceId) throws IOException {
return R.ok(defService.flowChart(instanceId));
}
}

View File

@@ -0,0 +1,79 @@
package org.dromara.workflow.controller;
import com.warm.flow.core.entity.Instance;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.workflow.service.IFlwInstanceService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 流程实例管理 控制层
*
* @author may
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/processInstance")
public class FlwInstanceController extends BaseController {
private final IFlwInstanceService flwInstanceService;
/**
* 分页查询正在运行的流程实例
*
* @param instance 参数
* @param pageQuery 分页
*/
@GetMapping("/getPageByRunning")
public TableDataInfo<Instance> getPageByRunning(Instance instance, PageQuery pageQuery) {
return flwInstanceService.getPageByRunning(instance, pageQuery);
}
/**
* 分页查询已结束的流程实例
*
* @param instance 参数
* @param pageQuery 分页
*/
@GetMapping("/getPageByFinish")
public TableDataInfo<Instance> getPageByFinish(Instance instance, PageQuery pageQuery) {
return flwInstanceService.getPageByFinish(instance, pageQuery);
}
/**
* 按照业务id删除流程实例
*
* @param businessIds 业务id
*/
@DeleteMapping("/deleteByBusinessIds/{businessIds}")
public R<Void> deleteByBusinessIds(@PathVariable List<Long> businessIds) {
return toAjax(flwInstanceService.deleteByBusinessIds(businessIds));
}
/**
* 按照实例id删除流程实例
*
* @param instanceIds 实例id
*/
@DeleteMapping("/deleteByInstanceIds/{instanceIds}")
public R<Void> deleteByInstanceIds(@PathVariable List<Long> instanceIds) {
return toAjax(flwInstanceService.deleteByInstanceIds(instanceIds));
}
/**
* 撤销流程
*
* @param businessId 业务id
*/
@PutMapping("/cancelProcessApply/{businessId}")
public R<Void> cancelProcessApply(@PathVariable String businessId) {
return toAjax(flwInstanceService.cancelProcessApply(businessId));
}
}

View File

@@ -0,0 +1,231 @@
package org.dromara.workflow.controller;
import com.warm.flow.core.dto.FlowParams;
import com.warm.flow.core.dto.ModifyHandler;
import com.warm.flow.core.entity.Instance;
import com.warm.flow.core.entity.Task;
import com.warm.flow.core.enums.CooperateType;
import com.warm.flow.core.service.TaskService;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.workflow.domain.bo.*;
import org.dromara.workflow.domain.vo.FlowHisTaskVo;
import org.dromara.workflow.domain.vo.FlowTaskVo;
import org.dromara.workflow.service.IFlwTaskService;
import org.dromara.workflow.utils.WorkflowUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.Map;
/**
* 任务管理 控制层
*
* @author may
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/task")
public class FlwTaskController extends BaseController {
private final IFlwTaskService flwTaskService;
private final TaskService taskService;
/**
* 启动任务
*
* @param startProcessBo 启动流程参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/startWorkFlow")
public R<Map<String, Object>> startWorkFlow(@Validated(AddGroup.class) @RequestBody StartProcessBo startProcessBo) {
Map<String, Object> map = flwTaskService.startWorkFlow(startProcessBo);
return R.ok("提交成功", map);
}
/**
* 办理任务
*
* @param completeTaskBo 办理任务参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/completeTask")
public R<Void> completeTask(@Validated(AddGroup.class) @RequestBody CompleteTaskBo completeTaskBo) {
return toAjax(flwTaskService.completeTask(completeTaskBo));
}
/**
* 查询当前用户的待办任务
*
* @param flowTaskBo 参数
* @param pageQuery 分页
*/
@GetMapping("/getPageByTaskWait")
public TableDataInfo<FlowTaskVo> getPageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
return flwTaskService.getPageByTaskWait(flowTaskBo, pageQuery);
}
/**
* 查询当前用户的已办任务
*
* @param flowTaskBo 参数
* @param pageQuery 分页
*/
@GetMapping("/getPageByTaskFinish")
public TableDataInfo<FlowHisTaskVo> getPageByTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
return flwTaskService.getPageByTaskFinish(flowTaskBo, pageQuery);
}
/**
* 查询当前用户的抄送
*
* @param flowTaskBo 参数
* @param pageQuery 分页
*/
@GetMapping("/getPageByTaskCopy")
public TableDataInfo<FlowTaskVo> getPageByTaskCopy(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
return flwTaskService.getPageByTaskCopy(flowTaskBo, pageQuery);
}
/**
* 根据taskId查询代表任务
*
* @param taskId 任务id
*/
@GetMapping("/getTaskById/{taskId}")
public R<Task> getTaskById(@PathVariable Long taskId) {
return R.ok(taskService.getById(taskId));
}
/**
* 终止任务
*
* @param bo 参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/terminationTask")
public R<Instance> terminationTask(@RequestBody TerminationBo bo) {
FlowParams flowParams = new FlowParams();
flowParams.handler(String.valueOf(LoginHelper.getUserId()));
flowParams.message(bo.getComment());
flowParams.permissionFlag(WorkflowUtils.permissionList());
return R.ok(taskService.termination(bo.getTaskId(), flowParams));
}
/**
* 委派任务
*
* @param bo 参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/delegateTask")
public R<Void> delegateTask(@Validated({AddGroup.class}) @RequestBody DelegateBo bo) {
return toAjax(taskService.depute(
bo.getTaskId(),
String.valueOf(LoginHelper.getUserId()),
WorkflowUtils.permissionList(),
bo.getUserIds(),
bo.getMessage()));
}
/**
* 转办任务
*
* @param bo 参数
*/
@Log(title = "任务管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/transferTask")
public R<Void> transferTask(@Validated({AddGroup.class}) @RequestBody TransferBo bo) {
return toAjax(taskService.transfer(
bo.getTaskId(),
String.valueOf(LoginHelper.getUserId()),
WorkflowUtils.permissionList(),
bo.getUserIds(),
bo.getMessage()));
}
/**
* 加签
*
* @param bo 参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/addSignature")
public R<Void> addSignature(@Validated({AddGroup.class}) @RequestBody AddSignatureBo bo) {
return toAjax(taskService.addSignature(
bo.getTaskId(),
String.valueOf(LoginHelper.getUserId()),
WorkflowUtils.permissionList(),
bo.getUserIds(),
bo.getMessage()));
}
/**
* 减签
*
* @param bo 参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/reductionSignature")
public R<Void> reductionSignature(@Validated({AddGroup.class}) @RequestBody ReductionSignatureBo bo) {
return toAjax(taskService.reductionSignature(
bo.getTaskId(),
String.valueOf(LoginHelper.getUserId()),
WorkflowUtils.permissionList(),
bo.getUserIds(),
bo.getMessage()));
}
/**
* 修改任务办理人
*
* @param taskId 任务id
* @param userId 办理人id
*/
@Log(title = "任务管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/updateAssignee/{taskId}/{userId}")
public R<Void> updateAssignee(@PathVariable Long taskId, @PathVariable String userId) {
ModifyHandler modifyHandler = new ModifyHandler()
.setTaskId(taskId)
.setAddHandlers(Collections.singletonList(userId))
.setPermissionFlag(WorkflowUtils.permissionList())
.setCooperateType(CooperateType.APPROVAL.getKey())
.setMessage("修改任务办理人")
.setCurUser(String.valueOf(LoginHelper.getUserId()))
.setIgnore(false);
return toAjax(taskService.updateHandler(modifyHandler));
}
/**
* 驳回审批
*
* @param bo 参数
*/
@Log(title = "任务管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/backProcess")
public R<Void> backProcess(@Validated({AddGroup.class}) @RequestBody BackProcessBo bo) {
return toAjax(flwTaskService.backProcess(bo));
}
}

View File

@@ -1,152 +0,0 @@
package org.dromara.workflow.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 流程实例对象 act_hi_procinst
*
* @author may
* @date 2023-07-22
*/
@Data
@TableName("act_hi_procinst")
public class ActHiProcinst implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@TableId(value = "ID_")
private String id;
/**
*
*/
@TableField(value = "REV_")
private Long rev;
/**
*
*/
@TableField(value = "PROC_INST_ID_")
private String procInstId;
/**
*
*/
@TableField(value = "BUSINESS_KEY_")
private String businessKey;
/**
*
*/
@TableField(value = "PROC_DEF_ID_")
private String procDefId;
/**
*
*/
@TableField(value = "START_TIME_")
private Date startTime;
/**
*
*/
@TableField(value = "END_TIME_")
private Date endTime;
/**
*
*/
@TableField(value = "DURATION_")
private Long duration;
/**
*
*/
@TableField(value = "START_USER_ID_")
private String startUserId;
/**
*
*/
@TableField(value = "START_ACT_ID_")
private String startActId;
/**
*
*/
@TableField(value = "END_ACT_ID_")
private String endActId;
/**
*
*/
@TableField(value = "SUPER_PROCESS_INSTANCE_ID_")
private String superProcessInstanceId;
/**
*
*/
@TableField(value = "DELETE_REASON_")
private String deleteReason;
/**
*
*/
@TableField(value = "TENANT_ID_")
private String tenantId;
/**
*
*/
@TableField(value = "NAME_")
private String name;
/**
*
*/
@TableField(value = "CALLBACK_ID_")
private String callbackId;
/**
*
*/
@TableField(value = "CALLBACK_TYPE_")
private String callbackType;
/**
*
*/
@TableField(value = "REFERENCE_ID_")
private String referenceId;
/**
*
*/
@TableField(value = "REFERENCE_TYPE_")
private String referenceType;
/**
*
*/
@TableField(value = "PROPAGATED_STAGE_INST_ID_")
private String propagatedStageInstId;
/**
*
*/
@TableField(value = "BUSINESS_STATUS_")
private String businessStatus;
}

View File

@@ -1,193 +0,0 @@
package org.dromara.workflow.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.io.Serial;
/**
* 流程历史任务对象 act_hi_taskinst
*
* @author may
* @date 2024-03-02
*/
@Data
@TableName("act_hi_taskinst")
public class ActHiTaskinst implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@TableId(value = "ID_")
private String id;
/**
* 版本
*/
@TableField(value = "REV_")
private Long rev;
/**
* 流程定义id
*/
@TableField(value = "PROC_DEF_ID_")
private String procDefId;
/**
*
*/
@TableField(value = "TASK_DEF_ID_")
private String taskDefId;
/**
* 任务节点id
*/
@TableField(value = "TASK_DEF_KEY_")
private String taskDefKey;
/**
* 流程实例id
*/
@TableField(value = "PROC_INST_ID_")
private String procInstId;
/**
* 流程执行id
*/
@TableField(value = "EXECUTION_ID")
private String executionId;
/**
*
*/
@TableField(value = "SCOPE_ID_")
private String scopeId;
/**
*
*/
@TableField(value = "SUB_SCOPE_ID_")
private String subScopeId;
/**
* 先用当前字段标识抄送类型
*/
@TableField(value = "SCOPE_TYPE_")
private String scopeType;
/**
*
*/
@TableField(value = "SCOPE_DEFINITION_ID_")
private String scopeDefinitionId;
/**
*
*/
@TableField(value = "PROPAGATED_STAGE_INST_ID_")
private String propagatedStageInstId;
/**
* 任务名称
*/
@TableField(value = "NAME_")
private String name;
/**
* 父级id
*/
@TableField(value = "PARENT_TASK_ID_")
private String parentTaskId;
/**
* 描述
*/
@TableField(value = "DESCRIPTION_")
private String description;
/**
* 办理人
*/
@TableField(value = "OWNER_")
private String owner;
/**
* 办理人
*/
@TableField(value = "ASSIGNEE_")
private String assignee;
/**
* 开始事件
*/
@TableField(value = "START_TIME_")
private Date startTime;
/**
* 认领时间
*/
@TableField(value = "CLAIM_TIME_")
private Date claimTime;
/**
* 结束时间
*/
@TableField(value = "END_TIME_")
private Date endTime;
/**
* 持续时间
*/
@TableField(value = "DURATION_")
private Long duration;
/**
* 删除原因
*/
@TableField(value = "DELETE_REASON_")
private String deleteReason;
/**
* 优先级
*/
@TableField(value = "PRIORITY_")
private Long priority;
/**
* 到期时间
*/
@TableField(value = "DUE_DATE_")
private Date dueDate;
/**
*
*/
@TableField(value = "FORM_KEY_")
private String formKey;
/**
* 分类
*/
@TableField(value = "CATEGORY_")
private String category;
/**
* 最后修改时间
*/
@TableField(value = "LAST_UPDATED_TIME_")
private Date lastUpdatedTime;
/**
* 租户id
*/
@TableField(value = "TENANT_ID_")
private String tenantId;
}

View File

@@ -45,7 +45,7 @@ public class WfDefinitionConfig extends BaseEntity {
/**
* 流程版本
*/
private Integer version;
private String version;
/**
* 备注

View File

@@ -1,61 +0,0 @@
package org.dromara.workflow.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.tenant.core.TenantEntity;
import java.io.Serial;
/**
* 节点驳回记录 wf_task_back_node
*
* @author may
* @date 2024-03-13
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("wf_task_back_node")
public class WfTaskBackNode extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id")
private Long id;
/**
* 实例id
*/
private String instanceId;
/**
* 节点id
*/
private String nodeId;
/**
* 节点名称
*/
private String nodeName;
/**
* 排序
*/
private Integer orderNo;
/**
* 节点类型
*/
private String taskType;
/**
* 办理人
*/
private String assignee;
}

View File

@@ -0,0 +1,38 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 加签请求对象
*
* @author may
*/
@Data
public class AddSignatureBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 加签人id
*/
@NotNull(message = "加签id不能为空", groups = {AddGroup.class})
private List<String> userIds;
/**
* 任务id
*/
@NotNull(message = "任务id不能为空", groups = {AddGroup.class})
private Long taskId;
/**
* 意见
*/
private String message;
}

View File

@@ -1,12 +1,16 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import java.io.Serial;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
@@ -23,8 +27,8 @@ public class BackProcessBo implements Serializable {
/**
* 任务ID
*/
@NotBlank(message = "任务ID不能为空", groups = AddGroup.class)
private String taskId;
@NotNull(message = "任务ID不能为空", groups = AddGroup.class)
private Long taskId;
/**
* 消息类型
@@ -35,10 +39,23 @@ public class BackProcessBo implements Serializable {
* 驳回的节点id(目前未使用,直接驳回到申请人)
*/
@NotBlank(message = "驳回的节点不能为空", groups = AddGroup.class)
private String targetActivityId;
private String targetNodeCode;
/**
* 办理意见
*/
private String message;
/**
* 流程变量
*/
private Map<String, Object> variables;
public Map<String, Object> getVariables() {
if (variables == null) {
return new HashMap<>(16);
}
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
return variables;
}
}

View File

@@ -1,6 +1,7 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.workflow.domain.vo.WfCopy;
@@ -26,8 +27,8 @@ public class CompleteTaskBo implements Serializable {
/**
* 任务id
*/
@NotBlank(message = "任务id不能为空", groups = {AddGroup.class})
private String taskId;
@NotNull(message = "任务id不能为空", groups = {AddGroup.class})
private Long taskId;
/**
* 附件id

View File

@@ -1,11 +1,12 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 委派任务请求对象
@@ -21,18 +22,17 @@ public class DelegateBo implements Serializable {
/**
* 委派人id
*/
@NotBlank(message = "委派人id不能为空", groups = {AddGroup.class})
private String userId;
/**
* 委派人名称
*/
@NotBlank(message = "委派人名称不能为空", groups = {AddGroup.class})
private String nickName;
@NotNull(message = "委派人id不能为空", groups = {AddGroup.class})
private List<String> userIds;
/**
* 任务id
*/
@NotBlank(message = "任务id不能为空", groups = {AddGroup.class})
private String taskId;
@NotNull(message = "任务id不能为空", groups = {AddGroup.class})
private Long taskId;
/**
* 意见
*/
private String message;
}

View File

@@ -0,0 +1,39 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.Future;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 任务请求对象
*
* @author may
*/
@Data
public class FlowTaskBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 任务名称
*/
private String nodeName;
/**
* 流程定义名称
*/
private String flowName;
/**
* 流程定义编码
*/
private String flowCode;
private Long instanceId;
private List<String> permissionList;
}

View File

@@ -11,7 +11,7 @@ import java.io.Serializable;
* @author may
*/
@Data
public class ProcessInstanceBo implements Serializable {
public class InstanceBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;

View File

@@ -0,0 +1,38 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 减签请求对象
*
* @author may
*/
@Data
public class ReductionSignatureBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 减签人id
*/
@NotNull(message = "减签id不能为空", groups = {AddGroup.class})
private List<String> userIds;
/**
* 任务id
*/
@NotNull(message = "任务id不能为空", groups = {AddGroup.class})
private Long taskId;
/**
* 意见
*/
private String message;
}

View File

@@ -1,6 +1,6 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
@@ -21,8 +21,8 @@ public class TerminationBo implements Serializable {
/**
* 任务id
*/
@NotBlank(message = "任务id为空", groups = AddGroup.class)
private String taskId;
@NotNull(message = "任务id为空", groups = AddGroup.class)
private Long taskId;
/**
* 审批意见

View File

@@ -1,11 +1,12 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 终转办务请求对象
@@ -13,25 +14,25 @@ import java.io.Serializable;
* @author may
*/
@Data
public class TransmitBo implements Serializable {
public class TransferBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 任务id
*/
@NotBlank(message = "任务id为空", groups = AddGroup.class)
private String taskId;
/**
* 转办人id
*/
@NotBlank(message = "转办人不能为空", groups = AddGroup.class)
private String userId;
@NotNull(message = "转办人id不能为空", groups = {AddGroup.class})
private List<String> userIds;
/**
* 审批意见
* 任务id
*/
private String comment;
@NotNull(message = "任务id不能为空", groups = {AddGroup.class})
private Long taskId;
/**
* 意见
*/
private String message;
}

View File

@@ -47,8 +47,8 @@ public class WfDefinitionConfigBo extends BaseEntity {
/**
* 流程版本
*/
@NotNull(message = "流程版本不能为空", groups = {AddGroup.class})
private Integer version;
@NotBlank(message = "流程版本不能为空", groups = {AddGroup.class})
private String version;
/**
* 备注

View File

@@ -3,7 +3,6 @@ package org.dromara.workflow.domain.vo;
import lombok.Data;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.flowable.engine.task.Attachment;
import java.io.Serial;
import java.io.Serializable;
@@ -85,9 +84,4 @@ public class ActHistoryInfoVo implements Serializable {
* 审批信息
*/
private String comment;
/**
* 审批附件
*/
private List<Attachment> attachmentList;
}

View File

@@ -0,0 +1,6 @@
package org.dromara.workflow.domain.vo;
import com.warm.flow.orm.entity.FlowDefinition;
public class FlowDefinitionVo extends FlowDefinition {
}

View File

@@ -0,0 +1,24 @@
package org.dromara.workflow.domain.vo;
import com.warm.flow.orm.entity.FlowHisTask;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 历史任务视图
*
* @author may
*/
@Data
public class FlowHisTaskVo extends FlowHisTask implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 流程定义编码
*/
private String flowCode;
}

View File

@@ -0,0 +1,29 @@
package org.dromara.workflow.domain.vo;
import com.warm.flow.orm.entity.FlowTask;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 任务视图
*
* @author may
*/
@Data
public class FlowTaskVo extends FlowTask implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 流程定义编码
*/
private String flowCode;
/**
* 流程状态
*/
private String flowStatus;
}

View File

@@ -53,7 +53,7 @@ public class WfDefinitionConfigVo implements Serializable {
* 流程版本
*/
@ExcelProperty(value = "流程版本")
private Integer version;
private String version;
/**
* 备注

View File

@@ -1,108 +0,0 @@
package org.dromara.workflow.flowable;
import org.flowable.bpmn.model.AssociationDirection;
import org.flowable.image.impl.DefaultProcessDiagramCanvas;
import java.awt.*;
import java.awt.geom.Line2D;
import java.awt.geom.RoundRectangle2D;
public class CustomDefaultProcessDiagramCanvas extends DefaultProcessDiagramCanvas {
//设置高亮线的颜色 这里我设置成绿色
protected static Color HIGHLIGHT_SEQUENCEFLOW_COLOR = Color.GREEN;
public CustomDefaultProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
}
/**
* 画线颜色设置
*/
public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType,
AssociationDirection associationDirection, boolean highLighted, double scaleFactor) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
g.setPaint(CONNECTION_COLOR);
if (connectionType.equals("association")) {
g.setStroke(ASSOCIATION_STROKE);
} else if (highLighted) {
//设置线的颜色
g.setPaint(HIGHLIGHT_SEQUENCEFLOW_COLOR);
g.setStroke(HIGHLIGHT_FLOW_STROKE);
}
for (int i = 1; i < xPoints.length; i++) {
Integer sourceX = xPoints[i - 1];
Integer sourceY = yPoints[i - 1];
Integer targetX = xPoints[i];
Integer targetY = yPoints[i];
Line2D.Double line = new Line2D.Double(sourceX, sourceY, targetX, targetY);
g.draw(line);
}
if (isDefault) {
Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]);
drawDefaultSequenceFlowIndicator(line, scaleFactor);
}
if (conditional) {
Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]);
drawConditionalSequenceFlowIndicator(line, scaleFactor);
}
if (associationDirection == AssociationDirection.ONE || associationDirection == AssociationDirection.BOTH) {
Line2D.Double line = new Line2D.Double(xPoints[xPoints.length - 2], yPoints[xPoints.length - 2], xPoints[xPoints.length - 1], yPoints[xPoints.length - 1]);
drawArrowHead(line, scaleFactor);
}
if (associationDirection == AssociationDirection.BOTH) {
Line2D.Double line = new Line2D.Double(xPoints[1], yPoints[1], xPoints[0], yPoints[0]);
drawArrowHead(line, scaleFactor);
}
g.setPaint(originalPaint);
g.setStroke(originalStroke);
}
/**
* 高亮节点设置
*/
public void drawHighLight(int x, int y, int width, int height) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
//设置高亮节点的颜色
g.setPaint(HIGHLIGHT_COLOR);
g.setStroke(THICK_TASK_BORDER_STROKE);
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
g.draw(rect);
g.setPaint(originalPaint);
g.setStroke(originalStroke);
}
/**
* @description: 高亮节点红色
* @param: x
* @param: y
* @param: width
* @param: height
* @return: void
* @author: gssong
* @date: 2022/4/12
*/
public void drawHighLightRed(int x, int y, int width, int height) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
//设置高亮节点的颜色
g.setPaint(Color.green);
g.setStroke(THICK_TASK_BORDER_STROKE);
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
g.draw(rect);
g.setPaint(originalPaint);
g.setStroke(originalStroke);
}
}

View File

@@ -1,61 +0,0 @@
package org.dromara.workflow.flowable.cmd;
import cn.hutool.core.collection.CollUtil;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.dromara.workflow.common.constant.FlowConstant.NUMBER_OF_INSTANCES;
/**
* 串行加签
*
* @author may
*/
public class AddSequenceMultiInstanceCmd implements Command<Void> {
/**
* 执行id
*/
private final String executionId;
/**
* 会签人员集合KEY
*/
private final String assigneeList;
/**
* 加签人员
*/
private final List<Long> assignees;
public AddSequenceMultiInstanceCmd(String executionId, String assigneeList, List<Long> assignees) {
this.executionId = executionId;
this.assigneeList = assigneeList;
this.assignees = assignees;
}
@Override
public Void execute(CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
ExecutionEntity entity = executionEntityManager.findById(executionId);
// 多实例任务总数加 assignees.size()
if (entity.getVariable(NUMBER_OF_INSTANCES) instanceof Integer nrOfInstances) {
entity.setVariable(NUMBER_OF_INSTANCES, nrOfInstances + assignees.size());
}
// 设置流程变量
if (entity.getVariable(assigneeList) instanceof List<?> userIds) {
CollUtil.addAll(userIds, assignees);
Map<String, Object> variables = new HashMap<>(16);
variables.put(assigneeList, userIds);
entity.setVariables(variables);
}
return null;
}
}

View File

@@ -1,66 +0,0 @@
package org.dromara.workflow.flowable.cmd;
import cn.hutool.core.collection.CollUtil;
import org.dromara.common.core.domain.dto.OssDTO;
import org.dromara.common.core.service.OssService;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.AttachmentEntity;
import org.flowable.engine.impl.persistence.entity.AttachmentEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import java.util.Date;
import java.util.List;
/**
* 附件上传
*
* @author may
*/
public class AttachmentCmd implements Command<Boolean> {
private final String fileId;
private final String taskId;
private final String processInstanceId;
private final OssService ossService;
public AttachmentCmd(String fileId, String taskId, String processInstanceId, OssService ossService) {
this.fileId = fileId;
this.taskId = taskId;
this.processInstanceId = processInstanceId;
this.ossService = ossService;
}
@Override
public Boolean execute(CommandContext commandContext) {
try {
if (StringUtils.isNotBlank(fileId)) {
List<OssDTO> ossList = ossService.selectByIds(fileId);
if (CollUtil.isNotEmpty(ossList)) {
for (OssDTO oss : ossList) {
AttachmentEntityManager attachmentEntityManager = CommandContextUtil.getAttachmentEntityManager();
AttachmentEntity attachmentEntity = attachmentEntityManager.create();
attachmentEntity.setRevision(1);
attachmentEntity.setUserId(LoginHelper.getUserId().toString());
attachmentEntity.setName(oss.getOriginalName());
attachmentEntity.setDescription(oss.getOriginalName());
attachmentEntity.setType(oss.getFileSuffix());
attachmentEntity.setTaskId(taskId);
attachmentEntity.setProcessInstanceId(processInstanceId);
attachmentEntity.setContentId(oss.getOssId().toString());
attachmentEntity.setTime(new Date());
attachmentEntityManager.insert(attachmentEntity);
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return true;
}
}

View File

@@ -1,36 +0,0 @@
package org.dromara.workflow.flowable.cmd;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import java.io.Serializable;
/**
* 删除执行数据
*
* @author may
*/
public class DeleteExecutionCmd implements Command<Void>, Serializable {
/**
* 执行id
*/
private final String executionId;
public DeleteExecutionCmd(String executionId) {
this.executionId = executionId;
}
@Override
public Void execute(CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
ExecutionEntity entity = executionEntityManager.findById(executionId);
if (entity != null) {
executionEntityManager.deleteExecutionAndRelatedData(entity, "", false, false);
}
return null;
}
}

View File

@@ -1,83 +0,0 @@
package org.dromara.workflow.flowable.cmd;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import org.dromara.common.core.utils.StreamUtils;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.dromara.workflow.common.constant.FlowConstant.LOOP_COUNTER;
import static org.dromara.workflow.common.constant.FlowConstant.NUMBER_OF_INSTANCES;
/**
* 串行减签
*
* @author may
*/
@AllArgsConstructor
public class DeleteSequenceMultiInstanceCmd implements Command<Void> {
/**
* 当前节点审批人员id
*/
private final String currentUserId;
/**
* 执行id
*/
private final String executionId;
/**
* 会签人员集合KEY
*/
private final String assigneeList;
/**
* 减签人员
*/
private final List<Long> assignees;
@Override
@SuppressWarnings("unchecked")
public Void execute(CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
ExecutionEntity entity = executionEntityManager.findById(executionId);
// 设置流程变量
List<Long> userIds = new ArrayList<>();
List<Object> variable = (List<Object>) entity.getVariable(assigneeList);
for (Object o : variable) {
userIds.add(Long.valueOf(o.toString()));
}
List<Long> userIdList = new ArrayList<>();
userIds.forEach(e -> {
Long userId = StreamUtils.findFirst(assignees, id -> ObjectUtil.equals(id, e));
if (userId == null) {
userIdList.add(e);
}
});
// 当前任务执行位置
int loopCounterIndex = -1;
for (int i = 0; i < userIdList.size(); i++) {
Long userId = userIdList.get(i);
if (currentUserId.equals(userId.toString())) {
loopCounterIndex = i;
}
}
Map<String, Object> variables = new HashMap<>(16);
variables.put(NUMBER_OF_INSTANCES, userIdList.size());
variables.put(assigneeList, userIdList);
variables.put(LOOP_COUNTER, loopCounterIndex);
entity.setVariables(variables);
return null;
}
}

View File

@@ -1,39 +0,0 @@
package org.dromara.workflow.flowable.cmd;
import org.dromara.common.core.utils.StreamUtils;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import java.io.Serializable;
import java.util.List;
/**
* 获取并行网关执行后保留的执行实例数据
*
* @author may
*/
public class ExecutionChildByExecutionIdCmd implements Command<List<ExecutionEntity>>, Serializable {
/**
* 当前任务执行实例id
*/
private final String executionId;
public ExecutionChildByExecutionIdCmd(String executionId) {
this.executionId = executionId;
}
@Override
public List<ExecutionEntity> execute(CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
// 获取当前执行数据
ExecutionEntity executionEntity = executionEntityManager.findById(executionId);
// 通过当前执行数据的父执行,查询所有子执行数据
List<ExecutionEntity> allChildrenExecution =
executionEntityManager.collectChildren(executionEntity.getParent());
return StreamUtils.filter(allChildrenExecution, e -> !e.isActive());
}
}

View File

@@ -1,37 +0,0 @@
package org.dromara.workflow.flowable.cmd;
import org.dromara.common.core.exception.ServiceException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntity;
import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
/**
* 修改流程状态
*
* @author may
*/
public class UpdateBusinessStatusCmd implements Command<Boolean> {
private final String processInstanceId;
private final String status;
public UpdateBusinessStatusCmd(String processInstanceId, String status) {
this.processInstanceId = processInstanceId;
this.status = status;
}
@Override
public Boolean execute(CommandContext commandContext) {
try {
HistoricProcessInstanceEntityManager manager = CommandContextUtil.getHistoricProcessInstanceEntityManager();
HistoricProcessInstanceEntity processInstance = manager.findById(processInstanceId);
processInstance.setBusinessStatus(status);
manager.update(processInstance);
return true;
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
}
}

View File

@@ -1,51 +0,0 @@
package org.dromara.workflow.flowable.cmd;
import org.dromara.common.core.exception.ServiceException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.service.HistoricTaskService;
import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity;
import java.util.Date;
import java.util.List;
/**
* 修改流程历史
*
* @author may
*/
public class UpdateHiTaskInstCmd implements Command<Boolean> {
private final List<String> taskIds;
private final String processDefinitionId;
private final String processInstanceId;
public UpdateHiTaskInstCmd(List<String> taskIds, String processDefinitionId, String processInstanceId) {
this.taskIds = taskIds;
this.processDefinitionId = processDefinitionId;
this.processInstanceId = processInstanceId;
}
@Override
public Boolean execute(CommandContext commandContext) {
try {
HistoricTaskService historicTaskService = CommandContextUtil.getHistoricTaskService();
for (String taskId : taskIds) {
HistoricTaskInstanceEntity historicTask = historicTaskService.getHistoricTask(taskId);
if (historicTask != null) {
historicTask.setProcessDefinitionId(processDefinitionId);
historicTask.setProcessInstanceId(processInstanceId);
historicTask.setCreateTime(new Date());
CommandContextUtil.getHistoricTaskService().updateHistoricTask(historicTask, true);
}
}
return true;
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
}
}

View File

@@ -1,32 +0,0 @@
package org.dromara.workflow.flowable.config;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import org.dromara.workflow.flowable.handler.TaskTimeoutJobHandler;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import java.util.Collections;
/**
* flowable配置
*
* @author may
*/
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
@Autowired
private GlobalFlowableListener globalFlowableListener;
@Autowired
private IdentifierGenerator identifierGenerator;
@Override
public void configure(SpringProcessEngineConfiguration processEngineConfiguration) {
processEngineConfiguration.setIdGenerator(() -> identifierGenerator.nextId(null).toString());
processEngineConfiguration.setEventListeners(Collections.singletonList(globalFlowableListener));
processEngineConfiguration.addCustomJobHandler(new TaskTimeoutJobHandler());
}
}

View File

@@ -1,139 +0,0 @@
package org.dromara.workflow.flowable.config;
import cn.hutool.core.collection.CollUtil;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.flowable.handler.TaskTimeoutJobHandler;
import org.dromara.workflow.utils.QueryUtils;
import org.flowable.bpmn.model.BoundaryEvent;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.common.engine.api.delegate.event.*;
import org.flowable.common.engine.impl.cfg.TransactionState;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.task.Comment;
import org.flowable.job.service.TimerJobService;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
/**
* 引擎调度监听
*
* @author may
*/
@Component
public class GlobalFlowableListener implements FlowableEventListener {
@Autowired
@Lazy
private TaskService taskService;
@Autowired
@Lazy
private RuntimeService runtimeService;
@Autowired
@Lazy
private RepositoryService repositoryService;
@Value("${flowable.async-executor-activate}")
private boolean asyncExecutorActivate;
@Override
public void onEvent(FlowableEvent flowableEvent) {
if (flowableEvent instanceof FlowableEngineEvent flowableEngineEvent) {
FlowableEngineEventType engineEventType = (FlowableEngineEventType) flowableEvent.getType();
switch (engineEventType) {
case JOB_EXECUTION_SUCCESS -> jobExecutionSuccess((FlowableEngineEntityEvent) flowableEngineEvent);
case TASK_DUEDATE_CHANGED, TASK_CREATED -> {
FlowableEntityEvent flowableEntityEvent = (FlowableEntityEvent) flowableEngineEvent;
Object entityObject = flowableEntityEvent.getEntity();
TaskEntity task = (TaskEntity) entityObject;
if (asyncExecutorActivate && task.getDueDate() != null && task.getDueDate().after(new Date())) {
//删除之前已经存在的定时任务
TimerJobService timerJobService = CommandContextUtil.getTimerJobService();
List<TimerJobEntity> timerJobEntityList = timerJobService.findTimerJobsByProcessInstanceId(task.getProcessInstanceId());
if (!CollUtil.isEmpty(timerJobEntityList)) {
for (TimerJobEntity timerJobEntity : timerJobEntityList) {
String taskId = timerJobEntity.getJobHandlerConfiguration();
if (task.getId().equals(taskId)) {
timerJobService.deleteTimerJob(timerJobEntity);
}
}
}
//创建job对象
TimerJobEntity timer = timerJobService.createTimerJob();
timer.setTenantId(TenantHelper.getTenantId());
//设置job类型
timer.setJobType(JobEntity.JOB_TYPE_TIMER);
timer.setJobHandlerType(TaskTimeoutJobHandler.TYPE);
timer.setDuedate(task.getDueDate());
timer.setProcessInstanceId(task.getProcessInstanceId());
//设置任务id
timer.setJobHandlerConfiguration(task.getId());
//保存并触发事件
timerJobService.scheduleTimerJob(timer);
}
}
}
}
}
@Override
public boolean isFailOnException() {
return true;
}
@Override
public boolean isFireOnTransactionLifecycleEvent() {
return false;
}
@Override
public String getOnTransaction() {
return TransactionState.COMMITTED.name();
}
/**
* 处理边界定时事件自动审批记录
*
* @param event 事件
*/
protected void jobExecutionSuccess(FlowableEngineEntityEvent event) {
if (event != null && StringUtils.isNotBlank(event.getExecutionId())) {
Execution execution = runtimeService.createExecutionQuery().executionId(event.getExecutionId()).singleResult();
if (execution != null) {
BpmnModel bpmnModel = repositoryService.getBpmnModel(event.getProcessDefinitionId());
FlowElement flowElement = bpmnModel.getFlowElement(execution.getActivityId());
if (flowElement instanceof BoundaryEvent) {
String attachedToRefId = ((BoundaryEvent) flowElement).getAttachedToRefId();
List<Execution> list = runtimeService.createExecutionQuery().activityId(attachedToRefId).list();
for (Execution ex : list) {
Task task = QueryUtils.taskQuery().executionId(ex.getId()).singleResult();
if (task != null) {
List<Comment> taskComments = taskService.getTaskComments(task.getId());
if (CollUtil.isEmpty(taskComments)) {
taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.PASS.getStatus(), "超时自动审批!");
}
}
}
}
}
}
}
}

View File

@@ -1,50 +0,0 @@
package org.dromara.workflow.flowable.handler;
import org.dromara.common.core.domain.event.ProcessEvent;
import org.dromara.common.core.domain.event.ProcessTaskEvent;
import org.dromara.common.core.utils.SpringUtils;
import org.springframework.stereotype.Component;
/**
* 流程监听服务
*
* @author may
* @date 2024-06-02
*/
@Component
public class FlowProcessEventHandler {
/**
* 总体流程监听(例如: 提交 退回 撤销 终止 作废等)
*
* @param key 流程key
* @param businessKey 业务id
* @param status 状态
* @param submit 当为true时为申请人节点办理
*/
public void processHandler(String key, String businessKey, String status, boolean submit) {
ProcessEvent processEvent = new ProcessEvent();
processEvent.setKey(key);
processEvent.setBusinessKey(businessKey);
processEvent.setStatus(status);
processEvent.setSubmit(submit);
SpringUtils.context().publishEvent(processEvent);
}
/**
* 执行办理任务监听
*
* @param key 流程key
* @param taskDefinitionKey 审批节点key
* @param taskId 任务id
* @param businessKey 业务id
*/
public void processTaskHandler(String key, String taskDefinitionKey, String taskId, String businessKey) {
ProcessTaskEvent processTaskEvent = new ProcessTaskEvent();
processTaskEvent.setKey(key);
processTaskEvent.setTaskDefinitionKey(taskDefinitionKey);
processTaskEvent.setTaskId(taskId);
processTaskEvent.setBusinessKey(businessKey);
SpringUtils.context().publishEvent(processTaskEvent);
}
}

View File

@@ -1,37 +0,0 @@
package org.dromara.workflow.flowable.handler;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.jobexecutor.TimerEventHandler;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.job.service.JobHandler;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.task.api.Task;
import org.flowable.variable.api.delegate.VariableScope;
/**
* 办理超时(过期)任务
*
* @author may
*/
public class TaskTimeoutJobHandler extends TimerEventHandler implements JobHandler {
public static final String TYPE = "taskTimeout";
@Override
public String getType() {
return TYPE;
}
@Override
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
TaskService taskService = CommandContextUtil.getProcessEngineConfiguration(commandContext)
.getTaskService();
Task task = taskService.createTaskQuery().taskId(configuration).singleResult();
if (task != null) {
taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.TIMEOUT.getStatus(), "超时自动审批!");
taskService.complete(configuration);
}
}
}

View File

@@ -1,16 +0,0 @@
package org.dromara.workflow.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.workflow.domain.ActHiProcinst;
/**
* 流程实例Mapper接口
*
* @author may
* @date 2023-07-22
*/
@InterceptorIgnore(tenantLine = "true")
public interface ActHiProcinstMapper extends BaseMapperPlus<ActHiProcinst, ActHiProcinst> {
}

View File

@@ -1,16 +0,0 @@
package org.dromara.workflow.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import org.dromara.workflow.domain.ActHiTaskinst;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 流程历史任务Mapper接口
*
* @author may
* @date 2024-03-02
*/
@InterceptorIgnore(tenantLine = "true")
public interface ActHiTaskinstMapper extends BaseMapperPlus<ActHiTaskinst, ActHiTaskinst> {
}

View File

@@ -1,47 +0,0 @@
package org.dromara.workflow.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.workflow.domain.vo.TaskVo;
/**
* 任务信息Mapper接口
*
* @author may
* @date 2024-03-02
*/
@InterceptorIgnore(tenantLine = "true")
public interface ActTaskMapper extends BaseMapperPlus<TaskVo, TaskVo> {
/**
* 获取待办信息
*
* @param page 分页
* @param queryWrapper 条件
* @return 结果
*/
Page<TaskVo> getTaskWaitByPage(@Param("page") Page<TaskVo> page, @Param(Constants.WRAPPER) Wrapper<TaskVo> queryWrapper);
/**
* 获取已办
*
* @param page 分页
* @param queryWrapper 条件
* @return 结果
*/
Page<TaskVo> getTaskFinishByPage(@Param("page") Page<TaskVo> page, @Param(Constants.WRAPPER) Wrapper<TaskVo> queryWrapper);
/**
* 查询当前用户的抄送
*
* @param page 分页
* @param queryWrapper 条件
* @return 结果
*/
Page<TaskVo> getTaskCopyByPage(@Param("page") Page<TaskVo> page, @Param(Constants.WRAPPER) QueryWrapper<TaskVo> queryWrapper);
}

View File

@@ -0,0 +1,51 @@
package org.dromara.workflow.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.workflow.domain.bo.FlowTaskBo;
import org.dromara.workflow.domain.vo.FlowHisTaskVo;
import org.dromara.workflow.domain.vo.FlowTaskVo;
/**
* 任务信息Mapper接口
*
* @author may
* @date 2024-03-02
*/
public interface FlwTaskMapper {
/**
* 获取待办信息
*
* @param page 分页
* @param queryWrapper 条件
* @return 结果
*/
Page<FlowTaskVo> getTaskWaitByPage(@Param("page") Page<FlowTaskVo> page,
@Param(Constants.WRAPPER) Wrapper<FlowTaskBo> queryWrapper);
/**
* 获取已办
*
* @param page 分页
* @param queryWrapper 条件
* @param flowTaskBo 条件
* @return 结果
*/
Page<FlowHisTaskVo> getTaskFinishByPage(@Param("page") Page<FlowTaskVo> page,
@Param(Constants.WRAPPER) Wrapper<FlowTaskBo> queryWrapper,
@Param("flowTaskBo") FlowTaskBo flowTaskBo);
/**
* 查询当前用户的抄送
*
* @param page 分页
* @param queryWrapper 条件
* @return 结果
*/
Page<FlowTaskVo> getTaskCopyByPage(@Param("page") Page<FlowTaskVo> page,
@Param(Constants.WRAPPER) QueryWrapper<FlowTaskBo> queryWrapper);
}

View File

@@ -1,13 +0,0 @@
package org.dromara.workflow.mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.workflow.domain.WfTaskBackNode;
/**
* 节点驳回记录Mapper接口
*
* @author may
* @date 2024-03-13
*/
public interface WfTaskBackNodeMapper extends BaseMapperPlus<WfTaskBackNode, WfTaskBackNode> {
}

View File

@@ -1,31 +0,0 @@
package org.dromara.workflow.service;
import org.dromara.workflow.domain.ActHiProcinst;
import java.util.List;
/**
* 流程实例Service接口
*
* @author may
* @date 2023-07-22
*/
public interface IActHiProcinstService {
/**
* 按照业务id查询
*
* @param businessKeys 业务id
* @return 结果
*/
List<ActHiProcinst> selectByBusinessKeyIn(List<String> businessKeys);
/**
* 按照业务id查询
*
* @param businessKey 业务id
* @return 结果
*/
ActHiProcinst selectByBusinessKey(String businessKey);
}

View File

@@ -1,11 +0,0 @@
package org.dromara.workflow.service;
/**
* 流程历史任务Service接口
*
* @author may
* @date 2024-03-02
*/
public interface IActHiTaskinstService {
}

View File

@@ -1,83 +0,0 @@
package org.dromara.workflow.service;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.workflow.domain.bo.ModelBo;
import org.dromara.workflow.domain.vo.ModelVo;
import org.flowable.engine.repository.Model;
import java.util.List;
/**
* 模型管理 服务层
*
* @author may
*/
public interface IActModelService {
/**
* 分页查询模型
*
* @param modelBo 模型参数
* @param pageQuery 参数
* @return 返回分页列表
*/
TableDataInfo<Model> page(ModelBo modelBo, PageQuery pageQuery);
/**
* 新增模型
*
* @param modelBo 模型请求对象
* @return 结果
*/
boolean saveNewModel(ModelBo modelBo);
/**
* 查询模型
*
* @param modelId 模型id
* @return 模型数据
*/
ModelVo getInfo(String modelId);
/**
* 修改模型信息
*
* @param modelBo 模型数据
* @return 结果
*/
boolean update(ModelBo modelBo);
/**
* 编辑模型XML
*
* @param modelBo 模型数据
* @return 结果
*/
boolean editModelXml(ModelBo modelBo);
/**
* 模型部署
*
* @param id 模型id
* @return 结果
*/
boolean modelDeploy(String id);
/**
* 导出模型zip压缩包
*
* @param modelIds 模型id
* @param response 响应
*/
void exportZip(List<String> modelIds, HttpServletResponse response);
/**
* 复制模型
*
* @param modelBo 模型数据
* @return 结果
*/
boolean copyModel(ModelBo modelBo);
}

View File

@@ -1,91 +0,0 @@
package org.dromara.workflow.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.workflow.domain.bo.ProcessDefinitionBo;
import org.dromara.workflow.domain.vo.ProcessDefinitionVo;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* 流程定义 服务层
*
* @author may
*/
public interface IActProcessDefinitionService {
/**
* 分页查询
*
* @param processDefinitionBo 参数
* @param pageQuery 分页
* @return 返回分页列表
*/
TableDataInfo<ProcessDefinitionVo> page(ProcessDefinitionBo processDefinitionBo, PageQuery pageQuery);
/**
* 查询历史流程定义列表
*
* @param key 流程定义key
* @return 结果
*/
List<ProcessDefinitionVo> getListByKey(String key);
/**
* 查看流程定义图片
*
* @param processDefinitionId 流程定义id
* @return 结果
*/
String definitionImage(String processDefinitionId);
/**
* 查看流程定义xml文件
*
* @param processDefinitionId 流程定义id
* @return 结果
*/
String definitionXml(String processDefinitionId);
/**
* 删除流程定义
*
* @param deploymentIds 部署id
* @param processDefinitionIds 流程定义id
* @return 结果
*/
boolean deleteDeployment(List<String> deploymentIds, List<String> processDefinitionIds);
/**
* 激活或者挂起流程定义
*
* @param processDefinitionId 流程定义id
* @return 结果
*/
boolean updateDefinitionState(String processDefinitionId);
/**
* 迁移流程定义
*
* @param currentProcessDefinitionId 当前流程定义id
* @param fromProcessDefinitionId 需要迁移到的流程定义id
* @return 结果
*/
boolean migrationDefinition(String currentProcessDefinitionId, String fromProcessDefinitionId);
/**
* 流程定义转换为模型
*
* @param processDefinitionId 流程定义id
* @return 结果
*/
boolean convertToModel(String processDefinitionId);
/**
* 通过zip或xml部署流程定义
*
* @param file 文件
* @param categoryCode 分类
*/
void deployByFile(MultipartFile file, String categoryCode);
}

View File

@@ -1,110 +0,0 @@
package org.dromara.workflow.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.workflow.domain.bo.ProcessInstanceBo;
import org.dromara.workflow.domain.bo.ProcessInvalidBo;
import org.dromara.workflow.domain.bo.TaskUrgingBo;
import org.dromara.workflow.domain.vo.ActHistoryInfoVo;
import org.dromara.workflow.domain.vo.ProcessInstanceVo;
import java.util.List;
import java.util.Map;
/**
* 流程实例 服务层
*
* @author may
*/
public interface IActProcessInstanceService {
/**
* 通过流程实例id获取历史流程图
*
* @param businessKey 流程实例id
* @return 结果
*/
String getHistoryImage(String businessKey);
/**
* 通过业务id获取历史流程图运行中历史等节点
*
* @param businessKey 业务id
* @return 结果
*/
Map<String, Object> getHistoryList(String businessKey);
/**
* 分页查询正在运行的流程实例
*
* @param processInstanceBo 参数
* @param pageQuery 分页
* @return 结果
*/
TableDataInfo<ProcessInstanceVo> getPageByRunning(ProcessInstanceBo processInstanceBo, PageQuery pageQuery);
/**
* 分页查询已结束的流程实例
*
* @param processInstanceBo 参数
* @param pageQuery 分页
* @return 结果
*/
TableDataInfo<ProcessInstanceVo> getPageByFinish(ProcessInstanceBo processInstanceBo, PageQuery pageQuery);
/**
* 获取审批记录
*
* @param businessKey 业务id
* @return 结果
*/
List<ActHistoryInfoVo> getHistoryRecord(String businessKey);
/**
* 作废流程实例,不会删除历史记录(删除运行中的实例)
*
* @param processInvalidBo 参数
* @return 结果
*/
boolean deleteRunInstance(ProcessInvalidBo processInvalidBo);
/**
* 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
*
* @param businessKeys 业务id
* @return 结果
*/
boolean deleteRunAndHisInstance(List<String> businessKeys);
/**
* 已完成的实例 删除程实例,删除历史记录,删除业务与流程关联信息
*
* @param businessKeys 业务id
* @return 结果
*/
boolean deleteFinishAndHisInstance(List<String> businessKeys);
/**
* 撤销流程申请
*
* @param businessKey 业务id
* @return 结果
*/
boolean cancelProcessApply(String businessKey);
/**
* 分页查询当前登录人单据
*
* @param processInstanceBo 参数
* @param pageQuery 分页
* @return 结果
*/
TableDataInfo<ProcessInstanceVo> getPageByCurrent(ProcessInstanceBo processInstanceBo, PageQuery pageQuery);
/**
* 任务催办(给当前任务办理人发送站内信,邮件,短信等)
*
* @param taskUrgingBo 任务催办
* @return 结果
*/
boolean taskUrging(TaskUrgingBo taskUrgingBo);
}

View File

@@ -1,161 +0,0 @@
package org.dromara.workflow.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.workflow.domain.bo.*;
import org.dromara.workflow.domain.vo.TaskVo;
import org.dromara.workflow.domain.vo.VariableVo;
import java.util.List;
import java.util.Map;
/**
* 任务 服务层
*
* @author may
*/
public interface IActTaskService {
/**
* 启动任务
*
* @param startProcessBo 启动流程参数
* @return 结果
*/
Map<String, Object> startWorkFlow(StartProcessBo startProcessBo);
/**
* 办理任务
*
* @param completeTaskBo 办理任务参数
* @return 结果
*/
boolean completeTask(CompleteTaskBo completeTaskBo);
/**
* 查询当前用户的待办任务
*
* @param taskBo 参数
* @param pageQuery 分页
* @return 结果
*/
TableDataInfo<TaskVo> getPageByTaskWait(TaskBo taskBo, PageQuery pageQuery);
/**
* 查询当前租户所有待办任务
*
* @param taskBo 参数
* @param pageQuery 分页
* @return 结果
*/
TableDataInfo<TaskVo> getPageByAllTaskWait(TaskBo taskBo, PageQuery pageQuery);
/**
* 查询当前用户的已办任务
*
* @param taskBo 参数
* @param pageQuery 参数
* @return 结果
*/
TableDataInfo<TaskVo> getPageByTaskFinish(TaskBo taskBo, PageQuery pageQuery);
/**
* 查询当前用户的抄送
*
* @param taskBo 参数
* @param pageQuery 参数
* @return 结果
*/
TableDataInfo<TaskVo> getPageByTaskCopy(TaskBo taskBo, PageQuery pageQuery);
/**
* 查询当前租户所有已办任务
*
* @param taskBo 参数
* @param pageQuery 参数
* @return 结果
*/
TableDataInfo<TaskVo> getPageByAllTaskFinish(TaskBo taskBo, PageQuery pageQuery);
/**
* 委派任务
*
* @param delegateBo 参数
* @return 结果
*/
boolean delegateTask(DelegateBo delegateBo);
/**
* 终止任务
*
* @param terminationBo 参数
* @return 结果
*/
boolean terminationTask(TerminationBo terminationBo);
/**
* 转办任务
*
* @param transmitBo 参数
* @return 结果
*/
boolean transferTask(TransmitBo transmitBo);
/**
* 会签任务加签
*
* @param addMultiBo 参数
* @return 结果
*/
boolean addMultiInstanceExecution(AddMultiBo addMultiBo);
/**
* 会签任务减签
*
* @param deleteMultiBo 参数
* @return 结果
*/
boolean deleteMultiInstanceExecution(DeleteMultiBo deleteMultiBo);
/**
* 驳回审批
*
* @param backProcessBo 参数
* @return 流程实例id
*/
String backProcess(BackProcessBo backProcessBo);
/**
* 修改任务办理人
*
* @param taskIds 任务id
* @param userId 办理人id
* @return 结果
*/
boolean updateAssignee(String[] taskIds, String userId);
/**
* 查询流程变量
*
* @param taskId 任务id
* @return 结果
*/
List<VariableVo> getInstanceVariable(String taskId);
/**
* 查询工作流任务用户选择加签人员
*
* @param taskId 任务id
* @return 结果
*/
String getTaskUserIdsByAddMultiInstance(String taskId);
/**
* 查询工作流选择减签人员
*
* @param taskId 任务id
* @return 结果
*/
List<TaskVo> getListByDeleteMultiInstance(String taskId);
}

View File

@@ -0,0 +1,35 @@
package org.dromara.workflow.service;
import com.warm.flow.core.entity.Definition;
import com.warm.flow.orm.entity.FlowDefinition;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.workflow.domain.vo.FlowDefinitionVo;
import java.io.IOException;
/**
* 流程定义 服务层
*
* @author may
*/
public interface IFlwDefinitionService {
/**
* 分页查询
*
* @param flowDefinition 参数
* @param pageQuery 分页
* @return 返回分页列表
*/
TableDataInfo<FlowDefinitionVo> page(FlowDefinition flowDefinition, PageQuery pageQuery);
/**
* 导出流程定义
*
* @param id 流程定义id
* @param response 响应
* @throws IOException 异常
*/
void exportDefinition(Long id, HttpServletResponse response) throws IOException;
}

View File

@@ -0,0 +1,66 @@
package org.dromara.workflow.service;
import com.warm.flow.core.entity.Instance;
import com.warm.flow.orm.entity.FlowInstance;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.List;
/**
* 流程实例 服务层
*
* @author may
*/
public interface IFlwInstanceService {
/**
* 分页查询正在运行的流程实例
*
* @param instance 参数
* @param pageQuery 分页
* @return 结果
*/
TableDataInfo<Instance> getPageByRunning(Instance instance, PageQuery pageQuery);
/**
* 分页查询已结束的流程实例
*
* @param instance 参数
* @param pageQuery 分页
* @return 结果
*/
TableDataInfo<Instance> getPageByFinish(Instance instance, PageQuery pageQuery);
/**
* 按照业务id查询流程实例
*
* @param businessId 业务id
* @return 结果
*/
FlowInstance instanceByBusinessId(String businessId);
/**
* 按照业务id删除流程实例
*
* @param businessIds 业务id
* @return 结果
*/
boolean deleteByBusinessIds(List<Long> businessIds);
/**
* 按照实例id删除流程实例
*
* @param instanceIds 实例id
* @return 结果
*/
boolean deleteByInstanceIds(List<Long> instanceIds);
/**
* 撤销流程
*
* @param businessId 业务id
* @return 结果
*/
boolean cancelProcessApply(String businessId);
}

View File

@@ -0,0 +1,68 @@
package org.dromara.workflow.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.workflow.domain.bo.*;
import org.dromara.workflow.domain.vo.FlowHisTaskVo;
import org.dromara.workflow.domain.vo.FlowTaskVo;
import java.util.Map;
/**
* 任务 服务层
*
* @author may
*/
public interface IFlwTaskService {
/**
* 启动任务
*
* @param startProcessBo 启动流程参数
* @return 结果
*/
Map<String, Object> startWorkFlow(StartProcessBo startProcessBo);
/**
* 办理任务
*
* @param completeTaskBo 办理任务参数
* @return 结果
*/
boolean completeTask(CompleteTaskBo completeTaskBo);
/**
* 查询当前用户的待办任务
*
* @param flowTaskBo 参数
* @param pageQuery 分页
* @return 结果
*/
TableDataInfo<FlowTaskVo> getPageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery);
/**
* 查询当前租户所有待办任务
*
* @param flowTaskBo 参数
* @param pageQuery 分页
* @return 结果
*/
TableDataInfo<FlowHisTaskVo> getPageByTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery);
/**
* 查询当前用户的抄送
*
* @param flowTaskBo 参数
* @param pageQuery 分页
* @return 结果
*/
TableDataInfo<FlowTaskVo> getPageByTaskCopy(FlowTaskBo flowTaskBo, PageQuery pageQuery);
/**
* 驳回审批
*
* @param bo 参数
* @return 结果
*/
boolean backProcess(BackProcessBo bo);
}

View File

@@ -1,65 +0,0 @@
package org.dromara.workflow.service;
import org.dromara.workflow.domain.WfTaskBackNode;
import org.flowable.task.api.Task;
import java.util.List;
/**
* 节点驳回记录Service接口
*
* @author may
* @date 2024-03-13
*/
public interface IWfTaskBackNodeService {
/**
* 记录审批节点
*
* @param task 任务
*/
void recordExecuteNode(Task task);
/**
* 按流程实例id查询
*
* @param processInstanceId 流程实例id
* @return 结果
*/
List<WfTaskBackNode> getListByInstanceId(String processInstanceId);
/**
* 按照流程实例id节点id查询
*
* @param processInstanceId 流程实例id
* @param nodeId 节点id
* @return 结果
*/
WfTaskBackNode getListByInstanceIdAndNodeId(String processInstanceId, String nodeId);
/**
* 删除驳回后的节点
*
* @param processInstanceId 流程实例id
* @param targetActivityId 节点id
* @return 结果
*/
boolean deleteBackTaskNode(String processInstanceId, String targetActivityId);
/**
* 按流程实例id删除
*
* @param processInstanceId 流程实例id
* @return 结果
*/
boolean deleteByInstanceId(String processInstanceId);
/**
* 按流程实例id删除
*
* @param processInstanceIds 流程实例id
* @return 结果
*/
boolean deleteByInstanceIds(List<String> processInstanceIds);
}

View File

@@ -1,51 +0,0 @@
package org.dromara.workflow.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.workflow.domain.ActHiProcinst;
import org.dromara.workflow.mapper.ActHiProcinstMapper;
import org.dromara.workflow.service.IActHiProcinstService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 流程实例Service业务层处理
*
* @author may
* @date 2023-07-22
*/
@RequiredArgsConstructor
@Service
public class ActHiProcinstServiceImpl implements IActHiProcinstService {
private final ActHiProcinstMapper baseMapper;
/**
* 按照业务id查询
*
* @param businessKeys 业务id
*/
@Override
public List<ActHiProcinst> selectByBusinessKeyIn(List<String> businessKeys) {
return baseMapper.selectList(new LambdaQueryWrapper<ActHiProcinst>()
.in(ActHiProcinst::getBusinessKey, businessKeys)
.eq(TenantHelper.isEnable(), ActHiProcinst::getTenantId, TenantHelper.getTenantId()));
}
/**
* 按照业务id查询
*
* @param businessKey 业务id
*/
@Override
public ActHiProcinst selectByBusinessKey(String businessKey) {
return baseMapper.selectOne(new LambdaQueryWrapper<ActHiProcinst>()
.eq(ActHiProcinst::getBusinessKey, businessKey)
.eq(TenantHelper.isEnable(), ActHiProcinst::getTenantId, TenantHelper.getTenantId()));
}
}

View File

@@ -1,18 +0,0 @@
package org.dromara.workflow.service.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.dromara.workflow.service.IActHiTaskinstService;
/**
* 流程历史任务Service业务层处理
*
* @author may
* @date 2024-03-02
*/
@RequiredArgsConstructor
@Service
public class ActHiTaskinstServiceImpl implements IActHiTaskinstService {
}

View File

@@ -1,429 +0,0 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.util.StringUtils;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.domain.WfNodeConfig;
import org.dromara.workflow.domain.bo.ModelBo;
import org.dromara.workflow.domain.bo.WfDefinitionConfigBo;
import org.dromara.workflow.domain.vo.ModelVo;
import org.dromara.workflow.domain.vo.WfDefinitionConfigVo;
import org.dromara.workflow.service.IActModelService;
import org.dromara.workflow.service.IWfDefinitionConfigService;
import org.dromara.workflow.service.IWfNodeConfigService;
import org.dromara.workflow.utils.ModelUtils;
import org.dromara.workflow.utils.QueryUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ModelQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.validation.ValidationError;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 模型管理 服务层实现
*
* @author may
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class ActModelServiceImpl implements IActModelService {
private final RepositoryService repositoryService;
private final IWfNodeConfigService wfNodeConfigService;
private final IWfDefinitionConfigService wfDefinitionConfigService;
/**
* 分页查询模型
*
* @param modelBo 模型参数
* @return 返回分页列表
*/
@Override
public TableDataInfo<Model> page(ModelBo modelBo, PageQuery pageQuery) {
ModelQuery query = QueryUtils.modelQuery();
if (StringUtils.isNotBlank(modelBo.getName())) {
query.modelNameLike("%" + modelBo.getName() + "%");
}
if (StringUtils.isNotBlank(modelBo.getKey())) {
query.modelKey(modelBo.getKey());
}
if (StringUtils.isNotBlank(modelBo.getCategoryCode())) {
query.modelCategory(modelBo.getCategoryCode());
}
query.orderByLastUpdateTime().desc();
// 创建时间降序排列
query.orderByCreateTime().desc();
// 分页查询
List<Model> modelList = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
// 总记录数
long total = query.count();
TableDataInfo<Model> build = TableDataInfo.build();
build.setRows(modelList);
build.setTotal(total);
return build;
}
/**
* 新增模型
*
* @param modelBo 模型请求对象
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean saveNewModel(ModelBo modelBo) {
try {
int version = 0;
String key = modelBo.getKey();
String name = modelBo.getName();
String description = modelBo.getDescription();
String categoryCode = modelBo.getCategoryCode();
String xml = modelBo.getXml();
Model checkModel = QueryUtils.modelQuery().modelKey(key).singleResult();
if (ObjectUtil.isNotNull(checkModel)) {
throw new ServiceException("模型key已存在");
}
//初始空的模型
Model model = repositoryService.newModel();
model.setKey(key);
model.setName(name);
model.setVersion(version);
model.setCategory(categoryCode);
model.setMetaInfo(description);
model.setTenantId(TenantHelper.getTenantId());
//保存初始化的模型基本信息数据
repositoryService.saveModel(model);
repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(xml));
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 查询模型
*
* @param id 模型id
* @return 模型数据
*/
@Override
public ModelVo getInfo(String id) {
ModelVo modelVo = new ModelVo();
Model model = repositoryService.getModel(id);
if (model != null) {
try {
byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId());
modelVo.setXml(StrUtil.utf8Str(modelEditorSource));
modelVo.setId(model.getId());
modelVo.setKey(model.getKey());
modelVo.setName(model.getName());
modelVo.setCategoryCode(model.getCategory());
modelVo.setDescription(model.getMetaInfo());
return modelVo;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
return modelVo;
}
/**
* 修改模型信息
*
* @param modelBo 模型数据
* @return 结果
*/
@Override
public boolean update(ModelBo modelBo) {
try {
Model model = repositoryService.getModel(modelBo.getId());
List<Model> list = QueryUtils.modelQuery().modelKey(modelBo.getKey()).list();
list.stream().filter(e -> !e.getId().equals(model.getId())).findFirst().ifPresent(e -> {
throw new ServiceException("模型KEY已存在");
});
model.setCategory(modelBo.getCategoryCode());
model.setMetaInfo(modelBo.getDescription());
repositoryService.saveModel(model);
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
return true;
}
/**
* 编辑模型XML
*
* @param modelBo 模型数据
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean editModelXml(ModelBo modelBo) {
try {
String xml = modelBo.getXml();
String svg = modelBo.getSvg();
String modelId = modelBo.getId();
String key = modelBo.getKey();
String name = modelBo.getName();
BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xml);
ModelUtils.checkBpmnModel(bpmnModel);
Model model = repositoryService.getModel(modelId);
List<Model> list = QueryUtils.modelQuery().modelKey(key).list();
list.stream().filter(e -> !e.getId().equals(model.getId())).findFirst().ifPresent(e -> {
throw new ServiceException("模型KEY已存在");
});
// 校验key命名规范
if (!Validator.isMatchRegex(FlowConstant.MODEL_KEY_PATTERN, key)) {
throw new ServiceException("模型标识KEY只能字符或者下划线开头");
}
model.setKey(key);
model.setName(name);
model.setVersion(model.getVersion() + 1);
repositoryService.saveModel(model);
repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(xml));
// 转换图片
InputStream svgStream = new ByteArrayInputStream(StrUtil.utf8Bytes(svg));
TranscoderInput input = new TranscoderInput(svgStream);
PNGTranscoder transcoder = new PNGTranscoder();
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
TranscoderOutput output = new TranscoderOutput(outStream);
transcoder.transcode(input, output);
final byte[] result = outStream.toByteArray();
repositoryService.addModelEditorSourceExtra(model.getId(), result);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 模型部署
*
* @param id 模型id
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean modelDeploy(String id) {
try {
// 查询流程定义模型xml
byte[] xmlBytes = repositoryService.getModelEditorSource(id);
if (ArrayUtil.isEmpty(xmlBytes)) {
throw new ServiceException("模型数据为空,请先设计流程定义模型,再进行部署!");
}
if (JSONUtil.isTypeJSON(new String(xmlBytes, StandardCharsets.UTF_8))) {
byte[] bytes = ModelUtils.bpmnJsonToXmlBytes(xmlBytes);
if (ArrayUtil.isEmpty(bytes)) {
throw new ServiceException("模型不能为空,请至少设计一条主线流程!");
}
}
BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xmlBytes);
// 校验模型
ModelUtils.checkBpmnModel(bpmnModel);
List<ValidationError> validationErrors = repositoryService.validateProcess(bpmnModel);
if (CollUtil.isNotEmpty(validationErrors)) {
String errorMsg = validationErrors.stream().map(ValidationError::getProblem).distinct().collect(Collectors.joining(","));
throw new ServiceException(errorMsg);
}
// 查询模型的基本信息
Model model = repositoryService.getModel(id);
ProcessDefinition processDefinition = QueryUtils.definitionQuery().processDefinitionKey(model.getKey()).latestVersion().singleResult();
// xml资源的名称 对应act_ge_bytearray表中的name_字段
String processName = model.getName() + ".bpmn20.xml";
// 调用部署相关的api方法进行部署流程定义
Deployment deployment = repositoryService.createDeployment()
// 部署名称
.name(model.getName())
// 部署标识key
.key(model.getKey())
// 部署流程分类
.category(model.getCategory())
// bpmn20.xml资源
.addBytes(processName, xmlBytes)
// 租户id
.tenantId(TenantHelper.getTenantId())
.deploy();
// 更新 部署id 到流程定义模型数据表中
model.setDeploymentId(deployment.getId());
repositoryService.saveModel(model);
// 更新分类
ProcessDefinition definition = QueryUtils.definitionQuery().deploymentId(deployment.getId()).singleResult();
repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory());
//更新流程定义配置
if (processDefinition != null) {
WfDefinitionConfigVo definitionVo = wfDefinitionConfigService.getByDefId(processDefinition.getId());
if (definitionVo != null) {
wfDefinitionConfigService.deleteByDefIds(Collections.singletonList(processDefinition.getId()));
WfDefinitionConfigBo wfFormDefinition = new WfDefinitionConfigBo();
wfFormDefinition.setDefinitionId(definition.getId());
wfFormDefinition.setProcessKey(definition.getKey());
wfFormDefinition.setTableName(definitionVo.getTableName());
wfFormDefinition.setVersion(definition.getVersion());
wfFormDefinition.setRemark(definitionVo.getRemark());
wfDefinitionConfigService.saveOrUpdate(wfFormDefinition);
}
}
//更新流程节点配置表单
List<UserTask> userTasks = ModelUtils.getUserTaskFlowElements(definition.getId());
UserTask applyUserTask = ModelUtils.getApplyUserTask(definition.getId());
List<WfNodeConfig> wfNodeConfigList = new ArrayList<>();
for (UserTask userTask : userTasks) {
if (StringUtils.isNotBlank(userTask.getFormKey()) && userTask.getFormKey().contains(StrUtil.COLON)) {
WfNodeConfig wfNodeConfig = new WfNodeConfig();
wfNodeConfig.setNodeId(userTask.getId());
wfNodeConfig.setNodeName(userTask.getName());
wfNodeConfig.setDefinitionId(definition.getId());
String[] split = userTask.getFormKey().split(StrUtil.COLON);
wfNodeConfig.setFormType(split[0]);
wfNodeConfig.setFormId(Long.valueOf(split[1]));
wfNodeConfig.setApplyUserTask(applyUserTask.getId().equals(userTask.getId()) ? FlowConstant.TRUE : FlowConstant.FALSE);
wfNodeConfigList.add(wfNodeConfig);
}
}
if (CollUtil.isNotEmpty(wfNodeConfigList)) {
wfNodeConfigService.saveOrUpdate(wfNodeConfigList);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 导出模型zip压缩包
*
* @param modelIds 模型id
* @param response 相应
*/
@Override
public void exportZip(List<String> modelIds, HttpServletResponse response) {
try (ZipOutputStream zos = ZipUtil.getZipOutputStream(response.getOutputStream(), StandardCharsets.UTF_8)) {
// 压缩包文件名
String zipName = "模型不存在";
// 查询模型基本信息
for (String modelId : modelIds) {
Model model = repositoryService.getModel(modelId);
byte[] xmlBytes = repositoryService.getModelEditorSource(modelId);
if (ObjectUtil.isNotNull(model)) {
if (JSONUtil.isTypeJSON(new String(xmlBytes, StandardCharsets.UTF_8)) && ArrayUtil.isEmpty(ModelUtils.bpmnJsonToXmlBytes(xmlBytes))) {
zipName = "模型不能为空,请至少设计一条主线流程!";
zos.putNextEntry(new ZipEntry(zipName + ".txt"));
zos.write(zipName.getBytes(StandardCharsets.UTF_8));
} else if (ArrayUtil.isEmpty(xmlBytes)) {
zipName = "模型数据为空,请先设计流程定义模型,再进行部署!";
zos.putNextEntry(new ZipEntry(zipName + ".txt"));
zos.write(zipName.getBytes(StandardCharsets.UTF_8));
} else {
String fileName = model.getName() + "-" + model.getKey();
// 压缩包文件名
zipName = fileName + ".zip";
// 将xml添加到压缩包中(指定xml文件名请假流程.bpmn20.xml
zos.putNextEntry(new ZipEntry(fileName + ".bpmn20.xml"));
zos.write(xmlBytes);
}
}
}
response.setHeader("Content-Disposition",
"attachment; filename=" + URLEncoder.encode(zipName, StandardCharsets.UTF_8) + ".zip");
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
// 刷出响应流
response.flushBuffer();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
/**
* 复制模型
*
* @param modelBo 模型数据
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean copyModel(ModelBo modelBo) {
try {
String key = modelBo.getKey();
if (StringUtils.isNotBlank(key)) {
// 查询模型
Model model = repositoryService.createModelQuery().modelId(modelBo.getId()).singleResult();
if (ObjectUtil.isNotNull(model)) {
byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId());
List<Model> list = QueryUtils.modelQuery().modelKey(key).list();
if (CollUtil.isNotEmpty(list)) {
throw new ServiceException("模型KEY已存在");
}
// 校验key命名规范
if (!Validator.isMatchRegex(FlowConstant.MODEL_KEY_PATTERN, key)) {
throw new ServiceException("模型标识KEY只能字符或者下划线开头");
}
// 复制模型数据
Model newModel = repositoryService.newModel();
newModel.setKey(modelBo.getKey());
newModel.setName(modelBo.getName());
newModel.setCategory(modelBo.getCategoryCode());
newModel.setVersion(1);
newModel.setMetaInfo(modelBo.getDescription());
newModel.setTenantId(TenantHelper.getTenantId());
String xml = StrUtil.utf8Str(modelEditorSource);
BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xml);
Process mainProcess = bpmnModel.getMainProcess();
mainProcess.setId(modelBo.getKey());
mainProcess.setName(modelBo.getName());
byte[] xmlBytes = new BpmnXMLConverter().convertToXML(bpmnModel);
repositoryService.saveModel(newModel);
repositoryService.addModelEditorSource(newModel.getId(), xmlBytes);
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
return true;
}
}

View File

@@ -1,440 +0,0 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.domain.WfCategory;
import org.dromara.workflow.domain.WfDefinitionConfig;
import org.dromara.workflow.domain.WfNodeConfig;
import org.dromara.workflow.domain.bo.ProcessDefinitionBo;
import org.dromara.workflow.domain.bo.WfDefinitionConfigBo;
import org.dromara.workflow.domain.vo.ProcessDefinitionVo;
import org.dromara.workflow.domain.vo.WfDefinitionConfigVo;
import org.dromara.workflow.mapper.WfDefinitionConfigMapper;
import org.dromara.workflow.service.IActProcessDefinitionService;
import org.dromara.workflow.service.IWfCategoryService;
import org.dromara.workflow.service.IWfDefinitionConfigService;
import org.dromara.workflow.service.IWfNodeConfigService;
import org.dromara.workflow.utils.ModelUtils;
import org.dromara.workflow.utils.QueryUtils;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.ProcessMigrationService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.bpmn.deployer.ResourceNameUtil;
import org.flowable.engine.repository.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* 流程定义 服务层实现
*
* @author may
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class ActProcessDefinitionServiceImpl implements IActProcessDefinitionService {
private final RepositoryService repositoryService;
private final ProcessMigrationService processMigrationService;
private final IWfCategoryService wfCategoryService;
private final IWfDefinitionConfigService wfDefinitionConfigService;
private final WfDefinitionConfigMapper wfDefinitionConfigMapper;
private final IWfNodeConfigService wfNodeConfigService;
/**
* 分页查询
*
* @param bo 参数
* @return 返回分页列表
*/
@Override
public TableDataInfo<ProcessDefinitionVo> page(ProcessDefinitionBo bo, PageQuery pageQuery) {
ProcessDefinitionQuery query = QueryUtils.definitionQuery();
if (StringUtils.isNotEmpty(bo.getKey())) {
query.processDefinitionKey(bo.getKey());
}
if (StringUtils.isNotEmpty(bo.getCategoryCode())) {
query.processDefinitionCategory(bo.getCategoryCode());
}
if (StringUtils.isNotEmpty(bo.getName())) {
query.processDefinitionNameLike("%" + bo.getName() + "%");
}
query.orderByDeploymentId().desc();
// 分页查询
List<ProcessDefinitionVo> processDefinitionVoList = new ArrayList<>();
List<ProcessDefinition> definitionList = query.latestVersion().listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
List<Deployment> deploymentList = null;
if (CollUtil.isNotEmpty(definitionList)) {
List<String> deploymentIds = StreamUtils.toList(definitionList, ProcessDefinition::getDeploymentId);
deploymentList = QueryUtils.deploymentQuery(deploymentIds).list();
}
if (CollUtil.isNotEmpty(definitionList)) {
List<String> ids = StreamUtils.toList(definitionList, ProcessDefinition::getId);
List<WfDefinitionConfigVo> wfDefinitionConfigVos = wfDefinitionConfigService.queryList(ids);
for (ProcessDefinition processDefinition : definitionList) {
ProcessDefinitionVo processDefinitionVo = BeanUtil.toBean(processDefinition, ProcessDefinitionVo.class);
if (CollUtil.isNotEmpty(deploymentList)) {
// 部署时间
deploymentList.stream().filter(e -> e.getId().equals(processDefinition.getDeploymentId())).findFirst().ifPresent(e -> {
processDefinitionVo.setDeploymentTime(e.getDeploymentTime());
});
}
if (CollUtil.isNotEmpty(wfDefinitionConfigVos)) {
wfDefinitionConfigVos.stream().filter(e -> e.getDefinitionId().equals(processDefinition.getId())).findFirst().ifPresent(processDefinitionVo::setWfDefinitionConfigVo);
}
processDefinitionVoList.add(processDefinitionVo);
}
}
// 总记录数
long total = query.count();
TableDataInfo<ProcessDefinitionVo> build = TableDataInfo.build();
build.setRows(processDefinitionVoList);
build.setTotal(total);
return build;
}
/**
* 查询历史流程定义列表
*
* @param key 流程定义key
*/
@Override
public List<ProcessDefinitionVo> getListByKey(String key) {
List<ProcessDefinitionVo> processDefinitionVoList = new ArrayList<>();
ProcessDefinitionQuery query = QueryUtils.definitionQuery();
List<ProcessDefinition> definitionList = query.processDefinitionKey(key).list();
List<Deployment> deploymentList = null;
if (CollUtil.isNotEmpty(definitionList)) {
List<String> deploymentIds = StreamUtils.toList(definitionList, ProcessDefinition::getDeploymentId);
deploymentList = QueryUtils.deploymentQuery(deploymentIds).list();
}
if (CollUtil.isNotEmpty(definitionList)) {
List<String> ids = StreamUtils.toList(definitionList, ProcessDefinition::getId);
List<WfDefinitionConfigVo> wfDefinitionConfigVos = wfDefinitionConfigService.queryList(ids);
for (ProcessDefinition processDefinition : definitionList) {
ProcessDefinitionVo processDefinitionVo = BeanUtil.toBean(processDefinition, ProcessDefinitionVo.class);
if (CollUtil.isNotEmpty(deploymentList)) {
// 部署时间
deploymentList.stream().filter(e -> e.getId().equals(processDefinition.getDeploymentId())).findFirst().ifPresent(e -> {
processDefinitionVo.setDeploymentTime(e.getDeploymentTime());
});
if (CollUtil.isNotEmpty(wfDefinitionConfigVos)) {
wfDefinitionConfigVos.stream().filter(e -> e.getDefinitionId().equals(processDefinition.getId())).findFirst().ifPresent(processDefinitionVo::setWfDefinitionConfigVo);
}
}
processDefinitionVoList.add(processDefinitionVo);
}
}
return CollUtil.reverse(processDefinitionVoList);
}
/**
* 查看流程定义图片
*
* @param processDefinitionId 流程定义id
*/
@SneakyThrows
@Override
public String definitionImage(String processDefinitionId) {
InputStream inputStream = repositoryService.getProcessDiagram(processDefinitionId);
return Base64.encode(IoUtil.readBytes(inputStream));
}
/**
* 查看流程定义xml文件
*
* @param processDefinitionId 流程定义id
*/
@Override
public String definitionXml(String processDefinitionId) {
StringBuilder xml = new StringBuilder();
ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
xml.append(IoUtil.read(inputStream, StandardCharsets.UTF_8));
return xml.toString();
}
/**
* 删除流程定义
*
* @param deploymentIds 部署id
* @param processDefinitionIds 流程定义id
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteDeployment(List<String> deploymentIds, List<String> processDefinitionIds) {
try {
List<HistoricProcessInstance> historicProcessInstances = QueryUtils.hisInstanceQuery().deploymentIdIn(deploymentIds).list();
if (CollUtil.isNotEmpty(historicProcessInstances)) {
Set<String> defIds = StreamUtils.toSet(historicProcessInstances, HistoricProcessInstance::getProcessDefinitionId);
List<ProcessDefinition> processDefinitions = QueryUtils.definitionQuery().processDefinitionIds(defIds).list();
if (CollUtil.isNotEmpty(processDefinitions)) {
Set<String> keys = StreamUtils.toSet(processDefinitions, ProcessDefinition::getKey);
throw new ServiceException("当前【" + String.join(",", keys) + "】流程定义已被使用不可删除!");
}
}
//删除流程定义
for (String deploymentId : deploymentIds) {
repositoryService.deleteDeployment(deploymentId);
}
//删除流程定义配置
wfDefinitionConfigService.deleteByDefIds(processDefinitionIds);
//删除节点配置
wfNodeConfigService.deleteByDefIds(processDefinitionIds);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 激活或者挂起流程定义
*
* @param processDefinitionId 流程定义id
*/
@Override
public boolean updateDefinitionState(String processDefinitionId) {
try {
ProcessDefinition processDefinition = QueryUtils.definitionQuery()
.processDefinitionId(processDefinitionId).singleResult();
//将当前为挂起状态更新为激活状态
//参数说明参数1流程定义id,参数2是否激活true是否级联对应流程实例激活了则对应流程实例都可以审批
//参数3什么时候激活如果为null则立即激活如果为具体时间则到达此时间后激活
if (processDefinition.isSuspended()) {
repositoryService.activateProcessDefinitionById(processDefinitionId, true, null);
} else {
repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException("操作失败:" + e.getMessage());
}
}
/**
* 迁移流程定义
*
* @param currentProcessDefinitionId 当前流程定义id
* @param fromProcessDefinitionId 需要迁移到的流程定义id
*/
@Override
public boolean migrationDefinition(String currentProcessDefinitionId, String fromProcessDefinitionId) {
try {
// 迁移验证
boolean migrationValid = processMigrationService.createProcessInstanceMigrationBuilder()
.migrateToProcessDefinition(currentProcessDefinitionId)
.validateMigrationOfProcessInstances(fromProcessDefinitionId)
.isMigrationValid();
if (!migrationValid) {
throw new ServiceException("流程定义差异过大无法迁移,请修改流程图");
}
// 已结束的流程实例不会迁移
processMigrationService.createProcessInstanceMigrationBuilder()
.migrateToProcessDefinition(currentProcessDefinitionId)
.migrateProcessInstances(fromProcessDefinitionId);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 流程定义转换为模型
*
* @param processDefinitionId 流程定义id
*/
@Override
public boolean convertToModel(String processDefinitionId) {
ProcessDefinition pd = QueryUtils.definitionQuery()
.processDefinitionId(processDefinitionId).singleResult();
InputStream inputStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), pd.getResourceName());
ModelQuery query = QueryUtils.modelQuery();
Model model = query.modelKey(pd.getKey()).singleResult();
try {
if (ObjectUtil.isNotNull(model)) {
repositoryService.addModelEditorSource(model.getId(), IoUtil.readBytes(inputStream));
} else {
Model modelData = repositoryService.newModel();
modelData.setKey(pd.getKey());
modelData.setName(pd.getName());
modelData.setTenantId(pd.getTenantId());
repositoryService.saveModel(modelData);
repositoryService.addModelEditorSource(modelData.getId(), IoUtil.readBytes(inputStream));
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 通过zip或xml部署流程定义
*
* @param file 文件
* @param categoryCode 分类
*/
@SneakyThrows
@Override
@Transactional(rollbackFor = Exception.class)
public void deployByFile(MultipartFile file, String categoryCode) {
WfCategory wfCategory = wfCategoryService.queryByCategoryCode(categoryCode);
if (wfCategory == null) {
throw new ServiceException("流程分类不存在");
}
// 文件后缀名
String suffix = FileUtil.extName(file.getOriginalFilename());
InputStream inputStream = file.getInputStream();
if (FlowConstant.ZIP.equalsIgnoreCase(suffix)) {
ZipInputStream zipInputStream = null;
try {
zipInputStream = new ZipInputStream(inputStream);
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String filename = zipEntry.getName();
String[] splitFilename = filename.substring(0, filename.lastIndexOf(".")).split("-");
//流程名称
String processName = splitFilename[0];
//流程key
String processKey = splitFilename[1];
ProcessDefinition oldProcessDefinition = QueryUtils.definitionQuery().processDefinitionKey(processKey).latestVersion().singleResult();
DeploymentBuilder builder = repositoryService.createDeployment();
Deployment deployment = builder.addInputStream(filename, zipInputStream)
.tenantId(TenantHelper.getTenantId())
.name(processName).key(processKey).category(categoryCode).deploy();
ProcessDefinition definition = QueryUtils.definitionQuery().deploymentId(deployment.getId()).singleResult();
repositoryService.setProcessDefinitionCategory(definition.getId(), categoryCode);
setWfConfig(oldProcessDefinition, definition);
zipInputStream.closeEntry();
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (zipInputStream != null) {
zipInputStream.close();
}
}
//初始化配置数据demo使用不用可删除
initWfDefConfig();
} else {
String originalFilename = file.getOriginalFilename();
if (StringUtils.containsAny(originalFilename, ResourceNameUtil.BPMN_RESOURCE_SUFFIXES)) {
// 文件名 = 流程名称-流程key
String[] splitFilename = originalFilename.substring(0, originalFilename.lastIndexOf(".")).split("-");
if (splitFilename.length < 2) {
throw new ServiceException("文件名 = 流程名称-流程KEY");
}
//流程名称
String processName = splitFilename[0];
//流程key
String processKey = splitFilename[1];
ProcessDefinition oldProcessDefinition = QueryUtils.definitionQuery().processDefinitionKey(processKey).latestVersion().singleResult();
DeploymentBuilder builder = repositoryService.createDeployment();
Deployment deployment = builder.addInputStream(originalFilename, inputStream)
.tenantId(TenantHelper.getTenantId())
.name(processName).key(processKey).category(categoryCode).deploy();
// 更新分类
ProcessDefinition definition = QueryUtils.definitionQuery().deploymentId(deployment.getId()).singleResult();
repositoryService.setProcessDefinitionCategory(definition.getId(), categoryCode);
setWfConfig(oldProcessDefinition, definition);
} else {
throw new ServiceException("文件类型上传错误!");
}
}
}
/**
* 初始化配置数据demo使用不用可删除
*/
private void initWfDefConfig() {
List<WfDefinitionConfig> wfDefinitionConfigs = wfDefinitionConfigMapper.selectList();
if (CollUtil.isEmpty(wfDefinitionConfigs)) {
ProcessDefinition processDefinition = QueryUtils.definitionQuery().processDefinitionKey("leave1").latestVersion().singleResult();
if (processDefinition != null) {
WfDefinitionConfigBo wfDefinitionConfigBo = new WfDefinitionConfigBo();
wfDefinitionConfigBo.setDefinitionId(processDefinition.getId());
wfDefinitionConfigBo.setProcessKey(processDefinition.getKey());
wfDefinitionConfigBo.setTableName("test_leave");
wfDefinitionConfigBo.setVersion(processDefinition.getVersion());
wfDefinitionConfigService.saveOrUpdate(wfDefinitionConfigBo);
}
}
}
/**
* 设置表单内容
*
* @param oldProcessDefinition 部署前最新流程定义
* @param definition 部署后最新流程定义
*/
private void setWfConfig(ProcessDefinition oldProcessDefinition, ProcessDefinition definition) {
//更新流程定义表单
if (oldProcessDefinition != null) {
WfDefinitionConfigVo definitionVo = wfDefinitionConfigService.getByDefId(oldProcessDefinition.getId());
if (definitionVo != null) {
wfDefinitionConfigService.deleteByDefIds(Collections.singletonList(oldProcessDefinition.getId()));
WfDefinitionConfigBo wfDefinitionConfigBo = new WfDefinitionConfigBo();
wfDefinitionConfigBo.setDefinitionId(definition.getId());
wfDefinitionConfigBo.setProcessKey(definition.getKey());
wfDefinitionConfigBo.setTableName(definitionVo.getTableName());
wfDefinitionConfigBo.setVersion(definition.getVersion());
wfDefinitionConfigBo.setRemark(definitionVo.getRemark());
wfDefinitionConfigService.saveOrUpdate(wfDefinitionConfigBo);
}
}
//更新流程节点配置表单
List<UserTask> userTasks = ModelUtils.getUserTaskFlowElements(definition.getId());
UserTask applyUserTask = ModelUtils.getApplyUserTask(definition.getId());
List<WfNodeConfig> wfNodeConfigList = new ArrayList<>();
for (UserTask userTask : userTasks) {
if (StringUtils.isNotBlank(userTask.getFormKey()) && userTask.getFormKey().contains(StrUtil.COLON)) {
WfNodeConfig wfNodeConfig = new WfNodeConfig();
wfNodeConfig.setNodeId(userTask.getId());
wfNodeConfig.setNodeName(userTask.getName());
wfNodeConfig.setDefinitionId(definition.getId());
String[] split = userTask.getFormKey().split(StrUtil.COLON);
wfNodeConfig.setFormType(split[0]);
wfNodeConfig.setFormId(Long.valueOf(split[1]));
wfNodeConfig.setApplyUserTask(applyUserTask.getId().equals(userTask.getId()) ? FlowConstant.TRUE : FlowConstant.FALSE);
wfNodeConfigList.add(wfNodeConfig);
}
}
if (CollUtil.isNotEmpty(wfNodeConfigList)) {
wfNodeConfigService.saveOrUpdate(wfNodeConfigList);
}
}
}

View File

@@ -1,685 +0,0 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.UserService;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.domain.ActHiProcinst;
import org.dromara.workflow.domain.bo.ProcessInstanceBo;
import org.dromara.workflow.domain.bo.ProcessInvalidBo;
import org.dromara.workflow.domain.bo.TaskUrgingBo;
import org.dromara.workflow.domain.vo.*;
import org.dromara.workflow.flowable.CustomDefaultProcessDiagramGenerator;
import org.dromara.workflow.flowable.cmd.DeleteExecutionCmd;
import org.dromara.workflow.flowable.cmd.ExecutionChildByExecutionIdCmd;
import org.dromara.workflow.flowable.handler.FlowProcessEventHandler;
import org.dromara.workflow.service.IActHiProcinstService;
import org.dromara.workflow.service.IActProcessInstanceService;
import org.dromara.workflow.service.IWfNodeConfigService;
import org.dromara.workflow.service.IWfTaskBackNodeService;
import org.dromara.workflow.utils.QueryUtils;
import org.dromara.workflow.utils.WorkflowUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.flowable.engine.task.Attachment;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.awt.*;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.*;
/**
* 流程实例 服务层实现
*
* @author may
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class ActProcessInstanceServiceImpl implements IActProcessInstanceService {
private final RepositoryService repositoryService;
private final RuntimeService runtimeService;
private final HistoryService historyService;
private final TaskService taskService;
private final IActHiProcinstService actHiProcinstService;
private final ManagementService managementService;
private final IWfTaskBackNodeService wfTaskBackNodeService;
private final IWfNodeConfigService wfNodeConfigService;
private final FlowProcessEventHandler flowProcessEventHandler;
private final UserService userService;
@Value("${flowable.activity-font-name}")
private String activityFontName;
@Value("${flowable.label-font-name}")
private String labelFontName;
@Value("${flowable.annotation-font-name}")
private String annotationFontName;
/**
* 分页查询正在运行的流程实例
*
* @param bo 参数
*/
@Override
public TableDataInfo<ProcessInstanceVo> getPageByRunning(ProcessInstanceBo bo, PageQuery pageQuery) {
List<ProcessInstanceVo> list = new ArrayList<>();
ProcessInstanceQuery query = QueryUtils.instanceQuery();
if (StringUtils.isNotBlank(bo.getName())) {
query.processInstanceNameLikeIgnoreCase("%" + bo.getName() + "%");
}
if (StringUtils.isNotBlank(bo.getKey())) {
query.processDefinitionKey(bo.getKey());
}
if (StringUtils.isNotBlank(bo.getStartUserId())) {
query.startedBy(bo.getStartUserId());
}
if (StringUtils.isNotBlank(bo.getBusinessKey())) {
query.processInstanceBusinessKey(bo.getBusinessKey());
}
if (StringUtils.isNotBlank(bo.getCategoryCode())) {
query.processDefinitionCategory(bo.getCategoryCode());
}
query.orderByStartTime().desc();
List<ProcessInstance> processInstances = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
for (ProcessInstance processInstance : processInstances) {
ProcessInstanceVo processInstanceVo = BeanUtil.toBean(processInstance, ProcessInstanceVo.class);
processInstanceVo.setIsSuspended(processInstance.isSuspended());
processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstance.getBusinessStatus()));
list.add(processInstanceVo);
}
if (CollUtil.isNotEmpty(list)) {
List<String> processDefinitionIds = StreamUtils.toList(list, ProcessInstanceVo::getProcessDefinitionId);
List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
for (ProcessInstanceVo processInstanceVo : list) {
if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(processInstanceVo.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(processInstanceVo::setWfNodeConfigVo);
}
}
}
long count = query.count();
TableDataInfo<ProcessInstanceVo> build = TableDataInfo.build();
build.setRows(list);
build.setTotal(count);
return build;
}
/**
* 分页查询已结束的流程实例
*
* @param bo 参数
*/
@Override
public TableDataInfo<ProcessInstanceVo> getPageByFinish(ProcessInstanceBo bo, PageQuery pageQuery) {
List<ProcessInstanceVo> list = new ArrayList<>();
HistoricProcessInstanceQuery query = QueryUtils.hisInstanceQuery()
.finished().orderByProcessInstanceEndTime().desc();
if (StringUtils.isNotEmpty(bo.getName())) {
query.processInstanceNameLikeIgnoreCase("%" + bo.getName() + "%");
}
if (StringUtils.isNotBlank(bo.getKey())) {
query.processDefinitionKey(bo.getKey());
}
if (StringUtils.isNotEmpty(bo.getStartUserId())) {
query.startedBy(bo.getStartUserId());
}
if (StringUtils.isNotBlank(bo.getBusinessKey())) {
query.processInstanceBusinessKey(bo.getBusinessKey());
}
if (StringUtils.isNotBlank(bo.getCategoryCode())) {
query.processDefinitionCategory(bo.getCategoryCode());
}
List<HistoricProcessInstance> historicProcessInstances = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
ProcessInstanceVo processInstanceVo = BeanUtil.toBean(historicProcessInstance, ProcessInstanceVo.class);
processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(historicProcessInstance.getBusinessStatus()));
list.add(processInstanceVo);
}
if (CollUtil.isNotEmpty(list)) {
List<String> processDefinitionIds = StreamUtils.toList(list, ProcessInstanceVo::getProcessDefinitionId);
List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
for (ProcessInstanceVo processInstanceVo : list) {
if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(processInstanceVo.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(processInstanceVo::setWfNodeConfigVo);
}
}
}
long count = query.count();
TableDataInfo<ProcessInstanceVo> build = TableDataInfo.build();
build.setRows(list);
build.setTotal(count);
return build;
}
/**
* 通过业务id获取历史流程图
*
* @param businessKey 业务id
*/
@SneakyThrows
@Override
public String getHistoryImage(String businessKey) {
String processDefinitionId;
// 获取当前的流程实例
ProcessInstance processInstance = QueryUtils.businessKeyQuery(businessKey).singleResult();
// 如果流程已经结束,则得到结束节点
if (Objects.isNull(processInstance)) {
HistoricProcessInstance pi = QueryUtils.hisInstanceQuery().processInstanceBusinessKey(businessKey).singleResult();
processDefinitionId = pi.getProcessDefinitionId();
} else {
// 根据流程实例ID获得当前处于活动状态的ActivityId合集
ProcessInstance pi = QueryUtils.instanceQuery(processInstance.getProcessInstanceId()).singleResult();
processDefinitionId = pi.getProcessDefinitionId();
}
// 获得活动的节点
List<HistoricActivityInstance> highLightedFlowList = QueryUtils.hisActivityInstanceQuery(processInstance.getProcessInstanceId()).orderByHistoricActivityInstanceStartTime().asc().list();
List<String> highLightedFlows = new ArrayList<>();
List<String> highLightedNodes = new ArrayList<>();
//高亮
for (HistoricActivityInstance tempActivity : highLightedFlowList) {
if (FlowConstant.SEQUENCE_FLOW.equals(tempActivity.getActivityType())) {
//高亮线
highLightedFlows.add(tempActivity.getActivityId());
} else {
//高亮节点
if (tempActivity.getEndTime() == null) {
highLightedNodes.add(Color.RED.toString() + tempActivity.getActivityId());
} else {
highLightedNodes.add(tempActivity.getActivityId());
}
}
}
List<String> highLightedNodeList = new ArrayList<>();
//运行中的节点
List<String> redNodeCollect = StreamUtils.filter(highLightedNodes, e -> e.contains(Color.RED.toString()));
//排除与运行中相同的节点
for (String nodeId : highLightedNodes) {
if (!nodeId.contains(Color.RED.toString()) && !redNodeCollect.contains(Color.RED + nodeId)) {
highLightedNodeList.add(nodeId);
}
}
highLightedNodeList.addAll(redNodeCollect);
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
CustomDefaultProcessDiagramGenerator diagramGenerator = new CustomDefaultProcessDiagramGenerator();
InputStream inputStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodeList, highLightedFlows, activityFontName, labelFontName, annotationFontName, null, 1.0, true);
return Base64.encode(IoUtil.readBytes(inputStream));
}
/**
* 通过业务id获取历史流程图运行中历史等节点
*
* @param businessKey 业务id
*/
@Override
public Map<String, Object> getHistoryList(String businessKey) {
Map<String, Object> map = new HashMap<>();
List<Map<String, Object>> taskList = new ArrayList<>();
HistoricProcessInstance historicProcessInstance = QueryUtils.hisBusinessKeyQuery(businessKey).singleResult();
String processInstanceId = historicProcessInstance.getId();
StringBuilder xml = new StringBuilder();
ProcessDefinition processDefinition = repositoryService.getProcessDefinition(historicProcessInstance.getProcessDefinitionId());
// 获取节点
List<HistoricActivityInstance> highLightedFlowList = QueryUtils.hisActivityInstanceQuery(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
for (HistoricActivityInstance tempActivity : highLightedFlowList) {
Map<String, Object> task = new HashMap<>();
switch (tempActivity.getActivityType()) {
case FlowConstant.SEQUENCE_FLOW, FlowConstant.PARALLEL_GATEWAY,
FlowConstant.EXCLUSIVE_GATEWAY, FlowConstant.INCLUSIVE_GATEWAY -> {}
default -> {
task.put("key", tempActivity.getActivityId());
task.put("completed", tempActivity.getEndTime() != null);
task.put("activityType", tempActivity.getActivityType());
taskList.add(task);
}
}
}
ProcessInstance processInstance = QueryUtils.instanceQuery(processInstanceId).singleResult();
if (processInstance != null) {
taskList = StreamUtils.filter(taskList, e -> !e.get("activityType").equals(FlowConstant.END_EVENT));
}
//查询出运行中节点
List<Map<String, Object>> runtimeNodeList = StreamUtils.filter(taskList, e -> !(Boolean) e.get("completed"));
if (CollUtil.isNotEmpty(runtimeNodeList)) {
Iterator<Map<String, Object>> iterator = taskList.iterator();
while (iterator.hasNext()) {
Map<String, Object> next = iterator.next();
runtimeNodeList.stream().filter(t -> t.get("key").equals(next.get("key")) && (Boolean) next.get("completed")).findFirst().ifPresent(t -> iterator.remove());
}
}
map.put("taskList", taskList);
List<ActHistoryInfoVo> historyTaskList = getHistoryTaskList(processInstanceId, processDefinition.getVersion());
map.put("historyList", historyTaskList);
InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
xml.append(IoUtil.read(inputStream, StandardCharsets.UTF_8));
map.put("xml", xml.toString());
return map;
}
/**
* 获取历史任务节点信息
*
* @param processInstanceId 流程实例id
* @param version 版本
*/
private List<ActHistoryInfoVo> getHistoryTaskList(String processInstanceId, Integer version) {
//查询任务办理记录
List<HistoricTaskInstance> list = QueryUtils.hisTaskInstanceQuery(processInstanceId).orderByHistoricTaskInstanceEndTime().desc().list();
list = StreamUtils.sorted(list, Comparator.comparing(HistoricTaskInstance::getEndTime, Comparator.nullsFirst(Date::compareTo)).reversed());
List<ActHistoryInfoVo> actHistoryInfoVoList = new ArrayList<>();
for (HistoricTaskInstance historicTaskInstance : list) {
ActHistoryInfoVo actHistoryInfoVo = new ActHistoryInfoVo();
BeanUtils.copyProperties(historicTaskInstance, actHistoryInfoVo);
actHistoryInfoVo.setStatus(actHistoryInfoVo.getEndTime() == null ? "待处理" : "已处理");
if (ObjectUtil.isNotEmpty(historicTaskInstance.getDurationInMillis())) {
actHistoryInfoVo.setRunDuration(getDuration(historicTaskInstance.getDurationInMillis()));
}
actHistoryInfoVo.setVersion(version);
actHistoryInfoVoList.add(actHistoryInfoVo);
}
List<ActHistoryInfoVo> historyInfoVoList = new ArrayList<>();
Map<String, List<ActHistoryInfoVo>> groupByKey = StreamUtils.groupByKey(actHistoryInfoVoList, ActHistoryInfoVo::getTaskDefinitionKey);
for (Map.Entry<String, List<ActHistoryInfoVo>> entry : groupByKey.entrySet()) {
ActHistoryInfoVo historyInfoVo = new ActHistoryInfoVo();
if (entry.getValue().size() > 1) {
List<ActHistoryInfoVo> historyInfoVos = StreamUtils.filter(entry.getValue(), e -> StringUtils.isNotBlank(e.getAssignee()));
if (CollUtil.isNotEmpty(historyInfoVos)) {
ActHistoryInfoVo infoVo = historyInfoVos.get(0);
BeanUtils.copyProperties(infoVo, historyInfoVo);
historyInfoVo.setStatus(infoVo.getEndTime() == null ? "待处理" : "已处理");
historyInfoVo.setStartTime(infoVo.getStartTime());
historyInfoVo.setEndTime(infoVo.getEndTime() == null ? null : infoVo.getEndTime());
historyInfoVo.setRunDuration(infoVo.getEndTime() == null ? null : infoVo.getRunDuration());
if (ObjectUtil.isEmpty(infoVo.getAssignee())) {
ParticipantVo participantVo = WorkflowUtils.getCurrentTaskParticipant(infoVo.getId(), userService);
if (ObjectUtil.isNotEmpty(participantVo) && CollUtil.isNotEmpty(participantVo.getCandidate())) {
historyInfoVo.setAssignee(StreamUtils.join(participantVo.getCandidate(), Convert::toStr));
}
}
}
} else {
actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey())).findFirst()
.ifPresent(e -> {
BeanUtils.copyProperties(e, historyInfoVo);
historyInfoVo.setStatus(e.getEndTime() == null ? "待处理" : "已处理");
historyInfoVo.setStartTime(e.getStartTime());
historyInfoVo.setEndTime(e.getEndTime() == null ? null : e.getEndTime());
historyInfoVo.setRunDuration(e.getEndTime() == null ? null : e.getRunDuration());
if (ObjectUtil.isEmpty(e.getAssignee())) {
ParticipantVo participantVo = WorkflowUtils.getCurrentTaskParticipant(e.getId(), userService);
if (ObjectUtil.isNotEmpty(participantVo) && CollUtil.isNotEmpty(participantVo.getCandidate())) {
historyInfoVo.setAssignee(StreamUtils.join(participantVo.getCandidate(), Convert::toStr));
}
}
});
}
historyInfoVoList.add(historyInfoVo);
}
return historyInfoVoList;
}
/**
* 获取审批记录
*
* @param businessKey 业务id
*/
@Override
public List<ActHistoryInfoVo> getHistoryRecord(String businessKey) {
// 查询任务办理记录
List<HistoricTaskInstance> list = QueryUtils.hisTaskBusinessKeyQuery(businessKey).orderByHistoricTaskInstanceEndTime().desc().list();
list = StreamUtils.sorted(list, Comparator.comparing(HistoricTaskInstance::getEndTime, Comparator.nullsFirst(Date::compareTo)).reversed());
HistoricProcessInstance historicProcessInstance = QueryUtils.hisBusinessKeyQuery(businessKey).singleResult();
String processInstanceId = historicProcessInstance.getId();
List<ActHistoryInfoVo> actHistoryInfoVoList = new ArrayList<>();
List<Comment> processInstanceComments = taskService.getProcessInstanceComments(processInstanceId);
//附件
List<Attachment> attachmentList = taskService.getProcessInstanceAttachments(processInstanceId);
for (HistoricTaskInstance historicTaskInstance : list) {
ActHistoryInfoVo actHistoryInfoVo = new ActHistoryInfoVo();
BeanUtils.copyProperties(historicTaskInstance, actHistoryInfoVo);
if (actHistoryInfoVo.getEndTime() == null) {
actHistoryInfoVo.setStatus(TaskStatusEnum.WAITING.getStatus());
actHistoryInfoVo.setStatusName(TaskStatusEnum.WAITING.getDesc());
}
if (CollUtil.isNotEmpty(processInstanceComments)) {
processInstanceComments.stream().filter(e -> e.getTaskId().equals(historicTaskInstance.getId())).findFirst().ifPresent(e -> {
actHistoryInfoVo.setComment(e.getFullMessage());
actHistoryInfoVo.setStatus(e.getType());
actHistoryInfoVo.setStatusName(TaskStatusEnum.findByStatus(e.getType()));
});
}
if (ObjectUtil.isNotEmpty(historicTaskInstance.getDurationInMillis())) {
actHistoryInfoVo.setRunDuration(getDuration(historicTaskInstance.getDurationInMillis()));
}
//附件
if (CollUtil.isNotEmpty(attachmentList)) {
List<Attachment> attachments = StreamUtils.filter(attachmentList, e -> e.getTaskId().equals(historicTaskInstance.getId()));
if (CollUtil.isNotEmpty(attachments)) {
actHistoryInfoVo.setAttachmentList(attachments);
}
}
//设置人员id
if (ObjectUtil.isEmpty(historicTaskInstance.getAssignee())) {
ParticipantVo participantVo = WorkflowUtils.getCurrentTaskParticipant(historicTaskInstance.getId(), userService);
if (ObjectUtil.isNotEmpty(participantVo) && CollUtil.isNotEmpty(participantVo.getCandidate())) {
actHistoryInfoVo.setAssignee(StreamUtils.join(participantVo.getCandidate(), Convert::toStr));
}
}
actHistoryInfoVoList.add(actHistoryInfoVo);
}
// 审批记录
Map<String, List<ActHistoryInfoVo>> groupByKey = StreamUtils.groupByKey(actHistoryInfoVoList, ActHistoryInfoVo::getTaskDefinitionKey);
for (Map.Entry<String, List<ActHistoryInfoVo>> entry : groupByKey.entrySet()) {
ActHistoryInfoVo actHistoryInfoVo = BeanUtil.toBean(entry.getValue().get(0), ActHistoryInfoVo.class);
actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey()) && e.getEndTime() != null).findFirst()
.ifPresent(e -> {
actHistoryInfoVo.setStatus("已处理");
actHistoryInfoVo.setStartTime(e.getStartTime());
});
actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey()) && e.getEndTime() == null).findFirst()
.ifPresent(e -> {
actHistoryInfoVo.setStatus("待处理");
actHistoryInfoVo.setStartTime(e.getStartTime());
actHistoryInfoVo.setEndTime(null);
actHistoryInfoVo.setRunDuration(null);
});
}
List<ActHistoryInfoVo> recordList = new ArrayList<>();
// 待办理
recordList.addAll(StreamUtils.filter(actHistoryInfoVoList, e -> e.getEndTime() == null));
// 已办理
recordList.addAll(StreamUtils.filter(actHistoryInfoVoList, e -> e.getEndTime() != null));
return recordList;
}
/**
* 任务完成时间处理
*
* @param time 时间
*/
private String getDuration(long time) {
long day = time / (24 * 60 * 60 * 1000);
long hour = (time / (60 * 60 * 1000) - day * 24);
long minute = ((time / (60 * 1000)) - day * 24 * 60 - hour * 60);
long second = (time / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60);
if (day > 0) {
return day + "" + hour + "小时" + minute + "分钟";
}
if (hour > 0) {
return hour + "小时" + minute + "分钟";
}
if (minute > 0) {
return minute + "分钟";
}
if (second > 0) {
return second + "";
} else {
return 0 + "";
}
}
/**
* 作废流程实例,不会删除历史记录(删除运行中的实例)
*
* @param processInvalidBo 参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteRunInstance(ProcessInvalidBo processInvalidBo) {
try {
List<Task> list = QueryUtils.taskQuery().processInstanceBusinessKey(processInvalidBo.getBusinessKey()).list();
String processInstanceId = list.get(0).getProcessInstanceId();
List<Task> subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId()));
if (CollUtil.isNotEmpty(subTasks)) {
subTasks.forEach(e -> taskService.deleteTask(e.getId()));
}
String deleteReason = LoginHelper.getLoginUser().getNickname() + "作废了当前申请!";
if (StringUtils.isNotBlank(processInvalidBo.getDeleteReason())) {
deleteReason = LoginHelper.getLoginUser().getNickname() + "作废理由:" + processInvalidBo.getDeleteReason();
}
for (Task task : StreamUtils.filter(list, e -> StringUtils.isBlank(e.getParentTaskId()))) {
taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.INVALID.getStatus(), deleteReason);
}
HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery(processInstanceId).singleResult();
BusinessStatusEnum.checkInvalidStatus(historicProcessInstance.getBusinessStatus());
runtimeService.updateBusinessStatus(processInstanceId, BusinessStatusEnum.INVALID.getStatus());
runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
//流程作废监听
flowProcessEventHandler.processHandler(historicProcessInstance.getProcessDefinitionKey(),
historicProcessInstance.getBusinessKey(), BusinessStatusEnum.INVALID.getStatus(), false);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
*
* @param businessKeys 业务id
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteRunAndHisInstance(List<String> businessKeys) {
try {
// 1.删除运行中流程实例
List<ActHiProcinst> actHiProcinsts = actHiProcinstService.selectByBusinessKeyIn(businessKeys);
if (CollUtil.isEmpty(actHiProcinsts)) {
log.warn("当前业务ID:{}查询到流程实例为空!", businessKeys);
return false;
}
List<String> processInstanceIds = StreamUtils.toList(actHiProcinsts, ActHiProcinst::getId);
List<Task> list = QueryUtils.taskQuery(processInstanceIds).list();
List<Task> subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId()));
if (CollUtil.isNotEmpty(subTasks)) {
subTasks.forEach(e -> taskService.deleteTask(e.getId()));
}
runtimeService.bulkDeleteProcessInstances(processInstanceIds, LoginHelper.getUserId() + "删除了当前流程申请");
// 2.删除历史记录
List<HistoricProcessInstance> historicProcessInstanceList = QueryUtils.hisInstanceQuery(new HashSet<>(processInstanceIds)).list();
if (ObjectUtil.isNotEmpty(historicProcessInstanceList)) {
historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
}
wfTaskBackNodeService.deleteByInstanceIds(processInstanceIds);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 已完成的实例 删除程实例,删除历史记录,删除业务与流程关联信息
*
* @param businessKeys 业务id
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteFinishAndHisInstance(List<String> businessKeys) {
try {
List<ActHiProcinst> actHiProcinsts = actHiProcinstService.selectByBusinessKeyIn(businessKeys);
if (CollUtil.isEmpty(actHiProcinsts)) {
log.warn("当前业务ID:{}查询到流程实例为空!", businessKeys);
return false;
}
List<String> processInstanceIds = StreamUtils.toList(actHiProcinsts, ActHiProcinst::getId);
historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
wfTaskBackNodeService.deleteByInstanceIds(processInstanceIds);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 撤销流程申请
*
* @param businessKey 业务id
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean cancelProcessApply(String businessKey) {
try {
ProcessInstance processInstance = QueryUtils.businessKeyQuery(businessKey)
.startedBy(String.valueOf(LoginHelper.getUserId())).singleResult();
if (ObjectUtil.isNull(processInstance)) {
throw new ServiceException("您不是流程发起人,撤销失败!");
}
if (processInstance.isSuspended()) {
throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
}
String processInstanceId = processInstance.getId();
BusinessStatusEnum.checkCancelStatus(processInstance.getBusinessStatus());
List<Task> taskList = QueryUtils.taskQuery(processInstanceId).list();
for (Task task : taskList) {
taskService.setAssignee(task.getId(), null);
taskService.addComment(task.getId(), processInstanceId, TaskStatusEnum.CANCEL.getStatus(), LoginHelper.getLoginUser().getNickname() + ":撤销申请");
}
HistoricTaskInstance historicTaskInstance = QueryUtils.hisTaskInstanceQuery(processInstanceId).finished().orderByHistoricTaskInstanceEndTime().asc().list().get(0);
List<String> nodeIds = StreamUtils.toList(taskList, Task::getTaskDefinitionKey);
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(processInstanceId)
.moveActivityIdsToSingleActivityId(nodeIds, historicTaskInstance.getTaskDefinitionKey()).changeState();
Task task = QueryUtils.taskQuery(processInstanceId).list().get(0);
taskService.setAssignee(task.getId(), historicTaskInstance.getAssignee());
//获取并行网关执行后保留的执行实例数据
ExecutionChildByExecutionIdCmd childByExecutionIdCmd = new ExecutionChildByExecutionIdCmd(task.getExecutionId());
List<ExecutionEntity> executionEntities = managementService.executeCommand(childByExecutionIdCmd);
//删除流程实例垃圾数据
for (ExecutionEntity executionEntity : executionEntities) {
DeleteExecutionCmd deleteExecutionCmd = new DeleteExecutionCmd(executionEntity.getId());
managementService.executeCommand(deleteExecutionCmd);
}
runtimeService.updateBusinessStatus(processInstanceId, BusinessStatusEnum.CANCEL.getStatus());
//流程作废监听
flowProcessEventHandler.processHandler(processInstance.getProcessDefinitionKey(),
processInstance.getBusinessKey(), BusinessStatusEnum.CANCEL.getStatus(), false);
return true;
} catch (Exception e) {
log.error("撤销失败:" + e.getMessage(), e);
throw new ServiceException("撤销失败:" + e.getMessage());
}
}
/**
* 分页查询当前登录人单据
*
* @param bo 参数
*/
@Override
public TableDataInfo<ProcessInstanceVo> getPageByCurrent(ProcessInstanceBo bo, PageQuery pageQuery) {
List<ProcessInstanceVo> list = new ArrayList<>();
HistoricProcessInstanceQuery query = QueryUtils.hisInstanceQuery();
query.startedBy(String.valueOf(LoginHelper.getUserId()));
if (StringUtils.isNotBlank(bo.getName())) {
query.processInstanceNameLikeIgnoreCase("%" + bo.getName() + "%");
}
if (StringUtils.isNotBlank(bo.getKey())) {
query.processDefinitionKey(bo.getKey());
}
if (StringUtils.isNotBlank(bo.getBusinessKey())) {
query.processInstanceBusinessKey(bo.getBusinessKey());
}
if (StringUtils.isNotBlank(bo.getCategoryCode())) {
query.processDefinitionCategory(bo.getCategoryCode());
}
query.orderByProcessInstanceStartTime().desc();
List<HistoricProcessInstance> historicProcessInstanceList = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
List<TaskVo> taskVoList = new ArrayList<>();
if (CollUtil.isNotEmpty(historicProcessInstanceList)) {
List<String> processInstanceIds = StreamUtils.toList(historicProcessInstanceList, HistoricProcessInstance::getId);
List<Task> taskList = QueryUtils.taskQuery(processInstanceIds).list();
for (Task task : taskList) {
taskVoList.add(BeanUtil.toBean(task, TaskVo.class));
}
}
for (HistoricProcessInstance processInstance : historicProcessInstanceList) {
ProcessInstanceVo processInstanceVo = BeanUtil.toBean(processInstance, ProcessInstanceVo.class);
processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstance.getBusinessStatus()));
if (CollUtil.isNotEmpty(taskVoList)) {
List<TaskVo> collect = StreamUtils.filter(taskVoList, e -> e.getProcessInstanceId().equals(processInstance.getId()));
processInstanceVo.setTaskVoList(CollUtil.isNotEmpty(collect) ? collect : Collections.emptyList());
}
list.add(processInstanceVo);
}
if (CollUtil.isNotEmpty(list)) {
List<String> processDefinitionIds = StreamUtils.toList(list, ProcessInstanceVo::getProcessDefinitionId);
List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
for (ProcessInstanceVo processInstanceVo : list) {
if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(processInstanceVo.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(processInstanceVo::setWfNodeConfigVo);
}
}
}
long count = query.count();
TableDataInfo<ProcessInstanceVo> build = TableDataInfo.build();
build.setRows(list);
build.setTotal(count);
return build;
}
/**
* 任务催办(给当前任务办理人发送站内信,邮件,短信等)
*
* @param taskUrgingBo 任务催办
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean taskUrging(TaskUrgingBo taskUrgingBo) {
try {
ProcessInstance processInstance = QueryUtils.instanceQuery(taskUrgingBo.getProcessInstanceId()).singleResult();
if (processInstance == null) {
throw new ServiceException("任务已结束!");
}
String message = taskUrgingBo.getMessage();
if (StringUtils.isBlank(message)) {
message = "您的【" + processInstance.getName() + "】单据还未审批,请您及时处理。";
}
List<Task> list = QueryUtils.taskQuery(taskUrgingBo.getProcessInstanceId()).list();
WorkflowUtils.sendMessage(list, processInstance.getName(), taskUrgingBo.getMessageType(), message, userService);
} catch (ServiceException e) {
throw new ServiceException(e.getMessage());
}
return true;
}
}

View File

@@ -1,852 +0,0 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.dto.RoleDTO;
import org.dromara.common.core.domain.dto.UserDTO;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.OssService;
import org.dromara.common.core.service.UserService;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.domain.ActHiTaskinst;
import org.dromara.workflow.domain.WfTaskBackNode;
import org.dromara.workflow.domain.bo.*;
import org.dromara.workflow.domain.vo.*;
import org.dromara.workflow.flowable.cmd.*;
import org.dromara.workflow.flowable.handler.FlowProcessEventHandler;
import org.dromara.workflow.mapper.ActHiTaskinstMapper;
import org.dromara.workflow.mapper.ActTaskMapper;
import org.dromara.workflow.service.IActTaskService;
import org.dromara.workflow.service.IWfDefinitionConfigService;
import org.dromara.workflow.service.IWfNodeConfigService;
import org.dromara.workflow.service.IWfTaskBackNodeService;
import org.dromara.workflow.utils.ModelUtils;
import org.dromara.workflow.utils.QueryUtils;
import org.dromara.workflow.utils.WorkflowUtils;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.variable.api.persistence.entity.VariableInstance;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
import static org.dromara.workflow.common.constant.FlowConstant.*;
/**
* 任务 服务层实现
*
* @author may
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class ActTaskServiceImpl implements IActTaskService {
private final RuntimeService runtimeService;
private final TaskService taskService;
private final HistoryService historyService;
private final IdentityService identityService;
private final ManagementService managementService;
private final ActTaskMapper actTaskMapper;
private final IWfTaskBackNodeService wfTaskBackNodeService;
private final ActHiTaskinstMapper actHiTaskinstMapper;
private final IWfNodeConfigService wfNodeConfigService;
private final IWfDefinitionConfigService wfDefinitionConfigService;
private final FlowProcessEventHandler flowProcessEventHandler;
private final UserService userService;
private final OssService ossService;
/**
* 启动任务
*
* @param startProcessBo 启动流程参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> startWorkFlow(StartProcessBo startProcessBo) {
Map<String, Object> map = new HashMap<>();
if (StringUtils.isBlank(startProcessBo.getBusinessKey())) {
throw new ServiceException("启动工作流时必须包含业务ID");
}
// 判断当前业务是否启动过流程
HistoricProcessInstanceQuery query = QueryUtils.hisInstanceQuery();
HistoricProcessInstance historicProcessInstance = query.processInstanceBusinessKey(startProcessBo.getBusinessKey()).singleResult();
if (ObjectUtil.isNotEmpty(historicProcessInstance)) {
BusinessStatusEnum.checkStartStatus(historicProcessInstance.getBusinessStatus());
}
List<Task> taskResult = QueryUtils.taskQuery().processInstanceBusinessKey(startProcessBo.getBusinessKey()).list();
if (CollUtil.isNotEmpty(taskResult)) {
if (CollUtil.isNotEmpty(startProcessBo.getVariables())) {
taskService.setVariables(taskResult.get(0).getId(), startProcessBo.getVariables());
}
map.put(PROCESS_INSTANCE_ID, taskResult.get(0).getProcessInstanceId());
map.put("taskId", taskResult.get(0).getId());
return map;
}
WfDefinitionConfigVo wfDefinitionConfigVo = wfDefinitionConfigService.getByTableNameLastVersion(startProcessBo.getTableName());
if (wfDefinitionConfigVo == null) {
throw new ServiceException("请到流程定义绑定业务表名与流程KEY");
}
// 设置启动人
identityService.setAuthenticatedUserId(String.valueOf(LoginHelper.getUserId()));
Authentication.setAuthenticatedUserId(String.valueOf(LoginHelper.getUserId()));
// 启动流程实例(提交申请)
Map<String, Object> variables = startProcessBo.getVariables();
// 启动跳过表达式
variables.put(FLOWABLE_SKIP_EXPRESSION_ENABLED, true);
// 流程发起人
variables.put(INITIATOR, (String.valueOf(LoginHelper.getUserId())));
ProcessInstance pi;
try {
if (TenantHelper.isEnable()) {
pi = runtimeService.startProcessInstanceByKeyAndTenantId(wfDefinitionConfigVo.getProcessKey(), startProcessBo.getBusinessKey(), variables, TenantHelper.getTenantId());
} else {
pi = runtimeService.startProcessInstanceByKey(wfDefinitionConfigVo.getProcessKey(), startProcessBo.getBusinessKey(), variables);
}
} catch (FlowableObjectNotFoundException e) {
throw new ServiceException("找不到当前【" + wfDefinitionConfigVo.getProcessKey() + "】流程定义!");
}
// 将流程定义名称 作为 流程实例名称
runtimeService.setProcessInstanceName(pi.getProcessInstanceId(), pi.getProcessDefinitionName());
// 申请人执行流程
List<Task> taskList = QueryUtils.taskQuery(pi.getId()).list();
if (taskList.size() > 1) {
throw new ServiceException("请检查流程第一个环节是否为申请人!");
}
runtimeService.updateBusinessStatus(pi.getProcessInstanceId(), BusinessStatusEnum.DRAFT.getStatus());
taskService.setAssignee(taskList.get(0).getId(), LoginHelper.getUserId().toString());
taskService.setVariable(taskList.get(0).getId(), PROCESS_INSTANCE_ID, pi.getProcessInstanceId());
taskService.setVariable(taskList.get(0).getId(), BUSINESS_KEY, pi.getBusinessKey());
map.put("processInstanceId", pi.getProcessInstanceId());
map.put("taskId", taskList.get(0).getId());
return map;
}
/**
* 办理任务
*
* @param completeTaskBo 办理任务参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean completeTask(CompleteTaskBo completeTaskBo) {
try {
String userId = String.valueOf(LoginHelper.getUserId());
Task task = WorkflowUtils.getTaskByCurrentUser(completeTaskBo.getTaskId());
if (task == null) {
throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
}
if (task.isSuspended()) {
throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
}
ProcessInstance processInstance = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult();
//办理委托任务
if (ObjectUtil.isNotEmpty(task.getDelegationState()) && FlowConstant.PENDING.equals(task.getDelegationState().name())) {
taskService.resolveTask(completeTaskBo.getTaskId());
TaskEntity newTask = WorkflowUtils.createNewTask(task);
taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.PASS.getStatus(), StringUtils.isNotBlank(completeTaskBo.getMessage()) ? completeTaskBo.getMessage() : StrUtil.EMPTY);
taskService.complete(newTask.getId());
return true;
}
//附件上传
AttachmentCmd attachmentCmd = new AttachmentCmd(completeTaskBo.getFileId(), task.getId(), task.getProcessInstanceId(), ossService);
managementService.executeCommand(attachmentCmd);
String businessStatus = WorkflowUtils.getBusinessStatus(processInstance.getBusinessKey());
//流程提交监听
if (BusinessStatusEnum.DRAFT.getStatus().equals(businessStatus) || BusinessStatusEnum.BACK.getStatus().equals(businessStatus) || BusinessStatusEnum.CANCEL.getStatus().equals(businessStatus)) {
flowProcessEventHandler.processHandler(processInstance.getProcessDefinitionKey(), processInstance.getBusinessKey(), businessStatus, true);
}
runtimeService.updateBusinessStatus(task.getProcessInstanceId(), BusinessStatusEnum.WAITING.getStatus());
//办理监听
flowProcessEventHandler.processTaskHandler(processInstance.getProcessDefinitionKey(), task.getTaskDefinitionKey(),
task.getId(), processInstance.getBusinessKey());
//办理意见
taskService.addComment(completeTaskBo.getTaskId(), task.getProcessInstanceId(), TaskStatusEnum.PASS.getStatus(), StringUtils.isBlank(completeTaskBo.getMessage()) ? "同意" : completeTaskBo.getMessage());
//办理任务
taskService.setAssignee(task.getId(), userId);
if (CollUtil.isNotEmpty(completeTaskBo.getVariables())) {
taskService.complete(completeTaskBo.getTaskId(), completeTaskBo.getVariables());
} else {
taskService.complete(completeTaskBo.getTaskId());
}
//记录执行过的流程任务节点
wfTaskBackNodeService.recordExecuteNode(task);
ProcessInstance pi = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult();
if (pi == null) {
UpdateBusinessStatusCmd updateBusinessStatusCmd = new UpdateBusinessStatusCmd(task.getProcessInstanceId(), BusinessStatusEnum.FINISH.getStatus());
managementService.executeCommand(updateBusinessStatusCmd);
flowProcessEventHandler.processHandler(processInstance.getProcessDefinitionKey(), processInstance.getBusinessKey(),
BusinessStatusEnum.FINISH.getStatus(), false);
} else {
List<Task> list = QueryUtils.taskQuery(task.getProcessInstanceId()).list();
for (Task t : list) {
if (ModelUtils.isUserTask(t.getProcessDefinitionId(), t.getTaskDefinitionKey())) {
List<HistoricIdentityLink> links = historyService.getHistoricIdentityLinksForTask(t.getId());
if (CollUtil.isEmpty(links) && StringUtils.isBlank(t.getAssignee())) {
throw new ServiceException("下一节点【" + t.getName() + "】没有办理人!");
}
}
}
if (CollUtil.isNotEmpty(list) && CollUtil.isNotEmpty(completeTaskBo.getWfCopyList())) {
TaskEntity newTask = WorkflowUtils.createNewTask(task);
taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.COPY.getStatus(), LoginHelper.getLoginUser().getNickname() + "【抄送】给" + String.join(",", StreamUtils.toList(completeTaskBo.getWfCopyList(), WfCopy::getUserName)));
taskService.complete(newTask.getId());
List<Task> taskList = QueryUtils.taskQuery(task.getProcessInstanceId()).list();
WorkflowUtils.createCopyTask(taskList, StreamUtils.toList(completeTaskBo.getWfCopyList(), WfCopy::getUserId));
}
sendMessage(list, processInstance.getName(), completeTaskBo.getMessageType(), null);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 发送消息
*
* @param list 任务
* @param name 流程名称
* @param messageType 消息类型
* @param message 消息内容,为空则发送默认配置的消息内容
*/
@Async
public void sendMessage(List<Task> list, String name, List<String> messageType, String message) {
WorkflowUtils.sendMessage(list, name, messageType, message, userService);
}
/**
* 查询当前用户的待办任务
*
* @param taskBo 参数
*/
@Override
public TableDataInfo<TaskVo> getPageByTaskWait(TaskBo taskBo, PageQuery pageQuery) {
QueryWrapper<TaskVo> queryWrapper = new QueryWrapper<>();
List<RoleDTO> roles = LoginHelper.getLoginUser().getRoles();
List<String> roleIds = StreamUtils.toList(roles, e -> String.valueOf(e.getRoleId()));
String userId = String.valueOf(LoginHelper.getUserId());
queryWrapper.eq("t.business_status_", BusinessStatusEnum.WAITING.getStatus());
queryWrapper.eq(TenantHelper.isEnable(), "t.tenant_id_", TenantHelper.getTenantId());
String ids = StreamUtils.join(roleIds, x -> "'" + x + "'");
queryWrapper.and(w1 -> w1.eq("t.assignee_", userId).or(w2 -> w2.isNull("t.assignee_").apply("exists ( select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TASK_ID_ = t.ID_ and LINK.TYPE_ = 'candidate' and (LINK.USER_ID_ = {0} or ( LINK.GROUP_ID_ IN ({1}) ) ))", userId, ids)));
if (StringUtils.isNotBlank(taskBo.getName())) {
queryWrapper.like("t.name_", taskBo.getName());
}
if (StringUtils.isNotBlank(taskBo.getProcessDefinitionName())) {
queryWrapper.like("t.processDefinitionName", taskBo.getProcessDefinitionName());
}
if (StringUtils.isNotBlank(taskBo.getProcessDefinitionKey())) {
queryWrapper.eq("t.processDefinitionKey", taskBo.getProcessDefinitionKey());
}
Page<TaskVo> page = actTaskMapper.getTaskWaitByPage(pageQuery.build(), queryWrapper);
List<TaskVo> taskList = page.getRecords();
if (CollUtil.isNotEmpty(taskList)) {
List<String> processDefinitionIds = StreamUtils.toList(taskList, TaskVo::getProcessDefinitionId);
List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
for (TaskVo task : taskList) {
task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus()));
task.setParticipantVo(WorkflowUtils.getCurrentTaskParticipant(task.getId(), userService));
task.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null);
if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
}
}
}
return TableDataInfo.build(page);
}
/**
* 查询当前租户所有待办任务
*
* @param taskBo 参数
*/
@Override
public TableDataInfo<TaskVo> getPageByAllTaskWait(TaskBo taskBo, PageQuery pageQuery) {
TaskQuery query = QueryUtils.taskQuery();
if (StringUtils.isNotBlank(taskBo.getName())) {
query.taskNameLike("%" + taskBo.getName() + "%");
}
if (StringUtils.isNotBlank(taskBo.getProcessDefinitionName())) {
query.processDefinitionNameLike("%" + taskBo.getProcessDefinitionName() + "%");
}
if (StringUtils.isNotBlank(taskBo.getProcessDefinitionKey())) {
query.processDefinitionKey(taskBo.getProcessDefinitionKey());
}
query.orderByTaskCreateTime().desc();
List<Task> taskList = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
List<ProcessInstance> processInstanceList = null;
if (CollUtil.isNotEmpty(taskList)) {
Set<String> processInstanceIds = StreamUtils.toSet(taskList, Task::getProcessInstanceId);
processInstanceList = QueryUtils.instanceQuery(processInstanceIds).list();
}
List<TaskVo> list = new ArrayList<>();
if (CollUtil.isNotEmpty(taskList)) {
List<String> processDefinitionIds = StreamUtils.toList(taskList, Task::getProcessDefinitionId);
List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
for (Task task : taskList) {
TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
if (CollUtil.isNotEmpty(processInstanceList)) {
processInstanceList.stream().filter(e -> e.getId().equals(task.getProcessInstanceId())).findFirst().ifPresent(e -> {
taskVo.setBusinessStatus(e.getBusinessStatus());
taskVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(taskVo.getBusinessStatus()));
taskVo.setProcessDefinitionKey(e.getProcessDefinitionKey());
taskVo.setProcessDefinitionName(e.getProcessDefinitionName());
taskVo.setProcessDefinitionVersion(e.getProcessDefinitionVersion());
taskVo.setBusinessKey(e.getBusinessKey());
});
}
taskVo.setAssignee(StringUtils.isNotBlank(task.getAssignee()) ? Long.valueOf(task.getAssignee()) : null);
taskVo.setParticipantVo(WorkflowUtils.getCurrentTaskParticipant(task.getId(), userService));
taskVo.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null);
if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(taskVo::setWfNodeConfigVo);
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(taskVo::setWfNodeConfigVo);
}
list.add(taskVo);
}
}
long count = query.count();
TableDataInfo<TaskVo> build = TableDataInfo.build();
build.setRows(list);
build.setTotal(count);
return build;
}
/**
* 查询当前用户的已办任务
*
* @param taskBo 参数
*/
@Override
public TableDataInfo<TaskVo> getPageByTaskFinish(TaskBo taskBo, PageQuery pageQuery) {
String userId = String.valueOf(LoginHelper.getUserId());
QueryWrapper<TaskVo> queryWrapper = new QueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(taskBo.getName()), "t.name_", taskBo.getName());
queryWrapper.like(StringUtils.isNotBlank(taskBo.getProcessDefinitionName()), "t.processDefinitionName", taskBo.getProcessDefinitionName());
queryWrapper.eq(StringUtils.isNotBlank(taskBo.getProcessDefinitionKey()), "t.processDefinitionKey", taskBo.getProcessDefinitionKey());
queryWrapper.eq("t.assignee_", userId);
Page<TaskVo> page = actTaskMapper.getTaskFinishByPage(pageQuery.build(), queryWrapper);
List<TaskVo> taskList = page.getRecords();
if (CollUtil.isNotEmpty(taskList)) {
List<String> processDefinitionIds = StreamUtils.toList(taskList, TaskVo::getProcessDefinitionId);
List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
for (TaskVo task : taskList) {
task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus()));
if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
}
}
}
return TableDataInfo.build(page);
}
/**
* 查询当前用户的抄送
*
* @param taskBo 参数
*/
@Override
public TableDataInfo<TaskVo> getPageByTaskCopy(TaskBo taskBo, PageQuery pageQuery) {
QueryWrapper<TaskVo> queryWrapper = new QueryWrapper<>();
String userId = String.valueOf(LoginHelper.getUserId());
if (StringUtils.isNotBlank(taskBo.getName())) {
queryWrapper.like("t.name_", taskBo.getName());
}
if (StringUtils.isNotBlank(taskBo.getProcessDefinitionName())) {
queryWrapper.like("t.processDefinitionName", taskBo.getProcessDefinitionName());
}
if (StringUtils.isNotBlank(taskBo.getProcessDefinitionKey())) {
queryWrapper.eq("t.processDefinitionKey", taskBo.getProcessDefinitionKey());
}
queryWrapper.eq("t.assignee_", userId);
Page<TaskVo> page = actTaskMapper.getTaskCopyByPage(pageQuery.build(), queryWrapper);
List<TaskVo> taskList = page.getRecords();
if (CollUtil.isNotEmpty(taskList)) {
List<String> processDefinitionIds = StreamUtils.toList(taskList, TaskVo::getProcessDefinitionId);
List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
for (TaskVo task : taskList) {
task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus()));
if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
}
}
}
return TableDataInfo.build(page);
}
/**
* 查询当前租户所有已办任务
*
* @param taskBo 参数
*/
@Override
public TableDataInfo<TaskVo> getPageByAllTaskFinish(TaskBo taskBo, PageQuery pageQuery) {
QueryWrapper<TaskVo> queryWrapper = new QueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(taskBo.getName()), "t.name_", taskBo.getName());
queryWrapper.like(StringUtils.isNotBlank(taskBo.getProcessDefinitionName()), "t.processDefinitionName", taskBo.getProcessDefinitionName());
queryWrapper.eq(StringUtils.isNotBlank(taskBo.getProcessDefinitionKey()), "t.processDefinitionKey", taskBo.getProcessDefinitionKey());
Page<TaskVo> page = actTaskMapper.getTaskFinishByPage(pageQuery.build(), queryWrapper);
List<TaskVo> taskList = page.getRecords();
if (CollUtil.isNotEmpty(taskList)) {
List<String> processDefinitionIds = StreamUtils.toList(taskList, TaskVo::getProcessDefinitionId);
List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
for (TaskVo task : taskList) {
task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus()));
if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
}
}
}
return TableDataInfo.build(page);
}
/**
* 委派任务
*
* @param delegateBo 参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean delegateTask(DelegateBo delegateBo) {
Task task = WorkflowUtils.getTaskByCurrentUser(delegateBo.getTaskId());
if (ObjectUtil.isEmpty(task)) {
throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
}
if (task.isSuspended()) {
throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
}
try {
TaskEntity newTask = WorkflowUtils.createNewTask(task);
taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.PENDING.getStatus(), "" + LoginHelper.getLoginUser().getNickname() + "】委派给【" + delegateBo.getNickName() + "");
//委托任务
taskService.delegateTask(delegateBo.getTaskId(), delegateBo.getUserId());
//办理生成的任务记录
taskService.complete(newTask.getId());
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 终止任务
*
* @param terminationBo 参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean terminationTask(TerminationBo terminationBo) {
TaskQuery query = QueryUtils.taskQuery();
Task task = query.taskId(terminationBo.getTaskId()).singleResult();
if (ObjectUtil.isEmpty(task)) {
throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
}
if (task.isSuspended()) {
throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
}
HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
BusinessStatusEnum.checkInvalidStatus(historicProcessInstance.getBusinessStatus());
try {
if (StringUtils.isBlank(terminationBo.getComment())) {
terminationBo.setComment(LoginHelper.getLoginUser().getNickname() + "终止了申请");
} else {
terminationBo.setComment(LoginHelper.getLoginUser().getNickname() + "终止了申请:" + terminationBo.getComment());
}
taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.TERMINATION.getStatus(), terminationBo.getComment());
List<Task> list = QueryUtils.taskQuery(task.getProcessInstanceId()).list();
if (CollUtil.isNotEmpty(list)) {
List<Task> subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId()));
if (CollUtil.isNotEmpty(subTasks)) {
subTasks.forEach(e -> taskService.deleteTask(e.getId()));
}
runtimeService.updateBusinessStatus(task.getProcessInstanceId(), BusinessStatusEnum.TERMINATION.getStatus());
runtimeService.deleteProcessInstance(task.getProcessInstanceId(), StrUtil.EMPTY);
}
//流程终止监听
flowProcessEventHandler.processHandler(historicProcessInstance.getProcessDefinitionKey(),
historicProcessInstance.getBusinessKey(), BusinessStatusEnum.TERMINATION.getStatus(), false);
return true;
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
}
/**
* 转办任务
*
* @param transmitBo 参数
*/
@Override
public boolean transferTask(TransmitBo transmitBo) {
Task task = WorkflowUtils.getTaskByCurrentUser(transmitBo.getTaskId());
if (ObjectUtil.isEmpty(task)) {
throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
}
if (task.isSuspended()) {
throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
}
try {
TaskEntity newTask = WorkflowUtils.createNewTask(task);
taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.TRANSFER.getStatus(), StringUtils.isNotBlank(transmitBo.getComment()) ? transmitBo.getComment() : LoginHelper.getUsername() + "转办了任务");
taskService.complete(newTask.getId());
taskService.setAssignee(task.getId(), transmitBo.getUserId());
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 会签任务加签
*
* @param addMultiBo 参数
*/
@Override
public boolean addMultiInstanceExecution(AddMultiBo addMultiBo) {
TaskQuery taskQuery = QueryUtils.taskQuery();
taskQuery.taskId(addMultiBo.getTaskId());
if (!LoginHelper.isSuperAdmin() && !LoginHelper.isTenantAdmin()) {
taskQuery.taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId()));
}
Task task = taskQuery.singleResult();
if (ObjectUtil.isEmpty(task)) {
throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
}
if (task.isSuspended()) {
throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
}
String taskDefinitionKey = task.getTaskDefinitionKey();
String processInstanceId = task.getProcessInstanceId();
String processDefinitionId = task.getProcessDefinitionId();
try {
MultiInstanceVo multiInstanceVo = WorkflowUtils.isMultiInstance(processDefinitionId, taskDefinitionKey);
if (multiInstanceVo == null) {
throw new ServiceException("当前环节不是会签节点");
}
if (multiInstanceVo.getType() instanceof ParallelMultiInstanceBehavior) {
for (Long assignee : addMultiBo.getAssignees()) {
runtimeService.addMultiInstanceExecution(taskDefinitionKey, processInstanceId, Collections.singletonMap(multiInstanceVo.getAssignee(), assignee));
}
} else if (multiInstanceVo.getType() instanceof SequentialMultiInstanceBehavior) {
AddSequenceMultiInstanceCmd addSequenceMultiInstanceCmd = new AddSequenceMultiInstanceCmd(task.getExecutionId(), multiInstanceVo.getAssigneeList(), addMultiBo.getAssignees());
managementService.executeCommand(addSequenceMultiInstanceCmd);
}
List<String> assigneeNames = addMultiBo.getAssigneeNames();
String username = LoginHelper.getUsername();
TaskEntity newTask = WorkflowUtils.createNewTask(task);
taskService.addComment(newTask.getId(), processInstanceId, TaskStatusEnum.SIGN.getStatus(), username + "加签【" + String.join(StringUtils.SEPARATOR, assigneeNames) + "");
taskService.complete(newTask.getId());
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 会签任务减签
*
* @param deleteMultiBo 参数
*/
@Override
public boolean deleteMultiInstanceExecution(DeleteMultiBo deleteMultiBo) {
TaskQuery taskQuery = QueryUtils.taskQuery();
taskQuery.taskId(deleteMultiBo.getTaskId());
if (!LoginHelper.isSuperAdmin() && !LoginHelper.isTenantAdmin()) {
taskQuery.taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId()));
}
Task task = taskQuery.singleResult();
if (ObjectUtil.isEmpty(task)) {
throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
}
if (task.isSuspended()) {
throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
}
String taskDefinitionKey = task.getTaskDefinitionKey();
String processInstanceId = task.getProcessInstanceId();
String processDefinitionId = task.getProcessDefinitionId();
try {
MultiInstanceVo multiInstanceVo = WorkflowUtils.isMultiInstance(processDefinitionId, taskDefinitionKey);
if (multiInstanceVo == null) {
throw new ServiceException("当前环节不是会签节点");
}
if (multiInstanceVo.getType() instanceof ParallelMultiInstanceBehavior) {
for (String executionId : deleteMultiBo.getExecutionIds()) {
runtimeService.deleteMultiInstanceExecution(executionId, false);
}
for (String taskId : deleteMultiBo.getTaskIds()) {
historyService.deleteHistoricTaskInstance(taskId);
}
} else if (multiInstanceVo.getType() instanceof SequentialMultiInstanceBehavior) {
DeleteSequenceMultiInstanceCmd deleteSequenceMultiInstanceCmd = new DeleteSequenceMultiInstanceCmd(task.getAssignee(), task.getExecutionId(), multiInstanceVo.getAssigneeList(), deleteMultiBo.getAssigneeIds());
managementService.executeCommand(deleteSequenceMultiInstanceCmd);
}
List<String> assigneeNames = deleteMultiBo.getAssigneeNames();
String username = LoginHelper.getUsername();
TaskEntity newTask = WorkflowUtils.createNewTask(task);
taskService.addComment(newTask.getId(), processInstanceId, TaskStatusEnum.SIGN_OFF.getStatus(), username + "减签【" + String.join(StringUtils.SEPARATOR, assigneeNames) + "");
taskService.complete(newTask.getId());
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 驳回审批
*
* @param backProcessBo 参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String backProcess(BackProcessBo backProcessBo) {
String userId = String.valueOf(LoginHelper.getUserId());
Task task = WorkflowUtils.getTaskByCurrentUser(backProcessBo.getTaskId());
if (ObjectUtil.isEmpty(task)) {
throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
}
if (task.isSuspended()) {
throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
}
try {
String processInstanceId = task.getProcessInstanceId();
ProcessInstance processInstance = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult();
//获取并行网关执行后保留的执行实例数据
ExecutionChildByExecutionIdCmd childByExecutionIdCmd = new ExecutionChildByExecutionIdCmd(task.getExecutionId());
List<ExecutionEntity> executionEntities = managementService.executeCommand(childByExecutionIdCmd);
//校验单据
BusinessStatusEnum.checkBackStatus(processInstance.getBusinessStatus());
//判断是否有多个任务
List<Task> taskList = QueryUtils.taskQuery(processInstanceId).list();
String backTaskDefinitionKey = backProcessBo.getTargetActivityId();
taskService.addComment(task.getId(), processInstanceId, TaskStatusEnum.BACK.getStatus(), StringUtils.isNotBlank(backProcessBo.getMessage()) ? backProcessBo.getMessage() : "退回");
if (taskList.size() > 1) {
//当前多个任务驳回到单个节点
runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveActivityIdsToSingleActivityId(taskList.stream().map(Task::getTaskDefinitionKey).distinct().collect(Collectors.toList()), backTaskDefinitionKey).changeState();
ActHiTaskinst actHiTaskinst = new ActHiTaskinst();
actHiTaskinst.setAssignee(userId);
actHiTaskinst.setId(task.getId());
actHiTaskinstMapper.updateById(actHiTaskinst);
} else {
//当前单个节点驳回单个节点
runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveActivityIdTo(task.getTaskDefinitionKey(), backTaskDefinitionKey).changeState();
}
//删除并行环节未办理记录
MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
if (multiInstance == null && taskList.size() > 1) {
List<Task> tasks = StreamUtils.filter(taskList, e -> !e.getTaskDefinitionKey().equals(task.getTaskDefinitionKey()));
if (CollUtil.isNotEmpty(tasks)) {
actHiTaskinstMapper.deleteByIds(StreamUtils.toList(tasks, Task::getId));
}
}
List<HistoricTaskInstance> instanceList = QueryUtils.hisTaskInstanceQuery(processInstanceId).finished().orderByHistoricTaskInstanceEndTime().desc().list();
List<Task> list = QueryUtils.taskQuery(processInstanceId).list();
for (Task t : list) {
instanceList.stream().filter(e -> e.getTaskDefinitionKey().equals(t.getTaskDefinitionKey())).findFirst().ifPresent(e -> {
taskService.setAssignee(t.getId(), e.getAssignee());
});
}
//发送消息
String message = "您的【" + processInstance.getName() + "】单据已经被驳回,请您注意查收。";
sendMessage(list, processInstance.getName(), backProcessBo.getMessageType(), message);
//删除流程实例垃圾数据
for (ExecutionEntity executionEntity : executionEntities) {
DeleteExecutionCmd deleteExecutionCmd = new DeleteExecutionCmd(executionEntity.getId());
managementService.executeCommand(deleteExecutionCmd);
}
WfTaskBackNode wfTaskBackNode = wfTaskBackNodeService.getListByInstanceIdAndNodeId(task.getProcessInstanceId(), backProcessBo.getTargetActivityId());
if (ObjectUtil.isNotNull(wfTaskBackNode) && wfTaskBackNode.getOrderNo() == 0) {
runtimeService.updateBusinessStatus(processInstanceId, BusinessStatusEnum.BACK.getStatus());
flowProcessEventHandler.processHandler(processInstance.getProcessDefinitionKey(),
processInstance.getBusinessKey(), BusinessStatusEnum.BACK.getStatus(), false);
}
//删除驳回后的流程节点
wfTaskBackNodeService.deleteBackTaskNode(processInstanceId, backProcessBo.getTargetActivityId());
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
return task.getProcessInstanceId();
}
/**
* 修改任务办理人
*
* @param taskIds 任务id
* @param userId 办理人id
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateAssignee(String[] taskIds, String userId) {
try {
List<Task> list = QueryUtils.taskQuery().taskIds(Arrays.asList(taskIds)).list();
for (Task task : list) {
taskService.setAssignee(task.getId(), userId);
}
} catch (Exception e) {
log.error("修改失败:" + e.getMessage(), e);
throw new ServiceException("修改失败:" + e.getMessage());
}
return true;
}
/**
* 查询流程变量
*
* @param taskId 任务id
*/
@Override
public List<VariableVo> getInstanceVariable(String taskId) {
List<VariableVo> variableVoList = new ArrayList<>();
Map<String, VariableInstance> variableInstances = taskService.getVariableInstances(taskId);
if (CollUtil.isNotEmpty(variableInstances)) {
for (Map.Entry<String, VariableInstance> entry : variableInstances.entrySet()) {
VariableVo variableVo = new VariableVo();
variableVo.setKey(entry.getKey());
variableVo.setValue(entry.getValue().getValue().toString());
variableVoList.add(variableVo);
}
}
return variableVoList;
}
/**
* 查询工作流任务用户选择加签人员
*
* @param taskId 任务id
* @return
*/
@Override
@SuppressWarnings("unchecked")
public String getTaskUserIdsByAddMultiInstance(String taskId) {
Task task = QueryUtils.taskQuery().taskId(taskId).singleResult();
if (task == null) {
throw new ServiceException("任务不存在");
}
MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
if (multiInstance == null) {
return "";
}
List<Long> userIds;
if (multiInstance.getType() instanceof SequentialMultiInstanceBehavior) {
userIds = (List<Long>) runtimeService.getVariable(task.getExecutionId(), multiInstance.getAssigneeList());
} else {
List<Task> list = QueryUtils.taskQuery(task.getProcessInstanceId()).list();
userIds = StreamUtils.toList(list, e -> Long.valueOf(e.getAssignee()));
}
return StringUtils.join(userIds, StringUtils.SEPARATOR);
}
/**
* 查询工作流选择减签人员
*
* @param taskId 任务id 任务id
*/
@Override
@SuppressWarnings("unchecked")
public List<TaskVo> getListByDeleteMultiInstance(String taskId) {
Task task = QueryUtils.taskQuery().taskId(taskId).singleResult();
List<Task> taskList = QueryUtils.taskQuery(task.getProcessInstanceId()).list();
MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
List<TaskVo> taskListVo = new ArrayList<>();
if (multiInstance == null) {
return List.of();
}
List<Long> assigneeList = new ArrayList<>();
if (multiInstance.getType() instanceof SequentialMultiInstanceBehavior) {
List<Object> variable = (List<Object>) runtimeService.getVariable(task.getExecutionId(), multiInstance.getAssigneeList());
for (Object o : variable) {
assigneeList.add(Long.valueOf(o.toString()));
}
}
if (multiInstance.getType() instanceof SequentialMultiInstanceBehavior) {
List<Long> userIds = StreamUtils.filter(assigneeList, e -> !String.valueOf(e).equals(task.getAssignee()));
List<UserDTO> userList = userService.selectListByIds(userIds);
for (Long userId : userIds) {
TaskVo taskVo = new TaskVo();
taskVo.setId("串行会签");
taskVo.setExecutionId("串行会签");
taskVo.setProcessInstanceId(task.getProcessInstanceId());
taskVo.setName(task.getName());
taskVo.setAssignee(userId);
if (CollUtil.isNotEmpty(userList)) {
userList.stream().filter(u -> u.getUserId().toString().equals(userId.toString())).findFirst().ifPresent(u -> taskVo.setAssigneeName(u.getNickName()));
}
taskListVo.add(taskVo);
}
return taskListVo;
} else if (multiInstance.getType() instanceof ParallelMultiInstanceBehavior) {
List<Task> tasks = StreamUtils.filter(taskList, e -> StringUtils.isBlank(e.getParentTaskId()) && !e.getExecutionId().equals(task.getExecutionId()) && e.getTaskDefinitionKey().equals(task.getTaskDefinitionKey()));
if (CollUtil.isNotEmpty(tasks)) {
List<Long> userIds = StreamUtils.toList(tasks, e -> Long.valueOf(e.getAssignee()));
List<UserDTO> userList = userService.selectListByIds(userIds);
for (Task t : tasks) {
TaskVo taskVo = new TaskVo();
taskVo.setId(t.getId());
taskVo.setExecutionId(t.getExecutionId());
taskVo.setProcessInstanceId(t.getProcessInstanceId());
taskVo.setName(t.getName());
taskVo.setAssignee(Long.valueOf(t.getAssignee()));
if (CollUtil.isNotEmpty(userList)) {
userList.stream().filter(u -> u.getUserId().toString().equals(t.getAssignee())).findFirst().ifPresent(e -> taskVo.setAssigneeName(e.getNickName()));
}
taskListVo.add(taskVo);
}
return taskListVo;
}
}
return List.of();
}
}

View File

@@ -0,0 +1,77 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.warm.flow.core.entity.Definition;
import com.warm.flow.core.service.DefService;
import com.warm.flow.core.utils.page.Page;
import com.warm.flow.orm.entity.FlowDefinition;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.workflow.domain.vo.FlowDefinitionVo;
import org.dromara.workflow.service.IFlwDefinitionService;
import org.springframework.stereotype.Service;
import java.io.IOException;
/**
* 流程定义 服务层实现
*
* @author may
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class FlwDefinitionServiceImpl implements IFlwDefinitionService {
private final DefService defService;
/**
* 分页查询
*
* @param flowDefinition 参数
* @return 返回分页列表
*/
@Override
public TableDataInfo<FlowDefinitionVo> page(FlowDefinition flowDefinition, PageQuery pageQuery) {
Page<Definition> page = Page.pageOf(pageQuery.getPageNum(), pageQuery.getPageSize());
page = defService.orderByCreateTime().desc().page(flowDefinition, page);
TableDataInfo<FlowDefinitionVo> build = TableDataInfo.build();
build.setRows(BeanUtil.copyToList(page.getList(), FlowDefinitionVo.class));
build.setTotal(page.getTotal());
return build;
}
/**
* 导出流程定义
*
* @param id 流程定义id
* @param response 响应
* @throws IOException 异常
*/
@Override
public void exportDefinition(Long id, HttpServletResponse response) throws IOException {
Document document = defService.exportXml(id);
// 设置生成xml的格式
OutputFormat of = OutputFormat.createPrettyPrint();
// 设置编码格式
of.setEncoding("UTF-8");
of.setIndent(true);
of.setIndent(" ");
of.setNewlines(true);
// 创建一个xml文档编辑器
XMLWriter writer = new XMLWriter(response.getOutputStream(), of);
writer.setEscapeText(false);
response.reset();
response.setCharacterEncoding("UTF-8");
response.setContentType("application/x-msdownload");
response.setHeader("Content-Disposition", "attachment;");
writer.write(document);
writer.close();
}
}

View File

@@ -0,0 +1,157 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.warm.flow.core.FlowFactory;
import com.warm.flow.core.constant.ExceptionCons;
import com.warm.flow.core.dto.FlowParams;
import com.warm.flow.core.entity.*;
import com.warm.flow.core.enums.FlowStatus;
import com.warm.flow.core.enums.NodeType;
import com.warm.flow.core.enums.SkipType;
import com.warm.flow.core.service.DefService;
import com.warm.flow.core.service.InsService;
import com.warm.flow.core.service.NodeService;
import com.warm.flow.core.service.TaskService;
import com.warm.flow.core.utils.AssertUtil;
import com.warm.flow.core.utils.page.Page;
import com.warm.flow.orm.entity.FlowInstance;
import com.warm.flow.orm.mapper.FlowInstanceMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.workflow.service.IFlwInstanceService;
import org.dromara.workflow.utils.WorkflowUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 流程实例 服务层实现
*
* @author may
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class FlwInstanceServiceImpl implements IFlwInstanceService {
private final InsService insService;
private final NodeService nodeService;
private final DefService defService;
private final TaskService taskService;
private final FlowInstanceMapper flowInstanceMapper;
/**
* 分页查询正在运行的流程实例
*
* @param instance 参数
* @param pageQuery 分页
*/
@Override
public TableDataInfo<Instance> getPageByRunning(Instance instance, PageQuery pageQuery) {
Page<Instance> page = Page.pageOf(pageQuery.getPageNum(), pageQuery.getPageSize());
instance.setFlowStatus(FlowStatus.APPROVAL.getKey());
page = insService.orderByCreateTime().desc().page(instance, page);
TableDataInfo<Instance> build = TableDataInfo.build();
build.setRows(page.getList());
build.setTotal(page.getTotal());
return build;
}
/**
* 分页查询已结束的流程实例
*
* @param instance 参数
* @param pageQuery 分页
*/
@Override
public TableDataInfo<Instance> getPageByFinish(Instance instance, PageQuery pageQuery) {
Page<Instance> page = Page.pageOf(pageQuery.getPageNum(), pageQuery.getPageSize());
instance.setFlowStatus(FlowStatus.FINISHED.getKey());
page = insService.orderByCreateTime().desc().page(instance, page);
TableDataInfo<Instance> build = TableDataInfo.build();
build.setRows(page.getList());
build.setTotal(page.getTotal());
return build;
}
/**
* 根据业务id查询流程实例
*
* @param businessId 业务id
*/
@Override
public FlowInstance instanceByBusinessId(String businessId) {
return flowInstanceMapper.selectOne(new LambdaQueryWrapper<FlowInstance>().eq(FlowInstance::getBusinessId, businessId));
}
/**
* 按照业务id删除流程实例
*
* @param businessIds 业务id
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteByBusinessIds(List<Long> businessIds) {
List<FlowInstance> flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper<FlowInstance>().in(FlowInstance::getBusinessId, businessIds));
if (CollUtil.isEmpty(flowInstances)) {
return false;
}
return insService.remove(StreamUtils.toList(flowInstances, FlowInstance::getId));
}
/**
* 按照实例id删除流程实例
*
* @param instanceIds 实例id
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteByInstanceIds(List<Long> instanceIds) {
return insService.remove(instanceIds);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean cancelProcessApply(String businessId) {
FlowInstance flowInstance = instanceByBusinessId(businessId);
if (ObjectUtil.isNull(flowInstance)) {
return false;
}
try {
Definition definition = defService.getById(flowInstance.getDefinitionId());
List<Task> list = taskService.list(FlowFactory.newTask().setInstanceId(flowInstance.getId()));
// 获取已发布的流程节点
List<Node> nodes = nodeService.getByFlowCode(definition.getFlowCode());
AssertUtil.isTrue(CollUtil.isEmpty(nodes), ExceptionCons.NOT_PUBLISH_NODE);
// 获取开始节点
Node startNode = nodes.stream().filter(t -> NodeType.isStart(t.getNodeType())).findFirst().orElse(null);
AssertUtil.isNull(startNode, ExceptionCons.LOST_START_NODE);
// 获取下一个节点,如果是网关节点,则重新获取后续节点
List<Node> nextNodes = FlowFactory.taskService().getNextByCheckGateWay(new FlowParams(), getFirstBetween(startNode));
Node node = nextNodes.get(0);
FlowParams flowParams = FlowParams.build()
.nodeCode(node.getNodeCode())
.skipType(SkipType.PASS.getKey())
.permissionFlag(WorkflowUtils.permissionList());
taskService.skip(list.get(0).getId(), flowParams);
} catch (Exception e) {
throw new RuntimeException(e);
}
return false;
}
private Node getFirstBetween(Node startNode) {
List<Skip> skips = FlowFactory.skipService().list(FlowFactory.newSkip()
.setDefinitionId(startNode.getDefinitionId()).setNowNodeCode(startNode.getNodeCode()));
Skip skip = skips.get(0);
return FlowFactory.nodeService().getOne(FlowFactory.newNode().setDefinitionId(startNode.getDefinitionId())
.setNodeCode(skip.getNextNodeCode()));
}
}

View File

@@ -0,0 +1,204 @@
package org.dromara.workflow.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.warm.flow.core.dto.FlowParams;
import com.warm.flow.core.entity.Instance;
import com.warm.flow.core.entity.Task;
import com.warm.flow.core.enums.SkipType;
import com.warm.flow.core.service.InsService;
import com.warm.flow.core.service.TaskService;
import com.warm.flow.orm.entity.FlowInstance;
import com.warm.flow.orm.entity.FlowTask;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.workflow.domain.bo.*;
import org.dromara.workflow.domain.vo.*;
import org.dromara.workflow.mapper.FlwTaskMapper;
import org.dromara.workflow.service.IFlwInstanceService;
import org.dromara.workflow.service.IFlwTaskService;
import org.dromara.workflow.service.IWfDefinitionConfigService;
import org.dromara.workflow.utils.WorkflowUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import static org.dromara.workflow.common.constant.FlowConstant.*;
/**
* 任务 服务层实现
*
* @author may
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class FlwTaskServiceImpl implements IFlwTaskService {
private final TaskService taskService;
private final InsService insService;
private final FlwTaskMapper flwTaskMapper;
private final IWfDefinitionConfigService wfDefinitionConfigService;
private final IFlwInstanceService iFlwInstanceService;
/**
* 启动任务
*
* @param startProcessBo 启动流程参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> startWorkFlow(StartProcessBo startProcessBo) {
Map<String, Object> map = new HashMap<>(16);
if (StringUtils.isBlank(startProcessBo.getBusinessKey())) {
throw new ServiceException("启动工作流时必须包含业务ID");
}
// 启动流程实例(提交申请)
Map<String, Object> variables = startProcessBo.getVariables();
// 流程发起人
variables.put(INITIATOR, (String.valueOf(LoginHelper.getUserId())));
// 业务id
variables.put(BUSINESS_KEY, startProcessBo.getBusinessKey());
WfDefinitionConfigVo wfDefinitionConfigVo = wfDefinitionConfigService.getByTableNameLastVersion(startProcessBo.getTableName());
if (wfDefinitionConfigVo == null) {
throw new ServiceException("请到流程定义绑定业务表名与流程KEY");
}
FlowInstance flowInstance = iFlwInstanceService.instanceByBusinessId(startProcessBo.getBusinessKey());
if (flowInstance != null) {
List<Task> taskList = taskService.list(new FlowTask().setInstanceId(flowInstance.getId()));
map.put("processInstanceId", taskList.get(0).getInstanceId());
map.put("taskId", taskList.get(0).getId());
return map;
}
FlowParams flowParams = new FlowParams();
flowParams.flowCode(wfDefinitionConfigVo.getProcessKey());
flowParams.variable(startProcessBo.getVariables());
Instance instance;
try {
instance = insService.start(startProcessBo.getBusinessKey(), flowParams);
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
// 申请人执行流程
List<Task> taskList = taskService.list(new FlowTask().setInstanceId(instance.getId()));
if (taskList.size() > 1) {
throw new ServiceException("请检查流程第一个环节是否为申请人!");
}
map.put("processInstanceId", instance.getId());
map.put("taskId", taskList.get(0).getId());
return map;
}
/**
* 办理任务
*
* @param completeTaskBo 办理任务参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean completeTask(CompleteTaskBo completeTaskBo) {
try {
String userId = String.valueOf(LoginHelper.getUserId());
Long taskId = completeTaskBo.getTaskId();
FlowParams flowParams = new FlowParams();
flowParams.variable(completeTaskBo.getVariables());
flowParams.skipType(SkipType.PASS.getKey());
flowParams.message(completeTaskBo.getMessage());
flowParams.handler(userId);
flowParams.permissionFlag(WorkflowUtils.permissionList());
taskService.skip(taskId, flowParams);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
/**
* 查询当前用户的待办任务
*
* @param flowTaskBo 参数
* @param pageQuery 分页
*/
@Override
public TableDataInfo<FlowTaskVo> getPageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
QueryWrapper<FlowTaskBo> queryWrapper = getFlowTaskBoQueryWrapper(flowTaskBo);
queryWrapper.in("t.processed_by", WorkflowUtils.permissionList());
Page<FlowTaskVo> page = flwTaskMapper.getTaskWaitByPage(pageQuery.build(), queryWrapper);
return TableDataInfo.build(page);
}
/**
* 查询当前用户的已办任务
*
* @param flowTaskBo 参数
* @param pageQuery 分页
*/
@Override
public TableDataInfo<FlowHisTaskVo> getPageByTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
QueryWrapper<FlowTaskBo> queryWrapper = getFlowTaskBoQueryWrapper(flowTaskBo);
flowTaskBo.setPermissionList(WorkflowUtils.permissionList());
Page<FlowHisTaskVo> page = flwTaskMapper.getTaskFinishByPage(pageQuery.build(), queryWrapper, flowTaskBo);
return TableDataInfo.build(page);
}
/**
* 查询当前用户的抄送
*
* @param flowTaskBo 参数
* @param pageQuery 分页
*/
@Override
public TableDataInfo<FlowTaskVo> getPageByTaskCopy(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
QueryWrapper<FlowTaskBo> queryWrapper = getFlowTaskBoQueryWrapper(flowTaskBo);
flowTaskBo.setPermissionList(WorkflowUtils.permissionList());
Page<FlowTaskVo> page = flwTaskMapper.getTaskCopyByPage(pageQuery.build(), queryWrapper);
return TableDataInfo.build(page);
}
/**
* 查询参数
*
* @param flowTaskBo 参数
*/
private QueryWrapper<FlowTaskBo> getFlowTaskBoQueryWrapper(FlowTaskBo flowTaskBo) {
QueryWrapper<FlowTaskBo> queryWrapper = new QueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(flowTaskBo.getNodeName()), "t.node_name", flowTaskBo.getNodeName());
queryWrapper.like(StringUtils.isNotBlank(flowTaskBo.getFlowName()), "t.flow_name", flowTaskBo.getFlowName());
queryWrapper.eq(StringUtils.isNotBlank(flowTaskBo.getFlowCode()), "t.flow_code", flowTaskBo.getFlowCode());
return queryWrapper;
}
/**
* 驳回任务
*
* @param bo 参数
*/
@Override
public boolean backProcess(BackProcessBo bo) {
try {
String userId = String.valueOf(LoginHelper.getUserId());
Long taskId = bo.getTaskId();
FlowParams flowParams = new FlowParams();
flowParams.variable(bo.getVariables());
flowParams.skipType(SkipType.REJECT.getKey());
flowParams.message(bo.getMessage());
flowParams.handler(userId);
flowParams.nodeCode(bo.getTargetNodeCode());
taskService.skip(taskId, flowParams);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException(e.getMessage());
}
}
}

View File

@@ -109,8 +109,8 @@ public class TestLeaveServiceImpl implements ITestLeaveService {
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids) {
List<String> idList = StreamUtils.toList(ids, String::valueOf);
workflowService.deleteRunAndHisInstance(idList);
List<Long> idList = StreamUtils.toList(ids, Long::valueOf);
workflowService.deleteInstance(idList);
return baseMapper.deleteByIds(ids) > 0;
}

View File

@@ -10,11 +10,6 @@ import org.dromara.workflow.domain.bo.WfCategoryBo;
import org.dromara.workflow.domain.vo.WfCategoryVo;
import org.dromara.workflow.mapper.WfCategoryMapper;
import org.dromara.workflow.service.IWfCategoryService;
import org.dromara.workflow.utils.QueryUtils;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -33,8 +28,6 @@ public class WfCategoryServiceImpl implements IWfCategoryService {
private final WfCategoryMapper baseMapper;
private final RepositoryService repositoryService;
/**
* 查询流程分类
*/
@@ -83,19 +76,6 @@ public class WfCategoryServiceImpl implements IWfCategoryService {
WfCategory update = MapstructUtils.convert(bo, WfCategory.class);
validEntityBeforeSave(update);
WfCategoryVo wfCategoryVo = baseMapper.selectVoById(bo.getId());
List<ProcessDefinition> processDefinitionList = QueryUtils.definitionQuery().processDefinitionCategory(wfCategoryVo.getCategoryCode()).list();
for (ProcessDefinition processDefinition : processDefinitionList) {
repositoryService.setProcessDefinitionCategory(processDefinition.getId(), bo.getCategoryCode());
}
List<Deployment> deploymentList = QueryUtils.deploymentQuery().deploymentCategory(wfCategoryVo.getCategoryCode()).list();
for (Deployment deployment : deploymentList) {
repositoryService.setDeploymentCategory(deployment.getId(), bo.getCategoryCode());
}
List<Model> modelList = QueryUtils.modelQuery().modelCategory(wfCategoryVo.getCategoryCode()).list();
for (Model model : modelList) {
model.setCategory(bo.getCategoryCode());
repositoryService.saveModel(model);
}
return baseMapper.updateById(update) > 0;
}

View File

@@ -1,144 +0,0 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.workflow.domain.WfTaskBackNode;
import org.dromara.workflow.domain.vo.MultiInstanceVo;
import org.dromara.workflow.mapper.WfTaskBackNodeMapper;
import org.dromara.workflow.service.IWfTaskBackNodeService;
import org.dromara.workflow.utils.WorkflowUtils;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import static org.dromara.workflow.common.constant.FlowConstant.MULTI_INSTANCE;
import static org.dromara.workflow.common.constant.FlowConstant.USER_TASK;
/**
* 节点驳回记录Service业务层处理
*
* @author may
* @date 2024-03-13
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class WfTaskBackNodeServiceImpl implements IWfTaskBackNodeService {
private final WfTaskBackNodeMapper wfTaskBackNodeMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public void recordExecuteNode(Task task) {
List<WfTaskBackNode> list = getListByInstanceId(task.getProcessInstanceId());
WfTaskBackNode wfTaskBackNode = new WfTaskBackNode();
wfTaskBackNode.setNodeId(task.getTaskDefinitionKey());
wfTaskBackNode.setNodeName(task.getName());
wfTaskBackNode.setInstanceId(task.getProcessInstanceId());
wfTaskBackNode.setAssignee(String.valueOf(LoginHelper.getUserId()));
MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
if (ObjectUtil.isNotEmpty(multiInstance)) {
wfTaskBackNode.setTaskType(MULTI_INSTANCE);
} else {
wfTaskBackNode.setTaskType(USER_TASK);
}
if (CollUtil.isEmpty(list)) {
wfTaskBackNode.setOrderNo(0);
wfTaskBackNodeMapper.insert(wfTaskBackNode);
} else {
WfTaskBackNode taskNode = StreamUtils.findFirst(list, e -> e.getNodeId().equals(wfTaskBackNode.getNodeId()) && e.getOrderNo() == 0);
if (ObjectUtil.isEmpty(taskNode)) {
wfTaskBackNode.setOrderNo(list.get(0).getOrderNo() + 1);
WfTaskBackNode node = getListByInstanceIdAndNodeId(wfTaskBackNode.getInstanceId(), wfTaskBackNode.getNodeId());
if (ObjectUtil.isNotEmpty(node)) {
node.setAssignee(node.getAssignee() + StringUtils.SEPARATOR + LoginHelper.getUserId());
wfTaskBackNodeMapper.updateById(node);
} else {
wfTaskBackNodeMapper.insert(wfTaskBackNode);
}
}
}
}
@Override
public List<WfTaskBackNode> getListByInstanceId(String processInstanceId) {
LambdaQueryWrapper<WfTaskBackNode> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId);
wrapper.orderByDesc(WfTaskBackNode::getOrderNo);
return wfTaskBackNodeMapper.selectList(wrapper);
}
@Override
public WfTaskBackNode getListByInstanceIdAndNodeId(String processInstanceId, String nodeId) {
LambdaQueryWrapper<WfTaskBackNode> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId);
queryWrapper.eq(WfTaskBackNode::getNodeId, nodeId);
return wfTaskBackNodeMapper.selectOne(queryWrapper);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteBackTaskNode(String processInstanceId, String targetActivityId) {
try {
LambdaQueryWrapper<WfTaskBackNode> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId);
queryWrapper.eq(WfTaskBackNode::getNodeId, targetActivityId);
WfTaskBackNode actTaskNode = wfTaskBackNodeMapper.selectOne(queryWrapper);
if (ObjectUtil.isNotNull(actTaskNode)) {
Integer orderNo = actTaskNode.getOrderNo();
List<WfTaskBackNode> taskNodeList = getListByInstanceId(processInstanceId);
List<Long> ids = new ArrayList<>();
if (CollUtil.isNotEmpty(taskNodeList)) {
for (WfTaskBackNode taskNode : taskNodeList) {
if (taskNode.getOrderNo() >= orderNo) {
ids.add(taskNode.getId());
}
}
}
if (CollUtil.isNotEmpty(ids)) {
wfTaskBackNodeMapper.deleteByIds(ids);
}
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new ServiceException("删除失败");
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteByInstanceId(String processInstanceId) {
LambdaQueryWrapper<WfTaskBackNode> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId);
List<WfTaskBackNode> list = wfTaskBackNodeMapper.selectList(wrapper);
int delete = wfTaskBackNodeMapper.delete(wrapper);
if (list.size() != delete) {
throw new ServiceException("删除失败");
}
return true;
}
@Override
public boolean deleteByInstanceIds(List<String> processInstanceIds) {
LambdaQueryWrapper<WfTaskBackNode> wrapper = new LambdaQueryWrapper<>();
wrapper.in(WfTaskBackNode::getInstanceId, processInstanceIds);
List<WfTaskBackNode> list = wfTaskBackNodeMapper.selectList(wrapper);
int delete = wfTaskBackNodeMapper.delete(wrapper);
if (list.size() != delete) {
throw new ServiceException("删除失败");
}
return true;
}
}

View File

@@ -1,13 +1,8 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.service.WorkflowService;
import org.dromara.workflow.domain.ActHiProcinst;
import org.dromara.workflow.service.IActHiProcinstService;
import org.dromara.workflow.service.IActProcessInstanceService;
import org.dromara.workflow.utils.WorkflowUtils;
import org.flowable.engine.RuntimeService;
import org.dromara.workflow.service.IFlwInstanceService;
import org.springframework.stereotype.Service;
import java.util.List;
@@ -22,18 +17,17 @@ import java.util.Map;
@Service
public class WorkflowServiceImpl implements WorkflowService {
private final IActProcessInstanceService iActProcessInstanceService;
private final RuntimeService runtimeService;
private final IActHiProcinstService iActHiProcinstService;
private final IFlwInstanceService iFlwInstanceService;
/**
* 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
* 删除程实例
*
* @param businessKeys 业务id
* @return 结果
*/
@Override
public boolean deleteRunAndHisInstance(List<String> businessKeys) {
return iActProcessInstanceService.deleteRunAndHisInstance(businessKeys);
public boolean deleteInstance(List<Long> businessKeys) {
return iFlwInstanceService.deleteByBusinessIds(businessKeys);
}
/**
@@ -43,7 +37,7 @@ public class WorkflowServiceImpl implements WorkflowService {
*/
@Override
public String getBusinessStatusByTaskId(String taskId) {
return WorkflowUtils.getBusinessStatusByTaskId(taskId);
return "";
}
/**
@@ -53,7 +47,7 @@ public class WorkflowServiceImpl implements WorkflowService {
*/
@Override
public String getBusinessStatus(String businessKey) {
return WorkflowUtils.getBusinessStatus(businessKey);
return "";
}
/**
@@ -65,7 +59,6 @@ public class WorkflowServiceImpl implements WorkflowService {
*/
@Override
public void setVariable(String taskId, String variableName, Object value) {
runtimeService.setVariable(taskId, variableName, value);
}
/**
@@ -76,7 +69,6 @@ public class WorkflowServiceImpl implements WorkflowService {
*/
@Override
public void setVariables(String taskId, Map<String, Object> variables) {
runtimeService.setVariables(taskId, variables);
}
/**
@@ -88,7 +80,6 @@ public class WorkflowServiceImpl implements WorkflowService {
*/
@Override
public void setVariableLocal(String taskId, String variableName, Object value) {
runtimeService.setVariableLocal(taskId, variableName, value);
}
/**
@@ -99,7 +90,6 @@ public class WorkflowServiceImpl implements WorkflowService {
*/
@Override
public void setVariablesLocal(String taskId, Map<String, Object> variables) {
runtimeService.setVariablesLocal(taskId, variables);
}
/**
@@ -110,10 +100,6 @@ public class WorkflowServiceImpl implements WorkflowService {
*/
@Override
public String getInstanceIdByBusinessKey(String businessKey) {
ActHiProcinst actHiProcinst = iActHiProcinstService.selectByBusinessKey(businessKey);
if (actHiProcinst == null) {
return StrUtil.EMPTY;
}
return actHiProcinst.getId();
return null;
}
}

View File

@@ -1,289 +0,0 @@
package org.dromara.workflow.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.workflow.domain.vo.MultiInstanceVo;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.engine.ProcessEngine;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.rmi.ServerException;
import java.util.*;
import java.util.stream.Collectors;
/**
* 模型工具
*
* @author may
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ModelUtils {
private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class);
public static BpmnModel xmlToBpmnModel(String xml) throws IOException {
if (xml == null) {
throw new ServerException("xml不能为空");
}
try {
InputStream inputStream = new ByteArrayInputStream(StrUtil.utf8Bytes(xml));
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader reader = factory.createXMLStreamReader(inputStream);
return new BpmnXMLConverter().convertToBpmnModel(reader);
} catch (XMLStreamException e) {
throw new ServerException(e.getMessage());
}
}
/**
* bpmnModel转为xml
*
* @param jsonBytes json
*/
public static byte[] bpmnJsonToXmlBytes(byte[] jsonBytes) throws IOException {
if (jsonBytes == null) {
return new byte[0];
}
// 1. json字节码转成 BpmnModel 对象
ObjectMapper objectMapper = JsonUtils.getObjectMapper();
JsonNode jsonNode = objectMapper.readTree(jsonBytes);
BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode);
if (bpmnModel.getProcesses().isEmpty()) {
return new byte[0];
}
// 2.将bpmnModel转为xml
return new BpmnXMLConverter().convertToXML(bpmnModel);
}
/**
* xml转为bpmnModel
*
* @param xmlBytes xml
*/
public static BpmnModel xmlToBpmnModel(byte[] xmlBytes) throws XMLStreamException {
ByteArrayInputStream byteArrayInputStream = IoUtil.toStream(xmlBytes);
XMLInputFactory xif = XMLInputFactory.newInstance();
XMLStreamReader xtr = xif.createXMLStreamReader(byteArrayInputStream);
return new BpmnXMLConverter().convertToBpmnModel(xtr);
}
/**
* 校验模型
*
* @param bpmnModel bpmn模型
*/
public static void checkBpmnModel(BpmnModel bpmnModel) throws ServerException {
Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
checkBpmnNode(flowElements, false);
List<SubProcess> subProcessList = flowElements.stream().filter(SubProcess.class::isInstance).map(SubProcess.class::cast).collect(Collectors.toList());
if (!CollUtil.isEmpty(subProcessList)) {
for (SubProcess subProcess : subProcessList) {
Collection<FlowElement> subProcessFlowElements = subProcess.getFlowElements();
checkBpmnNode(subProcessFlowElements, true);
}
}
List<MultiInstanceVo> multiInstanceVoList = new ArrayList<>();
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof UserTask && ObjectUtil.isNotEmpty(((UserTask) flowElement).getLoopCharacteristics()) && StringUtils.isNotBlank(((UserTask) flowElement).getLoopCharacteristics().getInputDataItem())) {
MultiInstanceVo multiInstanceVo = new MultiInstanceVo();
multiInstanceVo.setAssigneeList(((UserTask) flowElement).getLoopCharacteristics().getInputDataItem());
multiInstanceVoList.add(multiInstanceVo);
}
}
if (CollectionUtil.isNotEmpty(multiInstanceVoList) && multiInstanceVoList.size() > 1) {
Map<String, List<MultiInstanceVo>> assigneeListGroup = StreamUtils.groupByKey(multiInstanceVoList, MultiInstanceVo::getAssigneeList);
for (Map.Entry<String, List<MultiInstanceVo>> entry : assigneeListGroup.entrySet()) {
List<MultiInstanceVo> value = entry.getValue();
if (CollectionUtil.isNotEmpty(value) && value.size() > 1) {
String key = entry.getKey();
throw new ServerException("会签人员集合【" + key + "】重复,请重新设置集合KEY");
}
}
}
}
/**
* 校验bpmn节点是否合法
*
* @param flowElements 节点集合
* @param subtask 是否子流程
*/
private static void checkBpmnNode(Collection<FlowElement> flowElements, boolean subtask) throws ServerException {
if (CollUtil.isEmpty(flowElements)) {
throw new ServerException(subtask ? "子流程必须存在节点" : "必须存在节点!");
}
List<StartEvent> startEventList = flowElements.stream().filter(StartEvent.class::isInstance).map(StartEvent.class::cast).collect(Collectors.toList());
if (CollUtil.isEmpty(startEventList)) {
throw new ServerException(subtask ? "子流程必须存在开始节点" : "必须存在开始节点!");
}
if (startEventList.size() > 1) {
throw new ServerException(subtask ? "子流程只能存在一个开始节点" : "只能存在一个开始节点!");
}
StartEvent startEvent = startEventList.get(0);
List<SequenceFlow> outgoingFlows = startEvent.getOutgoingFlows();
if (CollUtil.isEmpty(outgoingFlows)) {
throw new ServerException(subtask ? "子流程流程节点为空,请至少设计一条主线流程!" : "流程节点为空,请至少设计一条主线流程!");
}
FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement();
if (!(targetFlowElement instanceof UserTask) && !subtask) {
throw new ServerException("开始节点后第一个节点必须是用户任务!");
}
//开始节点后第一个节点申请人节点
if ((targetFlowElement instanceof UserTask) && !subtask) {
UserTask userTask = (UserTask) targetFlowElement;
if (StringUtils.isBlank(userTask.getFormKey())) {
throw new ServerException("申请人节点必须选择表单!");
}
}
List<EndEvent> endEventList = flowElements.stream().filter(EndEvent.class::isInstance).map(EndEvent.class::cast).collect(Collectors.toList());
if (CollUtil.isEmpty(endEventList)) {
throw new ServerException(subtask ? "子流程必须存在结束节点!" : "必须存在结束节点!");
}
}
/**
* 获取流程全部用户节点
*
* @param processDefinitionId 流程定义id
*/
public static List<UserTask> getUserTaskFlowElements(String processDefinitionId) {
BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
List<UserTask> list = new ArrayList<>();
List<Process> processes = bpmnModel.getProcesses();
Collection<FlowElement> flowElements = processes.get(0).getFlowElements();
buildUserTaskFlowElements(flowElements, list);
return list;
}
/**
* 递归获取所有节点
*
* @param flowElements 节点信息
* @param list 集合
*/
private static void buildUserTaskFlowElements(Collection<FlowElement> flowElements, List<UserTask> list) {
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof SubProcess) {
Collection<FlowElement> subFlowElements = ((SubProcess) flowElement).getFlowElements();
buildUserTaskFlowElements(subFlowElements, list);
} else if (flowElement instanceof UserTask) {
list.add((UserTask) flowElement);
}
}
}
/**
* 获取流程全部节点
*
* @param processDefinitionId 流程定义id
*/
public static List<FlowElement> getFlowElements(String processDefinitionId) {
BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
List<FlowElement> list = new ArrayList<>();
List<Process> processes = bpmnModel.getProcesses();
Collection<FlowElement> flowElements = processes.get(0).getFlowElements();
buildFlowElements(flowElements, list);
return list;
}
/**
* 递归获取所有节点
*
* @param flowElements 节点信息
* @param list 集合
*/
private static void buildFlowElements(Collection<FlowElement> flowElements, List<FlowElement> list) {
for (FlowElement flowElement : flowElements) {
list.add(flowElement);
if (flowElement instanceof SubProcess) {
Collection<FlowElement> subFlowElements = ((SubProcess) flowElement).getFlowElements();
buildFlowElements(subFlowElements, list);
}
}
}
/**
* 获取全部扩展信息
*
* @param processDefinitionId 流程定义id
*/
public static Map<String, List<ExtensionElement>> getExtensionElements(String processDefinitionId) {
Map<String, List<ExtensionElement>> map = new HashMap<>();
List<FlowElement> flowElements = getFlowElements(processDefinitionId);
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof UserTask && CollUtil.isNotEmpty(flowElement.getExtensionElements())) {
map.putAll(flowElement.getExtensionElements());
}
}
return map;
}
/**
* 获取某个节点的扩展信息
*
* @param processDefinitionId 流程定义id
* @param flowElementId 节点id
*/
public static Map<String, List<ExtensionElement>> getExtensionElement(String processDefinitionId, String flowElementId) {
BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
Process process = bpmnModel.getMainProcess();
FlowElement flowElement = process.getFlowElement(flowElementId);
return flowElement.getExtensionElements();
}
/**
* 判断当前节点是否为用户任务
*
* @param processDefinitionId 流程定义id
* @param taskDefinitionKey 流程定义id
*/
public static boolean isUserTask(String processDefinitionId, String taskDefinitionKey) {
BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey);
return flowNode instanceof UserTask;
}
/**
* 获取申请人节点
*
* @param processDefinitionId 流程定义id
* @return 结果
*/
public static UserTask getApplyUserTask(String processDefinitionId) {
BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
List<StartEvent> startEventList = flowElements.stream().filter(StartEvent.class::isInstance).map(StartEvent.class::cast).collect(Collectors.toList());
StartEvent startEvent = startEventList.get(0);
List<SequenceFlow> outgoingFlows = startEvent.getOutgoingFlows();
FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement();
return (UserTask) targetFlowElement;
}
}

View File

@@ -1,169 +0,0 @@
package org.dromara.workflow.utils;
import cn.hutool.core.bean.BeanUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.workflow.domain.vo.TaskVo;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.history.HistoricActivityInstanceQuery;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.DeploymentQuery;
import org.flowable.engine.repository.ModelQuery;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* 查询工具
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class QueryUtils {
private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class);
public static ModelQuery modelQuery() {
ModelQuery query = PROCESS_ENGINE.getRepositoryService().createModelQuery();
if (TenantHelper.isEnable()) {
query.modelTenantId(TenantHelper.getTenantId());
}
return query;
}
public static ProcessDefinitionQuery definitionQuery() {
ProcessDefinitionQuery query = PROCESS_ENGINE.getRepositoryService().createProcessDefinitionQuery();
if (TenantHelper.isEnable()) {
query.processDefinitionTenantId(TenantHelper.getTenantId());
}
return query;
}
public static DeploymentQuery deploymentQuery() {
DeploymentQuery query = PROCESS_ENGINE.getRepositoryService().createDeploymentQuery();
if (TenantHelper.isEnable()) {
query.deploymentTenantId(TenantHelper.getTenantId());
}
return query;
}
public static DeploymentQuery deploymentQuery(String deploymentId) {
return deploymentQuery().deploymentId(deploymentId);
}
public static DeploymentQuery deploymentQuery(List<String> deploymentIds) {
return deploymentQuery().deploymentIds(deploymentIds);
}
public static HistoricTaskInstanceQuery hisTaskInstanceQuery() {
HistoricTaskInstanceQuery query = PROCESS_ENGINE.getHistoryService().createHistoricTaskInstanceQuery();
if (TenantHelper.isEnable()) {
query.taskTenantId(TenantHelper.getTenantId());
}
return query;
}
public static HistoricTaskInstanceQuery hisTaskInstanceQuery(String processInstanceId) {
return hisTaskInstanceQuery().processInstanceId(processInstanceId);
}
public static HistoricTaskInstanceQuery hisTaskBusinessKeyQuery(String businessKey) {
return hisTaskInstanceQuery().processInstanceBusinessKey(businessKey);
}
public static ProcessInstanceQuery instanceQuery() {
ProcessInstanceQuery query = PROCESS_ENGINE.getRuntimeService().createProcessInstanceQuery();
if (TenantHelper.isEnable()) {
query.processInstanceTenantId(TenantHelper.getTenantId());
}
return query;
}
public static ProcessInstanceQuery instanceQuery(String processInstanceId) {
return instanceQuery().processInstanceId(processInstanceId);
}
public static ProcessInstanceQuery businessKeyQuery(String businessKey) {
return instanceQuery().processInstanceBusinessKey(businessKey);
}
public static ProcessInstanceQuery instanceQuery(Set<String> processInstanceIds) {
return instanceQuery().processInstanceIds(processInstanceIds);
}
public static HistoricProcessInstanceQuery hisInstanceQuery() {
HistoricProcessInstanceQuery query = PROCESS_ENGINE.getHistoryService().createHistoricProcessInstanceQuery();
if (TenantHelper.isEnable()) {
query.processInstanceTenantId(TenantHelper.getTenantId());
}
return query;
}
public static HistoricProcessInstanceQuery hisInstanceQuery(String processInstanceId) {
return hisInstanceQuery().processInstanceId(processInstanceId);
}
public static HistoricProcessInstanceQuery hisBusinessKeyQuery(String businessKey) {
return hisInstanceQuery().processInstanceBusinessKey(businessKey);
}
public static HistoricProcessInstanceQuery hisInstanceQuery(Set<String> processInstanceIds) {
return hisInstanceQuery().processInstanceIds(processInstanceIds);
}
public static HistoricActivityInstanceQuery hisActivityInstanceQuery() {
HistoricActivityInstanceQuery query = PROCESS_ENGINE.getHistoryService().createHistoricActivityInstanceQuery();
if (TenantHelper.isEnable()) {
query.activityTenantId(TenantHelper.getTenantId());
}
return query;
}
public static HistoricActivityInstanceQuery hisActivityInstanceQuery(String processInstanceId) {
return hisActivityInstanceQuery().processInstanceId(processInstanceId);
}
public static TaskQuery taskQuery() {
TaskQuery query = PROCESS_ENGINE.getTaskService().createTaskQuery();
if (TenantHelper.isEnable()) {
query.taskTenantId(TenantHelper.getTenantId());
}
return query;
}
public static TaskQuery taskQuery(String processInstanceId) {
return taskQuery().processInstanceId(processInstanceId);
}
public static TaskQuery taskQuery(Collection<String> processInstanceIds) {
return taskQuery().processInstanceIdIn(processInstanceIds);
}
/**
* 按照任务id查询当前任务
*
* @param taskId 任务id
*/
public static TaskVo getTask(String taskId) {
Task task = PROCESS_ENGINE.getTaskService().createTaskQuery().taskId(taskId).singleResult();
if (task == null) {
return null;
}
ProcessInstance processInstance = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult();
TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
taskVo.setBusinessKey(processInstance.getBusinessKey());
taskVo.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null);
String businessStatus = WorkflowUtils.getBusinessStatus(taskVo.getBusinessKey());
taskVo.setBusinessStatus(businessStatus);
return taskVo;
}
}

View File

@@ -2,41 +2,20 @@ package org.dromara.workflow.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.warm.flow.core.entity.Task;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.domain.dto.RoleDTO;
import org.dromara.common.core.domain.dto.UserDTO;
import org.dromara.common.core.service.UserService;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mail.utils.MailUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.common.websocket.dto.WebSocketMessageDto;
import org.dromara.common.websocket.utils.WebSocketUtils;
import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.common.enums.MessageTypeEnum;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.domain.ActHiTaskinst;
import org.dromara.workflow.domain.vo.MultiInstanceVo;
import org.dromara.workflow.domain.vo.ParticipantVo;
import org.dromara.workflow.flowable.cmd.UpdateHiTaskInstCmd;
import org.dromara.workflow.mapper.ActHiTaskinstMapper;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.util.*;
@@ -48,183 +27,6 @@ import java.util.*;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class WorkflowUtils {
private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class);
private static final ActHiTaskinstMapper ACT_HI_TASKINST_MAPPER = SpringUtils.getBean(ActHiTaskinstMapper.class);
/**
* 创建一个新任务
*
* @param currentTask 参数
*/
public static TaskEntity createNewTask(Task currentTask) {
TaskEntity task = null;
if (ObjectUtil.isNotEmpty(currentTask)) {
task = (TaskEntity) PROCESS_ENGINE.getTaskService().newTask();
task.setCategory(currentTask.getCategory());
task.setDescription(currentTask.getDescription());
task.setAssignee(currentTask.getAssignee());
task.setName(currentTask.getName());
task.setProcessDefinitionId(currentTask.getProcessDefinitionId());
task.setProcessInstanceId(currentTask.getProcessInstanceId());
task.setTaskDefinitionKey(currentTask.getTaskDefinitionKey());
task.setPriority(currentTask.getPriority());
task.setCreateTime(new Date());
task.setTenantId(TenantHelper.getTenantId());
PROCESS_ENGINE.getTaskService().saveTask(task);
}
if (ObjectUtil.isNotNull(task)) {
UpdateHiTaskInstCmd updateHiTaskInstCmd = new UpdateHiTaskInstCmd(Collections.singletonList(task.getId()), task.getProcessDefinitionId(), task.getProcessInstanceId());
PROCESS_ENGINE.getManagementService().executeCommand(updateHiTaskInstCmd);
}
return task;
}
/**
* 抄送任务
*
* @param parentTaskList 父级任务
* @param userIds 人员id
*/
public static void createCopyTask(List<Task> parentTaskList, List<Long> userIds) {
List<Task> list = new ArrayList<>();
String tenantId = TenantHelper.getTenantId();
for (Task parentTask : parentTaskList) {
for (Long userId : userIds) {
TaskEntity newTask = (TaskEntity) PROCESS_ENGINE.getTaskService().newTask();
newTask.setParentTaskId(parentTask.getId());
newTask.setAssignee(userId.toString());
newTask.setName("【抄送】-" + parentTask.getName());
newTask.setProcessDefinitionId(parentTask.getProcessDefinitionId());
newTask.setProcessInstanceId(parentTask.getProcessInstanceId());
newTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());
newTask.setTenantId(tenantId);
list.add(newTask);
}
}
PROCESS_ENGINE.getTaskService().bulkSaveTasks(list);
if (CollUtil.isNotEmpty(list) && CollUtil.isNotEmpty(parentTaskList)) {
String processInstanceId = parentTaskList.get(0).getProcessInstanceId();
String processDefinitionId = parentTaskList.get(0).getProcessDefinitionId();
List<String> taskIds = StreamUtils.toList(list, Task::getId);
ActHiTaskinst actHiTaskinst = new ActHiTaskinst();
actHiTaskinst.setProcDefId(processDefinitionId);
actHiTaskinst.setProcInstId(processInstanceId);
actHiTaskinst.setScopeType(TaskStatusEnum.COPY.getStatus());
actHiTaskinst.setTenantId(tenantId);
LambdaUpdateWrapper<ActHiTaskinst> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(ActHiTaskinst::getId, taskIds);
ACT_HI_TASKINST_MAPPER.update(actHiTaskinst, updateWrapper);
for (Task task : list) {
PROCESS_ENGINE.getTaskService().addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.COPY.getStatus(), StrUtil.EMPTY);
}
}
}
/**
* 获取当前任务参与者
*
* @param taskId 任务id
*/
public static ParticipantVo getCurrentTaskParticipant(String taskId, UserService userService) {
ParticipantVo participantVo = new ParticipantVo();
List<HistoricIdentityLink> linksForTask = PROCESS_ENGINE.getHistoryService().getHistoricIdentityLinksForTask(taskId);
Task task = QueryUtils.taskQuery().taskId(taskId).singleResult();
if (task != null && CollUtil.isNotEmpty(linksForTask)) {
List<HistoricIdentityLink> groupList = StreamUtils.filter(linksForTask, e -> StringUtils.isNotBlank(e.getGroupId()));
if (CollUtil.isNotEmpty(groupList)) {
List<Long> groupIds = StreamUtils.toList(groupList, e -> Long.valueOf(e.getGroupId()));
List<Long> userIds = userService.selectUserIdsByRoleIds(groupIds);
if (CollUtil.isNotEmpty(userIds)) {
participantVo.setGroupIds(groupIds);
List<UserDTO> userList = userService.selectListByIds(userIds);
if (CollUtil.isNotEmpty(userList)) {
List<Long> userIdList = StreamUtils.toList(userList, UserDTO::getUserId);
List<String> nickNames = StreamUtils.toList(userList, UserDTO::getNickName);
participantVo.setCandidate(userIdList);
participantVo.setCandidateName(nickNames);
participantVo.setClaim(!StringUtils.isBlank(task.getAssignee()));
}
}
} else {
List<HistoricIdentityLink> candidateList = StreamUtils.filter(linksForTask, e -> FlowConstant.CANDIDATE.equals(e.getType()));
List<Long> userIdList = new ArrayList<>();
for (HistoricIdentityLink historicIdentityLink : linksForTask) {
try {
userIdList.add(Long.valueOf(historicIdentityLink.getUserId()));
} catch (NumberFormatException ignored) {
}
}
List<UserDTO> userList = userService.selectListByIds(userIdList);
if (CollUtil.isNotEmpty(userList)) {
List<Long> userIds = StreamUtils.toList(userList, UserDTO::getUserId);
List<String> nickNames = StreamUtils.toList(userList, UserDTO::getNickName);
participantVo.setCandidate(userIds);
participantVo.setCandidateName(nickNames);
// 判断当前任务是否具有多个办理人
if (CollUtil.isNotEmpty(candidateList) && candidateList.size() > 1) {
// 如果 assignee 存在,则设置当前任务已经被认领
participantVo.setClaim(StringUtils.isNotBlank(task.getAssignee()));
}
}
}
}
return participantVo;
}
/**
* 判断当前节点是否为会签节点
*
* @param processDefinitionId 流程定义id
* @param taskDefinitionKey 流程定义id
*/
public static MultiInstanceVo isMultiInstance(String processDefinitionId, String taskDefinitionKey) {
BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey);
MultiInstanceVo multiInstanceVo = new MultiInstanceVo();
//判断是否为并行会签节点
if (flowNode.getBehavior() instanceof ParallelMultiInstanceBehavior behavior && behavior.getCollectionExpression() != null) {
Expression collectionExpression = behavior.getCollectionExpression();
String assigneeList = collectionExpression.getExpressionText();
String assignee = behavior.getCollectionElementVariable();
multiInstanceVo.setType(behavior);
multiInstanceVo.setAssignee(assignee);
multiInstanceVo.setAssigneeList(assigneeList);
return multiInstanceVo;
//判断是否为串行会签节点
} else if (flowNode.getBehavior() instanceof SequentialMultiInstanceBehavior behavior && behavior.getCollectionExpression() != null) {
Expression collectionExpression = behavior.getCollectionExpression();
String assigneeList = collectionExpression.getExpressionText();
String assignee = behavior.getCollectionElementVariable();
multiInstanceVo.setType(behavior);
multiInstanceVo.setAssignee(assignee);
multiInstanceVo.setAssigneeList(assigneeList);
return multiInstanceVo;
}
return null;
}
/**
* 获取当前流程状态
*
* @param taskId 任务id
*/
public static String getBusinessStatusByTaskId(String taskId) {
HistoricTaskInstance historicTaskInstance = QueryUtils.hisTaskInstanceQuery().taskId(taskId).singleResult();
HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery(historicTaskInstance.getProcessInstanceId()).singleResult();
return historicProcessInstance.getBusinessStatus();
}
/**
* 获取当前流程状态
*
* @param businessKey 业务id
*/
public static String getBusinessStatus(String businessKey) {
HistoricProcessInstance historicProcessInstance = QueryUtils.hisBusinessKeyQuery(businessKey).singleResult();
return historicProcessInstance.getBusinessStatus();
}
/**
* 发送消息
*
@@ -239,7 +41,7 @@ public class WorkflowUtils {
message = "有新的【" + name + "】单据已经提交至您的待办,请您及时处理。";
}
for (Task t : list) {
ParticipantVo taskParticipant = WorkflowUtils.getCurrentTaskParticipant(t.getId(), userService);
ParticipantVo taskParticipant = null; //= WorkflowUtils.getCurrentTaskParticipant(t.getId(), userService);
if (CollUtil.isNotEmpty(taskParticipant.getGroupIds())) {
List<Long> userIdList = userService.selectUserIdsByRoleIds(taskParticipant.getGroupIds());
if (CollUtil.isNotEmpty(userIdList)) {
@@ -276,20 +78,17 @@ public class WorkflowUtils {
}
/**
* 根据任务id查询 当前用户的任务,检查 当前人员 是否是该 taskId 的办理人
* 当前用户所有权限
*
* @param taskId 任务id
* @return 结果
* @return 权限列表
*/
public static Task getTaskByCurrentUser(String taskId) {
TaskQuery taskQuery = QueryUtils.taskQuery();
taskQuery.taskId(taskId).taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId()));
public static List<String> permissionList() {
List<RoleDTO> roles = LoginHelper.getLoginUser().getRoles();
if (CollUtil.isNotEmpty(roles)) {
List<String> groupIds = StreamUtils.toList(roles, e -> String.valueOf(e.getRoleId()));
taskQuery.taskCandidateGroupIn(groupIds);
}
return taskQuery.singleResult();
Long userId = LoginHelper.getUserId();
Long deptId = LoginHelper.getDeptId();
List<String> permissionList = StreamUtils.toList(roles, role -> "role:" + role.getRoleId());
permissionList.add(String.valueOf(userId));
permissionList.add("dept:" + deptId);
return permissionList;
}
}

View File

@@ -1,7 +0,0 @@
<?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.dromara.workflow.mapper.ActHiProcinstMapper">
</mapper>

View File

@@ -1,7 +0,0 @@
<?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.dromara.workflow.mapper.ActHiTaskinstMapper">
</mapper>

View File

@@ -1,78 +0,0 @@
<?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.dromara.workflow.mapper.ActTaskMapper">
<resultMap type="org.dromara.workflow.domain.vo.TaskVo" id="TaskVoResult">
<result property="id" column="ID_"/>
<result property="name" column="NAME_"/>
<result property="description" column="DESCRIPTION_"/>
<result property="priority" column="PRIORITY_"/>
<result property="owner" column="OWNER_"/>
<result property="assignee" column="ASSIGNEE_"/>
<result property="processInstanceId" column="PROC_INST_ID_"/>
<result property="executionId" column="EXECUTION_ID_"/>
<result property="taskDefinitionId" column="TASK_DEF_ID_"/>
<result property="processDefinitionId" column="PROC_DEF_ID_"/>
<result property="createTime" column="CREATE_TIME_"/>
<result property="startTime" column="START_TIME_"/>
<result property="endTime" column="END_TIME_"/>
<result property="taskDefinitionKey" column="TASK_DEF_KEY_"/>
<result property="dueDate" column="DUE_DATE_"/>
<result property="category" column="CATEGORY_"/>
<result property="parentTaskId" column="PARENT_TASK_ID_"/>
<result property="tenantId" column="TENANT_ID_"/>
<result property="claimTime" column="CLAIM_TIME"/>
<result property="businessStatus" column="BUSINESS_STATUS_"/>
<result property="processDefinitionName" column="processDefinitionName"/>
<result property="processDefinitionKey" column="processDefinitionKey"/>
<result property="processDefinitionVersion" column="processDefinitionVersion"/>
<result property="businessKey" column="BUSINESS_KEY_"/>
</resultMap>
<select id="getTaskWaitByPage" resultMap="TaskVoResult">
select *
from (SELECT RES.*,
AHP.BUSINESS_STATUS_,
AHP.BUSINESS_KEY_,
ARP.NAME_ AS processDefinitionName,
ARP.KEY_ AS processDefinitionKey,
ARP.VERSION_ AS processDefinitionVersion
FROM ACT_RU_TASK RES
INNER JOIN ACT_HI_PROCINST AHP ON RES.PROC_INST_ID_ = AHP.PROC_INST_ID_
INNER JOIN ACT_RE_PROCDEF ARP ON ARP.ID_ = RES.PROC_DEF_ID_
WHERE RES.PARENT_TASK_ID_ IS NULL
ORDER BY RES.CREATE_TIME_ DESC) t ${ew.getCustomSqlSegment}
</select>
<select id="getTaskFinishByPage" resultMap="TaskVoResult">
select *
from (SELECT HTI.*,
AHP.BUSINESS_STATUS_,
AHP.BUSINESS_KEY_,
ARP.NAME_ AS processDefinitionName,
ARP.KEY_ AS processDefinitionKey,
ARP.VERSION_ AS processDefinitionVersion
FROM ACT_HI_TASKINST HTI
INNER JOIN ACT_HI_PROCINST AHP ON HTI.PROC_INST_ID_ = AHP.PROC_INST_ID_
INNER JOIN ACT_RE_PROCDEF ARP ON ARP.ID_ = HTI.PROC_DEF_ID_
WHERE HTI.PARENT_TASK_ID_ IS NULL AND HTI.END_TIME_ IS NOT NULL
ORDER BY HTI.START_TIME_ DESC) t ${ew.getCustomSqlSegment}
</select>
<select id="getTaskCopyByPage" resultMap="TaskVoResult">
select *
from (SELECT AHT.*,
AHP.BUSINESS_STATUS_,
AHP.BUSINESS_KEY_,
ARP.NAME_ as processDefinitionName,
ARP.KEY_ as processDefinitionKey,
ARP.VERSION_ AS processDefinitionVersion
FROM ACT_HI_TASKINST AHT
INNER JOIN ACT_HI_PROCINST AHP ON AHT.PROC_INST_ID_ = AHP.PROC_INST_ID_
INNER JOIN ACT_RE_PROCDEF ARP ON ARP.ID_ = AHT.PROC_DEF_ID_
WHERE AHT.PARENT_TASK_ID_ IS NOT NULL
and AHT.scope_type_ = 'copy'
ORDER BY AHT.START_TIME_ DESC) t ${ew.getCustomSqlSegment}
</select>
</mapper>

View File

@@ -0,0 +1,153 @@
<?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.dromara.workflow.mapper.FlwTaskMapper">
<resultMap type="org.dromara.workflow.domain.vo.FlowTaskVo" id="FlowTaskResult">
<result property="id" column="id"/>
<result property="nodeCode" column="node_code"/>
<result property="nodeName" column="node_name"/>
<result property="nodeType" column="node_type"/>
<result property="definitionId" column="definition_id"/>
<result property="instanceId" column="instance_id"/>
<result property="flowStatus" column="flow_status"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="tenantId" column="tenant_id"/>
<result property="businessId" column="business_id"/>
<result property="flowName" column="flow_name"/>
<result property="flowCode" column="flow_code"/>
<result property="fromCustom" column="from_custom"/>
<result property="fromPath" column="from_path"/>
<result property="delFlag" column="del_flag"/>
</resultMap>
<resultMap type="org.dromara.workflow.domain.vo.FlowHisTaskVo" id="FlowHisTaskResult">
<result property="id" column="id"/>
<result property="nodeCode" column="node_code"/>
<result property="nodeName" column="node_name"/>
<result property="nodeType" column="node_type"/>
<result property="targetNodeCode" column="target_node_code"/>
<result property="targetNodeName" column="target_node_name"/>
<result property="approver" column="approver"/>
<result property="collaborator" column="collaborator"/>
<result property="definitionId" column="definition_id"/>
<result property="instanceId" column="instance_id"/>
<result property="taskId" column="task_id"/>
<result property="cooperateType" column="cooperate_type"/>
<result property="flowStatus" column="flow_status"/>
<result property="message" column="message"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="businessId" column="business_id"/>
<result property="nodeName" column="node_name"/>
<result property="tenantId" column="tenant_id"/>
<result property="flowName" column="flow_name"/>
<result property="flowCode" column="flow_code"/>
<result property="delFlag" column="del_flag"/>
</resultMap>
<resultMap type="org.dromara.workflow.domain.vo.FlowTaskVo" id="TaskVoResult">
<result property="id" column="id"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="nodeCode" column="node_code"/>
<result property="nodeType" column="node_type"/>
<result property="tenantId" column="tenant_id"/>
<result property="delFlag" column="del_flag"/>
<result property="definitionId" column="definition_id"/>
<result property="instanceId" column="instance_id"/>
<result property="flowName" column="flow_name"/>
<result property="flowCode" column="flow_code"/>
</resultMap>
<select id="getTaskWaitByPage" resultMap="TaskVoResult">
select*from (
SELECT
distinct t.id,
t.node_code,
t.node_name,
t.node_type,
t.definition_id,
t.instance_id,
t.create_time,
t.update_time,
t.tenant_id,
i.business_id,
i.flow_status,
d.flow_name,
d.flow_code,
d.from_custom,
d.from_path,
uu.processed_by
FROM flow_task AS t
LEFT JOIN flow_user uu ON uu.associated = t.id
LEFT JOIN flow_definition d on t.definition_id = d.id
LEFT JOIN flow_instance i on t.instance_id = i.id where t.node_type = 1 and i.flow_status = '1') t
${ew.getCustomSqlSegment}
</select>
<select id="getTaskFinishByPage" resultMap="TaskVoResult">
select * from (
select
t.id,
t.node_code,
t.node_name,
t.cooperate_type,
t.approver,
t.collaborator,
t.node_type,
t.target_node_code,
t.target_node_name,
t.definition_id,
t.instance_id,
i.flow_status,
t.message,
t.ext,
t.create_time,
t.update_time,
t.tenant_id,
i.business_id,
i.node_name,
d.from_path fromPath,
d.flow_name,
d.flow_code
from ( SELECT MAX(id) as id
FROM flow_his_task
<where>
flow_status <![CDATA[<> '10']]>
<if test="flowTaskBo.permissionList != null and flowTaskBo.permissionList.size > 0">
AND approver in
<foreach item="permission" collection="flowTaskBo.permissionList" open="(" separator="," close=")">
#{permission}
</foreach>
</if>
<if test="flowTaskBo.nodeName != null and flowTaskBo.nodeName != ''">and node_name like concat('%',
#{flowTaskBo.nodeName}, '%')
</if>
<if test="flowTaskBo.instanceId != null ">and instance_id = #{flowTaskBo.instanceId}</if>
</where>
GROUP BY instance_id ) tmp
LEFT JOIN flow_his_task t ON t.id = tmp.id
LEFT JOIN flow_definition d on t.definition_id = d.id
LEFT JOIN flow_instance i on t.instance_id = i.id
order by t.create_time desc ) t ${ew.getCustomSqlSegment}
</select>
<select id="getTaskCopyByPage" resultMap="FlowTaskResult">
select * from (
SELECT
b.flow_status,
b.business_id,
a.create_time,
b.node_name,
b.node_code,
b.id ,
d.flow_name,
d.flow_code,
FROM
`flow_user` a
LEFT JOIN flow_instance b ON a.associated = b.id
LEFT JOIN flow_definition d on b.definition_id=d.id
WHERE
a.type = 4 ORDER BY create_time DESC
) t ${ew.getCustomSqlSegment}
</select>
</mapper>

View File

@@ -1,7 +0,0 @@
<?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.dromara.workflow.mapper.WfNodeConfigMapper">
</mapper>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<definition flowCode="leaveFlow-serial1" flowName="串行-简单" version="1.0" fromCustom="N"
fromPath="system/leave/approve">
<node nodeType="start" nodeCode="1" nodeName="开始" coordinate="120,280|120,280" skipAnyNode="N"
listenerType="start,assignment,finish,permission,create"
listenerPath="com.ruoyi.system.Listener.StartListener@@323@@com.ruoyi.system.Listener.FinishListener@@com.ruoyi.system.Listener.PermissionListener@@com.ruoyi.system.Listener.CreateListener">
<skip coordinate="140,280;230,280" skipType="PASS">2</skip>
</node>
<node nodeType="between" nodeCode="2" nodeName="待提交" permissionFlag="role:1,role:2" coordinate="280,280|280,280"
skipAnyNode="N" nodeRatio="0"
listenerType="start,assignment,finish,permission,create"
listenerPath="com.ruoyi.system.Listener.StartListener@@323@@com.ruoyi.system.Listener.FinishListener@@com.ruoyi.system.Listener.PermissionListener@@com.ruoyi.system.Listener.CreateListener">
<skip coordinate="330,280;430,280" skipType="PASS">3</skip>
</node>
<node nodeType="between" nodeCode="3" nodeName="组长审批" permissionFlag="role:1,role:2"
coordinate="480,280|480,280" skipAnyNode="Y" nodeRatio="0"
listenerType="start,assignment,finish,permission,create"
listenerPath="com.ruoyi.system.Listener.StartListener@@323@@com.ruoyi.system.Listener.FinishListener@@com.ruoyi.system.Listener.PermissionListener@@com.ruoyi.system.Listener.CreateListener">
<skip coordinate="530,280;650,280" skipType="PASS">4</skip>
</node>
<node nodeType="between" nodeCode="4" nodeName="部门经理审批" permissionFlag="role:1,role:2"
coordinate="700,280|700,280" skipAnyNode="N" nodeRatio="0"
listenerType="start,assignment,finish,permission,create"
listenerPath="com.ruoyi.system.Listener.StartListener@@323@@com.ruoyi.system.Listener.FinishListener@@com.ruoyi.system.Listener.PermissionListener@@com.ruoyi.system.Listener.CreateListener">
<skip coordinate="750,280;870,280" skipType="PASS">5</skip>
<skip coordinate="700,240;700,210;280,210;280,240" skipType="REJECT">2</skip>
</node>
<node nodeType="between" nodeCode="5" nodeName="hr审批" permissionFlag="warmFlowInitiator"
coordinate="920,280|920,280" skipAnyNode="Y" nodeRatio="0"
listenerType="start,assignment,finish,permission,create"
listenerPath="com.ruoyi.system.Listener.StartListener@@323@@com.ruoyi.system.Listener.FinishListener@@com.ruoyi.system.Listener.PermissionListener@@com.ruoyi.system.Listener.CreateListener">
<skip coordinate="970,280;1100,280" skipType="PASS">6</skip>
</node>
<node nodeType="end" nodeCode="6" nodeName="结束" coordinate="1120,280|1120,280" skipAnyNode="N"
listenerType="start,assignment,finish,permission,create"
listenerPath="com.ruoyi.system.Listener.StartListener@@323@@com.ruoyi.system.Listener.FinishListener@@com.ruoyi.system.Listener.PermissionListener@@com.ruoyi.system.Listener.CreateListener"/>
</definition>

Binary file not shown.

View File

@@ -83,7 +83,7 @@ create table wf_definition_config
table_name varchar(255) not null comment '表名',
definition_id varchar(255) not null comment '流程定义ID',
process_key varchar(255) not null comment '流程KEY',
version int(10) not null comment '流程版本',
version varchar(255) not null comment '流程版本',
create_dept bigint null comment '创建部门',
create_by bigint null comment '创建者',
create_time datetime null comment '创建时间',