!491 合并flowable工作流功能
* update 优化 表字段映射于数据库保持一致
* remove 删除无用代码
* remove 删除无用代码
* fix 修复 实体类未实现序列化接口问题
* update 优化 表字段映射于数据库保持一致
* update 优化 统一sql名称
* fix 修复 接口名称编写错误
* merge dev
* update 调整sql 添加抄送查询
* update 调整菜单
* update 调整sql脚本
* update 调整任务查询 添加抄送
* add 抄送任务
* remove 删除错误代码
* remove 删除无用代码
* update 调整作废,撤销等校验
* fix 修复 流程作废异常问题
* update 优化 flowable 配置到主yml文件
* update 调整 数据排序规则
* fix 修复 数据库无法自动执行建表sql问题
* update 优化 工作流id生成器保持全局统一
* add 添加附件任务查询
* add 添加审批附件上传
* update 调整bpmn文件修复驳回失败问题
* update 调整会签类型转换异常
* add 添加获取运行中流程信息,流程扩展信息,补充注解,删除无用代码
* update 调整流程转换,流程启动,上传新bpmn文件
* update 调整方法
* update 调整模型修改
* fix 修复 user与dept xml 编写错误
* remove 移除原生ui接口,增加新ui接口
* update 优化 下拉选接口数据权限
* update 优化 删除观测用日志记录
* reset 还原修复命名
* update 修复命名
* add 新增 用户、部门、角色、岗位 下拉选接口与代码实现优化
* update 调整任务办理异步时流程状态错误问题
* add 工作流用户查询
* remove 删除无用注释 添加非空校验
* update 优化获取审批记录
* update 调整事件办理
* update 调整工作流选人接口
* Merge branch 'dev' into future/flowable
* update 办理调整执行顺序
* update 调整流程办理优化撤销,驳回,草稿等动作
* fix 修复子流程中设置发起人变量错误问题
* Merge branch 'dev' into future/flowable
* update 调整流程执行非空校验,调整任务节点执行
* update 调整注释
* add 添加自定义任务监听策略
* !469 update-完善对模型key校验逻辑
* update-修改常量命名
* update-完善对模型key校验逻辑
* add 添加sql脚本
* Merge branch 'dev' into future/flowable
* Merge branch 'dev' into future/flowable
* Merge branch 'future/flowable' of https://gitee.com/dromara/RuoYi-Vue-…
* add 添加SQLserve脚本
* add 添加流程监听示例
* update 调整获取审批记录
* Merge branch 'future/flowable' of https://gitee.com/dromara/RuoYi-Vue-…
* update 调整请假查询 修改流程定义查看xml
* update 调整流程实例删除
* update 调整sql
* add 添加sql脚本
* update 调整sql
* update 调整请假申请,调整菜单sql
* update 调整设计器保存发起人变量,修改菜单sql
* update 依赖调整
* update 调整flw依赖
* update 升级7.0后移除画图mule类型
* update 调整flw依赖
* update 移动模型设计器翻译方法
* update 调整flw依赖
* fix 修复 误删依赖
* Merge remote-tracking branch 'origin/dev' into future/flowable
* remove 移除动态表单
* Merge remote-tracking branch 'origin/dev' into future/flowable
* update 优化代码结构
* update 调整请假申请包结构
* Merge branch 'dev' into future/flowable
* add 添加文件,调整分类查询
* Merge branch 'dev' into future/flowable
* Merge branch 'future/flowable' of https://gitee.com/dromara/RuoYi-Vue-…
* add bpmn文件 调整流程办理
* Merge branch 'dev' into future/flowable
* Merge branch 'future/flowable' of https://gitee.com/dromara/RuoYi-Vue-…
* Merge branch '5.X' into future/flowable
* update 调整消息发送
* update 调整名称
* update 调整流程实例查询
* add 添加任务催办,任务改派
* fix 修复 用户注册接口校验用户名不区分租户问题
* update 还原待办任务,添加待办消息发送
* update 优化任务待办,排除非待办任务
* Merge branch '5.X' into future/flowable
* update 修改流程启动后重新覆盖流程变量,删除并行流程驳回,撤销后,垃圾数据
* update 升级flowable7.0,添加业务单据删除流程信息
* Merge branch '5.X' into future/flowable
* add 添加动态表单提交流程
* Merge branch 'future/flowable' of https://gitee.com/dromara/RuoYi-Vue-…
* add 添加动态表单单据
* update 升级flowable到7.0.0.M2,调整工作流提交校验,调整工作流工具类
* add 新增流程定义与表单关联
* update 调整修改流程分类后更新流程分类编码
* update 调整流程定义图片预览
* update 调整人员查询
* update 优化作废,撤销等备注
* Merge branch '5.X' into future/flowable
* Merge branch 'future/flowable' of https://gitee.com/dromara/RuoYi-Vue-…
* fix 解决设计器选择设置流程发起人设置变量有问题
* add 添加引擎调度监听
* merge 合并5.x分支代码
* remove 移除flow-ui
* update 调整日志打印
* add 添加按照业务id删除流程记录
* add 添加请假申请示例,添加流程定义文件部署,添加sql菜单
* update 移除流程表单 formConfig 属性,表单配置信息都放一起便于使用。
* update 调整菜单
* add 添加mysql工作流菜单
* update 调整获取加签人,审判记录
* update 调整流程作废
* add 添加任务完成状态
* add 添加加签,减签人员接口
* update 调整任务驳回后设置审批人
* add 添加驳回申请人
* add 添加查询当前租户所有待办,已办任务
* add 添加会签任务加签减签,添加任务作废理由
* update 调整流程实例,流程定义检索
* update 调整撤销流程申请,当前登录人单据
* add 添加办理人名称翻译
* add 添加流程流程实例,流程定义分类查询
* add 添加模型分类查询
* add 添加流程分类
* add 添加流程表单操作相关接口
* fix 修复修改流程历史流程实例错误问题
* update 调整已办任务排序,添加注释
* update 调整用户,用户组查询
* add 添加获取当前任务参与者,优化任务待办,已办
* add 添加当前登录人单据列表,添加单据状态
* update 补充任务撤销事务
* add 添加撤销流程申请
* update 优化流程实例删除
* fix 修复流程实例查询挂起状态错误
* update 优化流程办理 流程挂起抛出异常
* add 添加业务状态枚举。添加流程启动,审批,终止等状态
* update 优化流程启动
* add 添加流程实例作废,运行中流程实例删除,已完成流程实例删除
* add 添加节点信息
* 调整流程预览
* add 添加审批记录
* 还原代码
* fix 修复模型导出错误
* add 增加委托办理,调整流程启动
* add 添加转办任务
* add 添加任务拾取,任务归还,任务终止,任务委托
* fix 修复任务,流程实例分页模糊查询失效
* add 添加流程实例运行中,已结束分页查询
* add 添加通过流程实例id获取历史流程图,添加flowable配置,调整流程办理
* add 添加流程办理,流程待办,已办分页查询
* 删除无用导入
* 调整流程查询租户id
* add 添加流程启动
* 添加模型人员用户,组查询
* add 添加模型部署模型校验
* 修改模型部署导出校验
* fix 修复模型画图保存时key不回显问题
* add 添加流程定义转换为模型
* 优化模型编辑校验,流程定义删除,流程定义激活挂起等
* add 添加流程定义删除,流程定义挂起激活,流程定义版本迁移
* 调整ObjectNode.put警告
* 删除无用依赖,优化模型修改,导出,部署非空校验
* 删除无用导入
* 添加流程定义分页,查看图片,查看xml
* 添加模型部署,导出模型
* 修改画图账户登录信息
* 添加模型编辑key重复校验,添加租户查询,删除忽略token注解
* 添加模型新增校验
* 添加工作流模型新增,修改,查询,删除
* 【ADD】集成原生Flowable-ui
* 添加workflow模块,添加flowable依赖,yml配置信息
已添加107个文件
已修改6个文件
9773 ■■■■■ 文件已修改
pom.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-dev.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-prod.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/pom.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/pom.xml 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/PageEntity.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java 246 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfCategoryController.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WorkflowUserController.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiProcinst.java 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiTaskinst.java 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/TestLeave.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfCategory.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/AddMultiBo.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DelegateBo.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DeleteMultiBo.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ModelBo.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessDefinitionBo.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessInstanceBo.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessInvalidBo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/SysUserMultiBo.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskBo.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskUrgingBo.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TerminationBo.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TransmitBo.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfCategoryBo.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ActHistoryInfoVo.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/GraphicInfoVo.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/GroupRepresentation.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ModelVo.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/MultiInstanceVo.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ParticipantVo.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessDefinitionVo.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessInstanceVo.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TaskVo.java 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCategoryVo.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCopy.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramCanvas.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramGenerator.java 1120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AddSequenceMultiInstanceCmd.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AttachmentCmd.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteExecutionCmd.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteSequenceMultiInstanceCmd.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/ExecutionChildByExecutionIdCmd.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateBusinessStatusCmd.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateHiTaskInstCmd.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/FlowableConfig.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/GlobalFlowableListener.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowEventStrategy.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowProcessEventHandler.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowTaskEventHandler.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestCustomProcessHandler.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestCustomTaskHandler.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestLeaveExecutionListener.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestLeaveTaskListener.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiProcinstMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiTaskinstMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActTaskMapper.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/TestLeaveMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfCategoryMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiProcinstService.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiTaskinstService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActModelService.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessDefinitionService.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessInstanceService.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/ITestLeaveService.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfCategoryService.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWorkflowUserService.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiProcinstServiceImpl.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiTaskinstServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActModelServiceImpl.java 334 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessDefinitionServiceImpl.java 328 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java 695 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java 708 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfCategoryServiceImpl.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowUserServiceImpl.java 216 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/WorkflowUtils.java 341 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/package-info.md 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiProcinstMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiTaskinstMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActTaskMapper.xml 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/TestLeaveMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfCategoryMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/bpmn/请假流程(会签)-leave5.zip 补丁 | 查看 | 原始文档 | blame | 历史
script/bpmn/请假流程(包容网关)-leave4.zip 补丁 | 查看 | 原始文档 | blame | 历史
script/bpmn/请假流程(子流程)-leave6.zip 补丁 | 查看 | 原始文档 | blame | 历史
script/bpmn/请假流程(并行网关)-leave3.zip 补丁 | 查看 | 原始文档 | blame | 历史
script/bpmn/请假流程(排他网关)-leave2.zip 补丁 | 查看 | 原始文档 | blame | 历史
script/bpmn/请假流程(普通流程)-leave1.zip 补丁 | 查看 | 原始文档 | blame | 历史
script/bpmn/请假流程(监听)-leave7.zip 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/flowable.sql 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/oracle/flowable.sql 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/postgres/flowable.sql 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/sqlserver/flowable.sql 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -57,6 +57,9 @@
        <maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
        <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
        <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
        <!--工作流配置-->
        <flowable.version>7.0.0</flowable.version>
    </properties>
    <profiles>
@@ -107,6 +110,14 @@
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-bom</artifactId>
                <version>${hutool.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.flowable</groupId>
                <artifactId>flowable-bom</artifactId>
                <version>${flowable.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
@@ -353,6 +364,13 @@
                <version>${revision}</version>
            </dependency>
            <!--  å·¥ä½œæµæ¨¡å—  -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-workflow</artifactId>
                <version>${revision}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
ruoyi-admin/pom.xml
@@ -75,6 +75,12 @@
            <artifactId>ruoyi-demo</artifactId>
        </dependency>
        <!--  å·¥ä½œæµæ¨¡å—  -->
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-workflow</artifactId>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
ruoyi-admin/src/main/resources/application-dev.yml
@@ -43,7 +43,7 @@
          driverClassName: com.mysql.cj.jdbc.Driver
          # jdbc æ‰€æœ‰å‚数配置参考 https://lionli.blog.csdn.net/article/details/122018562
          # rewriteBatchedStatements=true æ‰¹å¤„理优化 å¤§å¹…提升批量插入更新删除性能(对数据库有性能损耗 ä½¿ç”¨æ‰¹é‡æ“ä½œåº”考虑性能问题)
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
          username: root
          password: root
        # ä»Žåº“数据源
@@ -51,7 +51,7 @@
          lazy: true
          type: ${spring.datasource.type}
          driverClassName: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
          username:
          password:
#        oracle:
ruoyi-admin/src/main/resources/application-prod.yml
@@ -46,7 +46,7 @@
          driverClassName: com.mysql.cj.jdbc.Driver
          # jdbc æ‰€æœ‰å‚数配置参考 https://lionli.blog.csdn.net/article/details/122018562
          # rewriteBatchedStatements=true æ‰¹å¤„理优化 å¤§å¹…提升批量插入更新删除性能(对数据库有性能损耗 ä½¿ç”¨æ‰¹é‡æ“ä½œåº”考虑性能问题)
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
          username: root
          password: root
        # ä»Žåº“数据源
@@ -54,7 +54,7 @@
          lazy: true
          type: ${spring.datasource.type}
          driverClassName: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
          username:
          password:
#        oracle:
ruoyi-admin/src/main/resources/application.yml
@@ -266,3 +266,21 @@
  path: /resource/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
ruoyi-modules/pom.xml
@@ -14,6 +14,7 @@
        <module>ruoyi-generator</module>
        <module>ruoyi-job</module>
        <module>ruoyi-system</module>
        <module>ruoyi-workflow</module>
    </modules>
    <artifactId>ruoyi-modules</artifactId>
ruoyi-modules/ruoyi-workflow/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-modules</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <artifactId>ruoyi-workflow</artifactId>
    <description>
        å·¥ä½œæµæ¨¡å—
    </description>
    <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-system</artifactId>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-sms</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package org.dromara.workflow.annotation;
import java.lang.annotation.*;
/**
 * æµç¨‹ä»»åŠ¡ç›‘å¬æ³¨è§£
 *
 * @author may
 * @date 2023-12-27
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface FlowListenerAnnotation {
    /**
     * æµç¨‹å®šä¹‰key
     */
    String processDefinitionKey();
    /**
     * èŠ‚ç‚¹id
     */
    String taskDefId() default "";
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/PageEntity.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package org.dromara.workflow.common;
import lombok.Data;
/**
 * åˆ†é¡µå‚æ•°
 *
 * @author may
 */
@Data
public class PageEntity {
    /**
     * å½“前页码
     */
    private Integer pageNum = 0;
    /**
     * é¡µå®¹é‡
     */
    private Integer pageSize = 10;
    public Integer getPageNum() {
        return (pageNum - 1) * pageSize;
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
package org.dromara.workflow.common.constant;
/**
 * å·¥ä½œæµå¸¸é‡
 *
 * @author may
 */
public interface FlowConstant {
    String MESSAGE_CURRENT_TASK_IS_NULL = "当前任务不存在或你不是任务办理人!";
    String MESSAGE_SUSPENDED = "当前任务已挂起不可审批!";
    /**
     * è¿žçº¿
     */
    String SEQUENCE_FLOW = "sequenceFlow";
    /**
     * å¹¶è¡Œç½‘å…³
     */
    String PARALLEL_GATEWAY = "parallelGateway";
    /**
     * æŽ’它网关
     */
    String EXCLUSIVE_GATEWAY = "exclusiveGateway";
    /**
     * åŒ…含网关
     */
    String INCLUSIVE_GATEWAY = "inclusiveGateway";
    /**
     * ç»“束节点
     */
    String END_EVENT = "endEvent";
    /**
     * æµç¨‹å§”派标识
     */
    String PENDING = "PENDING";
    /**
     * å€™é€‰äººæ ‡è¯†
     */
    String CANDIDATE = "candidate";
    /**
     * ä¼šç­¾ä»»åŠ¡æ€»æ•°
     */
    String NUMBER_OF_INSTANCES = "nrOfInstances";
    /**
     * æ­£åœ¨æ‰§è¡Œçš„会签总数
     */
    String NUMBER_OF_ACTIVE_INSTANCES = "nrOfActiveInstances";
    /**
     * å·²å®Œæˆçš„会签任务总数
     */
    String NUMBER_OF_COMPLETED_INSTANCES = "nrOfCompletedInstances";
    /**
     * å¾ªçŽ¯çš„ç´¢å¼•å€¼ï¼Œå¯ä»¥ä½¿ç”¨elementIndexVariable属性修改loopCounter的变量名
     */
    String LOOP_COUNTER = "loopCounter";
    String ZIP = "ZIP";
    /**
     * æµç¨‹å®žä¾‹å¯¹è±¡
     */
    String PROCESS_INSTANCE_VO = "processInstanceVo";
    /**
     * æµç¨‹å‘起人
     */
    String INITIATOR = "initiator";
    /**
     * å¼€å¯è·³è¿‡è¡¨è¾¾å¼å˜é‡
     */
    String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED";
    /**
     * æ¨¡åž‹æ ‡è¯†key命名规范正则表达式
     */
    String MODEL_KEY_PATTERN = "^[a-zA-Z][a-zA-Z0-9_]{0,254}$";
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
package org.dromara.workflow.common.enums;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import java.util.Arrays;
/**
 * ä¸šåŠ¡çŠ¶æ€æžšä¸¾
 *
 * @author may
 */
@Getter
@AllArgsConstructor
public enum BusinessStatusEnum {
    /**
     * å·²æ’¤é”€
     */
    CANCEL("cancel", "已撤销"),
    /**
     * è‰ç¨¿
     */
    DRAFT("draft", "草稿"),
    /**
     * å¾…审核
     */
    WAITING("waiting", "待审核"),
    /**
     * å·²å®Œæˆ
     */
    FINISH("finish", "已完成"),
    /**
     * å·²ä½œåºŸ
     */
    INVALID("invalid", "已作废"),
    /**
     * å·²é€€å›ž
     */
    BACK("back", "已退回"),
    /**
     * å·²ç»ˆæ­¢
     */
    TERMINATION("termination", "已终止");
    /**
     * çŠ¶æ€
     */
    private final String status;
    /**
     * æè¿°
     */
    private final String desc;
    /**
     * èŽ·å–ä¸šåŠ¡çŠ¶æ€
     *
     * @param status çŠ¶æ€
     */
    public static String findByStatus(String status) {
        if (StringUtils.isBlank(status)) {
            return StrUtil.EMPTY;
        }
        return Arrays.stream(BusinessStatusEnum.values())
            .filter(statusEnum -> statusEnum.getStatus().equals(status))
            .findFirst()
            .map(BusinessStatusEnum::getDesc)
            .orElse(StrUtil.EMPTY);
    }
    /**
     * å¯åŠ¨æµç¨‹æ ¡éªŒ
     *
     * @param status çŠ¶æ€
     */
    public static void checkStartStatus(String status) {
        if (WAITING.getStatus().equals(status)) {
            throw new ServiceException("该单据已提交过申请,正在审批中!");
        } else if (FINISH.getStatus().equals(status)) {
            throw new ServiceException("该单据已完成申请!");
        } else if (INVALID.getStatus().equals(status)) {
            throw new ServiceException("该单据已作废!");
        } else if (TERMINATION.getStatus().equals(status)) {
            throw new ServiceException("该单据已终止!");
        } else if (StringUtils.isBlank(status)) {
            throw new ServiceException("流程状态为空!");
        }
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package org.dromara.workflow.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * æ¶ˆæ¯ç±»åž‹æžšä¸¾
 *
 * @author may
 */
@Getter
@AllArgsConstructor
public enum MessageTypeEnum {
    /**
     * ç«™å†…ä¿¡
     */
    SYSTEM_MESSAGE("1", "站内信"),
    /**
     * é‚®ç®±
     */
    EMAIL_MESSAGE("2", "邮箱"),
    /**
     * çŸ­ä¿¡
     */
    SMS_MESSAGE("3", "短信");
    private final String code;
    private final String desc;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package org.dromara.workflow.common.enums;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
/**
 * ä»»åŠ¡çŠ¶æ€æžšä¸¾
 *
 * @author may
 */
@Getter
@AllArgsConstructor
public enum TaskStatusEnum {
    /**
     * æ’¤é”€
     */
    CANCEL("cancel", "撤销"),
    /**
     * é€šè¿‡
     */
    PASS("pass", "通过"),
    /**
     * å¾…审核
     */
    WAITING("waiting", "待审核"),
    /**
     * ä½œåºŸ
     */
    INVALID("invalid", "作废"),
    /**
     * é€€å›ž
     */
    BACK("back", "退回"),
    /**
     * ç»ˆæ­¢
     */
    TERMINATION("termination", "终止"),
    /**
     * è½¬åŠž
     */
    TRANSFER("transfer", "转办"),
    /**
     * å§”托
     */
    PENDING("pending", "委托"),
    /**
     * æŠ„送
     */
    COPY("copy", "抄送"),
    /**
     * åŠ ç­¾
     */
    SIGN("sign", "加签"),
    /**
     * å‡ç­¾
     */
    SIGN_OFF("sign_off", "减签");
    /**
     * çŠ¶æ€
     */
    private final String status;
    /**
     * æè¿°
     */
    private final String desc;
    /**
     * ä»»åŠ¡ä¸šåŠ¡çŠ¶æ€
     *
     * @param status çŠ¶æ€
     */
    public static String findByStatus(String status) {
        if (StringUtils.isBlank(status)) {
            return StrUtil.EMPTY;
        }
        return Arrays.stream(TaskStatusEnum.values())
            .filter(statusEnum -> statusEnum.getStatus().equals(status))
            .findFirst()
            .map(TaskStatusEnum::getDesc)
            .orElse(StrUtil.EMPTY);
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,135 @@
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.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;
/**
 * æ¨¡åž‹ç®¡ç† æŽ§åˆ¶å±‚
 *
 * @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) {
        return actModelService.page(modelBo);
    }
    /**
     * æ–°å¢žæ¨¡åž‹
     *
     * @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 modelId  æ¨¡åž‹id
     * @param response ç›¸åº”
     */
    @GetMapping("/export/zip/{modelId}")
    public void exportZip(@NotEmpty(message = "模型id不能为空") @PathVariable String modelId,
                          HttpServletResponse response) {
        actModelService.exportZip(modelId, response);
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,146 @@
package org.dromara.workflow.controller;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
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.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 processDefinitionBo å‚æ•°
     */
    @GetMapping("/list")
    public TableDataInfo<ProcessDefinitionVo> page(ProcessDefinitionBo processDefinitionBo) {
        return actProcessDefinitionService.page(processDefinitionBo);
    }
    /**
     * æŸ¥è¯¢åŽ†å²æµç¨‹å®šä¹‰åˆ—è¡¨
     *
     * @param key æµç¨‹å®šä¹‰key
     */
    @GetMapping("/getProcessDefinitionListByKey/{key}")
    public R<List<ProcessDefinitionVo>> getProcessDefinitionListByKey(@NotEmpty(message = "流程定义key不能为空") @PathVariable String key) {
        return R.ok("操作成功", actProcessDefinitionService.getProcessDefinitionListByKey(key));
    }
    /**
     * æŸ¥çœ‹æµç¨‹å®šä¹‰å›¾ç‰‡
     *
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     */
    @GetMapping("/processDefinitionImage/{processDefinitionId}")
    public R<String> processDefinitionImage(@PathVariable String processDefinitionId) {
        return R.ok("操作成功", actProcessDefinitionService.processDefinitionImage(processDefinitionId));
    }
    /**
     * æŸ¥çœ‹æµç¨‹å®šä¹‰xml文件
     *
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     */
    @GetMapping("/processDefinitionXml/{processDefinitionId}")
    public R<Map<String, Object>> getXml(@NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) {
        Map<String, Object> map = new HashMap<>();
        String xmlStr = actProcessDefinitionService.processDefinitionXml(processDefinitionId);
        map.put("xml", Arrays.asList(xmlStr.split("\n")));
        map.put("xmlStr", xmlStr);
        return R.ok(map);
    }
    /**
     * åˆ é™¤æµç¨‹å®šä¹‰
     *
     * @param deploymentId        éƒ¨ç½²id
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     */
    @Log(title = "流程定义管理", businessType = BusinessType.DELETE)
    @RepeatSubmit()
    @DeleteMapping("/{deploymentId}/{processDefinitionId}")
    public R<Void> deleteDeployment(@NotBlank(message = "流程部署id不能为空") @PathVariable String deploymentId,
                                    @NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) {
        return toAjax(actProcessDefinitionService.deleteDeployment(deploymentId, processDefinitionId));
    }
    /**
     * æ¿€æ´»æˆ–者挂起流程定义
     *
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     */
    @Log(title = "流程定义管理", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping("/updateProcessDefState/{processDefinitionId}")
    public R<Void> updateProcDefState(@NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) {
        return toAjax(actProcessDefinitionService.updateProcessDefState(processDefinitionId));
    }
    /**
     * è¿ç§»æµç¨‹å®šä¹‰
     *
     * @param currentProcessDefinitionId å½“前流程定义id
     * @param fromProcessDefinitionId    éœ€è¦è¿ç§»åˆ°çš„æµç¨‹å®šä¹‰id
     */
    @Log(title = "流程定义管理", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping("/migrationProcessDefinition/{currentProcessDefinitionId}/{fromProcessDefinitionId}")
    public R<Void> migrationProcessDefinition(@NotBlank(message = "当前流程定义id") @PathVariable String currentProcessDefinitionId,
                                              @NotBlank(message = "需要迁移到的流程定义id") @PathVariable String fromProcessDefinitionId) {
        return toAjax(actProcessDefinitionService.migrationProcessDefinition(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)
    @RepeatSubmit()
    @PostMapping("/deployByFile")
    public R<Void> deployByFile(@RequestParam("file") MultipartFile file, @RequestParam("categoryCode") String categoryCode) {
        return toAjax(actProcessDefinitionService.deployByFile(file, categoryCode));
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,157 @@
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.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.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.Map;
/**
 * æµç¨‹å®žä¾‹ç®¡ç† æŽ§åˆ¶å±‚
 *
 * @author may
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/processInstance")
public class ActProcessInstanceController extends BaseController {
    private final IActProcessInstanceService actProcessInstanceService;
    /**
     * åˆ†é¡µæŸ¥è¯¢æ­£åœ¨è¿è¡Œçš„æµç¨‹å®žä¾‹
     *
     * @param processInstanceBo å‚æ•°
     */
    @GetMapping("/getProcessInstanceRunningByPage")
    public TableDataInfo<ProcessInstanceVo> getProcessInstanceRunningByPage(ProcessInstanceBo processInstanceBo) {
        return actProcessInstanceService.getProcessInstanceRunningByPage(processInstanceBo);
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢å·²ç»“束的流程实例
     *
     * @param processInstanceBo å‚æ•°
     */
    @GetMapping("/getProcessInstanceFinishByPage")
    public TableDataInfo<ProcessInstanceVo> getProcessInstanceFinishByPage(ProcessInstanceBo processInstanceBo) {
        return actProcessInstanceService.getProcessInstanceFinishByPage(processInstanceBo);
    }
    /**
     * é€šè¿‡æµç¨‹å®žä¾‹id获取历史流程图
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     */
    @GetMapping("/getHistoryProcessImage/{processInstanceId}")
    public R<String> getHistoryProcessImage(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) {
        return R.ok("操作成功", actProcessInstanceService.getHistoryProcessImage(processInstanceId));
    }
    /**
     * é€šè¿‡æµç¨‹å®žä¾‹id获取历史流程图运行中,历史等节点
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     */
    @GetMapping("/getHistoryProcessList/{processInstanceId}")
    public R<Map<String, Object>> getHistoryProcessList(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) {
        return R.ok("操作成功", actProcessInstanceService.getHistoryProcessList(processInstanceId));
    }
    /**
     * èŽ·å–å®¡æ‰¹è®°å½•
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     */
    @GetMapping("/getHistoryRecord/{processInstanceId}")
    public R<Map<String, Object>> getHistoryRecord(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) {
        return R.ok(actProcessInstanceService.getHistoryRecord(processInstanceId));
    }
    /**
     * ä½œåºŸæµç¨‹å®žä¾‹ï¼Œä¸ä¼šåˆ é™¤åŽ†å²è®°å½•(删除运行中的实例)
     *
     * @param processInvalidBo å‚æ•°
     */
    @Log(title = "流程实例管理", businessType = BusinessType.DELETE)
    @RepeatSubmit()
    @PostMapping("/deleteRuntimeProcessInst")
    public R<Void> deleteRuntimeProcessInst(@Validated(AddGroup.class) @RequestBody ProcessInvalidBo processInvalidBo) {
        return toAjax(actProcessInstanceService.deleteRuntimeProcessInst(processInvalidBo));
    }
    /**
     * è¿è¡Œä¸­çš„实例 åˆ é™¤ç¨‹å®žä¾‹ï¼Œåˆ é™¤åŽ†å²è®°å½•ï¼Œåˆ é™¤ä¸šåŠ¡ä¸Žæµç¨‹å…³è”ä¿¡æ¯
     *
     * @param processInstanceIds æµç¨‹å®žä¾‹id
     */
    @Log(title = "流程实例管理", businessType = BusinessType.DELETE)
    @RepeatSubmit()
    @DeleteMapping("/deleteRuntimeProcessAndHisInst/{processInstanceIds}")
    public R<Void> deleteRuntimeProcessAndHisInst(@NotNull(message = "流程实例id不能为空") @PathVariable String[] processInstanceIds) {
        return toAjax(actProcessInstanceService.deleteRuntimeProcessAndHisInst(Arrays.asList(processInstanceIds)));
    }
    /**
     * å·²å®Œæˆçš„实例 åˆ é™¤ç¨‹å®žä¾‹ï¼Œåˆ é™¤åŽ†å²è®°å½•ï¼Œåˆ é™¤ä¸šåŠ¡ä¸Žæµç¨‹å…³è”ä¿¡æ¯
     *
     * @param processInstanceIds æµç¨‹å®žä¾‹id
     */
    @Log(title = "流程实例管理", businessType = BusinessType.DELETE)
    @RepeatSubmit()
    @DeleteMapping("/deleteFinishProcessAndHisInst/{processInstanceIds}")
    public R<Void> deleteFinishProcessAndHisInst(@NotNull(message = "流程实例id不能为空") @PathVariable String[] processInstanceIds) {
        return toAjax(actProcessInstanceService.deleteFinishProcessAndHisInst(Arrays.asList(processInstanceIds)));
    }
    /**
     * æ’¤é”€æµç¨‹ç”³è¯·
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     */
    @Log(title = "流程实例管理", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping("/cancelProcessApply/{processInstanceId}")
    public R<Void> cancelProcessApply(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) {
        return toAjax(actProcessInstanceService.cancelProcessApply(processInstanceId));
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢å½“前登录人单据
     *
     * @param processInstanceBo å‚æ•°
     */
    @GetMapping("/getCurrentSubmitByPage")
    public TableDataInfo<ProcessInstanceVo> getCurrentSubmitByPage(ProcessInstanceBo processInstanceBo) {
        return actProcessInstanceService.getCurrentSubmitByPage(processInstanceBo);
    }
    /**
     * ä»»åŠ¡å‚¬åŠž(给当前任务办理人发送站内信,邮件,短信等)
     *
     * @param taskUrgingBo ä»»åŠ¡å‚¬åŠž
     */
    @Log(title = "流程实例管理", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping("/taskUrging")
    public R<Void> taskUrging(@RequestBody TaskUrgingBo taskUrgingBo) {
        return toAjax(actProcessInstanceService.taskUrging(taskUrgingBo));
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,246 @@
package org.dromara.workflow.controller;
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.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.TaskVo;
import org.dromara.workflow.service.IActTaskService;
import org.dromara.workflow.utils.WorkflowUtils;
import org.flowable.engine.TaskService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
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;
    /**
     * å¯åŠ¨ä»»åŠ¡
     *
     * @param startProcessBo å¯åŠ¨æµç¨‹å‚æ•°
     */
    @Log(title = "任务管理", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping("/startWorkFlow")
    public R<Map<String, Object>> startWorkFlow(@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("/getTaskWaitByPage")
    public TableDataInfo<TaskVo> getTaskWaitByPage(TaskBo taskBo) {
        return actTaskService.getTaskWaitByPage(taskBo);
    }
    /**
     * æŸ¥è¯¢å½“前租户所有待办任务
     *
     * @param taskBo å‚æ•°
     */
    @GetMapping("/getAllTaskWaitByPage")
    public TableDataInfo<TaskVo> getAllTaskWaitByPage(TaskBo taskBo) {
        return actTaskService.getAllTaskWaitByPage(taskBo);
    }
    /**
     * æŸ¥è¯¢å½“前用户的已办任务
     *
     * @param taskBo å‚æ•°
     */
    @GetMapping("/getTaskFinishByPage")
    public TableDataInfo<TaskVo> getTaskFinishByPage(TaskBo taskBo) {
        return actTaskService.getTaskFinishByPage(taskBo);
    }
    /**
     * æŸ¥è¯¢å½“前用户的抄送
     *
     * @param taskBo å‚æ•°
     */
    @GetMapping("/getTaskCopyByPage")
    public TableDataInfo<TaskVo> getTaskCopyByPage(TaskBo taskBo) {
        return actTaskService.getTaskCopyByPage(taskBo);
    }
    /**
     * æŸ¥è¯¢å½“前租户所有已办任务
     *
     * @param taskBo å‚æ•°
     */
    @GetMapping("/getAllTaskFinishByPage")
    public TableDataInfo<TaskVo> getAllTaskFinishByPage(TaskBo taskBo) {
        return actTaskService.getAllTaskFinishByPage(taskBo);
    }
    /**
     * ç­¾æ”¶ï¼ˆæ‹¾å–)任务
     *
     * @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(@RequestBody BackProcessBo backProcessBo) {
        return R.ok(actTaskService.backProcess(backProcessBo));
    }
    /**
     * èŽ·å–æµç¨‹çŠ¶æ€
     *
     * @param taskId ä»»åŠ¡id
     */
    @GetMapping("/getBusinessStatus/{taskId}")
    public R<String> getBusinessStatus(@PathVariable String taskId) {
        return R.ok("操作成功", WorkflowUtils.getBusinessStatusByTaskId(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));
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
package org.dromara.workflow.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
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.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
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.TestLeave;
import org.dromara.workflow.domain.bo.TestLeaveBo;
import org.dromara.workflow.domain.vo.TestLeaveVo;
import org.dromara.workflow.service.ITestLeaveService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * è¯·å‡
 *
 * @author may
 * @date 2023-07-21
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/leave")
public class TestLeaveController extends BaseController {
    private final ITestLeaveService testLeaveService;
    /**
     * æŸ¥è¯¢è¯·å‡åˆ—表
     */
    @SaCheckPermission("demo:leave:list")
    @GetMapping("/list")
    public TableDataInfo<TestLeaveVo> list(TestLeaveBo bo, PageQuery pageQuery) {
        return testLeaveService.queryPageList(bo, pageQuery);
    }
    /**
     * å¯¼å‡ºè¯·å‡åˆ—表
     */
    @SaCheckPermission("demo:leave:export")
    @Log(title = "请假", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(TestLeaveBo bo, HttpServletResponse response) {
        List<TestLeaveVo> list = testLeaveService.queryList(bo);
        ExcelUtil.exportExcel(list, "请假", TestLeaveVo.class, response);
    }
    /**
     * èŽ·å–è¯·å‡è¯¦ç»†ä¿¡æ¯
     *
     * @param id ä¸»é”®
     */
    @SaCheckPermission("demo:leave:query")
    @GetMapping("/{id}")
    public R<TestLeaveVo> getInfo(@NotNull(message = "主键不能为空")
                                  @PathVariable Long id) {
        return R.ok(testLeaveService.queryById(id));
    }
    /**
     * æ–°å¢žè¯·å‡
     */
    @SaCheckPermission("demo:leave:add")
    @Log(title = "请假", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<TestLeave> add(@Validated(AddGroup.class) @RequestBody TestLeaveBo bo) {
        return R.ok(testLeaveService.insertByBo(bo));
    }
    /**
     * ä¿®æ”¹è¯·å‡
     */
    @SaCheckPermission("demo:leave:edit")
    @Log(title = "请假", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<TestLeave> edit(@Validated(EditGroup.class) @RequestBody TestLeaveBo bo) {
        return R.ok(testLeaveService.updateByBo(bo));
    }
    /**
     * åˆ é™¤è¯·å‡
     *
     * @param ids ä¸»é”®ä¸²
     */
    @SaCheckPermission("demo:leave:remove")
    @Log(title = "请假", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] ids) {
        return toAjax(testLeaveService.deleteWithValidByIds(List.of(ids)));
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfCategoryController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,106 @@
package org.dromara.workflow.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
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.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
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.web.core.BaseController;
import org.dromara.workflow.domain.bo.WfCategoryBo;
import org.dromara.workflow.domain.vo.WfCategoryVo;
import org.dromara.workflow.service.IWfCategoryService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * æµç¨‹åˆ†ç±»
 *
 * @author may
 * @date 2023-06-28
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/category")
public class WfCategoryController extends BaseController {
    private final IWfCategoryService wfCategoryService;
    /**
     * æŸ¥è¯¢æµç¨‹åˆ†ç±»åˆ—表
     */
    @SaCheckPermission("workflow:category:list")
    @GetMapping("/list")
    public R<List<WfCategoryVo>> list(WfCategoryBo bo) {
        List<WfCategoryVo> list = wfCategoryService.queryList(bo);
        return R.ok(list);
    }
    /**
     * å¯¼å‡ºæµç¨‹åˆ†ç±»åˆ—表
     */
    @SaCheckPermission("workflow:category:export")
    @Log(title = "流程分类", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(WfCategoryBo bo, HttpServletResponse response) {
        List<WfCategoryVo> list = wfCategoryService.queryList(bo);
        ExcelUtil.exportExcel(list, "流程分类", WfCategoryVo.class, response);
    }
    /**
     * èŽ·å–æµç¨‹åˆ†ç±»è¯¦ç»†ä¿¡æ¯
     *
     * @param id ä¸»é”®
     */
    @SaCheckPermission("workflow:category:query")
    @GetMapping("/{id}")
    public R<WfCategoryVo> getInfo(@NotNull(message = "主键不能为空")
                                   @PathVariable Long id) {
        return R.ok(wfCategoryService.queryById(id));
    }
    /**
     * æ–°å¢žæµç¨‹åˆ†ç±»
     */
    @SaCheckPermission("workflow:category:add")
    @Log(title = "流程分类", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody WfCategoryBo bo) {
        return toAjax(wfCategoryService.insertByBo(bo));
    }
    /**
     * ä¿®æ”¹æµç¨‹åˆ†ç±»
     */
    @SaCheckPermission("workflow:category:edit")
    @Log(title = "流程分类", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody WfCategoryBo bo) {
        return toAjax(wfCategoryService.updateByBo(bo));
    }
    /**
     * åˆ é™¤æµç¨‹åˆ†ç±»
     *
     * @param ids ä¸»é”®ä¸²
     */
    @SaCheckPermission("workflow:category:remove")
    @Log(title = "流程分类", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] ids) {
        return toAjax(wfCategoryService.deleteWithValidByIds(List.of(ids), true));
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WorkflowUserController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package org.dromara.workflow.controller;
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.system.domain.bo.SysUserBo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.workflow.domain.bo.SysUserMultiBo;
import org.dromara.workflow.domain.vo.TaskVo;
import org.dromara.workflow.service.IWorkflowUserService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * å·¥ä½œæµç”¨æˆ·é€‰äººç®¡ç† æŽ§åˆ¶å±‚
 *
 * @author may
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/user")
public class WorkflowUserController extends BaseController {
    private final IWorkflowUserService workflowUserService;
    /**
     * åˆ†é¡µæŸ¥è¯¢å·¥ä½œæµé€‰æ‹©åŠ ç­¾äººå‘˜
     *
     * @param sysUserMultiBo å‚æ•°
     */
    @GetMapping("/getWorkflowAddMultiListByPage")
    public TableDataInfo<SysUserVo> getWorkflowAddMultiInstanceByPage(SysUserMultiBo sysUserMultiBo) {
        return workflowUserService.getWorkflowAddMultiInstanceByPage(sysUserMultiBo);
    }
    /**
     * æŸ¥è¯¢å·¥ä½œæµé€‰æ‹©å‡ç­¾äººå‘˜
     *
     * @param taskId ä»»åŠ¡id
     */
    @GetMapping("/getWorkflowDeleteMultiInstanceList/{taskId}")
    public R<List<TaskVo>> getWorkflowDeleteMultiInstanceList(@PathVariable String taskId) {
        return R.ok(workflowUserService.getWorkflowDeleteMultiInstanceList(taskId));
    }
    /**
     * æŒ‰ç…§ç”¨æˆ·id查询用户
     *
     * @param userIds ç”¨æˆ·id
     */
    @GetMapping("/getUserListByIds/{userIds}")
    public R<List<SysUserVo>> getUserListByIds(@PathVariable List<Long> userIds) {
        return R.ok(workflowUserService.getUserListByIds(userIds));
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ç”¨æˆ·
     *
     * @param sysUserBo å‚æ•°
     * @param pageQuery åˆ†é¡µ
     */
    @GetMapping("/getUserListByPage")
    public TableDataInfo<SysUserVo> getUserListByPage(SysUserBo sysUserBo, PageQuery pageQuery) {
        return workflowUserService.getUserListByPage(sysUserBo, pageQuery);
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiProcinst.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,152 @@
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;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiTaskinst.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,193 @@
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 gssong
 * @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;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/TestLeave.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
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.mybatis.core.domain.BaseEntity;
import java.io.Serial;
import java.util.Date;
/**
 * è¯·å‡å¯¹è±¡ test_leave
 *
 * @author may
 * @date 2023-07-21
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("test_leave")
public class TestLeave extends BaseEntity {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®
     */
    @TableId(value = "id")
    private Long id;
    /**
     * è¯·å‡ç±»åž‹
     */
    private String leaveType;
    /**
     * å¼€å§‹æ—¶é—´
     */
    private Date startDate;
    /**
     * ç»“束时间
     */
    private Date endDate;
    /**
     * è¯·å‡å¤©æ•°
     */
    private Integer leaveDays;
    /**
     * è¯·å‡åŽŸå› 
     */
    private String remark;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfCategory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
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_category
 *
 * @author may
 * @date 2023-06-27
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("wf_category")
public class WfCategory extends TenantEntity {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®
     */
    @TableId(value = "id")
    private Long id;
    /**
     * åˆ†ç±»åç§°
     */
    private String categoryName;
    /**
     * åˆ†ç±»ç¼–码
     */
    private String categoryCode;
    /**
     * çˆ¶çº§id
     */
    private Long parentId;
    /**
     * æŽ’序
     */
    private Long sortNum;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/AddMultiBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
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 AddMultiBo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä»»åŠ¡ID
     */
    @NotBlank(message = "任务ID不能为空", groups = AddGroup.class)
    private String taskId;
    /**
     * åŠ ç­¾äººå‘˜id
     */
    @NotEmpty(message = "加签人员不能为空", groups = AddGroup.class)
    private List<Long> assignees;
    /**
     * åŠ ç­¾äººå‘˜åç§°
     */
    @NotEmpty(message = "加签人员不能为空", groups = AddGroup.class)
    private List<String> assigneeNames;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
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 BackProcessBo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä»»åŠ¡ID
     */
    @NotBlank(message = "任务ID不能为空", groups = AddGroup.class)
    private String taskId;
    /**
     * æ¶ˆæ¯ç±»åž‹
     */
    private List<String> messageType;
    /**
     * é©³å›žçš„节点id(目前未使用,直接驳回到申请人)
     */
    private String targetActivityId;
    /**
     * åŠžç†æ„è§
     */
    private String message;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.workflow.domain.vo.WfCopy;
import java.io.Serial;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
 * åŠžç†ä»»åŠ¡è¯·æ±‚å¯¹è±¡
 *
 * @author may
 */
@Data
public class CompleteTaskBo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä»»åŠ¡id
     */
    @NotBlank(message = "任务id不能为空", groups = {AddGroup.class})
    private String taskId;
    /**
     * é™„ä»¶id
     */
    private String fileId;
    /**
     * æŠ„送人员
     */
    private List<WfCopy> wfCopyList;
    /**
     * æ¶ˆæ¯ç±»åž‹
     */
    private List<String> messageType;
    /**
     * åŠžç†æ„è§
     */
    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;
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DelegateBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import java.io.Serial;
import java.io.Serializable;
/**
 * å§”派任务请求对象
 *
 * @author may
 */
@Data
public class DelegateBo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * å§”派人id
     */
    @NotBlank(message = "委派人id不能为空", groups = {AddGroup.class})
    private String userId;
    /**
     * å§”派人名称
     */
    @NotBlank(message = "委派人名称不能为空", groups = {AddGroup.class})
    private String nickName;
    /**
     * ä»»åŠ¡id
     */
    @NotBlank(message = "任务id不能为空", groups = {AddGroup.class})
    private String taskId;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DeleteMultiBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
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 DeleteMultiBo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä»»åŠ¡ID
     */
    @NotBlank(message = "任务ID不能为空", groups = AddGroup.class)
    private String taskId;
    /**
     * å‡ç­¾äººå‘˜
     */
    @NotEmpty(message = "减签人员不能为空", groups = AddGroup.class)
    private List<String> taskIds;
    /**
     * æ‰§è¡Œid
     */
    @NotEmpty(message = "执行id不能为空", groups = AddGroup.class)
    private List<String> executionIds;
    /**
     * äººå‘˜id
     */
    @NotEmpty(message = "减签人员id不能为空", groups = AddGroup.class)
    private List<Long> assigneeIds;
    /**
     * äººå‘˜åç§°
     */
    @NotEmpty(message = "减签人员不能为空", groups = AddGroup.class)
    private List<String> assigneeNames;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ModelBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.workflow.common.PageEntity;
import org.dromara.workflow.common.constant.FlowConstant;
import java.io.Serial;
import java.io.Serializable;
/**
 * æ¨¡åž‹è¯·æ±‚对象
 *
 * @author may
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class ModelBo extends PageEntity implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æ¨¡åž‹id
     */
    @NotBlank(message = "模型ID不能为空", groups = {EditGroup.class})
    private String id;
    /**
     * æ¨¡åž‹åç§°
     */
    @NotBlank(message = "模型名称不能为空", groups = {AddGroup.class})
    private String name;
    /**
     * æ¨¡åž‹æ ‡è¯†key
     */
    @NotBlank(message = "模型标识key不能为空", groups = {AddGroup.class})
    @Pattern(regexp = FlowConstant.MODEL_KEY_PATTERN, message = "模型标识key只能字符或者下划线开头", groups = {AddGroup.class})
    private String key;
    /**
     * æ¨¡åž‹åˆ†ç±»
     */
    @NotBlank(message = "模型分类不能为空", groups = {AddGroup.class})
    private String categoryCode;
    /**
     * æ¨¡åž‹XML
     */
    @NotBlank(message = "模型XML不能为空", groups = {AddGroup.class})
    private String xml;
    /**
     * æ¨¡åž‹SVG图片
     */
    @NotBlank(message = "模型SVG不能为空", groups = {EditGroup.class})
    private String svg;
    /**
     * å¤‡æ³¨
     */
    private String description;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessDefinitionBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package org.dromara.workflow.domain.bo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.workflow.common.PageEntity;
import java.io.Serial;
import java.io.Serializable;
/**
 * æµç¨‹å®šä¹‰è¯·æ±‚对象
 *
 * @author may
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class ProcessDefinitionBo extends PageEntity implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æµç¨‹å®šä¹‰åç§°key
     */
    private String key;
    /**
     * æµç¨‹å®šä¹‰åç§°
     */
    private String name;
    /**
     * æ¨¡åž‹åˆ†ç±»
     */
    private String categoryCode;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessInstanceBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package org.dromara.workflow.domain.bo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.workflow.common.PageEntity;
import java.io.Serial;
import java.io.Serializable;
/**
 * æµç¨‹å®žä¾‹è¯·æ±‚对象
 *
 * @author may
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class ProcessInstanceBo extends PageEntity implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æµç¨‹åç§°
     */
    private String name;
    /**
     * æµç¨‹key
     */
    private String key;
    /**
     * ä»»åŠ¡å‘èµ·äºº
     */
    private String startUserId;
    /**
     * ä¸šåŠ¡id
     */
    private String businessKey;
    /**
     * æ¨¡åž‹åˆ†ç±»
     */
    private String categoryCode;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessInvalidBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import java.io.Serial;
import java.io.Serializable;
/**
 * æµç¨‹å®žä¾‹ä½œåºŸè¯·æ±‚对象
 *
 * @author may
 */
@Data
public class ProcessInvalidBo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æµç¨‹å®žä¾‹id
     */
    @NotBlank(message = "流程实例id不能为空", groups = {AddGroup.class})
    private String processInstanceId;
    /**
     * ä½œåºŸåŽŸå› 
     */
    private String deleteReason;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package org.dromara.workflow.domain.bo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
 * å¯åŠ¨æµç¨‹å¯¹è±¡
 *
 * @author may
 */
@Data
public class StartProcessBo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¸šåС唝䏀值id
     */
    private String businessKey;
    /**
     * æµç¨‹æ‰§è¡Œkey
     */
    private String processKey;
    /**
     * æµç¨‹å˜é‡ï¼Œå‰ç«¯ä¼šæäº¤ä¸€ä¸ªå…ƒç´ {'entity': {业务详情数据对象}}
     */
    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;
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/SysUserMultiBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package org.dromara.workflow.domain.bo;
import lombok.Data;
import org.dromara.workflow.common.PageEntity;
/**
 * ç”¨æˆ·åŠ ç­¾æŸ¥è¯¢
 *
 * @author may
 */
@Data
public class SysUserMultiBo extends PageEntity {
    /**
     * äººå‘˜åç§°
     */
    private String userName;
    /**
     * äººå‘˜åç§°
     */
    private String nickName;
    /**
     * éƒ¨é—¨id
     */
    private String deptId;
    /**
     * ä»»åŠ¡id
     */
    private String taskId;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package org.dromara.workflow.domain.bo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.workflow.common.PageEntity;
import java.io.Serial;
import java.io.Serializable;
/**
 * ä»»åŠ¡è¯·æ±‚å¯¹è±¡
 *
 * @author may
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class TaskBo extends PageEntity implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä»»åŠ¡åç§°
     */
    private String name;
    /**
     * æµç¨‹å®šä¹‰åç§°
     */
    private String processDefinitionName;
    /**
     * æµç¨‹å®šä¹‰key
     */
    private String processDefinitionKey;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskUrgingBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package org.dromara.workflow.domain.bo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
 * ä»»åŠ¡å‚¬åŠž
 *
 * @author may
 */
@Data
public class TaskUrgingBo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æµç¨‹å®žä¾‹id
     */
    private String processInstanceId;
    /**
     * æ¶ˆæ¯ç±»åž‹
     */
    private List<String> messageType;
    /**
     * å‚¬åŠžå†…å®¹ï¼ˆä¸ºç©ºé»˜è®¤ç³»ç»Ÿå†…ç½®ä¿¡æ¯ï¼‰
     */
    private String message;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TerminationBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import java.io.Serial;
import java.io.Serializable;
/**
 * ç»ˆæ­¢ä»»åŠ¡è¯·æ±‚å¯¹è±¡
 *
 * @author may
 */
@Data
public class TerminationBo 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;
    /**
     * å®¡æ‰¹æ„è§
     */
    private String comment;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
package org.dromara.workflow.domain.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.workflow.domain.TestLeave;
import java.util.Date;
/**
 * è¯·å‡ä¸šåŠ¡å¯¹è±¡ test_leave
 *
 * @author may
 * @date 2023-07-21
 */
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = TestLeave.class, reverseConvertGenerate = false)
public class TestLeaveBo extends BaseEntity {
    /**
     * ä¸»é”®
     */
    @NotNull(message = "主键不能为空", groups = {EditGroup.class})
    private Long id;
    /**
     * è¯·å‡ç±»åž‹
     */
    @NotBlank(message = "请假类型不能为空", groups = {AddGroup.class, EditGroup.class})
    private String leaveType;
    /**
     * å¼€å§‹æ—¶é—´
     */
    @NotNull(message = "开始时间不能为空", groups = {AddGroup.class, EditGroup.class})
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date startDate;
    /**
     * ç»“束时间
     */
    @NotNull(message = "结束时间不能为空", groups = {AddGroup.class, EditGroup.class})
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date endDate;
    /**
     * è¯·å‡å¤©æ•°
     */
    @NotNull(message = "请假天数不能为空", groups = {AddGroup.class, EditGroup.class})
    private Integer leaveDays;
    /**
     * å¼€å§‹æ—¶é—´
     */
    private Integer startLeaveDays;
    /**
     * ç»“束时间
     */
    private Integer endLeaveDays;
    /**
     * è¯·å‡åŽŸå› 
     */
    private String remark;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TransmitBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import java.io.Serial;
import java.io.Serializable;
/**
 * ç»ˆè½¬åŠžåŠ¡è¯·æ±‚å¯¹è±¡
 *
 * @author may
 */
@Data
public class TransmitBo 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;
    /**
     * å®¡æ‰¹æ„è§
     */
    private String comment;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfCategoryBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package org.dromara.workflow.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.workflow.domain.WfCategory;
/**
 * æµç¨‹åˆ†ç±»ä¸šåŠ¡å¯¹è±¡ wf_category
 *
 * @author may
 * @date 2023-06-27
 */
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = WfCategory.class, reverseConvertGenerate = false)
public class WfCategoryBo extends BaseEntity {
    /**
     * ä¸»é”®
     */
    @NotNull(message = "主键不能为空", groups = {EditGroup.class})
    private Long id;
    /**
     * åˆ†ç±»åç§°
     */
    @NotBlank(message = "分类名称不能为空", groups = {AddGroup.class, EditGroup.class})
    private String categoryName;
    /**
     * åˆ†ç±»ç¼–码
     */
    @NotBlank(message = "分类编码不能为空", groups = {AddGroup.class, EditGroup.class})
    private String categoryCode;
    /**
     * çˆ¶çº§id
     */
    @NotNull(message = "父级id不能为空", groups = {AddGroup.class, EditGroup.class})
    private Long parentId;
    /**
     * æŽ’序
     */
    private Long sortNum;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ActHistoryInfoVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
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;
import java.util.Date;
import java.util.List;
/**
 * æµç¨‹å®¡æ‰¹è®°å½•视图
 *
 * @author may
 */
@Data
public class ActHistoryInfoVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä»»åŠ¡id
     */
    private String id;
    /**
     * èŠ‚ç‚¹id
     */
    private String taskDefinitionKey;
    /**
     * ä»»åŠ¡åç§°
     */
    private String name;
    /**
     * æµç¨‹å®žä¾‹id
     */
    private String processInstanceId;
    /**
     * å¼€å§‹æ—¶é—´
     */
    private Date startTime;
    /**
     * ç»“束时间
     */
    private Date endTime;
    /**
     * è¿è¡Œæ—¶é•¿
     */
    private String runDuration;
    /**
     * çŠ¶æ€
     */
    private String status;
    /**
     * çŠ¶æ€
     */
    private String statusName;
    /**
     * åŠžç†äººid
     */
    private Long assignee;
    /**
     * åŠžç†äººåç§°
     */
    @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "assignee")
    private String nickName;
    /**
     * åŠžç†äººid
     */
    private String owner;
    /**
     * å®¡æ‰¹ä¿¡æ¯id
     */
    private String commentId;
    /**
     * å®¡æ‰¹ä¿¡æ¯
     */
    private String comment;
    /**
     * å®¡æ‰¹é™„ä»¶
     */
    private List<Attachment> attachmentList;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/GraphicInfoVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
 * èŠ‚ç‚¹å›¾å½¢ä¿¡æ¯
 *
 * @author may
 */
@Data
public class GraphicInfoVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * x坐标
     */
    private double x;
    /**
     * y坐标
     */
    private double y;
    /**
     * èŠ‚ç‚¹é«˜åº¦
     */
    private double height;
    /**
     * èŠ‚ç‚¹å®½åº¦
     */
    private double width;
    /**
     * èŠ‚ç‚¹id
     */
    private String nodeId;
    /**
     * èŠ‚ç‚¹åç§°
     */
    private String nodeName;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/GroupRepresentation.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.dromara.workflow.domain.vo;
import lombok.Data;
/**
 * @author Joram Barrez
 */
@Data
public class GroupRepresentation {
    protected String id;
    protected String name;
    protected String type;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ModelVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
 * æ¨¡åž‹è§†å›¾å¯¹è±¡
 *
 * @author may
 */
@Data
public class ModelVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æ¨¡åž‹id
     */
    private String id;
    /**
     * æ¨¡åž‹åç§°
     */
    private String name;
    /**
     * æ¨¡åž‹æ ‡è¯†key
     */
    private String key;
    /**
     * æ¨¡åž‹åˆ†ç±»
     */
    private String categoryCode;
    /**
     * æ¨¡åž‹XML
     */
    private String xml;
    /**
     * å¤‡æ³¨
     */
    private String description;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/MultiInstanceVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
 * å¤šå®žä¾‹ä¿¡æ¯
 *
 * @author may
 */
@Data
public class MultiInstanceVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¼šç­¾ç±»åž‹ï¼ˆä¸²è¡Œï¼Œå¹¶è¡Œï¼‰
     */
    private Object type;
    /**
     * ä¼šç­¾äººå‘˜KEY
     */
    private String assignee;
    /**
     * ä¼šç­¾äººå‘˜é›†åˆKEY
     */
    private String assigneeList;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ParticipantVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
 * å‚与者
 *
 * @author may
 */
@Data
public class ParticipantVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ç»„id(角色id)
     */
    private List<Long> groupIds;
    /**
     * å€™é€‰äººid(用户id) å½“组id不为空时,将组内人员查出放入candidate
     */
    private List<Long> candidate;
    /**
     * å€™é€‰äººåç§°ï¼ˆç”¨æˆ·åç§°ï¼‰ å½“组id不为空时,将组内人员查出放入candidateName
     */
    private List<String> candidateName;
    /**
     * æ˜¯å¦è®¤é¢†æ ‡è¯†
     * å½“为空时默认当前任务不需要认领
     * å½“为true时当前任务说明为候选模式并且有人已经认领了任务可以归还,
     * å½“为false时当前任务说明为候选模式该任务未认领,
     */
    private Boolean claim;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessDefinitionVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
 * æµç¨‹å®šä¹‰è§†å›¾
 *
 * @author may
 */
@Data
public class ProcessDefinitionVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æµç¨‹å®šä¹‰id
     */
    private String id;
    /**
     * æµç¨‹å®šä¹‰åç§°
     */
    private String name;
    /**
     * æµç¨‹å®šä¹‰æ ‡è¯†key
     */
    private String key;
    /**
     * æµç¨‹å®šä¹‰ç‰ˆæœ¬
     */
    private int version;
    /**
     * æµç¨‹å®šä¹‰æŒ‚起或激活 1激活 2挂起
     */
    private int suspensionState;
    /**
     * æµç¨‹xml名称
     */
    private String resourceName;
    /**
     * æµç¨‹å›¾ç‰‡åç§°
     */
    private String diagramResourceName;
    /**
     * æµç¨‹éƒ¨ç½²id
     */
    private String deploymentId;
    /**
     * æµç¨‹éƒ¨ç½²æ—¶é—´
     */
    private Date deploymentTime;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessInstanceVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
 * æµç¨‹å®žä¾‹è§†å›¾
 *
 * @author may
 */
@Data
public class ProcessInstanceVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æµç¨‹å®žä¾‹id
     */
    private String id;
    /**
     * æµç¨‹å®šä¹‰id
     */
    private String processDefinitionId;
    /**
     * æµç¨‹å®šä¹‰åç§°
     */
    private String processDefinitionName;
    /**
     * æµç¨‹å®šä¹‰key
     */
    private String processDefinitionKey;
    /**
     * æµç¨‹å®šä¹‰ç‰ˆæœ¬
     */
    private String processDefinitionVersion;
    /**
     * éƒ¨ç½²id
     */
    private String deploymentId;
    /**
     * ä¸šåŠ¡id
     */
    private String businessKey;
    /**
     * æ˜¯å¦æŒ‚èµ·
     */
    private Boolean isSuspended;
    /**
     * ç§Ÿæˆ·id
     */
    private String tenantId;
    /**
     * å¯åŠ¨æ—¶é—´
     */
    private Date startTime;
    /**
     * ç»“束时间
     */
    private Date endTime;
    /**
     * å¯åŠ¨äººid
     */
    private String startUserId;
    /**
     * æµç¨‹çŠ¶æ€
     */
    private String businessStatus;
    /**
     * æµç¨‹çŠ¶æ€
     */
    private String businessStatusName;
    /**
     * å¾…办任务集合
     */
    private List<TaskVo> taskVoList;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TaskVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,143 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import java.util.Date;
/**
 * ä»»åŠ¡è§†å›¾
 *
 * @author may
 */
@Data
public class TaskVo {
    /**
     * ä»»åŠ¡id
     */
    private String id;
    /**
     * ä»»åŠ¡åç§°
     */
    private String name;
    /**
     * æè¿°
     */
    private String description;
    /**
     * ä¼˜å…ˆçº§
     */
    private Integer priority;
    /**
     * è´Ÿè´£æ­¤ä»»åŠ¡çš„äººå‘˜çš„ç”¨æˆ·id
     */
    private String owner;
    /**
     * åŠžç†äººid
     */
    private Long assignee;
    /**
     * åŠžç†äºº
     */
    @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "assignee")
    private String assigneeName;
    /**
     * æµç¨‹å®žä¾‹id
     */
    private String processInstanceId;
    /**
     * æ‰§è¡Œid
     */
    private String executionId;
    /**
     * æ— ç”¨
     */
    private String taskDefinitionId;
    /**
     * æµç¨‹å®šä¹‰id
     */
    private String processDefinitionId;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    private Date createTime;
    /**
     * ç»“束时间
     */
    private Date endTime;
    /**
     * èŠ‚ç‚¹id
     */
    private String taskDefinitionKey;
    /**
     * ä»»åŠ¡æˆªæ­¢æ—¥æœŸ
     */
    private Date dueDate;
    /**
     * æµç¨‹ç±»åˆ«
     */
    private String category;
    /**
     * çˆ¶çº§ä»»åŠ¡id
     */
    private String parentTaskId;
    /**
     * ç§Ÿæˆ·id
     */
    private String tenantId;
    /**
     * è®¤é¢†æ—¶é—´
     */
    private Date claimTime;
    /**
     * æµç¨‹çŠ¶æ€
     */
    private String businessStatus;
    /**
     * æµç¨‹çŠ¶æ€
     */
    private String businessStatusName;
    /**
     * æµç¨‹å®šä¹‰åç§°
     */
    private String processDefinitionName;
    /**
     * æµç¨‹å®šä¹‰key
     */
    private String processDefinitionKey;
    /**
     * å‚与者
     */
    private ParticipantVo participantVo;
    /**
     * æ˜¯å¦ä¼šç­¾
     */
    private Boolean multiInstance;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,70 @@
package org.dromara.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.workflow.domain.TestLeave;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
 * è¯·å‡è§†å›¾å¯¹è±¡ test_leave
 *
 * @author may
 * @date 2023-07-21
 */
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = TestLeave.class)
public class TestLeaveVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®
     */
    @ExcelProperty(value = "主键")
    private Long id;
    /**
     * è¯·å‡ç±»åž‹
     */
    @ExcelProperty(value = "请假类型")
    private String leaveType;
    /**
     * å¼€å§‹æ—¶é—´
     */
    @ExcelProperty(value = "开始时间")
    private Date startDate;
    /**
     * ç»“束时间
     */
    @ExcelProperty(value = "结束时间")
    private Date endDate;
    /**
     * è¯·å‡å¤©æ•°
     */
    @ExcelProperty(value = "请假天数")
    private Integer leaveDays;
    /**
     * å¤‡æ³¨
     */
    @ExcelProperty(value = "请假原因")
    private String remark;
    /**
     * æµç¨‹å®žä¾‹å¯¹è±¡
     */
    private ProcessInstanceVo processInstanceVo;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCategoryVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package org.dromara.workflow.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.workflow.domain.WfCategory;
import java.io.Serial;
import java.io.Serializable;
/**
 * æµç¨‹åˆ†ç±»è§†å›¾å¯¹è±¡ wf_category
 *
 * @author may
 * @date 2023-06-27
 */
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = WfCategory.class)
public class WfCategoryVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®
     */
    @ExcelProperty(value = "主键")
    private Long id;
    /**
     * åˆ†ç±»åç§°
     */
    @ExcelProperty(value = "分类名称")
    private String categoryName;
    /**
     * åˆ†ç±»ç¼–码
     */
    @ExcelProperty(value = "分类编码")
    private String categoryCode;
    /**
     * çˆ¶çº§id
     */
    @ExcelProperty(value = "父级id")
    private Long parentId;
    /**
     * æŽ’序
     */
    @ExcelProperty(value = "排序")
    private Long sortNum;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCopy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
/**
 * æŠ„送
 *
 * @author may
 */
@Data
public class WfCopy {
    /**
     * ç”¨æˆ·id
     */
    private Long userId;
    /**
     * ç”¨æˆ·åç§°
     */
    private String userName;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramCanvas.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,108 @@
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);
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramGenerator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1120 @@
/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.dromara.workflow.flowable;
import org.flowable.bpmn.model.Event;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.image.ProcessDiagramGenerator;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.util.List;
import java.util.*;
/**
 * Class to generate an image based the diagram interchange information in a BPMN 2.0 process.
 *
 * @author Joram Barrez
 * @author Tijs Rademakers
 * @author Zheng Ji
 */
public class CustomDefaultProcessDiagramGenerator implements ProcessDiagramGenerator {
    protected Map<Class<? extends BaseElement>, ActivityDrawInstruction> activityDrawInstructions = new HashMap<>();
    protected Map<Class<? extends BaseElement>, ArtifactDrawInstruction> artifactDrawInstructions = new HashMap<>();
    public CustomDefaultProcessDiagramGenerator() {
        this(1.0);
    }
    // The instructions on how to draw a certain construct is
    // created statically and stored in a map for performance.
    public CustomDefaultProcessDiagramGenerator(final double scaleFactor) {
        // start event
        activityDrawInstructions.put(StartEvent.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                StartEvent startEvent = (StartEvent) flowNode;
                if (startEvent.getEventDefinitions() != null && !startEvent.getEventDefinitions().isEmpty()) {
                    EventDefinition eventDefinition = startEvent.getEventDefinitions().get(0);
                    if (eventDefinition instanceof TimerEventDefinition) {
                        processDiagramCanvas.drawTimerStartEvent(graphicInfo, scaleFactor);
                    } else if (eventDefinition instanceof ErrorEventDefinition) {
                        processDiagramCanvas.drawErrorStartEvent(graphicInfo, scaleFactor);
                    } else if (eventDefinition instanceof EscalationEventDefinition) {
                        processDiagramCanvas.drawEscalationStartEvent(graphicInfo, scaleFactor);
                    } else if (eventDefinition instanceof ConditionalEventDefinition) {
                        processDiagramCanvas.drawConditionalStartEvent(graphicInfo, scaleFactor);
                    } else if (eventDefinition instanceof SignalEventDefinition) {
                        processDiagramCanvas.drawSignalStartEvent(graphicInfo, scaleFactor);
                    } else if (eventDefinition instanceof MessageEventDefinition) {
                        processDiagramCanvas.drawMessageStartEvent(graphicInfo, scaleFactor);
                    } else {
                        processDiagramCanvas.drawNoneStartEvent(graphicInfo);
                    }
                } else {
                    List<ExtensionElement> eventTypeElements = startEvent.getExtensionElements().get("eventType");
                    if (eventTypeElements != null && eventTypeElements.size() > 0) {
                        processDiagramCanvas.drawEventRegistryStartEvent(graphicInfo, scaleFactor);
                    } else {
                        processDiagramCanvas.drawNoneStartEvent(graphicInfo);
                    }
                }
            }
        });
        // signal catch
        activityDrawInstructions.put(IntermediateCatchEvent.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                IntermediateCatchEvent intermediateCatchEvent = (IntermediateCatchEvent) flowNode;
                if (intermediateCatchEvent.getEventDefinitions() != null && !intermediateCatchEvent.getEventDefinitions().isEmpty()) {
                    if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) {
                        processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
                    } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof TimerEventDefinition) {
                        processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
                    } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof MessageEventDefinition) {
                        processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
                    } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof ConditionalEventDefinition) {
                        processDiagramCanvas.drawCatchingConditionalEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
                    }
                }
            }
        });
        // signal throw
        activityDrawInstructions.put(ThrowEvent.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                ThrowEvent throwEvent = (ThrowEvent) flowNode;
                if (throwEvent.getEventDefinitions() != null && !throwEvent.getEventDefinitions().isEmpty()) {
                    if (throwEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) {
                        processDiagramCanvas.drawThrowingSignalEvent(graphicInfo, scaleFactor);
                    } else if (throwEvent.getEventDefinitions().get(0) instanceof EscalationEventDefinition) {
                        processDiagramCanvas.drawThrowingEscalationEvent(graphicInfo, scaleFactor);
                    } else if (throwEvent.getEventDefinitions().get(0) instanceof CompensateEventDefinition) {
                        processDiagramCanvas.drawThrowingCompensateEvent(graphicInfo, scaleFactor);
                    } else {
                        processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor);
                    }
                } else {
                    processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor);
                }
            }
        });
        // end event
        activityDrawInstructions.put(EndEvent.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                EndEvent endEvent = (EndEvent) flowNode;
                if (endEvent.getEventDefinitions() != null && !endEvent.getEventDefinitions().isEmpty()) {
                    if (endEvent.getEventDefinitions().get(0) instanceof ErrorEventDefinition) {
                        processDiagramCanvas.drawErrorEndEvent(flowNode.getName(), graphicInfo, scaleFactor);
                    } else if (endEvent.getEventDefinitions().get(0) instanceof EscalationEventDefinition) {
                        processDiagramCanvas.drawEscalationEndEvent(flowNode.getName(), graphicInfo, scaleFactor);
                    } else {
                        processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor);
                    }
                } else {
                    processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor);
                }
            }
        });
        // task
        activityDrawInstructions.put(Task.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });
        // user task
        activityDrawInstructions.put(UserTask.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawUserTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });
        // script task
        activityDrawInstructions.put(ScriptTask.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawScriptTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });
        // service task
        activityDrawInstructions.put(ServiceTask.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                ServiceTask serviceTask = (ServiceTask) flowNode;
                if ("camel".equalsIgnoreCase(serviceTask.getType())) {
                    processDiagramCanvas.drawCamelTask(serviceTask.getName(), graphicInfo, scaleFactor);
                }else if (ServiceTask.HTTP_TASK.equalsIgnoreCase(serviceTask.getType())) {
                    processDiagramCanvas.drawHttpTask(serviceTask.getName(), graphicInfo, scaleFactor);
                } else if (ServiceTask.DMN_TASK.equalsIgnoreCase(serviceTask.getType())) {
                    processDiagramCanvas.drawDMNTask(serviceTask.getName(), graphicInfo, scaleFactor);
                } else if (ServiceTask.SHELL_TASK.equalsIgnoreCase(serviceTask.getType())) {
                    processDiagramCanvas.drawShellTask(serviceTask.getName(), graphicInfo, scaleFactor);
                } else {
                    processDiagramCanvas.drawServiceTask(serviceTask.getName(), graphicInfo, scaleFactor);
                }
            }
        });
        // http service task
        activityDrawInstructions.put(HttpServiceTask.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawHttpTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });
        // receive task
        activityDrawInstructions.put(ReceiveTask.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawReceiveTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });
        // send task
        activityDrawInstructions.put(SendTask.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawSendTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });
        // manual task
        activityDrawInstructions.put(ManualTask.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawManualTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });
        // send event service task
        activityDrawInstructions.put(SendEventServiceTask.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawSendEventServiceTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });
        // external worker service task
        activityDrawInstructions.put(ExternalWorkerServiceTask.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                ServiceTask serviceTask = (ServiceTask) flowNode;
                processDiagramCanvas.drawServiceTask(serviceTask.getName(), graphicInfo, scaleFactor);
            }
        });
        // case service task
        activityDrawInstructions.put(CaseServiceTask.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawCaseServiceTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });
        // businessRuleTask task
        activityDrawInstructions.put(BusinessRuleTask.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawBusinessRuleTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });
        // exclusive gateway
        activityDrawInstructions.put(ExclusiveGateway.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawExclusiveGateway(graphicInfo, scaleFactor);
            }
        });
        // inclusive gateway
        activityDrawInstructions.put(InclusiveGateway.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawInclusiveGateway(graphicInfo, scaleFactor);
            }
        });
        // parallel gateway
        activityDrawInstructions.put(ParallelGateway.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawParallelGateway(graphicInfo, scaleFactor);
            }
        });
        // event based gateway
        activityDrawInstructions.put(EventGateway.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawEventBasedGateway(graphicInfo, scaleFactor);
            }
        });
        // Boundary timer
        activityDrawInstructions.put(BoundaryEvent.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                BoundaryEvent boundaryEvent = (BoundaryEvent) flowNode;
                if (boundaryEvent.getEventDefinitions() != null && !boundaryEvent.getEventDefinitions().isEmpty()) {
                    EventDefinition eventDefinition = boundaryEvent.getEventDefinitions().get(0);
                    if (eventDefinition instanceof TimerEventDefinition) {
                        processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
                    } else if (eventDefinition instanceof ConditionalEventDefinition) {
                        processDiagramCanvas.drawCatchingConditionalEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
                    } else if (eventDefinition instanceof ErrorEventDefinition) {
                        processDiagramCanvas.drawCatchingErrorEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
                    } else if (eventDefinition instanceof EscalationEventDefinition) {
                        processDiagramCanvas.drawCatchingEscalationEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
                    } else if (eventDefinition instanceof SignalEventDefinition) {
                        processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
                    } else if (eventDefinition instanceof MessageEventDefinition) {
                        processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
                    } else if (eventDefinition instanceof CompensateEventDefinition) {
                        processDiagramCanvas.drawCatchingCompensateEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
                    }
                } else {
                    List<ExtensionElement> eventTypeElements = boundaryEvent.getExtensionElements().get("eventType");
                    if (eventTypeElements != null && eventTypeElements.size() > 0) {
                        processDiagramCanvas.drawCatchingEventRegistryEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
                    }
                }
            }
        });
        // subprocess
        activityDrawInstructions.put(SubProcess.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
                    processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
                } else {
                    processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
                }
            }
        });
        // transaction
        activityDrawInstructions.put(Transaction.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
                    processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
                } else {
                    processDiagramCanvas.drawExpandedTransaction(flowNode.getName(), graphicInfo, scaleFactor);
                }
            }
        });
        // Event subprocess
        activityDrawInstructions.put(EventSubProcess.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
                    processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, true, scaleFactor);
                } else {
                    processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, true, scaleFactor);
                }
            }
        });
        // Adhoc subprocess
        activityDrawInstructions.put(AdhocSubProcess.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
                    processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
                } else {
                    processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
                }
            }
        });
        // call activity
        activityDrawInstructions.put(CallActivity.class, new ActivityDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawCollapsedCallActivity(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });
        // text annotation
        artifactDrawInstructions.put(TextAnnotation.class, new ArtifactDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(artifact.getId());
                TextAnnotation textAnnotation = (TextAnnotation) artifact;
                processDiagramCanvas.drawTextAnnotation(textAnnotation.getText(), graphicInfo, scaleFactor);
            }
        });
        // association
        artifactDrawInstructions.put(Association.class, new ArtifactDrawInstruction() {
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {
                Association association = (Association) artifact;
                String sourceRef = association.getSourceRef();
                String targetRef = association.getTargetRef();
                // source and target can be instance of FlowElement or Artifact
                BaseElement sourceElement = bpmnModel.getFlowElement(sourceRef);
                BaseElement targetElement = bpmnModel.getFlowElement(targetRef);
                if (sourceElement == null) {
                    sourceElement = bpmnModel.getArtifact(sourceRef);
                }
                if (targetElement == null) {
                    targetElement = bpmnModel.getArtifact(targetRef);
                }
                List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());
                graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList);
                int[] xPoints = new int[graphicInfoList.size()];
                int[] yPoints = new int[graphicInfoList.size()];
                for (int i = 1; i < graphicInfoList.size(); i++) {
                    GraphicInfo graphicInfo = graphicInfoList.get(i);
                    GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);
                    if (i == 1) {
                        xPoints[0] = (int) previousGraphicInfo.getX();
                        yPoints[0] = (int) previousGraphicInfo.getY();
                    }
                    xPoints[i] = (int) graphicInfo.getX();
                    yPoints[i] = (int) graphicInfo.getY();
                }
                AssociationDirection associationDirection = association.getAssociationDirection();
                processDiagramCanvas.drawAssociation(xPoints, yPoints, associationDirection, false, scaleFactor);
            }
        });
    }
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows,
                                       String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows,
            activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI).generateImage(imageType);
    }
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, 1.0, drawSequenceFlowNameWithNoLabelDI);
    }
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType,
                                       List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.emptyList(), drawSequenceFlowNameWithNoLabelDI);
    }
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.emptyList(), scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,
                                       String labelFontName, String annotationFontName, ClassLoader customClassLoader, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, Collections.emptyList(), Collections.emptyList(),
            activityFontName, labelFontName, annotationFontName, customClassLoader, 1.0, drawSequenceFlowNameWithNoLabelDI);
    }
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,
                                       String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, Collections.emptyList(), Collections.emptyList(),
            activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }
    @Override
    public InputStream generatePngDiagram(BpmnModel bpmnModel, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generatePngDiagram(bpmnModel, 1.0, drawSequenceFlowNameWithNoLabelDI);
    }
    @Override
    public InputStream generatePngDiagram(BpmnModel bpmnModel, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }
    @Override
    public InputStream generateJpgDiagram(BpmnModel bpmnModel) {
        return generateJpgDiagram(bpmnModel, 1.0, false);
    }
    @Override
    public InputStream generateJpgDiagram(BpmnModel bpmnModel, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, "jpg", Collections.emptyList(), Collections.emptyList(), drawSequenceFlowNameWithNoLabelDI);
    }
    public BufferedImage generateImage(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows,
                                       String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows,
            activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI).generateBufferedImage(imageType);
    }
    public BufferedImage generateImage(BpmnModel bpmnModel, String imageType,
                                       List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateImage(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }
    @Override
    public BufferedImage generatePngImage(BpmnModel bpmnModel, double scaleFactor) {
        return generateImage(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), scaleFactor, false);
    }
    protected CustomDefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType,
                                                                       List<String> highLightedActivities, List<String> highLightedFlows,
                                                                       String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        prepareBpmnModel(bpmnModel);
        CustomDefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
        // Draw pool shape, if process is participant in collaboration
        for (Pool pool : bpmnModel.getPools()) {
            GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
            processDiagramCanvas.drawPoolOrLane(pool.getName(), graphicInfo, scaleFactor);
        }
        // Draw lanes
        for (Process process : bpmnModel.getProcesses()) {
            for (Lane lane : process.getLanes()) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(lane.getId());
                processDiagramCanvas.drawPoolOrLane(lane.getName(), graphicInfo, scaleFactor);
            }
        }
        // Draw activities and their sequence-flows
        for (Process process : bpmnModel.getProcesses()) {
            for (FlowNode flowNode : process.findFlowElementsOfType(FlowNode.class)) {
                if (!isPartOfCollapsedSubProcess(flowNode, bpmnModel)) {
                    drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
                }
            }
        }
        // Draw artifacts
        for (Process process : bpmnModel.getProcesses()) {
            for (Artifact artifact : process.getArtifacts()) {
                drawArtifact(processDiagramCanvas, bpmnModel, artifact);
            }
            List<SubProcess> subProcesses = process.findFlowElementsOfType(SubProcess.class, true);
            if (subProcesses != null) {
                for (SubProcess subProcess : subProcesses) {
                    GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(subProcess.getId());
                    if (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
                        continue;
                    }
                    if (!isPartOfCollapsedSubProcess(subProcess, bpmnModel)) {
                        for (Artifact subProcessArtifact : subProcess.getArtifacts()) {
                            drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact);
                        }
                    }
                }
            }
        }
        return processDiagramCanvas;
    }
    protected void prepareBpmnModel(BpmnModel bpmnModel) {
        // Need to make sure all elements have positive x and y.
        // Check all graphicInfo and update the elements accordingly
        List<GraphicInfo> allGraphicInfos = new ArrayList<>();
        if (bpmnModel.getLocationMap() != null) {
            allGraphicInfos.addAll(bpmnModel.getLocationMap().values());
        }
        if (bpmnModel.getLabelLocationMap() != null) {
            allGraphicInfos.addAll(bpmnModel.getLabelLocationMap().values());
        }
        if (bpmnModel.getFlowLocationMap() != null) {
            for (List<GraphicInfo> flowGraphicInfos : bpmnModel.getFlowLocationMap().values()) {
                allGraphicInfos.addAll(flowGraphicInfos);
            }
        }
        if (allGraphicInfos.size() > 0) {
            boolean needsTranslationX = false;
            boolean needsTranslationY = false;
            double lowestX = 0.0;
            double lowestY = 0.0;
            // Collect lowest x and y
            for (GraphicInfo graphicInfo : allGraphicInfos) {
                double x = graphicInfo.getX();
                double y = graphicInfo.getY();
                if (x < lowestX) {
                    needsTranslationX = true;
                    lowestX = x;
                }
                if (y < lowestY) {
                    needsTranslationY = true;
                    lowestY = y;
                }
            }
            // Update all graphicInfo objects
            if (needsTranslationX || needsTranslationY) {
                double translationX = Math.abs(lowestX);
                double translationY = Math.abs(lowestY);
                for (GraphicInfo graphicInfo : allGraphicInfos) {
                    if (needsTranslationX) {
                        graphicInfo.setX(graphicInfo.getX() + translationX);
                    }
                    if (needsTranslationY) {
                        graphicInfo.setY(graphicInfo.getY() + translationY);
                    }
                }
            }
        }
    }
    protected void drawActivity(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel,
                                FlowNode flowNode, List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) {
        ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass());
        if (drawInstruction != null) {
            drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode);
            // Gather info on the multi instance marker
            boolean multiInstanceSequential = false;
            boolean multiInstanceParallel = false;
            boolean collapsed = false;
            if (flowNode instanceof Activity) {
                Activity activity = (Activity) flowNode;
                MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics();
                if (multiInstanceLoopCharacteristics != null) {
                    multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential();
                    multiInstanceParallel = !multiInstanceSequential;
                }
            }
            // Gather info on the collapsed marker
            GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
            if (flowNode instanceof SubProcess) {
                collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded();
            } else if (flowNode instanceof CallActivity) {
                collapsed = true;
            }
            if (scaleFactor == 1.0) {
                // Actually draw the markers
                processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(),
                    multiInstanceSequential, multiInstanceParallel, collapsed);
            }
            // Draw highlighted activities
            if (highLightedActivities.contains(flowNode.getId())) {
                drawHighLightRed(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
            } else if (highLightedActivities.contains(Color.RED.toString() + flowNode.getId())) {
                drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
            }
        } else if (flowNode instanceof Task) {
            activityDrawInstructions.get(Task.class).draw(processDiagramCanvas, bpmnModel, flowNode);
            if (highLightedActivities.contains(flowNode.getId())) {
                drawHighLightRed(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
            } else if (highLightedActivities.contains(Color.RED.toString() + flowNode.getId())) {
                drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
            }
        }
        // Outgoing transitions of activity
        for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
            boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId()));
            String defaultFlow = null;
            if (flowNode instanceof Activity) {
                defaultFlow = ((Activity) flowNode).getDefaultFlow();
            } else if (flowNode instanceof Gateway) {
                defaultFlow = ((Gateway) flowNode).getDefaultFlow();
            }
            boolean isDefault = false;
            if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) {
                isDefault = true;
            }
            boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && sequenceFlow.getConditionExpression().trim().length() > 0 && !(flowNode instanceof Gateway);
            String sourceRef = sequenceFlow.getSourceRef();
            String targetRef = sequenceFlow.getTargetRef();
            FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef);
            FlowElement targetElement = bpmnModel.getFlowElement(targetRef);
            List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
            if (graphicInfoList != null && graphicInfoList.size() > 0) {
                graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList);
                int[] xPoints = new int[graphicInfoList.size()];
                int[] yPoints = new int[graphicInfoList.size()];
                for (int i = 1; i < graphicInfoList.size(); i++) {
                    GraphicInfo graphicInfo = graphicInfoList.get(i);
                    GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);
                    if (i == 1) {
                        xPoints[0] = (int) previousGraphicInfo.getX();
                        yPoints[0] = (int) previousGraphicInfo.getY();
                    }
                    xPoints[i] = (int) graphicInfo.getX();
                    yPoints[i] = (int) graphicInfo.getY();
                }
                processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted, scaleFactor);
                // Draw sequenceflow label
                GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId());
                if (labelGraphicInfo != null) {
                    processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false);
                } else {
                    if (drawSequenceFlowNameWithNoLabelDI) {
                        GraphicInfo lineCenter = getLineCenter(graphicInfoList);
                        processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false);
                    }
                }
            }
        }
        // Nested elements
        if (flowNode instanceof FlowElementsContainer) {
            for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) {
                if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) {
                    drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement,
                        highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
                }
            }
        }
    }
    /**
     * This method makes coordinates of connection flow better.
     *
     * @param processDiagramCanvas
     * @param bpmnModel
     * @param sourceElement
     * @param targetElement
     * @param graphicInfoList
     * @return
     */
    protected static List<GraphicInfo> connectionPerfectionizer(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, BaseElement sourceElement, BaseElement targetElement, List<GraphicInfo> graphicInfoList) {
        GraphicInfo sourceGraphicInfo = bpmnModel.getGraphicInfo(sourceElement.getId());
        GraphicInfo targetGraphicInfo = bpmnModel.getGraphicInfo(targetElement.getId());
        CustomDefaultProcessDiagramCanvas.SHAPE_TYPE sourceShapeType = getShapeType(sourceElement);
        CustomDefaultProcessDiagramCanvas.SHAPE_TYPE targetShapeType = getShapeType(targetElement);
        return processDiagramCanvas.connectionPerfectionizer(sourceShapeType, targetShapeType, sourceGraphicInfo, targetGraphicInfo, graphicInfoList);
    }
    /**
     * This method returns shape type of base element.<br>
     * Each element can be presented as rectangle, rhombus, or ellipse.
     *
     * @param baseElement
     * @return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE
     */
    protected static CustomDefaultProcessDiagramCanvas.SHAPE_TYPE getShapeType(BaseElement baseElement) {
        if (baseElement instanceof Task || baseElement instanceof Activity || baseElement instanceof TextAnnotation) {
            return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Rectangle;
        } else if (baseElement instanceof Gateway) {
            return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Rhombus;
        } else if (baseElement instanceof Event) {
            return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Ellipse;
        } else {
            // unknown source element, just do not correct coordinates
        }
        return null;
    }
    protected static GraphicInfo getLineCenter(List<GraphicInfo> graphicInfoList) {
        GraphicInfo gi = new GraphicInfo();
        int[] xPoints = new int[graphicInfoList.size()];
        int[] yPoints = new int[graphicInfoList.size()];
        double length = 0;
        double[] lengths = new double[graphicInfoList.size()];
        lengths[0] = 0;
        double m;
        for (int i = 1; i < graphicInfoList.size(); i++) {
            GraphicInfo graphicInfo = graphicInfoList.get(i);
            GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);
            if (i == 1) {
                xPoints[0] = (int) previousGraphicInfo.getX();
                yPoints[0] = (int) previousGraphicInfo.getY();
            }
            xPoints[i] = (int) graphicInfo.getX();
            yPoints[i] = (int) graphicInfo.getY();
            length += Math.sqrt(
                Math.pow((int) graphicInfo.getX() - (int) previousGraphicInfo.getX(), 2) +
                    Math.pow((int) graphicInfo.getY() - (int) previousGraphicInfo.getY(), 2));
            lengths[i] = length;
        }
        m = length / 2;
        int p1 = 0;
        int p2 = 1;
        for (int i = 1; i < lengths.length; i++) {
            double len = lengths[i];
            p1 = i - 1;
            p2 = i;
            if (len > m) {
                break;
            }
        }
        GraphicInfo graphicInfo1 = graphicInfoList.get(p1);
        GraphicInfo graphicInfo2 = graphicInfoList.get(p2);
        double AB = (int) graphicInfo2.getX() - (int) graphicInfo1.getX();
        double OA = (int) graphicInfo2.getY() - (int) graphicInfo1.getY();
        double OB = lengths[p2] - lengths[p1];
        double ob = m - lengths[p1];
        double ab = AB * ob / OB;
        double oa = OA * ob / OB;
        double mx = graphicInfo1.getX() + ab;
        double my = graphicInfo1.getY() + oa;
        gi.setX(mx);
        gi.setY(my);
        return gi;
    }
    protected void drawArtifact(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {
        ArtifactDrawInstruction drawInstruction = artifactDrawInstructions.get(artifact.getClass());
        if (drawInstruction != null) {
            drawInstruction.draw(processDiagramCanvas, bpmnModel, artifact);
        }
    }
    private static void drawHighLight(CustomDefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
        processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
    }
    private static void drawHighLightRed(CustomDefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
        processDiagramCanvas.drawHighLightRed((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
    }
    protected static CustomDefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType,
                                                                                String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
        // We need to calculate maximum values to know how big the image will be in its entirety
        double minX = Double.MAX_VALUE;
        double maxX = 0;
        double minY = Double.MAX_VALUE;
        double maxY = 0;
        for (Pool pool : bpmnModel.getPools()) {
            GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
            minX = graphicInfo.getX();
            maxX = graphicInfo.getX() + graphicInfo.getWidth();
            minY = graphicInfo.getY();
            maxY = graphicInfo.getY() + graphicInfo.getHeight();
        }
        List<FlowNode> flowNodes = gatherAllFlowNodes(bpmnModel);
        for (FlowNode flowNode : flowNodes) {
            GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
            // width
            if (flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth() > maxX) {
                maxX = flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth();
            }
            if (flowNodeGraphicInfo.getX() < minX) {
                minX = flowNodeGraphicInfo.getX();
            }
            // height
            if (flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight() > maxY) {
                maxY = flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight();
            }
            if (flowNodeGraphicInfo.getY() < minY) {
                minY = flowNodeGraphicInfo.getY();
            }
            for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
                List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
                if (graphicInfoList != null) {
                    for (GraphicInfo graphicInfo : graphicInfoList) {
                        // width
                        if (graphicInfo.getX() > maxX) {
                            maxX = graphicInfo.getX();
                        }
                        if (graphicInfo.getX() < minX) {
                            minX = graphicInfo.getX();
                        }
                        // height
                        if (graphicInfo.getY() > maxY) {
                            maxY = graphicInfo.getY();
                        }
                        if (graphicInfo.getY() < minY) {
                            minY = graphicInfo.getY();
                        }
                    }
                }
            }
        }
        List<Artifact> artifacts = gatherAllArtifacts(bpmnModel);
        for (Artifact artifact : artifacts) {
            GraphicInfo artifactGraphicInfo = bpmnModel.getGraphicInfo(artifact.getId());
            if (artifactGraphicInfo != null) {
                // width
                if (artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth() > maxX) {
                    maxX = artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth();
                }
                if (artifactGraphicInfo.getX() < minX) {
                    minX = artifactGraphicInfo.getX();
                }
                // height
                if (artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight() > maxY) {
                    maxY = artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight();
                }
                if (artifactGraphicInfo.getY() < minY) {
                    minY = artifactGraphicInfo.getY();
                }
            }
            List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());
            if (graphicInfoList != null) {
                for (GraphicInfo graphicInfo : graphicInfoList) {
                    // width
                    if (graphicInfo.getX() > maxX) {
                        maxX = graphicInfo.getX();
                    }
                    if (graphicInfo.getX() < minX) {
                        minX = graphicInfo.getX();
                    }
                    // height
                    if (graphicInfo.getY() > maxY) {
                        maxY = graphicInfo.getY();
                    }
                    if (graphicInfo.getY() < minY) {
                        minY = graphicInfo.getY();
                    }
                }
            }
        }
        int nrOfLanes = 0;
        for (Process process : bpmnModel.getProcesses()) {
            for (Lane l : process.getLanes()) {
                nrOfLanes++;
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());
                // // width
                if (graphicInfo.getX() + graphicInfo.getWidth() > maxX) {
                    maxX = graphicInfo.getX() + graphicInfo.getWidth();
                }
                if (graphicInfo.getX() < minX) {
                    minX = graphicInfo.getX();
                }
                // height
                if (graphicInfo.getY() + graphicInfo.getHeight() > maxY) {
                    maxY = graphicInfo.getY() + graphicInfo.getHeight();
                }
                if (graphicInfo.getY() < minY) {
                    minY = graphicInfo.getY();
                }
            }
        }
        // Special case, see https://activiti.atlassian.net/browse/ACT-1431
        if (flowNodes.isEmpty() && bpmnModel.getPools().isEmpty() && nrOfLanes == 0) {
            // Nothing to show
            minX = 0;
            minY = 0;
        }
        return new CustomDefaultProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY,
            imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
    }
    protected static List<Artifact> gatherAllArtifacts(BpmnModel bpmnModel) {
        List<Artifact> artifacts = new ArrayList<>();
        for (Process process : bpmnModel.getProcesses()) {
            artifacts.addAll(process.getArtifacts());
        }
        return artifacts;
    }
    protected static List<FlowNode> gatherAllFlowNodes(BpmnModel bpmnModel) {
        List<FlowNode> flowNodes = new ArrayList<>();
        for (Process process : bpmnModel.getProcesses()) {
            flowNodes.addAll(gatherAllFlowNodes(process));
        }
        return flowNodes;
    }
    protected static List<FlowNode> gatherAllFlowNodes(FlowElementsContainer flowElementsContainer) {
        List<FlowNode> flowNodes = new ArrayList<>();
        for (FlowElement flowElement : flowElementsContainer.getFlowElements()) {
            if (flowElement instanceof FlowNode) {
                flowNodes.add((FlowNode) flowElement);
            }
            if (flowElement instanceof FlowElementsContainer) {
                flowNodes.addAll(gatherAllFlowNodes((FlowElementsContainer) flowElement));
            }
        }
        return flowNodes;
    }
    protected boolean isPartOfCollapsedSubProcess(FlowElement flowElement, BpmnModel model) {
        SubProcess subProcess = flowElement.getSubProcess();
        if (subProcess != null) {
            GraphicInfo graphicInfo = model.getGraphicInfo(subProcess.getId());
            if (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
                return true;
            }
            return isPartOfCollapsedSubProcess(subProcess, model);
        }
        return false;
    }
    public Map<Class<? extends BaseElement>, ActivityDrawInstruction> getActivityDrawInstructions() {
        return activityDrawInstructions;
    }
    public void setActivityDrawInstructions(
        Map<Class<? extends BaseElement>, ActivityDrawInstruction> activityDrawInstructions) {
        this.activityDrawInstructions = activityDrawInstructions;
    }
    public Map<Class<? extends BaseElement>, ArtifactDrawInstruction> getArtifactDrawInstructions() {
        return artifactDrawInstructions;
    }
    public void setArtifactDrawInstructions(
        Map<Class<? extends BaseElement>, ArtifactDrawInstruction> artifactDrawInstructions) {
        this.artifactDrawInstructions = artifactDrawInstructions;
    }
    protected interface ActivityDrawInstruction {
        void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode);
    }
    protected interface ArtifactDrawInstruction {
        void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact);
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AddSequenceMultiInstanceCmd.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
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;
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AttachmentCmd.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package org.dromara.workflow.flowable.cmd;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
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.satoken.utils.LoginHelper;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysOssService;
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.Arrays;
import java.util.Date;
import java.util.List;
/**
 * ä¸²è¡ŒåŠ ç­¾
 *
 * @author é™„件上传
 */
public class AttachmentCmd implements Command<Boolean> {
    private final String fileId;
    private final String taskId;
    private final String processInstanceId;
    public AttachmentCmd(String fileId, String taskId, String processInstanceId) {
        this.fileId = fileId;
        this.taskId = taskId;
        this.processInstanceId = processInstanceId;
    }
    @Override
    public Boolean execute(CommandContext commandContext) {
        try {
            if (StringUtils.isNotBlank(fileId)) {
                List<Long> fileIds = StreamUtils.toList(Arrays.asList(fileId.split(StrUtil.COMMA)), Long::valueOf);
                List<SysOssVo> sysOssVos = SpringUtils.getBean(ISysOssService.class).listByIds(fileIds);
                if (CollUtil.isNotEmpty(sysOssVos)) {
                    for (SysOssVo sysOssVo : sysOssVos) {
                        AttachmentEntityManager attachmentEntityManager = CommandContextUtil.getAttachmentEntityManager();
                        AttachmentEntity attachmentEntity = attachmentEntityManager.create();
                        attachmentEntity.setRevision(1);
                        attachmentEntity.setUserId(LoginHelper.getUserId().toString());
                        attachmentEntity.setName(sysOssVo.getOriginalName());
                        attachmentEntity.setDescription(sysOssVo.getOriginalName());
                        attachmentEntity.setType(sysOssVo.getFileSuffix());
                        attachmentEntity.setTaskId(taskId);
                        attachmentEntity.setProcessInstanceId(processInstanceId);
                        attachmentEntity.setContentId(sysOssVo.getOssId().toString());
                        attachmentEntity.setTime(new Date());
                        attachmentEntityManager.insert(attachmentEntity);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return true;
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteExecutionCmd.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
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;
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteSequenceMultiInstanceCmd.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,82 @@
package org.dromara.workflow.flowable.cmd;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
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 = assignees.stream().filter(id -> ObjectUtil.equals(id, e)).findFirst().orElse(null);
            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;
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/ExecutionChildByExecutionIdCmd.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
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());
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateBusinessStatusCmd.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
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());
        }
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateHiTaskInstCmd.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
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());
        }
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/FlowableConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package org.dromara.workflow.flowable.config;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.flowable.common.engine.impl.cfg.IdGenerator;
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));
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/GlobalFlowableListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,91 @@
package org.dromara.workflow.flowable.config;
import cn.hutool.core.collection.CollUtil;
import org.dromara.workflow.common.enums.TaskStatusEnum;
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.runtime.Execution;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
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;
    @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);
            }
        }
    }
    @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) {
        Execution execution = runtimeService.createExecutionQuery().executionId(event.getExecutionId()).singleResult();
        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 = taskService.createTaskQuery().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(), "超时自动审批!");
                    }
                }
            }
        }
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowEventStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package org.dromara.workflow.flowable.strategy;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.workflow.annotation.FlowListenerAnnotation;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
 * æµç¨‹ä»»åŠ¡ç›‘å¬ç­–ç•¥
 *
 * @author may
 * @date 2023-12-27
 */
@Component
public class FlowEventStrategy implements BeanPostProcessor {
    private final Map<String, FlowTaskEventHandler> flowTaskEventHandlers = new HashMap<>();
    private final Map<String, FlowProcessEventHandler> flowProcessEventHandlers = new HashMap<>();
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof FlowTaskEventHandler) {
            FlowListenerAnnotation annotation = bean.getClass().getAnnotation(FlowListenerAnnotation.class);
            if (null != annotation) {
                if (StringUtils.isNotBlank(annotation.processDefinitionKey()) && StringUtils.isNotBlank(annotation.taskDefId())) {
                    String id = annotation.processDefinitionKey() + "_" + annotation.taskDefId();
                    if (!flowTaskEventHandlers.containsKey(id)) {
                        flowTaskEventHandlers.put(id, (FlowTaskEventHandler) bean);
                    }
                }
            }
        }
        if (bean instanceof FlowProcessEventHandler) {
            FlowListenerAnnotation annotation = bean.getClass().getAnnotation(FlowListenerAnnotation.class);
            if (null != annotation) {
                if (StringUtils.isNotBlank(annotation.processDefinitionKey()) && StringUtils.isBlank(annotation.taskDefId())) {
                    if (!flowProcessEventHandlers.containsKey(annotation.processDefinitionKey())) {
                        flowProcessEventHandlers.put(annotation.processDefinitionKey(), (FlowProcessEventHandler) bean);
                    }
                }
            }
        }
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }
    /**
     * èŽ·å–å¯æ‰§è¡Œbean
     *
     * @param key key
     */
    public FlowTaskEventHandler getTaskHandler(String key) {
        if (!flowTaskEventHandlers.containsKey(key)) {
            return null;
        }
        return flowTaskEventHandlers.get(key);
    }
    /**
     * èŽ·å–å¯æ‰§è¡Œbean
     *
     * @param key key
     */
    public FlowProcessEventHandler getProcessHandler(String key) {
        if (!flowProcessEventHandlers.containsKey(key)) {
            return null;
        }
        return flowProcessEventHandlers.get(key);
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowProcessEventHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package org.dromara.workflow.flowable.strategy;
/**
 * æµç¨‹ç›‘听
 *
 * @author may
 * @date 2023-12-27
 */
public interface FlowProcessEventHandler {
    /**
     * æ‰§è¡ŒåŠžç†ä»»åŠ¡ç›‘å¬
     *
     * @param businessKey ä¸šåŠ¡id
     * @param status      çŠ¶æ€
     * @param submit      å½“为true时为申请人节点办理
     */
    void handleProcess(String businessKey, String status, boolean submit);
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowTaskEventHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package org.dromara.workflow.flowable.strategy;
import org.flowable.task.api.Task;
/**
 * æµç¨‹ä»»åŠ¡ç›‘å¬
 *
 * @author may
 * @date 2023-12-27
 */
public interface FlowTaskEventHandler {
    /**
     * æ‰§è¡ŒåŠžç†ä»»åŠ¡ç›‘å¬
     *
     * @param task        ä»»åŠ¡
     * @param businessKey ä¸šåŠ¡id
     */
    void handleTask(Task task, String businessKey);
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestCustomProcessHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package org.dromara.workflow.listener;
import lombok.extern.slf4j.Slf4j;
import org.dromara.workflow.annotation.FlowListenerAnnotation;
import org.dromara.workflow.flowable.strategy.FlowProcessEventHandler;
import org.springframework.stereotype.Component;
/**
 * è‡ªå®šä¹‰ç›‘听测试
 *
 * @author may
 * @date 2023-12-27
 */
@Slf4j
@Component
@FlowListenerAnnotation(processDefinitionKey = "leave1")
public class TestCustomProcessHandler implements FlowProcessEventHandler {
    @Override
    public void handleProcess(String businessKey, String status, boolean submit) {
        log.info("业务ID:" + businessKey + ",状态:" + status);
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestCustomTaskHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package org.dromara.workflow.listener;
import lombok.extern.slf4j.Slf4j;
import org.dromara.workflow.annotation.FlowListenerAnnotation;
import org.dromara.workflow.flowable.strategy.FlowTaskEventHandler;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Component;
/**
 * è‡ªå®šä¹‰ç›‘听测试
 *
 * @author may
 * @date 2023-12-27
 */
@Slf4j
@Component
@FlowListenerAnnotation(processDefinitionKey = "leave1", taskDefId = "Activity_14633hx")
public class TestCustomTaskHandler implements FlowTaskEventHandler {
    @Override
    public void handleTask(Task task, String businessKey) {
        log.info("任务名称:" + task.getName() + ",业务ID:" + businessKey);
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestLeaveExecutionListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package org.dromara.workflow.listener;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Component;
/**
 * æµç¨‹å®žä¾‹ç›‘听测试
 *
 * @author may
 * @date 2023-12-12
 */
@Slf4j
@RequiredArgsConstructor
@Component("testLeaveExecutionListener")
public class TestLeaveExecutionListener implements ExecutionListener {
    private final TaskService taskService;
    @Override
    public void notify(DelegateExecution execution) {
        Task task = taskService.createTaskQuery().executionId(execution.getId()).singleResult();
        log.info("执行监听【" + task.getName() + "】");
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestLeaveTaskListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package org.dromara.workflow.listener;
import lombok.extern.slf4j.Slf4j;
import org.flowable.task.service.delegate.DelegateTask;
import org.flowable.task.service.delegate.TaskListener;
import org.springframework.stereotype.Component;
/**
 * æµç¨‹ä»»åŠ¡ç›‘å¬æµ‹è¯•
 *
 * @author may
 * @date 2023-12-12
 */
@Slf4j
@Component("testLeaveTaskListener")
public class TestLeaveTaskListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
        log.info("执行监听【" + delegateTask.getName() + "】");
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiProcinstMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
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> {
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiTaskinstMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
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 gssong
 * @date 2024-03-02
 */
@InterceptorIgnore(tenantLine = "true")
public interface ActHiTaskinstMapper extends BaseMapperPlus<ActHiTaskinst, ActHiTaskinst> {
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActTaskMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
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 gssong
 * @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> getTaskCopyByPage(@Param("page") Page<TaskVo> page, @Param(Constants.WRAPPER) QueryWrapper<TaskVo> queryWrapper);
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/TestLeaveMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package org.dromara.workflow.mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.workflow.domain.TestLeave;
import org.dromara.workflow.domain.vo.TestLeaveVo;
/**
 * è¯·å‡Mapper接口
 *
 * @author may
 * @date 2023-07-21
 */
public interface TestLeaveMapper extends BaseMapperPlus<TestLeave, TestLeaveVo> {
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfCategoryMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package org.dromara.workflow.mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.workflow.domain.WfCategory;
import org.dromara.workflow.domain.vo.WfCategoryVo;
/**
 * æµç¨‹åˆ†ç±»Mapper接口
 *
 * @author may
 * @date 2023-06-27
 */
public interface WfCategoryMapper extends BaseMapperPlus<WfCategory, WfCategoryVo> {
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiProcinstService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
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);
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiTaskinstService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
package org.dromara.workflow.service;
/**
 * æµç¨‹åŽ†å²ä»»åŠ¡Service接口
 *
 * @author gssong
 * @date 2024-03-02
 */
public interface IActHiTaskinstService {
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActModelService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package org.dromara.workflow.service;
import jakarta.servlet.http.HttpServletResponse;
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;
/**
 * æ¨¡åž‹ç®¡ç† æœåС层
 *
 * @author may
 */
public interface IActModelService {
    /**
     * åˆ†é¡µæŸ¥è¯¢æ¨¡åž‹
     *
     * @param modelBo æ¨¡åž‹å‚æ•°
     * @return è¿”回分页列表
     */
    TableDataInfo<Model> page(ModelBo modelBo);
    /**
     * æ–°å¢žæ¨¡åž‹
     *
     * @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 modelId  æ¨¡åž‹id
     * @param response ç›¸åº”
     */
    void exportZip(String modelId, HttpServletResponse response);
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessDefinitionService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package org.dromara.workflow.service;
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 å‚æ•°
     * @return è¿”回分页列表
     */
    TableDataInfo<ProcessDefinitionVo> page(ProcessDefinitionBo processDefinitionBo);
    /**
     * æŸ¥è¯¢åŽ†å²æµç¨‹å®šä¹‰åˆ—è¡¨
     *
     * @param key æµç¨‹å®šä¹‰key
     * @return ç»“æžœ
     */
    List<ProcessDefinitionVo> getProcessDefinitionListByKey(String key);
    /**
     * æŸ¥çœ‹æµç¨‹å®šä¹‰å›¾ç‰‡
     *
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     * @return ç»“æžœ
     */
    String processDefinitionImage(String processDefinitionId);
    /**
     * æŸ¥çœ‹æµç¨‹å®šä¹‰xml文件
     *
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     * @return ç»“æžœ
     */
    String processDefinitionXml(String processDefinitionId);
    /**
     * åˆ é™¤æµç¨‹å®šä¹‰
     *
     * @param deploymentId        éƒ¨ç½²id
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     * @return ç»“æžœ
     */
    boolean deleteDeployment(String deploymentId, String processDefinitionId);
    /**
     * æ¿€æ´»æˆ–者挂起流程定义
     *
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     * @return ç»“æžœ
     */
    boolean updateProcessDefState(String processDefinitionId);
    /**
     * è¿ç§»æµç¨‹å®šä¹‰
     *
     * @param currentProcessDefinitionId å½“前流程定义id
     * @param fromProcessDefinitionId    éœ€è¦è¿ç§»åˆ°çš„æµç¨‹å®šä¹‰id
     * @return ç»“æžœ
     */
    boolean migrationProcessDefinition(String currentProcessDefinitionId, String fromProcessDefinitionId);
    /**
     * æµç¨‹å®šä¹‰è½¬æ¢ä¸ºæ¨¡åž‹
     *
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     * @return ç»“æžœ
     */
    boolean convertToModel(String processDefinitionId);
    /**
     * é€šè¿‡zip或xml部署流程定义
     *
     * @param file         æ–‡ä»¶
     * @param categoryCode åˆ†ç±»
     * @return ç»“æžœ
     */
    boolean deployByFile(MultipartFile file, String categoryCode);
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessInstanceService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,113 @@
package org.dromara.workflow.service;
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.ProcessInstanceVo;
import java.util.List;
import java.util.Map;
/**
 * æµç¨‹å®žä¾‹ æœåС层
 *
 * @author may
 */
public interface IActProcessInstanceService {
    /**
     * é€šè¿‡æµç¨‹å®žä¾‹id获取历史流程图
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     * @return ç»“æžœ
     */
    String getHistoryProcessImage(String processInstanceId);
    /**
     * é€šè¿‡æµç¨‹å®žä¾‹id获取历史流程图运行中,历史等节点
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     * @return ç»“æžœ
     */
    Map<String, Object> getHistoryProcessList(String processInstanceId);
    /**
     * åˆ†é¡µæŸ¥è¯¢æ­£åœ¨è¿è¡Œçš„æµç¨‹å®žä¾‹
     *
     * @param processInstanceBo å‚æ•°
     * @return ç»“æžœ
     */
    TableDataInfo<ProcessInstanceVo> getProcessInstanceRunningByPage(ProcessInstanceBo processInstanceBo);
    /**
     * åˆ†é¡µæŸ¥è¯¢å·²ç»“束的流程实例
     *
     * @param processInstanceBo å‚æ•°
     * @return ç»“æžœ
     */
    TableDataInfo<ProcessInstanceVo> getProcessInstanceFinishByPage(ProcessInstanceBo processInstanceBo);
    /**
     * èŽ·å–å®¡æ‰¹è®°å½•
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     * @return ç»“æžœ
     */
    Map<String, Object> getHistoryRecord(String processInstanceId);
    /**
     * ä½œåºŸæµç¨‹å®žä¾‹ï¼Œä¸ä¼šåˆ é™¤åŽ†å²è®°å½•(删除运行中的实例)
     *
     * @param processInvalidBo å‚æ•°
     * @return ç»“æžœ
     */
    boolean deleteRuntimeProcessInst(ProcessInvalidBo processInvalidBo);
    /**
     * è¿è¡Œä¸­çš„实例 åˆ é™¤ç¨‹å®žä¾‹ï¼Œåˆ é™¤åŽ†å²è®°å½•ï¼Œåˆ é™¤ä¸šåŠ¡ä¸Žæµç¨‹å…³è”ä¿¡æ¯
     *
     * @param processInstanceIds æµç¨‹å®žä¾‹id
     * @return ç»“æžœ
     */
    boolean deleteRuntimeProcessAndHisInst(List<String> processInstanceIds);
    /**
     * æŒ‰ç…§ä¸šåŠ¡id删除 è¿è¡Œä¸­çš„实例 åˆ é™¤ç¨‹å®žä¾‹ï¼Œåˆ é™¤åŽ†å²è®°å½•ï¼Œåˆ é™¤ä¸šåŠ¡ä¸Žæµç¨‹å…³è”ä¿¡æ¯
     *
     * @param businessKeys ä¸šåŠ¡id
     * @return ç»“æžœ
     */
    boolean deleteRuntimeProcessAndHisInstByBusinessKeys(List<String> businessKeys);
    /**
     * å·²å®Œæˆçš„实例 åˆ é™¤ç¨‹å®žä¾‹ï¼Œåˆ é™¤åŽ†å²è®°å½•ï¼Œåˆ é™¤ä¸šåŠ¡ä¸Žæµç¨‹å…³è”ä¿¡æ¯
     *
     * @param processInstanceIds æµç¨‹å®žä¾‹id
     * @return ç»“æžœ
     */
    boolean deleteFinishProcessAndHisInst(List<String> processInstanceIds);
    /**
     * æ’¤é”€æµç¨‹ç”³è¯·
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     * @return ç»“æžœ
     */
    boolean cancelProcessApply(String processInstanceId);
    /**
     * åˆ†é¡µæŸ¥è¯¢å½“前登录人单据
     *
     * @param processInstanceBo å‚æ•°
     * @return ç»“æžœ
     */
    TableDataInfo<ProcessInstanceVo> getCurrentSubmitByPage(ProcessInstanceBo processInstanceBo);
    /**
     * ä»»åŠ¡å‚¬åŠž(给当前任务办理人发送站内信,邮件,短信等)
     *
     * @param taskUrgingBo ä»»åŠ¡å‚¬åŠž
     * @return ç»“æžœ
     */
    boolean taskUrging(TaskUrgingBo taskUrgingBo);
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,129 @@
package org.dromara.workflow.service;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.workflow.domain.bo.*;
import org.dromara.workflow.domain.vo.TaskVo;
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 å‚æ•°
     * @return ç»“æžœ
     */
    TableDataInfo<TaskVo> getTaskWaitByPage(TaskBo taskBo);
    /**
     * æŸ¥è¯¢å½“前租户所有待办任务
     *
     * @param taskBo å‚æ•°
     * @return ç»“æžœ
     */
    TableDataInfo<TaskVo> getAllTaskWaitByPage(TaskBo taskBo);
    /**
     * æŸ¥è¯¢å½“前用户的已办任务
     *
     * @param taskBo å‚æ•°
     * @return ç»“æžœ
     */
    TableDataInfo<TaskVo> getTaskFinishByPage(TaskBo taskBo);
    /**
     * æŸ¥è¯¢å½“前用户的抄送
     *
     * @param taskBo å‚æ•°
     * @return ç»“æžœ
     */
    TableDataInfo<TaskVo> getTaskCopyByPage(TaskBo taskBo);
    /**
     * æŸ¥è¯¢å½“前租户所有已办任务
     *
     * @param taskBo å‚æ•°
     * @return ç»“æžœ
     */
    TableDataInfo<TaskVo> getAllTaskFinishByPage(TaskBo taskBo);
    /**
     * å§”派任务
     *
     * @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);
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/ITestLeaveService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
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.TestLeave;
import org.dromara.workflow.domain.bo.TestLeaveBo;
import org.dromara.workflow.domain.vo.TestLeaveVo;
import java.util.Collection;
import java.util.List;
/**
 * è¯·å‡Service接口
 *
 * @author may
 * @date 2023-07-21
 */
public interface ITestLeaveService {
    /**
     * æŸ¥è¯¢è¯·å‡
     */
    TestLeaveVo queryById(Long id);
    /**
     * æŸ¥è¯¢è¯·å‡åˆ—表
     */
    TableDataInfo<TestLeaveVo> queryPageList(TestLeaveBo bo, PageQuery pageQuery);
    /**
     * æŸ¥è¯¢è¯·å‡åˆ—表
     */
    List<TestLeaveVo> queryList(TestLeaveBo bo);
    /**
     * æ–°å¢žè¯·å‡
     */
    TestLeave insertByBo(TestLeaveBo bo);
    /**
     * ä¿®æ”¹è¯·å‡
     */
    TestLeave updateByBo(TestLeaveBo bo);
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤è¯·å‡ä¿¡æ¯
     */
    Boolean deleteWithValidByIds(Collection<Long> ids);
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfCategoryService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package org.dromara.workflow.service;
import org.dromara.workflow.domain.WfCategory;
import org.dromara.workflow.domain.bo.WfCategoryBo;
import org.dromara.workflow.domain.vo.WfCategoryVo;
import java.util.Collection;
import java.util.List;
/**
 * æµç¨‹åˆ†ç±»Service接口
 *
 * @author may
 * @date 2023-06-28
 */
public interface IWfCategoryService {
    /**
     * æŸ¥è¯¢æµç¨‹åˆ†ç±»
     */
    WfCategoryVo queryById(Long id);
    /**
     * æŸ¥è¯¢æµç¨‹åˆ†ç±»åˆ—表
     */
    List<WfCategoryVo> queryList(WfCategoryBo bo);
    /**
     * æ–°å¢žæµç¨‹åˆ†ç±»
     */
    Boolean insertByBo(WfCategoryBo bo);
    /**
     * ä¿®æ”¹æµç¨‹åˆ†ç±»
     */
    Boolean updateByBo(WfCategoryBo bo);
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤æµç¨‹åˆ†ç±»ä¿¡æ¯
     */
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
    /**
     * æŒ‰ç…§ç±»åˆ«ç¼–码查询
     *
     * @param categoryCode åˆ†ç±»æ¯”吗
     * @return ç»“æžœ
     */
    WfCategory queryByCategoryCode(String categoryCode);
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWorkflowUserService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
package org.dromara.workflow.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.domain.SysUserRole;
import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.workflow.domain.bo.SysUserMultiBo;
import org.dromara.workflow.domain.vo.TaskVo;
import java.util.List;
/**
 * å·¥ä½œæµç”¨æˆ·é€‰äººç®¡ç† æœåС层
 *
 * @author may
 */
public interface IWorkflowUserService {
    /**
     * åˆ†é¡µæŸ¥è¯¢å·¥ä½œæµé€‰æ‹©åŠ ç­¾äººå‘˜
     *
     * @param sysUserMultiBo å‚æ•°
     * @return ç»“æžœ
     */
    TableDataInfo<SysUserVo> getWorkflowAddMultiInstanceByPage(SysUserMultiBo sysUserMultiBo);
    /**
     * æŸ¥è¯¢å·¥ä½œæµé€‰æ‹©å‡ç­¾äººå‘˜
     *
     * @param taskId ä»»åŠ¡id
     * @return ç»“æžœ
     */
    List<TaskVo> getWorkflowDeleteMultiInstanceList(String taskId);
    /**
     * æŒ‰ç…§ç”¨æˆ·id查询用户
     *
     * @param userIds ç”¨æˆ·id
     * @return ç»“æžœ
     */
    List<SysUserVo> getUserListByIds(List<Long> userIds);
    /**
     * æŒ‰ç…§è§’色id查询关联用户id
     *
     * @param roleIds è§’色id
     * @return ç»“æžœ
     */
    List<SysUserRole> getUserRoleListByRoleIds(List<Long> roleIds);
    /**
     * åˆ†é¡µæŸ¥è¯¢ç”¨æˆ·
     *
     * @param sysUserBo å‚æ•°
     * @param pageQuery åˆ†é¡µ
     * @return ç»“æžœ
     */
    TableDataInfo<SysUserVo> getUserListByPage(SysUserBo sysUserBo, PageQuery pageQuery);
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiProcinstServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
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()));
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiTaskinstServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.workflow.service.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.dromara.workflow.service.IActHiTaskinstService;
/**
 * æµç¨‹åŽ†å²ä»»åŠ¡Service业务层处理
 *
 * @author gssong
 * @date 2024-03-02
 */
@RequiredArgsConstructor
@Service
public class ActHiTaskinstServiceImpl implements IActHiTaskinstService {
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActModelServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,334 @@
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 jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.dromara.common.core.exception.ServiceException;
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.bo.ModelBo;
import org.dromara.workflow.domain.vo.ModelVo;
import org.dromara.workflow.service.IActModelService;
import org.dromara.workflow.utils.ModelUtils;
import org.flowable.bpmn.model.BpmnModel;
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.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
 * æ¨¡åž‹ç®¡ç† æœåŠ¡å±‚å®žçŽ°
 *
 * @author may
 */
@RequiredArgsConstructor
@Service
public class ActModelServiceImpl implements IActModelService {
    private final RepositoryService repositoryService;
    /**
     * åˆ†é¡µæŸ¥è¯¢æ¨¡åž‹
     *
     * @param modelBo æ¨¡åž‹å‚æ•°
     * @return è¿”回分页列表
     */
    @Override
    public TableDataInfo<Model> page(ModelBo modelBo) {
        ModelQuery query = repositoryService.createModelQuery();
        query.modelTenantId(TenantHelper.getTenantId());
        if (StringUtils.isNotEmpty(modelBo.getName())) {
            query.modelNameLike("%" + modelBo.getName() + "%");
        }
        if (StringUtils.isNotEmpty(modelBo.getKey())) {
            query.modelKey(modelBo.getKey());
        }
        if (StringUtils.isNotEmpty(modelBo.getCategoryCode())) {
            query.modelCategory(modelBo.getCategoryCode());
        }
        query.orderByLastUpdateTime().desc();
        // åˆ›å»ºæ—¶é—´é™åºæŽ’列
        query.orderByCreateTime().desc();
        // åˆ†é¡µæŸ¥è¯¢
        List<Model> modelList = query.listPage(modelBo.getPageNum(), modelBo.getPageSize());
        // æ€»è®°å½•æ•°
        long total = query.count();
        return new TableDataInfo<>(modelList, total);
    }
    /**
     * æ–°å¢žæ¨¡åž‹
     *
     * @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 = repositoryService.createModelQuery().modelKey(key).modelTenantId(TenantHelper.getTenantId()).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) {
            e.printStackTrace();
            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) {
                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 = repositoryService.createModelQuery().modelTenantId(TenantHelper.getTenantId()).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) {
            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 = repositoryService.createModelQuery().modelTenantId(TenantHelper.getTenantId()).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) {
            e.printStackTrace();
            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(IOUtils.toString(xmlBytes, StandardCharsets.UTF_8.toString()))) {
                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);
            // 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 = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
            repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * å¯¼å‡ºæ¨¡åž‹zip压缩包
     *
     * @param modelId  æ¨¡åž‹id
     * @param response ç›¸åº”
     */
    @Override
    public void exportZip(String modelId, HttpServletResponse response) {
        ZipOutputStream zos = null;
        try {
            zos = ZipUtil.getZipOutputStream(response.getOutputStream(), StandardCharsets.UTF_8);
            // åŽ‹ç¼©åŒ…æ–‡ä»¶å
            String zipName = "模型不存在";
            // æŸ¥è¯¢æ¨¡åž‹åŸºæœ¬ä¿¡æ¯
            Model model = repositoryService.getModel(modelId);
            byte[] xmlBytes = repositoryService.getModelEditorSource(modelId);
            if (ObjectUtil.isNotNull(model)) {
                if (JSONUtil.isTypeJSON(IOUtils.toString(xmlBytes, StandardCharsets.UTF_8.toString())) && 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.flushBuffer();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (zos != null) {
                try {
                    zos.closeEntry();
                    zos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessDefinitionServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,328 @@
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.collection.CollectionUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
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.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.bo.ProcessDefinitionBo;
import org.dromara.workflow.domain.vo.ProcessDefinitionVo;
import org.dromara.workflow.service.IActProcessDefinitionService;
import org.dromara.workflow.service.IWfCategoryService;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ProcessMigrationService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.bpmn.deployer.ResourceNameUtil;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Service;
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.List;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;
/**
 * æµç¨‹å®šä¹‰ æœåŠ¡å±‚å®žçŽ°
 *
 * @author may
 */
@RequiredArgsConstructor
@Service
public class ActProcessDefinitionServiceImpl implements IActProcessDefinitionService {
    private final RepositoryService repositoryService;
    private final HistoryService historyService;
    private final ProcessMigrationService processMigrationService;
    private final IWfCategoryService wfCategoryService;
    /**
     * åˆ†é¡µæŸ¥è¯¢
     *
     * @param processDefinitionBo å‚æ•°
     * @return è¿”回分页列表
     */
    @Override
    public TableDataInfo<ProcessDefinitionVo> page(ProcessDefinitionBo processDefinitionBo) {
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        query.processDefinitionTenantId(TenantHelper.getTenantId());
        if (StringUtils.isNotEmpty(processDefinitionBo.getKey())) {
            query.processDefinitionKey(processDefinitionBo.getKey());
        }
        if (StringUtils.isNotEmpty(processDefinitionBo.getCategoryCode())) {
            query.processDefinitionCategory(processDefinitionBo.getCategoryCode());
        }
        if (StringUtils.isNotEmpty(processDefinitionBo.getName())) {
            query.processDefinitionNameLike("%" + processDefinitionBo.getName() + "%");
        }
        query.orderByDeploymentId().desc();
        // åˆ†é¡µæŸ¥è¯¢
        List<ProcessDefinitionVo> processDefinitionVoList = new ArrayList<>();
        List<ProcessDefinition> definitionList = query.latestVersion().listPage(processDefinitionBo.getPageNum(), processDefinitionBo.getPageSize());
        List<Deployment> deploymentList = null;
        if (CollUtil.isNotEmpty(definitionList)) {
            List<String> deploymentIds = StreamUtils.toList(definitionList, ProcessDefinition::getDeploymentId);
            deploymentList = repositoryService.createDeploymentQuery().deploymentIds(deploymentIds).list();
        }
        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());
                });
            }
            processDefinitionVoList.add(processDefinitionVo);
        }
        // æ€»è®°å½•æ•°
        long total = query.count();
        return new TableDataInfo<>(processDefinitionVoList, total);
    }
    /**
     * æŸ¥è¯¢åŽ†å²æµç¨‹å®šä¹‰åˆ—è¡¨
     *
     * @param key æµç¨‹å®šä¹‰key
     */
    @Override
    public List<ProcessDefinitionVo> getProcessDefinitionListByKey(String key) {
        List<ProcessDefinitionVo> processDefinitionVoList = new ArrayList<>();
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        List<ProcessDefinition> definitionList = query.processDefinitionTenantId(TenantHelper.getTenantId()).processDefinitionKey(key).list();
        List<Deployment> deploymentList = null;
        if (CollUtil.isNotEmpty(definitionList)) {
            List<String> deploymentIds = definitionList.stream().map(ProcessDefinition::getDeploymentId).collect(Collectors.toList());
            deploymentList = repositoryService.createDeploymentQuery()
                .deploymentIds(deploymentIds).list();
        }
        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());
                });
            }
            processDefinitionVoList.add(processDefinitionVo);
        }
        return CollectionUtil.reverse(processDefinitionVoList);
    }
    /**
     * æŸ¥çœ‹æµç¨‹å®šä¹‰å›¾ç‰‡
     *
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     */
    @SneakyThrows
    @Override
    public String processDefinitionImage(String processDefinitionId) {
        InputStream inputStream = repositoryService.getProcessDiagram(processDefinitionId);
        return Base64.encode(IOUtils.toByteArray(inputStream));
    }
    /**
     * æŸ¥çœ‹æµç¨‹å®šä¹‰xml文件
     *
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     */
    @Override
    public String processDefinitionXml(String processDefinitionId) {
        StringBuilder xml = new StringBuilder();
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
        InputStream inputStream;
        try {
            inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
            xml.append(IOUtils.toString(inputStream, StandardCharsets.UTF_8));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return xml.toString();
    }
    /**
     * åˆ é™¤æµç¨‹å®šä¹‰
     *
     * @param deploymentId        éƒ¨ç½²id
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     */
    @Override
    public boolean deleteDeployment(String deploymentId, String processDefinitionId) {
        try {
            List<HistoricTaskInstance> taskInstanceList = historyService.createHistoricTaskInstanceQuery()
                .processDefinitionId(processDefinitionId).list();
            if (CollectionUtil.isNotEmpty(taskInstanceList)) {
                throw new ServiceException("当前流程定义已被使用不可删除!");
            }
            //删除流程定义
            repositoryService.deleteDeployment(deploymentId);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * æ¿€æ´»æˆ–者挂起流程定义
     *
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     */
    @Override
    public boolean updateProcessDefState(String processDefinitionId) {
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId).processDefinitionTenantId(TenantHelper.getTenantId()).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) {
            e.printStackTrace();
            throw new ServiceException("操作失败:" + e.getMessage());
        }
    }
    /**
     * è¿ç§»æµç¨‹å®šä¹‰
     *
     * @param currentProcessDefinitionId å½“前流程定义id
     * @param fromProcessDefinitionId    éœ€è¦è¿ç§»åˆ°çš„æµç¨‹å®šä¹‰id
     */
    @Override
    public boolean migrationProcessDefinition(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) {
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * æµç¨‹å®šä¹‰è½¬æ¢ä¸ºæ¨¡åž‹
     *
     * @param processDefinitionId æµç¨‹å®šä¹‰id
     */
    @Override
    public boolean convertToModel(String processDefinitionId) {
        ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
            .processDefinitionId(processDefinitionId).singleResult();
        InputStream inputStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), pd.getResourceName());
        Model model = repositoryService.createModelQuery().modelKey(pd.getKey()).modelTenantId(TenantHelper.getTenantId()).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) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * é€šè¿‡zip或xml部署流程定义
     *
     * @param file         æ–‡ä»¶
     * @param categoryCode åˆ†ç±»
     */
    @Override
    public boolean deployByFile(MultipartFile file, String categoryCode) {
        try {
            WfCategory wfCategory = wfCategoryService.queryByCategoryCode(categoryCode);
            if (wfCategory == null) {
                throw new ServiceException("流程分类不存在");
            }
            // æ–‡ä»¶å = æµç¨‹åç§°-流程key
            String filename = file.getOriginalFilename();
            assert filename != null;
            String[] splitFilename = filename.substring(0, filename.lastIndexOf(".")).split("-");
            if (splitFilename.length < 2) {
                throw new ServiceException("流程分类不能为空(文件名 = æµç¨‹åç§°-流程key)");
            }
            //流程名称
            String processName = splitFilename[0];
            //流程key
            String processKey = splitFilename[1];
            // æ–‡ä»¶åŽç¼€å
            String suffix = filename.substring(filename.lastIndexOf(".") + 1).toUpperCase();
            InputStream inputStream = file.getInputStream();
            Deployment deployment;
            if (FlowConstant.ZIP.equals(suffix)) {
                deployment = repositoryService.createDeployment()
                    .tenantId(TenantHelper.getTenantId())
                    .addZipInputStream(new ZipInputStream(inputStream)).name(processName).key(processKey).category(categoryCode).deploy();
            } else {
                String[] list = ResourceNameUtil.BPMN_RESOURCE_SUFFIXES;
                boolean flag = false;
                for (String str : list) {
                    if (filename.contains(str)) {
                        flag = true;
                        break;
                    }
                }
                if (flag) {
                    deployment = repositoryService.createDeployment()
                        .tenantId(TenantHelper.getTenantId())
                        .addInputStream(filename, inputStream).name(processName).key(processKey).category(categoryCode).deploy();
                } else {
                    throw new ServiceException("文件类型上传错误!");
                }
            }
            // æ›´æ–°åˆ†ç±»
            ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
            repositoryService.setProcessDefinitionCategory(definition.getId(), categoryCode);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException("部署失败" + e.getMessage());
        }
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,695 @@
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.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
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.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.workflow.common.enums.BusinessStatusEnum;
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.ActHistoryInfoVo;
import org.dromara.workflow.domain.vo.GraphicInfoVo;
import org.dromara.workflow.domain.vo.ProcessInstanceVo;
import org.dromara.workflow.domain.vo.TaskVo;
import org.dromara.workflow.flowable.CustomDefaultProcessDiagramGenerator;
import org.dromara.workflow.flowable.strategy.FlowEventStrategy;
import org.dromara.workflow.flowable.cmd.DeleteExecutionCmd;
import org.dromara.workflow.flowable.cmd.ExecutionChildByExecutionIdCmd;
import org.dromara.workflow.flowable.strategy.FlowProcessEventHandler;
import org.dromara.workflow.service.IActHiProcinstService;
import org.dromara.workflow.service.IActProcessInstanceService;
import org.dromara.workflow.utils.WorkflowUtils;
import org.flowable.bpmn.model.*;
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.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
/**
 * æµç¨‹å®žä¾‹ æœåŠ¡å±‚å®žçŽ°
 *
 * @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 FlowEventStrategy flowEventStrategy;
    @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 processInstanceBo å‚æ•°
     */
    @Override
    public TableDataInfo<ProcessInstanceVo> getProcessInstanceRunningByPage(ProcessInstanceBo processInstanceBo) {
        List<ProcessInstanceVo> list = new ArrayList<>();
        ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery();
        query.processInstanceTenantId(TenantHelper.getTenantId());
        if (StringUtils.isNotBlank(processInstanceBo.getName())) {
            query.processInstanceNameLikeIgnoreCase("%" + processInstanceBo.getName() + "%");
        }
        if (StringUtils.isNotBlank(processInstanceBo.getKey())) {
            query.processDefinitionKey(processInstanceBo.getKey());
        }
        if (StringUtils.isNotBlank(processInstanceBo.getStartUserId())) {
            query.startedBy(processInstanceBo.getStartUserId());
        }
        if (StringUtils.isNotBlank(processInstanceBo.getBusinessKey())) {
            query.processInstanceBusinessKey(processInstanceBo.getBusinessKey());
        }
        if (StringUtils.isNotBlank(processInstanceBo.getCategoryCode())) {
            query.processDefinitionCategory(processInstanceBo.getCategoryCode());
        }
        query.orderByStartTime().desc();
        List<ProcessInstance> processInstances = query.listPage(processInstanceBo.getPageNum(), processInstanceBo.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);
        }
        long count = query.count();
        return new TableDataInfo<>(list, count);
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢å·²ç»“束的流程实例
     *
     * @param processInstanceBo å‚æ•°
     */
    @Override
    public TableDataInfo<ProcessInstanceVo> getProcessInstanceFinishByPage(ProcessInstanceBo processInstanceBo) {
        List<ProcessInstanceVo> list = new ArrayList<>();
        HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery().finished()
            .orderByProcessInstanceEndTime().desc();
        query.processInstanceTenantId(TenantHelper.getTenantId());
        if (StringUtils.isNotEmpty(processInstanceBo.getName())) {
            query.processInstanceNameLikeIgnoreCase("%" + processInstanceBo.getName() + "%");
        }
        if (StringUtils.isNotBlank(processInstanceBo.getKey())) {
            query.processDefinitionKey(processInstanceBo.getKey());
        }
        if (StringUtils.isNotEmpty(processInstanceBo.getStartUserId())) {
            query.startedBy(processInstanceBo.getStartUserId());
        }
        if (StringUtils.isNotBlank(processInstanceBo.getBusinessKey())) {
            query.processInstanceBusinessKey(processInstanceBo.getBusinessKey());
        }
        if (StringUtils.isNotBlank(processInstanceBo.getCategoryCode())) {
            query.processDefinitionCategory(processInstanceBo.getCategoryCode());
        }
        List<HistoricProcessInstance> historicProcessInstances = query.listPage(processInstanceBo.getPageNum(), processInstanceBo.getPageSize());
        for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
            ProcessInstanceVo processInstanceVo = BeanUtil.toBean(historicProcessInstance, ProcessInstanceVo.class);
            processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(historicProcessInstance.getBusinessStatus()));
            list.add(processInstanceVo);
        }
        long count = query.count();
        return new TableDataInfo<>(list, count);
    }
    /**
     * é€šè¿‡æµç¨‹å®žä¾‹id获取历史流程图
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     */
    @SneakyThrows
    @Override
    public String getHistoryProcessImage(String processInstanceId) {
        String processDefinitionId;
        // èŽ·å–å½“å‰çš„æµç¨‹å®žä¾‹
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        // å¦‚果流程已经结束,则得到结束节点
        if (Objects.isNull(processInstance)) {
            HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId = pi.getProcessDefinitionId();
        } else {
            // æ ¹æ®æµç¨‹å®žä¾‹ID获得当前处于活动状态的ActivityId合集
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId = pi.getProcessDefinitionId();
        }
        // èŽ·å¾—æ´»åŠ¨çš„èŠ‚ç‚¹
        List<HistoricActivityInstance> highLightedFlowList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).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(IOUtils.toByteArray(inputStream));
    }
    /**
     * é€šè¿‡æµç¨‹å®žä¾‹id获取历史流程图运行中,历史等节点
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     */
    @Override
    public Map<String, Object> getHistoryProcessList(String processInstanceId) {
        Map<String, Object> map = new HashMap<>();
        List<Map<String, Object>> taskList = new ArrayList<>();
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        StringBuilder xml = new StringBuilder();
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(historicProcessInstance.getProcessDefinitionId());
        // èŽ·å–èŠ‚ç‚¹
        List<HistoricActivityInstance> highLightedFlowList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
        for (HistoricActivityInstance tempActivity : highLightedFlowList) {
            Map<String, Object> task = new HashMap<>();
            if (!FlowConstant.SEQUENCE_FLOW.equals(tempActivity.getActivityType()) &&
                !FlowConstant.PARALLEL_GATEWAY.equals(tempActivity.getActivityType()) &&
                !FlowConstant.EXCLUSIVE_GATEWAY.equals(tempActivity.getActivityType()) &&
                !FlowConstant.INCLUSIVE_GATEWAY.equals(tempActivity.getActivityType())
            ) {
                task.put("key", tempActivity.getActivityId());
                task.put("completed", tempActivity.getEndTime() != null);
                task.put("activityType", tempActivity.getActivityType());
                taskList.add(task);
            }
        }
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (processInstance != null) {
            taskList = taskList.stream().filter(e -> !e.get("activityType").equals(FlowConstant.END_EVENT)).collect(Collectors.toList());
        }
        //查询出运行中节点
        List<Map<String, Object>> runtimeNodeList = taskList.stream().filter(e -> !(Boolean) e.get("completed")).collect(Collectors.toList());
        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);
        map.put("historyList", historyTaskList);
        InputStream inputStream;
        try {
            inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
            xml.append(IOUtils.toString(inputStream, String.valueOf(StandardCharsets.UTF_8)));
            map.put("xml", xml.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }
    /**
     * èŽ·å–åŽ†å²ä»»åŠ¡èŠ‚ç‚¹ä¿¡æ¯
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     */
    private List<ActHistoryInfoVo> getHistoryTaskList(String processInstanceId) {
        //查询任务办理记录
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(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()));
            }
            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();
            BeanUtils.copyProperties(entry.getValue().get(0), historyInfoVo);
            actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey()) && e.getEndTime() == null).findFirst()
                .ifPresent(e -> {
                    historyInfoVo.setStatus("待处理");
                    historyInfoVo.setStartTime(e.getStartTime());
                    historyInfoVo.setEndTime(null);
                    historyInfoVo.setRunDuration(null);
                });
            historyInfoVoList.add(historyInfoVo);
        }
        return historyInfoVoList;
    }
    /**
     * èŽ·å–å®¡æ‰¹è®°å½•
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     */
    @Override
    public Map<String, Object> getHistoryRecord(String processInstanceId) {
        Map<String, Object> map = new HashMap<>();
        // æŸ¥è¯¢ä»»åŠ¡åŠžç†è®°å½•
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
            .processInstanceId(processInstanceId).taskTenantId(TenantHelper.getTenantId()).orderByHistoricTaskInstanceEndTime().desc().list();
        list = StreamUtils.sorted(list, Comparator.comparing(HistoricTaskInstance::getEndTime, Comparator.nullsFirst(Date::compareTo)).reversed());
        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()));
            }
            try {
                actHistoryInfoVo.setAssignee(StringUtils.isNotBlank(historicTaskInstance.getAssignee()) ? Long.valueOf(historicTaskInstance.getAssignee()) : null);
            } catch (NumberFormatException ignored) {
                log.warn("当前任务【{}】,办理人转换人员ID【{}】异常!", historicTaskInstance.getName(), historicTaskInstance.getAssignee());
            }
            //附件
            if (CollUtil.isNotEmpty(attachmentList)) {
                List<Attachment> attachments = attachmentList.stream().filter(e -> e.getTaskId().equals(historicTaskInstance.getId())).collect(Collectors.toList());
                if (CollUtil.isNotEmpty(attachments)) {
                    actHistoryInfoVo.setAttachmentList(attachments);
                }
            }
            actHistoryInfoVoList.add(actHistoryInfoVo);
        }
        List<ActHistoryInfoVo> collect = new ArrayList<>();
        // å¾…办理
        List<ActHistoryInfoVo> waitingTask = StreamUtils.filter(actHistoryInfoVoList, e -> e.getEndTime() == null);
        // å·²åŠžç†
        List<ActHistoryInfoVo> finishTask = StreamUtils.filter(actHistoryInfoVoList, e -> e.getEndTime() != null);
        collect.addAll(waitingTask);
        collect.addAll(finishTask);
        // å®¡æ‰¹è®°å½•
        map.put("historyRecordList", collect);
        List<ActHistoryInfoVo> nodeInfoList = new ArrayList<>();
        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);
            String nickName = entry.getValue().stream().filter(e -> StringUtils.isNotBlank(e.getNickName()) && e.getEndTime() == null).map(ActHistoryInfoVo::getNickName).toList().stream().distinct().collect(Collectors.joining(StringUtils.SEPARATOR));
            if (StringUtils.isNotBlank(nickName)) {
                actHistoryInfoVo.setNickName(nickName);
            }
            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);
                });
            nodeInfoList.add(actHistoryInfoVo);
        }
        // èŠ‚ç‚¹ä¿¡æ¯
        map.put("nodeListInfo", nodeInfoList);
        BpmnModel bpmnModel = repositoryService.getBpmnModel(list.get(0).getProcessDefinitionId());
        List<GraphicInfoVo> graphicInfoVos = new ArrayList<>();
        Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
        //节点图形信息
        buildGraphicInfo(flowElements, graphicInfoVos, bpmnModel);
        map.put("graphicInfoVos", graphicInfoVos);
        return map;
    }
    /**
     * æž„建节点图形信息
     *
     * @param flowElements èŠ‚ç‚¹
     */
    private static void buildGraphicInfo(Collection<FlowElement> flowElements, List<GraphicInfoVo> graphicInfoVos, BpmnModel bpmnModel) {
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof SubProcess) {
                Collection<FlowElement> subFlowElements = ((SubProcess) flowElement).getFlowElements();
                buildGraphicInfo(subFlowElements, graphicInfoVos, bpmnModel);
            } else {
                if (flowElement instanceof UserTask) {
                    GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowElement.getId());
                    GraphicInfoVo graphicInfoVo = BeanUtil.toBean(graphicInfo, GraphicInfoVo.class);
                    graphicInfoVo.setNodeId(flowElement.getId());
                    graphicInfoVo.setNodeName(flowElement.getName());
                    graphicInfoVos.add(graphicInfoVo);
                }
            }
        }
    }
    /**
     * ä»»åŠ¡å®Œæˆæ—¶é—´å¤„ç†
     *
     * @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 deleteRuntimeProcessInst(ProcessInvalidBo processInvalidBo) {
        try {
            List<Task> list = taskService.createTaskQuery().processInstanceId(processInvalidBo.getProcessInstanceId())
                .taskTenantId(TenantHelper.getTenantId()).list();
            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 = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInvalidBo.getProcessInstanceId()).processInstanceTenantId(TenantHelper.getTenantId()).singleResult();
            if (ObjectUtil.isNotEmpty(historicProcessInstance) && BusinessStatusEnum.FINISH.getStatus().equals(historicProcessInstance.getBusinessStatus())) {
                throw new ServiceException("该单据已完成申请!");
            }
            runtimeService.updateBusinessStatus(processInvalidBo.getProcessInstanceId(), BusinessStatusEnum.INVALID.getStatus());
            runtimeService.deleteProcessInstance(processInvalidBo.getProcessInstanceId(), deleteReason);
            FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(historicProcessInstance.getProcessDefinitionKey());
            if (processHandler != null) {
                processHandler.handleProcess(historicProcessInstance.getBusinessKey(), BusinessStatusEnum.INVALID.getStatus(), false);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * è¿è¡Œä¸­çš„实例 åˆ é™¤ç¨‹å®žä¾‹ï¼Œåˆ é™¤åŽ†å²è®°å½•ï¼Œåˆ é™¤ä¸šåŠ¡ä¸Žæµç¨‹å…³è”ä¿¡æ¯
     *
     * @param processInstanceIds æµç¨‹å®žä¾‹id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteRuntimeProcessAndHisInst(List<String> processInstanceIds) {
        try {
            // 1.删除运行中流程实例
            List<Task> list = taskService.createTaskQuery().processInstanceIdIn(processInstanceIds)
                .taskTenantId(TenantHelper.getTenantId()).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 = historyService.createHistoricProcessInstanceQuery()
                .processInstanceTenantId(TenantHelper.getTenantId()).processInstanceIds(new HashSet<>(processInstanceIds)).list();
            if (ObjectUtil.isNotEmpty(historicProcessInstanceList)) {
                historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * æŒ‰ç…§ä¸šåŠ¡id删除 è¿è¡Œä¸­çš„实例 åˆ é™¤ç¨‹å®žä¾‹ï¼Œåˆ é™¤åŽ†å²è®°å½•ï¼Œåˆ é™¤ä¸šåŠ¡ä¸Žæµç¨‹å…³è”ä¿¡æ¯
     *
     * @param businessKeys ä¸šåŠ¡id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteRuntimeProcessAndHisInstByBusinessKeys(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 = taskService.createTaskQuery().processInstanceIdIn(processInstanceIds)
                .taskTenantId(TenantHelper.getTenantId()).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 = historyService.createHistoricProcessInstanceQuery()
                .processInstanceTenantId(TenantHelper.getTenantId()).processInstanceIds(new HashSet<>(processInstanceIds)).list();
            if (ObjectUtil.isNotEmpty(historicProcessInstanceList)) {
                historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * å·²å®Œæˆçš„实例 åˆ é™¤ç¨‹å®žä¾‹ï¼Œåˆ é™¤åŽ†å²è®°å½•ï¼Œåˆ é™¤ä¸šåŠ¡ä¸Žæµç¨‹å…³è”ä¿¡æ¯
     *
     * @param processInstanceIds æµç¨‹å®žä¾‹id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteFinishProcessAndHisInst(List<String> processInstanceIds) {
        try {
            historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * æ’¤é”€æµç¨‹ç”³è¯·
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancelProcessApply(String processInstanceId) {
        try {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId).processInstanceTenantId(TenantHelper.getTenantId()).startedBy(String.valueOf(LoginHelper.getUserId())).singleResult();
            if (ObjectUtil.isNull(processInstance)) {
                throw new ServiceException("您不是流程发起人,撤销失败!");
            }
            if (processInstance.isSuspended()) {
                throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
            }
            if (BusinessStatusEnum.CANCEL.getStatus().equals(processInstance.getBusinessStatus())) {
                throw new ServiceException("该单据已撤销!");
            }
            List<Task> taskList = taskService.createTaskQuery().taskTenantId(TenantHelper.getTenantId()).processInstanceId(processInstanceId).list();
            for (Task task : taskList) {
                taskService.setAssignee(task.getId(), String.valueOf(LoginHelper.getUserId()));
                taskService.addComment(task.getId(), processInstanceId, TaskStatusEnum.CANCEL.getStatus(), LoginHelper.getLoginUser().getNickname() + ":撤销申请");
            }
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().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 = taskService.createTaskQuery().taskTenantId(TenantHelper.getTenantId()).processInstanceId(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 = flowEventStrategy.getProcessHandler(processInstance.getProcessDefinitionKey());
            if (processHandler != null) {
                processHandler.handleProcess(processInstance.getBusinessKey(), BusinessStatusEnum.CANCEL.getStatus(), false);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("撤销失败:" + e.getMessage());
        }
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢å½“前登录人单据
     *
     * @param processInstanceBo å‚æ•°
     */
    @Override
    public TableDataInfo<ProcessInstanceVo> getCurrentSubmitByPage(ProcessInstanceBo processInstanceBo) {
        List<ProcessInstanceVo> list = new ArrayList<>();
        HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery();
        query.processInstanceTenantId(TenantHelper.getTenantId());
        query.startedBy(processInstanceBo.getStartUserId());
        if (StringUtils.isNotBlank(processInstanceBo.getName())) {
            query.processInstanceNameLikeIgnoreCase("%" + processInstanceBo.getName() + "%");
        }
        if (StringUtils.isNotBlank(processInstanceBo.getKey())) {
            query.processDefinitionKey(processInstanceBo.getKey());
        }
        if (StringUtils.isNotBlank(processInstanceBo.getBusinessKey())) {
            query.processInstanceBusinessKey(processInstanceBo.getBusinessKey());
        }
        if (StringUtils.isNotBlank(processInstanceBo.getCategoryCode())) {
            query.processDefinitionCategory(processInstanceBo.getCategoryCode());
        }
        query.orderByProcessInstanceStartTime().desc();
        List<HistoricProcessInstance> historicProcessInstanceList = query.listPage(processInstanceBo.getPageNum(), processInstanceBo.getPageSize());
        List<TaskVo> taskVoList = new ArrayList<>();
        if (CollUtil.isNotEmpty(historicProcessInstanceList)) {
            List<String> processInstanceIds = StreamUtils.toList(historicProcessInstanceList, HistoricProcessInstance::getId);
            List<Task> taskList = taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).taskTenantId(TenantHelper.getTenantId()).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);
        }
        long count = query.count();
        return new TableDataInfo<>(list, count);
    }
    /**
     * ä»»åŠ¡å‚¬åŠž(给当前任务办理人发送站内信,邮件,短信等)
     *
     * @param taskUrgingBo ä»»åŠ¡å‚¬åŠž
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean taskUrging(TaskUrgingBo taskUrgingBo) {
        try {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(taskUrgingBo.getProcessInstanceId())
                .processInstanceTenantId(TenantHelper.getTenantId()).singleResult();
            if (processInstance == null) {
                throw new ServiceException("任务已结束!");
            }
            String message = taskUrgingBo.getMessage();
            if (StringUtils.isBlank(message)) {
                message = "您的【" + processInstance.getName() + "】单据还未审批,请您及时处理。";
            }
            List<Task> list = taskService.createTaskQuery().processInstanceId(taskUrgingBo.getProcessInstanceId()).list();
            WorkflowUtils.sendMessage(list, processInstance.getName(), taskUrgingBo.getMessageType(), message);
        } catch (ServiceException e) {
            throw new ServiceException(e.getMessage());
        }
        return true;
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,708 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
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 org.dromara.common.core.domain.dto.RoleDTO;
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.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.common.enums.BusinessStatusEnum;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.domain.bo.*;
import org.dromara.workflow.domain.vo.MultiInstanceVo;
import org.dromara.workflow.domain.vo.TaskVo;
import org.dromara.workflow.domain.vo.WfCopy;
import org.dromara.workflow.flowable.strategy.FlowEventStrategy;
import org.dromara.workflow.flowable.cmd.*;
import org.dromara.workflow.flowable.strategy.FlowProcessEventHandler;
import org.dromara.workflow.flowable.strategy.FlowTaskEventHandler;
import org.dromara.workflow.mapper.ActTaskMapper;
import org.dromara.workflow.service.IActTaskService;
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.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.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
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.FLOWABLE_SKIP_EXPRESSION_ENABLED;
import static org.dromara.workflow.common.constant.FlowConstant.INITIATOR;
/**
 * ä»»åŠ¡ æœåŠ¡å±‚å®žçŽ°
 *
 * @author may
 */
@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 FlowEventStrategy flowEventStrategy;
    private final ActTaskMapper actTaskMapper;
    /**
     * å¯åŠ¨ä»»åŠ¡
     *
     * @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");
        }
        // åˆ¤æ–­å½“前业务是否启动过流程
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(startProcessBo.getBusinessKey()).processInstanceTenantId(TenantHelper.getTenantId()).singleResult();
        if (ObjectUtil.isNotEmpty(historicProcessInstance)) {
            BusinessStatusEnum.checkStartStatus(historicProcessInstance.getBusinessStatus());
        }
        TaskQuery taskQuery = taskService.createTaskQuery();
        List<Task> taskResult = taskQuery.processInstanceBusinessKey(startProcessBo.getBusinessKey()).taskTenantId(TenantHelper.getTenantId()).list();
        if (CollUtil.isNotEmpty(taskResult)) {
            if (CollUtil.isNotEmpty(startProcessBo.getVariables())) {
                taskService.setVariables(taskResult.get(0).getId(), startProcessBo.getVariables());
            }
            map.put("processInstanceId", taskResult.get(0).getProcessInstanceId());
            map.put("taskId", taskResult.get(0).getId());
            return map;
        }
        // è®¾ç½®å¯åŠ¨äºº
        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 {
            pi = runtimeService.startProcessInstanceByKeyAndTenantId(startProcessBo.getProcessKey(), startProcessBo.getBusinessKey(), variables, TenantHelper.getTenantId());
        } catch (FlowableObjectNotFoundException e) {
            throw new ServiceException("找不到当前【" + startProcessBo.getProcessKey() + "】流程定义!");
        }
        // å°†æµç¨‹å®šä¹‰åç§° ä½œä¸º æµç¨‹å®žä¾‹åç§°
        runtimeService.setProcessInstanceName(pi.getProcessInstanceId(), pi.getProcessDefinitionName());
        // ç”³è¯·äººæ‰§è¡Œæµç¨‹
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(pi.getId()).taskTenantId(TenantHelper.getTenantId()).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(), "processInstanceId", pi.getProcessInstanceId());
        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 {
            List<RoleDTO> roles = LoginHelper.getLoginUser().getRoles();
            String userId = String.valueOf(LoginHelper.getUserId());
            TaskQuery taskQuery = taskService.createTaskQuery();
            taskQuery.taskId(completeTaskBo.getTaskId()).taskTenantId(TenantHelper.getTenantId()).taskCandidateOrAssigned(userId);
            if (CollUtil.isNotEmpty(roles)) {
                List<String> groupIds = StreamUtils.toList(roles, e -> String.valueOf(e.getRoleId()));
                taskQuery.taskCandidateGroupIn(groupIds);
            }
            Task task = taskQuery.singleResult();
            if (task == null) {
                throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
            }
            if (task.isSuspended()) {
                throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
            }
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(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(), completeTaskBo.getMessage());
                taskService.complete(newTask.getId());
                return true;
            }
            //附件上传
            AttachmentCmd attachmentCmd = new AttachmentCmd(completeTaskBo.getFileId(), task.getId(), task.getProcessInstanceId());
            managementService.executeCommand(attachmentCmd);
            FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(processInstance.getProcessDefinitionKey());
            String businessStatus = WorkflowUtils.getBusinessStatus(task.getProcessInstanceId());
            if (BusinessStatusEnum.DRAFT.getStatus().equals(businessStatus) || BusinessStatusEnum.BACK.getStatus().equals(businessStatus) || BusinessStatusEnum.CANCEL.getStatus().equals(businessStatus)) {
                if (processHandler != null) {
                    processHandler.handleProcess(processInstance.getBusinessKey(), businessStatus, true);
                }
            }
            runtimeService.updateBusinessStatus(task.getProcessInstanceId(), BusinessStatusEnum.WAITING.getStatus());
            String key = processInstance.getProcessDefinitionKey() + "_" + task.getTaskDefinitionKey();
            FlowTaskEventHandler taskHandler = flowEventStrategy.getTaskHandler(key);
            if (taskHandler != null) {
                taskHandler.handleTask(task, processInstance.getBusinessKey());
            }
            //办理意见
            taskService.addComment(completeTaskBo.getTaskId(), task.getProcessInstanceId(), TaskStatusEnum.PASS.getStatus(), StringUtils.isBlank(completeTaskBo.getMessage()) ? "同意" : completeTaskBo.getMessage());
            //办理任务
            if (CollUtil.isNotEmpty(completeTaskBo.getVariables())) {
                taskService.complete(completeTaskBo.getTaskId(), completeTaskBo.getVariables());
            } else {
                taskService.complete(completeTaskBo.getTaskId());
            }
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId())
                .processInstanceTenantId(TenantHelper.getTenantId()).singleResult();
            if (pi == null) {
                UpdateBusinessStatusCmd updateBusinessStatusCmd = new UpdateBusinessStatusCmd(task.getProcessInstanceId(), BusinessStatusEnum.FINISH.getStatus());
                managementService.executeCommand(updateBusinessStatusCmd);
                if (processHandler != null) {
                    processHandler.handleProcess(processInstance.getBusinessKey(), BusinessStatusEnum.FINISH.getStatus(), false);
                }
            } else {
                List<Task> list = taskService.createTaskQuery().taskTenantId(TenantHelper.getTenantId()).processInstanceId(task.getProcessInstanceId()).list();
                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 = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
                    WorkflowUtils.createCopyTask(taskList, StreamUtils.toList(completeTaskBo.getWfCopyList(), WfCopy::getUserId));
                }
                sendMessage(list, processInstance.getName(), completeTaskBo.getMessageType(), null);
            }
            return true;
        } catch (Exception 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);
    }
    /**
     * æŸ¥è¯¢å½“前用户的待办任务
     *
     * @param taskBo å‚æ•°
     */
    @Override
    public TableDataInfo<TaskVo> getTaskWaitByPage(TaskBo taskBo) {
        PageQuery pageQuery = new PageQuery();
        pageQuery.setPageNum(taskBo.getPageNum());
        pageQuery.setPageSize(taskBo.getPageSize());
        QueryWrapper<TaskVo> queryWrapper = new QueryWrapper<>();
        List<RoleDTO> roles = LoginHelper.getLoginUser().getRoles();
        String userId = String.valueOf(LoginHelper.getUserId());
        queryWrapper.eq("t.business_status_", BusinessStatusEnum.WAITING.getStatus());
        queryWrapper.eq("t.tenant_id_", TenantHelper.getTenantId());
        queryWrapper.and(w1 ->
            w1.eq("t.assignee_", userId)
                .or(w2 -> w2.isNull("t.assignee_")
                    .and(w3 -> w3.eq("t.user_id_", userId).or().in("t.group_id_", StreamUtils.toList(roles, RoleDTO::getRoleId))))
        );
        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();
        for (TaskVo task : taskList) {
            task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus()));
            task.setParticipantVo(WorkflowUtils.getCurrentTaskParticipant(task.getId()));
            task.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null);
        }
        return new TableDataInfo<>(taskList, page.getTotal());
    }
    /**
     * æŸ¥è¯¢å½“前租户所有待办任务
     *
     * @param taskBo å‚æ•°
     */
    @Override
    public TableDataInfo<TaskVo> getAllTaskWaitByPage(TaskBo taskBo) {
        TaskQuery query = taskService.createTaskQuery().taskTenantId(TenantHelper.getTenantId());
        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(taskBo.getPageNum(), taskBo.getPageSize());
        List<ProcessInstance> processInstanceList = null;
        if (CollUtil.isNotEmpty(taskList)) {
            Set<String> processInstanceIds = StreamUtils.toSet(taskList, Task::getProcessInstanceId);
            processInstanceList = runtimeService.createProcessInstanceQuery().processInstanceIds(processInstanceIds).list();
        }
        List<TaskVo> list = new ArrayList<>();
        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.setAssignee(StringUtils.isNotBlank(task.getAssignee()) ? Long.valueOf(task.getAssignee()) : null);
            taskVo.setParticipantVo(WorkflowUtils.getCurrentTaskParticipant(task.getId()));
            taskVo.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null);
            list.add(taskVo);
        }
        long count = query.count();
        return new TableDataInfo<>(list, count);
    }
    /**
     * æŸ¥è¯¢å½“前用户的已办任务
     *
     * @param taskBo å‚æ•°
     */
    @Override
    public TableDataInfo<TaskVo> getTaskFinishByPage(TaskBo taskBo) {
        String userId = String.valueOf(LoginHelper.getUserId());
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().taskAssignee(userId).taskTenantId(TenantHelper.getTenantId()).finished().orderByHistoricTaskInstanceStartTime().desc();
        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());
        }
        List<HistoricTaskInstance> taskInstanceList = query.listPage(taskBo.getPageNum(), taskBo.getPageSize());
        List<HistoricProcessInstance> historicProcessInstanceList = null;
        if (CollUtil.isNotEmpty(taskInstanceList)) {
            Set<String> processInstanceIds = StreamUtils.toSet(taskInstanceList, HistoricTaskInstance::getProcessInstanceId);
            historicProcessInstanceList = historyService.createHistoricProcessInstanceQuery().processInstanceIds(processInstanceIds).list();
        }
        List<TaskVo> list = new ArrayList<>();
        for (HistoricTaskInstance task : taskInstanceList) {
            TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
            if (CollUtil.isNotEmpty(historicProcessInstanceList)) {
                historicProcessInstanceList.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.setAssignee(StringUtils.isNotBlank(task.getAssignee()) ? Long.valueOf(task.getAssignee()) : null);
            list.add(taskVo);
        }
        long count = query.count();
        return new TableDataInfo<>(list, count);
    }
    /**
     * æŸ¥è¯¢å½“前用户的抄送
     *
     * @param taskBo å‚æ•°
     */
    @Override
    public TableDataInfo<TaskVo> getTaskCopyByPage(TaskBo taskBo) {
        PageQuery pageQuery = new PageQuery();
        pageQuery.setPageNum(taskBo.getPageNum());
        pageQuery.setPageSize(taskBo.getPageSize());
        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();
        for (TaskVo task : taskList) {
            task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus()));
            task.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null);
        }
        return new TableDataInfo<>(taskList, page.getTotal());
    }
    /**
     * æŸ¥è¯¢å½“前租户所有已办任务
     *
     * @param taskBo å‚æ•°
     */
    @Override
    public TableDataInfo<TaskVo> getAllTaskFinishByPage(TaskBo taskBo) {
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().taskTenantId(TenantHelper.getTenantId()).finished().orderByHistoricTaskInstanceStartTime().desc();
        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());
        }
        List<HistoricTaskInstance> taskInstanceList = query.listPage(taskBo.getPageNum(), taskBo.getPageSize());
        List<HistoricProcessInstance> historicProcessInstanceList = null;
        if (CollUtil.isNotEmpty(taskInstanceList)) {
            Set<String> processInstanceIds = StreamUtils.toSet(taskInstanceList, HistoricTaskInstance::getProcessInstanceId);
            historicProcessInstanceList = historyService.createHistoricProcessInstanceQuery().processInstanceIds(processInstanceIds).list();
        }
        List<TaskVo> list = new ArrayList<>();
        for (HistoricTaskInstance task : taskInstanceList) {
            TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
            if (CollUtil.isNotEmpty(historicProcessInstanceList)) {
                historicProcessInstanceList.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.setAssignee(Convert.toLong(task.getAssignee()));
            list.add(taskVo);
        }
        long count = query.count();
        return new TableDataInfo<>(list, count);
    }
    /**
     * å§”派任务
     *
     * @param delegateBo å‚æ•°
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delegateTask(DelegateBo delegateBo) {
        TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskTenantId(TenantHelper.getTenantId()).taskId(delegateBo.getTaskId()).taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId())).singleResult();
        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) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * ç»ˆæ­¢ä»»åŠ¡
     *
     * @param terminationBo å‚æ•°
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean terminationTask(TerminationBo terminationBo) {
        Task task = taskService.createTaskQuery().taskTenantId(TenantHelper.getTenantId()).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 = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(task.getProcessInstanceId()).processInstanceTenantId(TenantHelper.getTenantId()).singleResult();
        if (ObjectUtil.isNotEmpty(historicProcessInstance) && BusinessStatusEnum.TERMINATION.getStatus().equals(historicProcessInstance.getBusinessStatus())) {
            throw new ServiceException("该单据已终止!");
        }
        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 = taskService.createTaskQuery().taskTenantId(TenantHelper.getTenantId()).processInstanceId(task.getProcessInstanceId()).list();
            if (CollectionUtil.isNotEmpty(list)) {
                List<Task> subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId()));
                if (CollectionUtil.isNotEmpty(subTasks)) {
                    subTasks.forEach(e -> taskService.deleteTask(e.getId()));
                }
                runtimeService.deleteProcessInstance(task.getProcessInstanceId(), StrUtil.EMPTY);
            }
            runtimeService.updateBusinessStatus(task.getProcessInstanceId(), BusinessStatusEnum.TERMINATION.getStatus());
            FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(historicProcessInstance.getProcessDefinitionKey());
            if (processHandler != null) {
                processHandler.handleProcess(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 = taskService.createTaskQuery().taskId(transmitBo.getTaskId()).taskTenantId(TenantHelper.getTenantId()).taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId())).singleResult();
        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) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * ä¼šç­¾ä»»åŠ¡åŠ ç­¾
     *
     * @param addMultiBo å‚æ•°
     */
    @Override
    public boolean addMultiInstanceExecution(AddMultiBo addMultiBo) {
        TaskQuery taskQuery = taskService.createTaskQuery();
        taskQuery.taskId(addMultiBo.getTaskId());
        taskQuery.taskTenantId(TenantHelper.getTenantId());
        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) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * ä¼šç­¾ä»»åŠ¡å‡ç­¾
     *
     * @param deleteMultiBo å‚æ•°
     */
    @Override
    public boolean deleteMultiInstanceExecution(DeleteMultiBo deleteMultiBo) {
        TaskQuery taskQuery = taskService.createTaskQuery();
        taskQuery.taskId(deleteMultiBo.getTaskId());
        taskQuery.taskTenantId(TenantHelper.getTenantId());
        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) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * é©³å›žå®¡æ‰¹
     *
     * @param backProcessBo å‚æ•°
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String backProcess(BackProcessBo backProcessBo) {
        Task task = taskService.createTaskQuery().taskId(backProcessBo.getTaskId()).taskTenantId(TenantHelper.getTenantId()).taskAssignee(String.valueOf(LoginHelper.getUserId())).singleResult();
        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 = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            //获取并行网关执行后保留的执行实例数据
            ExecutionChildByExecutionIdCmd childByExecutionIdCmd = new ExecutionChildByExecutionIdCmd(task.getExecutionId());
            List<ExecutionEntity> executionEntities = managementService.executeCommand(childByExecutionIdCmd);
            //校验单据
            if (BusinessStatusEnum.BACK.getStatus().equals(processInstance.getBusinessStatus())) {
                throw new ServiceException("该单据已退回!");
            }
            //判断是否有多个任务
            List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).taskTenantId(TenantHelper.getTenantId()).list();
            //申请人节点
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).finished().orderByHistoricTaskInstanceEndTime().asc().list().get(0);
            String backTaskDefinitionKey = historicTaskInstance.getTaskDefinitionKey();
            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();
            } else {
                //当前单个节点驳回单个节点
                runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveActivityIdTo(task.getTaskDefinitionKey(), backTaskDefinitionKey).changeState();
            }
            List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId).taskTenantId(TenantHelper.getTenantId()).list();
            for (Task t : list) {
                taskService.setAssignee(t.getId(), historicTaskInstance.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);
            }
            runtimeService.updateBusinessStatus(processInstanceId, BusinessStatusEnum.BACK.getStatus());
            FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(processInstance.getProcessDefinitionKey());
            if (processHandler != null) {
                processHandler.handleProcess(processInstance.getBusinessKey(), BusinessStatusEnum.BACK.getStatus(), false);
            }
        } catch (Exception 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 = taskService.createTaskQuery().taskIds(Arrays.asList(taskIds)).taskTenantId(TenantHelper.getTenantId()).list();
            for (Task task : list) {
                taskService.setAssignee(task.getId(), userId);
            }
        } catch (Exception e) {
            throw new ServiceException("修改失败:" + e.getMessage());
        }
        return true;
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,124 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.workflow.domain.TestLeave;
import org.dromara.workflow.domain.bo.TestLeaveBo;
import org.dromara.workflow.domain.vo.TestLeaveVo;
import org.dromara.workflow.mapper.TestLeaveMapper;
import org.dromara.workflow.service.IActProcessInstanceService;
import org.dromara.workflow.service.ITestLeaveService;
import org.dromara.workflow.utils.WorkflowUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
 * è¯·å‡Service业务层处理
 *
 * @author may
 * @date 2023-07-21
 */
@RequiredArgsConstructor
@Service
public class TestLeaveServiceImpl implements ITestLeaveService {
    private final TestLeaveMapper baseMapper;
    private final IActProcessInstanceService iActProcessInstanceService;
    /**
     * æŸ¥è¯¢è¯·å‡
     */
    @Override
    public TestLeaveVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }
    /**
     * æŸ¥è¯¢è¯·å‡åˆ—表
     */
    @Override
    public TableDataInfo<TestLeaveVo> queryPageList(TestLeaveBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TestLeave> lqw = buildQueryWrapper(bo);
        Page<TestLeaveVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        TableDataInfo<TestLeaveVo> build = TableDataInfo.build(result);
        List<TestLeaveVo> rows = build.getRows();
        if (CollUtil.isNotEmpty(rows)) {
            List<String> ids = StreamUtils.toList(rows, e -> String.valueOf(e.getId()));
            WorkflowUtils.setProcessInstanceListVo(rows, ids, "id");
        }
        return build;
    }
    /**
     * æŸ¥è¯¢è¯·å‡åˆ—表
     */
    @Override
    public List<TestLeaveVo> queryList(TestLeaveBo bo) {
        LambdaQueryWrapper<TestLeave> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }
    private LambdaQueryWrapper<TestLeave> buildQueryWrapper(TestLeaveBo bo) {
        LambdaQueryWrapper<TestLeave> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getLeaveType()), TestLeave::getLeaveType, bo.getLeaveType());
        lqw.ge(bo.getStartLeaveDays() != null,TestLeave::getLeaveDays, bo.getStartLeaveDays());
        lqw.le(bo.getEndLeaveDays() != null,TestLeave::getLeaveDays, bo.getEndLeaveDays());
        lqw.orderByDesc(BaseEntity::getCreateTime);
        return lqw;
    }
    /**
     * æ–°å¢žè¯·å‡
     */
    @Override
    public TestLeave insertByBo(TestLeaveBo bo) {
        TestLeave add = MapstructUtils.convert(bo, TestLeave.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return add;
    }
    /**
     * ä¿®æ”¹è¯·å‡
     */
    @Override
    public TestLeave updateByBo(TestLeaveBo bo) {
        TestLeave update = MapstructUtils.convert(bo, TestLeave.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0 ? update : null;
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     */
    private void validEntityBeforeSave(TestLeave entity) {
        //TODO åšä¸€äº›æ•°æ®æ ¡éªŒ,如唯一约束
    }
    /**
     * æ‰¹é‡åˆ é™¤è¯·å‡
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWithValidByIds(Collection<Long> ids) {
        List<String> idList = StreamUtils.toList(ids, String::valueOf);
        iActProcessInstanceService.deleteRuntimeProcessAndHisInstByBusinessKeys(idList);
        return baseMapper.deleteBatchIds(ids) > 0;
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfCategoryServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,128 @@
package org.dromara.workflow.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.workflow.domain.WfCategory;
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.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;
import java.util.Collection;
import java.util.List;
/**
 * æµç¨‹åˆ†ç±»Service业务层处理
 *
 * @author may
 * @date 2023-06-28
 */
@RequiredArgsConstructor
@Service
public class WfCategoryServiceImpl implements IWfCategoryService {
    private final WfCategoryMapper baseMapper;
    private final RepositoryService repositoryService;
    /**
     * æŸ¥è¯¢æµç¨‹åˆ†ç±»
     */
    @Override
    public WfCategoryVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }
    /**
     * æŸ¥è¯¢æµç¨‹åˆ†ç±»åˆ—表
     */
    @Override
    public List<WfCategoryVo> queryList(WfCategoryBo bo) {
        LambdaQueryWrapper<WfCategory> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }
    private LambdaQueryWrapper<WfCategory> buildQueryWrapper(WfCategoryBo bo) {
        LambdaQueryWrapper<WfCategory> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getCategoryName()), WfCategory::getCategoryName, bo.getCategoryName());
        lqw.eq(StringUtils.isNotBlank(bo.getCategoryCode()), WfCategory::getCategoryCode, bo.getCategoryCode());
        return lqw;
    }
    /**
     * æ–°å¢žæµç¨‹åˆ†ç±»
     */
    @Override
    public Boolean insertByBo(WfCategoryBo bo) {
        WfCategory add = MapstructUtils.convert(bo, WfCategory.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }
    /**
     * ä¿®æ”¹æµç¨‹åˆ†ç±»
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByBo(WfCategoryBo bo) {
        WfCategory update = MapstructUtils.convert(bo, WfCategory.class);
        validEntityBeforeSave(update);
        WfCategoryVo wfCategoryVo = baseMapper.selectVoById(bo.getId());
        List<ProcessDefinition> processDefinitionList = repositoryService.createProcessDefinitionQuery().processDefinitionCategory(wfCategoryVo.getCategoryCode()).list();
        for (ProcessDefinition processDefinition : processDefinitionList) {
            repositoryService.setProcessDefinitionCategory(processDefinition.getId(), bo.getCategoryCode());
        }
        List<Deployment> deploymentList = repositoryService.createDeploymentQuery().deploymentCategory(wfCategoryVo.getCategoryCode()).list();
        for (Deployment deployment : deploymentList) {
            repositoryService.setDeploymentCategory(deployment.getId(), bo.getCategoryCode());
        }
        List<Model> modelList = repositoryService.createModelQuery().modelCategory(wfCategoryVo.getCategoryCode()).list();
        for (Model model : modelList) {
            model.setCategory(bo.getCategoryCode());
            repositoryService.saveModel(model);
        }
        return baseMapper.updateById(update) > 0;
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     */
    private void validEntityBeforeSave(WfCategory entity) {
        //TODO åšä¸€äº›æ•°æ®æ ¡éªŒ,如唯一约束
    }
    /**
     * æ‰¹é‡åˆ é™¤æµç¨‹åˆ†ç±»
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }
    /**
     * æŒ‰ç…§ç±»åˆ«ç¼–码查询
     *
     * @param categoryCode åˆ†ç±»æ¯”吗
     */
    @Override
    public WfCategory queryByCategoryCode(String categoryCode) {
        return baseMapper.selectOne(new LambdaQueryWrapper<WfCategory>().eq(WfCategory::getCategoryCode, categoryCode));
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowUserServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,216 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.enums.UserStatus;
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.system.domain.SysUser;
import org.dromara.system.domain.SysUserRole;
import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.mapper.SysUserRoleMapper;
import org.dromara.workflow.domain.bo.SysUserMultiBo;
import org.dromara.workflow.domain.vo.MultiInstanceVo;
import org.dromara.workflow.domain.vo.TaskVo;
import org.dromara.workflow.service.IWorkflowUserService;
import org.dromara.workflow.utils.WorkflowUtils;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
 * å·¥ä½œæµç”¨æˆ·é€‰äººç®¡ç† ä¸šåŠ¡å¤„ç†å±‚
 *
 * @author may
 */
@RequiredArgsConstructor
@Service
public class WorkflowUserServiceImpl implements IWorkflowUserService {
    private final SysUserMapper sysUserMapper;
    private final SysUserRoleMapper sysUserRoleMapper;
    private final TaskService taskService;
    private final RuntimeService runtimeService;
    /**
     * åˆ†é¡µæŸ¥è¯¢å·¥ä½œæµé€‰æ‹©åŠ ç­¾äººå‘˜
     *
     * @param sysUserMultiBo å‚æ•°
     */
    @Override
    @SuppressWarnings("unchecked")
    public TableDataInfo<SysUserVo> getWorkflowAddMultiInstanceByPage(SysUserMultiBo sysUserMultiBo) {
        Task task = taskService.createTaskQuery().taskId(sysUserMultiBo.getTaskId()).singleResult();
        if (task == null) {
            throw new ServiceException("任务不存在");
        }
        MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
        if (multiInstance == null) {
            return TableDataInfo.build();
        }
        LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.lambdaQuery();
        //检索条件
        queryWrapper.eq(StringUtils.isNotEmpty(sysUserMultiBo.getDeptId()), SysUser::getDeptId, sysUserMultiBo.getDeptId());
        queryWrapper.eq(SysUser::getStatus, UserStatus.OK.getCode());
        if (multiInstance.getType() instanceof SequentialMultiInstanceBehavior) {
            List<Long> assigneeList = (List<Long>) runtimeService.getVariable(task.getExecutionId(), multiInstance.getAssigneeList());
            queryWrapper.notIn(CollectionUtil.isNotEmpty(assigneeList), SysUser::getUserId, assigneeList);
        } else {
            List<Task> list = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
            List<Long> userIds = StreamUtils.toList(list, e -> Long.valueOf(e.getAssignee()));
            queryWrapper.notIn(CollectionUtil.isNotEmpty(userIds), SysUser::getUserId, userIds);
        }
        queryWrapper.like(StringUtils.isNotEmpty(sysUserMultiBo.getUserName()), SysUser::getUserName, sysUserMultiBo.getUserName());
        queryWrapper.like(StringUtils.isNotEmpty(sysUserMultiBo.getNickName()), SysUser::getNickName, sysUserMultiBo.getNickName());
        Page<SysUser> page = new Page<>(sysUserMultiBo.getPageNum(), sysUserMultiBo.getPageSize());
        Page<SysUserVo> userPage = sysUserMapper.selectVoPage(page, queryWrapper);
        return TableDataInfo.build(recordPage(userPage));
    }
    /**
     * æŸ¥è¯¢å·¥ä½œæµé€‰æ‹©å‡ç­¾äººå‘˜
     *
     * @param taskId ä»»åŠ¡id ä»»åŠ¡id
     */
    @Override
    @SuppressWarnings("unchecked")
    public List<TaskVo> getWorkflowDeleteMultiInstanceList(String taskId) {
        Task task = taskService.createTaskQuery().taskTenantId(TenantHelper.getTenantId()).taskId(taskId).singleResult();
        List<Task> taskList = taskService.createTaskQuery().taskTenantId(TenantHelper.getTenantId()).processInstanceId(task.getProcessInstanceId()).list();
        MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
        List<TaskVo> taskListVo = new ArrayList<>();
        if (multiInstance == null) {
            return Collections.emptyList();
        }
        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<SysUserVo> sysUsers = null;
            if (CollectionUtil.isNotEmpty(userIds)) {
                sysUsers = sysUserMapper.selectVoBatchIds(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 (CollectionUtil.isNotEmpty(sysUsers)) {
                    sysUsers.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 (CollectionUtil.isNotEmpty(tasks)) {
                List<Long> userIds = StreamUtils.toList(tasks, e -> Long.valueOf(e.getAssignee()));
                List<SysUserVo> sysUsers = null;
                if (CollectionUtil.isNotEmpty(userIds)) {
                    sysUsers = sysUserMapper.selectVoBatchIds(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 (CollectionUtil.isNotEmpty(sysUsers)) {
                        sysUsers.stream().filter(u -> u.getUserId().toString().equals(t.getAssignee())).findFirst().ifPresent(e -> taskVo.setAssigneeName(e.getNickName()));
                    }
                    taskListVo.add(taskVo);
                }
                return taskListVo;
            }
        }
        return Collections.emptyList();
    }
    /**
     * ç¿»è¯‘部门
     *
     * @param page ç”¨æˆ·åˆ†é¡µæ•°æ®
     */
    private Page<SysUserVo> recordPage(Page<SysUserVo> page) {
        List<SysUserVo> records = page.getRecords();
        if (CollUtil.isEmpty(records)) {
            return page;
        }
        List<Long> collectDeptId = StreamUtils.toList(records, SysUserVo::getDeptId);
        if (CollUtil.isEmpty(collectDeptId)) {
            return page;
        }
        page.setRecords(records);
        return page;
    }
    /**
     * æŒ‰ç…§ç”¨æˆ·id查询用户
     *
     * @param userIds ç”¨æˆ·id
     */
    @Override
    public List<SysUserVo> getUserListByIds(List<Long> userIds) {
        if (CollUtil.isEmpty(userIds)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.lambdaQuery();
        // æ£€ç´¢æ¡ä»¶
        queryWrapper.eq(SysUser::getStatus, UserStatus.OK.getCode());
        queryWrapper.in(SysUser::getUserId, userIds);
        return sysUserMapper.selectVoList(queryWrapper);
    }
    /**
     * æŒ‰ç…§è§’色id查询关联用户id
     *
     * @param roleIds è§’色id
     */
    @Override
    public List<SysUserRole> getUserRoleListByRoleIds(List<Long> roleIds) {
        return sysUserRoleMapper.selectList(new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getRoleId, roleIds));
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ç”¨æˆ·
     *
     * @param sysUserBo å‚æ•°
     * @param pageQuery åˆ†é¡µ
     */
    @Override
    public TableDataInfo<SysUserVo> getUserListByPage(SysUserBo sysUserBo, PageQuery pageQuery) {
        LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(sysUserBo.getDeptId() != null, SysUser::getDeptId, sysUserBo.getDeptId());
        queryWrapper.eq(SysUser::getStatus, UserStatus.OK.getCode());
        queryWrapper.like(StringUtils.isNotEmpty(sysUserBo.getUserName()), SysUser::getUserName, sysUserBo.getUserName());
        queryWrapper.like(StringUtils.isNotEmpty(sysUserBo.getNickName()), SysUser::getNickName, sysUserBo.getNickName());
        Page<SysUserVo> userPage = sysUserMapper.selectVoPage(pageQuery.build(), queryWrapper);
        return TableDataInfo.build(recordPage(userPage));
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,220 @@
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 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.impl.util.ProcessDefinitionUtil;
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
 */
public class ModelUtils {
    public ModelUtils() {
    }
    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("开始节点后第一个节点必须是用户任务!");
        }
        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<FlowElement> getFlowElements(String processDefinitionId) {
        BpmnModel bpmnModel = ProcessDefinitionUtil.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 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 Map<String, List<ExtensionElement>> getExtensionElement(String processDefinitionId, String flowElementId) {
        BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);
        Process process = bpmnModel.getMainProcess();
        FlowElement flowElement = process.getFlowElement(flowElementId);
        return flowElement.getExtensionElements();
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/WorkflowUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,341 @@
package org.dromara.workflow.utils;
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.update.LambdaUpdateWrapper;
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.core.utils.reflect.ReflectUtils;
import org.dromara.common.mail.utils.MailUtils;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.common.websocket.dto.WebSocketMessageDto;
import org.dromara.common.websocket.utils.WebSocketUtils;
import org.dromara.system.domain.SysUserRole;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.common.enums.BusinessStatusEnum;
import org.dromara.workflow.common.enums.MessageTypeEnum;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.domain.ActHiProcinst;
import org.dromara.workflow.domain.ActHiTaskinst;
import org.dromara.workflow.domain.vo.MultiInstanceVo;
import org.dromara.workflow.domain.vo.ParticipantVo;
import org.dromara.workflow.domain.vo.ProcessInstanceVo;
import org.dromara.workflow.flowable.cmd.UpdateHiTaskInstCmd;
import org.dromara.workflow.mapper.ActHiTaskinstMapper;
import org.dromara.workflow.service.IActHiProcinstService;
import org.dromara.workflow.service.IWorkflowUserService;
import org.dromara.workflow.service.impl.WorkflowUserServiceImpl;
import org.flowable.bpmn.model.*;
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.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.util.*;
import static org.dromara.workflow.common.constant.FlowConstant.*;
/**
 * å·¥ä½œæµå·¥å…·
 *
 * @author may
 */
public class WorkflowUtils {
    public WorkflowUtils() {
    }
    private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class);
    private static final IWorkflowUserService I_WORKFLOW_USER_SERVICE = SpringUtils.getBean(WorkflowUserServiceImpl.class);
    private static final IActHiProcinstService I_ACT_HI_PROCINST_SERVICE = SpringUtils.getBean(IActHiProcinstService.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.setTenantId(currentTask.getTenantId());
            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<>();
        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(TenantHelper.getTenantId());
                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(TenantHelper.getTenantId());
            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) {
        ParticipantVo participantVo = new ParticipantVo();
        List<HistoricIdentityLink> linksForTask = PROCESS_ENGINE.getHistoryService().getHistoricIdentityLinksForTask(taskId);
        Task task = PROCESS_ENGINE.getTaskService().createTaskQuery().taskTenantId(TenantHelper.getTenantId()).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<SysUserRole> sysUserRoles = I_WORKFLOW_USER_SERVICE.getUserRoleListByRoleIds(groupIds);
                if (CollUtil.isNotEmpty(sysUserRoles)) {
                    participantVo.setGroupIds(groupIds);
                    List<Long> userIdList = StreamUtils.toList(sysUserRoles, SysUserRole::getUserId);
                    List<SysUserVo> sysUsers = I_WORKFLOW_USER_SERVICE.getUserListByIds(userIdList);
                    if (CollUtil.isNotEmpty(sysUsers)) {
                        List<Long> userIds = StreamUtils.toList(sysUsers, SysUserVo::getUserId);
                        List<String> nickNames = StreamUtils.toList(sysUsers, SysUserVo::getNickName);
                        participantVo.setCandidate(userIds);
                        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<SysUserVo> sysUsers = I_WORKFLOW_USER_SERVICE.getUserListByIds(userIdList);
                if (CollUtil.isNotEmpty(sysUsers)) {
                    List<Long> userIds = StreamUtils.toList(sysUsers, SysUserVo::getUserId);
                    List<String> nickNames = StreamUtils.toList(sysUsers, SysUserVo::getNickName);
                    participantVo.setCandidate(userIds);
                    participantVo.setCandidateName(nickNames);
                    if (StringUtils.isBlank(task.getAssignee()) && CollUtil.isNotEmpty(candidateList)) {
                        participantVo.setClaim(false);
                    }
                    if (!StringUtils.isBlank(task.getAssignee()) && CollUtil.isNotEmpty(candidateList)) {
                        participantVo.setClaim(true);
                    }
                }
            }
        }
        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 = PROCESS_ENGINE.getHistoryService().createHistoricTaskInstanceQuery().taskId(taskId).taskTenantId(TenantHelper.getTenantId()).singleResult();
        HistoricProcessInstance historicProcessInstance = PROCESS_ENGINE.getHistoryService().createHistoricProcessInstanceQuery().processInstanceId(historicTaskInstance.getProcessInstanceId()).processInstanceTenantId(TenantHelper.getTenantId()).singleResult();
        return historicProcessInstance.getBusinessStatus();
    }
    /**
     * èŽ·å–å½“å‰æµç¨‹çŠ¶æ€
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     */
    public static String getBusinessStatus(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance = PROCESS_ENGINE.getHistoryService().createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).processInstanceTenantId(TenantHelper.getTenantId()).singleResult();
        return historicProcessInstance.getBusinessStatus();
    }
    /**
     * è®¾ç½®æµç¨‹å®žä¾‹å¯¹è±¡
     *
     * @param obj         ä¸šåŠ¡å¯¹è±¡
     * @param businessKey ä¸šåŠ¡id
     */
    public static void setProcessInstanceVo(Object obj, String businessKey) {
        if (StringUtils.isBlank(businessKey)) {
            return;
        }
        ActHiProcinst actHiProcinst = I_ACT_HI_PROCINST_SERVICE.selectByBusinessKey(businessKey);
        if (actHiProcinst == null) {
            ProcessInstanceVo processInstanceVo = new ProcessInstanceVo();
            processInstanceVo.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus());
            ReflectUtils.invokeSetter(obj, PROCESS_INSTANCE_VO, processInstanceVo);
            return;
        }
        ProcessInstanceVo processInstanceVo = BeanUtil.toBean(actHiProcinst, ProcessInstanceVo.class);
        processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstanceVo.getBusinessStatus()));
        ReflectUtils.invokeSetter(obj, PROCESS_INSTANCE_VO, processInstanceVo);
    }
    /**
     * è®¾ç½®æµç¨‹å®žä¾‹å¯¹è±¡
     *
     * @param obj       ä¸šåŠ¡å¯¹è±¡
     * @param idList    ä¸šåŠ¡id
     * @param fieldName ä¸»é”®å±žæ€§åç§°
     */
    public static void setProcessInstanceListVo(Object obj, List<String> idList, String fieldName) {
        if (CollUtil.isEmpty(idList)) {
            return;
        }
        List<ActHiProcinst> actHiProcinstList = I_ACT_HI_PROCINST_SERVICE.selectByBusinessKeyIn(idList);
        if (obj instanceof Collection<?> collection) {
            for (Object o : collection) {
                String fieldValue = ReflectUtils.invokeGetter(o, fieldName).toString();
                if (CollUtil.isEmpty(actHiProcinstList)) {
                    ProcessInstanceVo processInstanceVo = new ProcessInstanceVo();
                    processInstanceVo.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus());
                    processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstanceVo.getBusinessStatus()));
                    ReflectUtils.invokeSetter(o, PROCESS_INSTANCE_VO, processInstanceVo);
                } else {
                    ActHiProcinst actHiProcinst = actHiProcinstList.stream().filter(e -> e.getBusinessKey().equals(fieldValue)).findFirst().orElse(null);
                    if (ObjectUtil.isNotEmpty(actHiProcinst)) {
                        ProcessInstanceVo processInstanceVo = BeanUtil.toBean(actHiProcinst, ProcessInstanceVo.class);
                        processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstanceVo.getBusinessStatus()));
                        ReflectUtils.invokeSetter(o, PROCESS_INSTANCE_VO, processInstanceVo);
                    } else {
                        ProcessInstanceVo processInstanceVo = new ProcessInstanceVo();
                        processInstanceVo.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus());
                        processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstanceVo.getBusinessStatus()));
                        ReflectUtils.invokeSetter(o, PROCESS_INSTANCE_VO, processInstanceVo);
                    }
                }
            }
        }
    }
    /**
     * å‘送消息
     *
     * @param list        ä»»åŠ¡
     * @param name        æµç¨‹åç§°
     * @param messageType æ¶ˆæ¯ç±»åž‹
     * @param message     æ¶ˆæ¯å†…容,为空则发送默认配置的消息内容
     */
    public static void sendMessage(List<Task> list, String name, List<String> messageType, String message) {
        Set<Long> userIds = new HashSet<>();
        if (StringUtils.isBlank(message)) {
            message = "有新的【" + name + "】单据已经提交至您的待办,请您及时处理。";
        }
        for (Task t : list) {
            ParticipantVo taskParticipant = WorkflowUtils.getCurrentTaskParticipant(t.getId());
            if (CollUtil.isNotEmpty(taskParticipant.getGroupIds())) {
                List<SysUserRole> sysUserRoles = I_WORKFLOW_USER_SERVICE.getUserRoleListByRoleIds(taskParticipant.getGroupIds());
                if (CollUtil.isNotEmpty(sysUserRoles)) {
                    userIds.addAll(StreamUtils.toList(sysUserRoles, SysUserRole::getUserId));
                }
            }
            List<Long> candidate = taskParticipant.getCandidate();
            if (CollUtil.isNotEmpty(candidate)) {
                userIds.addAll(candidate);
            }
        }
        if (CollUtil.isNotEmpty(userIds)) {
            List<SysUserVo> sysUserVoList = I_WORKFLOW_USER_SERVICE.getUserListByIds(new ArrayList<>(userIds));
            for (String code : messageType) {
                if (code.equals(MessageTypeEnum.SYSTEM_MESSAGE.getCode())) {
                    WebSocketMessageDto dto = new WebSocketMessageDto();
                    dto.setSessionKeys(new ArrayList<>(userIds));
                    dto.setMessage(message);
                    WebSocketUtils.publishMessage(dto);
                }
                if (code.equals(MessageTypeEnum.EMAIL_MESSAGE.getCode())) {
                    MailUtils.sendText(StreamUtils.join(sysUserVoList, SysUserVo::getEmail), "单据审批提醒", message);
                }
                if (code.equals(MessageTypeEnum.SMS_MESSAGE.getCode())) {
                    //todo çŸ­ä¿¡å‘送
                }
            }
        }
    }
}
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/package-info.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
java包使用 `.` åˆ†å‰² resource ç›®å½•使用 `/` åˆ†å‰²
<br>
此文件目的 é˜²æ­¢æ–‡ä»¶å¤¹ç²˜è¿žæ‰¾ä¸åˆ° `xml` æ–‡ä»¶
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiProcinstMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
<?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>
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiTaskinstMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
<?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>
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActTaskMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
<?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="TaskWaitingVoResult">
        <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="endTime" column="END_TIME_"/>
        <result property="taskDefinitionKey" column="TASK_DEF_KEY_"/>
        <result property="dueDate" column="DUE_DATE_"/>
        <result property="processDefinitionKey" column="key_"/>
        <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="processDefinitionName"/>
    </resultMap>
    <select id="getTaskWaitByPage" resultMap="TaskWaitingVoResult">
        select *
        from (SELECT RES.*,
                     AHP.BUSINESS_STATUS_,
                     ARP.NAME_ as processDefinitionName,
                     ARP.KEY_  as processDefinitionKey,
                     LINK.USER_ID_,
                     LINK.GROUP_ID_
              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_
                       LEFT JOIN ACT_RU_IDENTITYLINK LINK ON LINK.TASK_ID_ = RES.ID_ AND LINK.TYPE_ = 'candidate'
              WHERE RES.PARENT_TASK_ID_ is null
              ORDER BY RES.CREATE_TIME_ DESC) t ${ew.getCustomSqlSegment}
    </select>
    <select id="getTaskCopyByPage" resultMap="TaskWaitingVoResult">
        select *
        from (SELECT AHT.*,
                     AHP.BUSINESS_STATUS_,
                     ARP.NAME_ as processDefinitionName,
                     ARP.KEY_  as processDefinitionKey
              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>
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/TestLeaveMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
<?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.TestLeaveMapper">
</mapper>
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfCategoryMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
<?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.WfCategoryMapper">
</mapper>
script/bpmn/Çë¼ÙÁ÷³Ì£¨»áÇ©£©-leave5.zip
Binary files differ
script/bpmn/Çë¼ÙÁ÷³Ì£¨°üÈÝÍø¹Ø£©-leave4.zip
Binary files differ
script/bpmn/Çë¼ÙÁ÷³Ì£¨×ÓÁ÷³Ì£©-leave6.zip
Binary files differ
script/bpmn/Çë¼ÙÁ÷³Ì£¨²¢ÐÐÍø¹Ø£©-leave3.zip
Binary files differ
script/bpmn/Çë¼ÙÁ÷³Ì£¨ÅÅËûÍø¹Ø£©-leave2.zip
Binary files differ
script/bpmn/Çë¼ÙÁ÷³Ì£¨ÆÕͨÁ÷³Ì£©-leave1.zip
Binary files differ
script/bpmn/Çë¼ÙÁ÷³Ì£¨¼àÌý£©-leave7.zip
Binary files differ
script/sql/flowable.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
insert into sys_menu values('11616', '工作流'  , '0',    '6', 'workflow',          '',                                 '', '1', '0', 'M', '0', '0', '',                       'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values('11617', '模型管理', '11616', '2', 'model',             'workflow/model/index',             '', '1', '1', 'C', '0', '0', 'workflow:model:list',    'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values('11618', '我的任务', '0', '7', 'task',              '',                                 '', '1', '0', 'M', '0', '0', '',                       'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values('11619', '我的待办', '11618', '2', 'taskWaiting',       'workflow/task/taskWaiting',              '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values('11632', '我的已办', '11618', '3', 'taskFinish',       'workflow/task/taskFinish',              '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values('11633', '我的抄送', '11618', '4', 'taskCopyList',       'workflow/task/taskCopyList',              '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values('11621', '流程实例', '11630', '1', 'processInstance',   'workflow/processInstance/index',   '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values('11622', '流程分类', '11616', '1', 'category',          'workflow/category/index',          '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values('11629', '我发起的', '11618', '1', 'myDocument',        'workflow/task/myDocument',         '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values('11630', '流程监控', '11616', '4', 'monitor',           '',                                 '', '1', '0', 'M', '0', '0', '',                       'tree-table', 103, 1, sysdate(), NULL, NULL, '');
insert into sys_menu values('11631', '待办任务', '11630', '2', 'allTaskWaiting',    'workflow/task/allTaskWaiting',     '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate(), NULL, NULL, '');
-- æµç¨‹åˆ†ç±»ç®¡ç†ç›¸å…³æŒ‰é’®
insert into sys_menu values ('11623', '流程分类查询', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add',   '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11625', '流程分类修改', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit',  '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11626', '流程分类删除', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove','#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values ('11627', '流程分类导出', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export','#', 103, 1, sysdate(), null, null, '');
-- è¯·å‡å•信息
DROP TABLE if EXISTS test_leave;
create table test_leave
(
    id          bigint                       not null comment '主键',
    leave_type  varchar(255)                 not null comment '请假类型',
    start_date   datetime                     not null comment '开始时间',
    end_date     datetime                     not null comment '结束时间',
    leave_days  int(10)                      not null comment '请假天数',
    remark      varchar(255)                 null comment '请假原因',
    create_dept bigint                       null comment '创建部门',
    create_by   bigint                       null comment '创建者',
    create_time datetime                     null comment '创建时间',
    update_by   bigint                       null comment '更新者',
    update_time datetime                     null comment '更新时间',
    tenant_id   varchar(20) default '000000' null comment '租户编号',
    PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB COMMENT = '请假申请表';
-- æµç¨‹åˆ†ç±»ä¿¡æ¯è¡¨
DROP TABLE if EXISTS wf_category;
create table wf_category
(
    id            bigint                       not null comment '主键'
        primary key,
    category_name varchar(255)                 null comment '分类名称',
    category_code varchar(255)                 null comment '分类编码',
    parent_id     bigint                       null comment '父级id',
    sort_num      int(19)                      null comment '排序',
    tenant_id     varchar(20) default '000000' null comment '租户编号',
    create_dept   bigint                       null comment '创建部门',
    create_by     bigint                       null comment '创建者',
    create_time   datetime                     null comment '创建时间',
    update_by     bigint                       null comment '更新者',
    update_time   datetime                     null comment '更新时间',
    constraint uni_category_code
        unique (category_code)
) engine=innodb comment= '流程分类';
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '请假申请', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'demo:leave:list', '#', 103, 1, sysdate(), NULL, NULL, '请假申请菜单');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11639, '请假申请查询', 11638, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:query', '#', 103, 1, sysdate(), NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11640, '请假申请新增', 11638, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:add', '#', 103, 1, sysdate(), NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11641, '请假申请修改', 11638, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:edit', '#', 103, 1, sysdate(), NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11642, '请假申请删除', 11638, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:remove', '#', 103, 1, sysdate(), NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11643, '请假申请导出', 11638, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:export', '#', 103, 1, sysdate(), NULL, NULL, '');
script/sql/oracle/flowable.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
insert into sys_menu values('11616', '工作流'  , '0',    '6', 'workflow',          '',                                 '', '1', '0', 'M', '0', '0', '',                       'tree-table', 103, 1, sysdate, NULL, NULL, '');
insert into sys_menu values('11617', '模型管理', '11616', '2', 'model',             'workflow/model/index',             '', '1', '1', 'C', '0', '0', 'workflow:model:list',    'tree-table', 103, 1, sysdate, NULL, NULL, '');
insert into sys_menu values('11618', '我的任务', '0', '7', 'task',              '',                                 '', '1', '0', 'M', '0', '0', '',                       'tree-table', 103, 1, sysdate, NULL, NULL, '');
insert into sys_menu values('11619', '我的待办', '11618', '2', 'taskWaiting',       'workflow/task/taskWaiting',              '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate, NULL, NULL, '');
insert into sys_menu values('11632', '我的已办', '11618', '3', 'taskFinish',       'workflow/task/taskFinish',              '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate, NULL, NULL, '');
insert into sys_menu values('11633', '我的抄送', '11618', '4', 'taskCopyList',       'workflow/task/taskCopyList',              '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate, NULL, NULL, '');
insert into sys_menu values('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate, NULL, NULL, '');
insert into sys_menu values('11621', '流程实例', '11630', '1', 'processInstance',   'workflow/processInstance/index',   '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate, NULL, NULL, '');
insert into sys_menu values('11622', '流程分类', '11616', '1', 'category',          'workflow/category/index',          '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'tree-table', 103, 1, sysdate, NULL, NULL, '');
insert into sys_menu values('11629', '我发起的', '11618', '1', 'myDocument',        'workflow/task/myDocument',         '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate, NULL, NULL, '');
insert into sys_menu values('11630', '流程监控', '11616', '4', 'monitor',           '',                                 '', '1', '0', 'M', '0', '0', '',                       'tree-table', 103, 1, sysdate, NULL, NULL, '');
insert into sys_menu values('11631', '待办任务', '11630', '2', 'allTaskWaiting',    'workflow/task/allTaskWaiting',     '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate, NULL, NULL, '');
-- æµç¨‹åˆ†ç±»ç®¡ç†ç›¸å…³æŒ‰é’®
insert into sys_menu values ('11623', '流程分类查询', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add',   '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values ('11625', '流程分类修改', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit',  '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values ('11626', '流程分类删除', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove','#', 103, 1, sysdate, null, null, '');
insert into sys_menu values ('11627', '流程分类导出', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export','#', 103, 1, sysdate, null, null, '');
-- è¯·å‡å•信息
create table TEST_LEAVE
(
    ID          NUMBER(20) not null
        constraint PK_TEST_LEAVE
        primary key,
    LEAVE_TYPE  VARCHAR2(255),
    START_DATE  DATE,
    END_DATE    DATE,
    LEAVE_DAYS  NUMBER(10),
    REMARK      VARCHAR2(255),
    CREATE_DEPT NUMBER(20),
    CREATE_BY   NUMBER(20),
    CREATE_TIME DATE,
    UPDATE_BY   NUMBER(20),
    UPDATE_TIME DATE,
    TENANT_ID   VARCHAR2(255) default '000000'
);
comment on table TEST_LEAVE is '请假申请表'
comment on column TEST_LEAVE.ID is '主键'
comment on column TEST_LEAVE.LEAVE_TYPE is '请假类型'
comment on column TEST_LEAVE.START_DATE is '开始时间'
comment on column TEST_LEAVE.END_DATE is '结束时间'
comment on column TEST_LEAVE.LEAVE_DAYS is '请假天数'
comment on column TEST_LEAVE.REMARK is '请假原因'
comment on column TEST_LEAVE.CREATE_DEPT is '创建部门'
comment on column TEST_LEAVE.CREATE_BY is '创建者'
comment on column TEST_LEAVE.CREATE_TIME is '创建时间'
comment on column TEST_LEAVE.UPDATE_BY is '更新者'
comment on column TEST_LEAVE.UPDATE_TIME is '更新时间'
comment on column TEST_LEAVE.TENANT_ID is '租户编号'
-- æµç¨‹åˆ†ç±»ä¿¡æ¯è¡¨
create table WF_CATEGORY
(
    ID            NUMBER(20) not null
        constraint PK_WF_CATEGORY
        primary key,
    CATEGORY_NAME VARCHAR2(255),
    CATEGORY_CODE VARCHAR2(255)
        constraint UNI_CATEGORY_CODE
        unique,
    PARENT_ID     NUMBER(20),
    SORT_NUM      NUMBER(10),
    TENANT_ID     NUMBER(20),
    CREATE_DEPT   NUMBER(20),
    CREATE_BY     NUMBER(20),
    CREATE_TIME   DATE,
    UPDATE_BY     NUMBER(20),
    UPDATE_TIME   DATE
);
comment on table WF_CATEGORY is '流程分类'
comment on column WF_CATEGORY.ID is '主键'
comment on column WF_CATEGORY.CATEGORY_NAME is '分类名称'
comment on column WF_CATEGORY.CATEGORY_CODE is '分类编码'
comment on column WF_CATEGORY.PARENT_ID is '父级id'
comment on column WF_CATEGORY.SORT_NUM is '排序'
comment on column WF_CATEGORY.TENANT_ID is '租户编号'
comment on column WF_CATEGORY.CREATE_DEPT is '创建部门'
comment on column WF_CATEGORY.CREATE_BY is '创建者'
comment on column WF_CATEGORY.CREATE_TIME is '创建时间'
comment on column WF_CATEGORY.UPDATE_BY is '更新者'
comment on column WF_CATEGORY.UPDATE_TIME is '更新时间'
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '请假申请', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'demo:leave:list', '#', 103, 1, sysdate, NULL, NULL, '请假申请菜单');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11639, '请假申请查询', 11638, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:query', '#', 103, 1, sysdate, NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11640, '请假申请新增', 11638, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:add', '#', 103, 1, sysdate, NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11641, '请假申请修改', 11638, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:edit', '#', 103, 1, sysdate, NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11642, '请假申请删除', 11638, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:remove', '#', 103, 1, sysdate, NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11643, '请假申请导出', 11638, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:export', '#', 103, 1, sysdate, NULL, NULL, '');
script/sql/postgres/flowable.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,122 @@
insert into sys_menu values('11616', '工作流'  , '0',    '6', 'workflow',          '',                                 '', '1', '0', 'M', '0', '0', '',                       'tree-table', 103, 1, now(), NULL, NULL, '');
insert into sys_menu values('11617', '模型管理', '11616', '2', 'model',             'workflow/model/index',             '', '1', '1', 'C', '0', '0', 'workflow:model:list',    'tree-table', 103, 1, now(), NULL, NULL, '');
insert into sys_menu values('11618', '我的任务', '0', '7', 'task',              '',                                 '', '1', '0', 'M', '0', '0', '',                       'tree-table', 103, 1, now(), NULL, NULL, '');
insert into sys_menu values('11619', '我的待办', '11618', '2', 'taskWaiting',       'workflow/task/taskWaiting',              '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, now(), NULL, NULL, '');
insert into sys_menu values('11632', '我的已办', '11618', '3', 'taskFinish',       'workflow/task/taskFinish',              '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, now(), NULL, NULL, '');
insert into sys_menu values('11633', '我的抄送', '11618', '4', 'taskCopyList',       'workflow/task/taskCopyList',              '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, now(), NULL, NULL, '');
insert into sys_menu values('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, now(), NULL, NULL, '');
insert into sys_menu values('11621', '流程实例', '11630', '1', 'processInstance',   'workflow/processInstance/index',   '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, now(), NULL, NULL, '');
insert into sys_menu values('11622', '流程分类', '11616', '1', 'category',          'workflow/category/index',          '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'tree-table', 103, 1, now(), NULL, NULL, '');
insert into sys_menu values('11629', '我发起的', '11618', '1', 'myDocument',        'workflow/task/myDocument',         '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, now(), NULL, NULL, '');
insert into sys_menu values('11630', '流程监控', '11616', '4', 'monitor',           '',                                 '', '1', '0', 'M', '0', '0', '',                       'tree-table', 103, 1, now(), NULL, NULL, '');
insert into sys_menu values('11631', '待办任务', '11630', '2', 'allTaskWaiting',    'workflow/task/allTaskWaiting',     '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, now(), NULL, NULL, '');
-- æµç¨‹åˆ†ç±»ç®¡ç†ç›¸å…³æŒ‰é’®
insert into sys_menu values ('11623', '流程分类查询', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1, now(), null, null, '');
insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add',   '#', 103, 1, now(), null, null, '');
insert into sys_menu values ('11625', '流程分类修改', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit',  '#', 103, 1, now(), null, null, '');
insert into sys_menu values ('11626', '流程分类删除', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove','#', 103, 1, now(), null, null, '');
insert into sys_menu values ('11627', '流程分类导出', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export','#', 103, 1, now(), null, null, '');
-- è¯·å‡å•信息
DROP TABLE if EXISTS test_leave;
create table test_leave
(
    id          bigint not null
        constraint test_leave_pk
            primary key,
    leave_type  varchar(255),
    start_date  timestamp,
    end_date    timestamp,
    leave_days  bigint,
    remark      varchar(255),
    create_dept bigint,
    create_by   bigint,
    create_time timestamp,
    update_by   bigint,
    update_time timestamp,
    tenant_id   varchar(20)
);
comment on table test_leave is '请假申请表';
comment on column test_leave.id is '主键';
comment on column test_leave.leave_type is '请假类型';
comment on column test_leave.start_date is '开始时间';
comment on column test_leave.end_date is '结束时间';
comment on column test_leave.remark is '请假原因';
comment on column test_leave.create_dept is '创建部门';
comment on column test_leave.create_by is '创建者';
comment on column test_leave.create_time is '创建时间';
comment on column test_leave.update_by is '更新者';
comment on column test_leave.update_time is '更新时间';
comment on column test_leave.tenant_id is '租户编码';
alter table test_leave
    owner to postgres;
-- æµç¨‹åˆ†ç±»ä¿¡æ¯è¡¨
DROP TABLE if EXISTS wf_category;
create table wf_category
(
    id            bigint not null
        constraint wf_category_pk
            primary key,
    category_name varchar(255),
    category_code varchar(255),
    parent_id     bigint,
    sort_num      bigint,
    tenant_id     bigint,
    create_dept   bigint,
    create_by     bigint,
    create_time   timestamp,
    update_by     bigint,
    update_time   timestamp
);
comment on table wf_category is '流程分类';
comment on column wf_category.id is '主键';
comment on column wf_category.category_name is '分类名称';
comment on column wf_category.category_code is '分类编码';
comment on column wf_category.parent_id is '父级id';
comment on column wf_category.sort_num is '排序';
comment on column wf_category.tenant_id is '租户id';
comment on column wf_category.create_dept is '创建部门';
comment on column wf_category.create_by is '创建者';
comment on column wf_category.create_time is '创建时间';
comment on column wf_category.update_by is '修改者';
comment on column wf_category.update_time is '修改时间';
alter table wf_category
    owner to postgres;
create unique index uni_category_code
    on wf_category (category_code);
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '请假申请', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'demo:leave:list', '#', 103, 1, now(), NULL, NULL, '请假申请菜单');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11639, '请假申请查询', 11638, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:query', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11640, '请假申请新增', 11638, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:add', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11641, '请假申请修改', 11638, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:edit', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11642, '请假申请删除', 11638, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:remove', '#', 103, 1, now(), NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11643, '请假申请导出', 11638, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:export', '#', 103, 1, now(), NULL, NULL, '');
script/sql/sqlserver/flowable.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,155 @@
insert into sys_menu values('11616', '工作流'  , '0',    '6', 'workflow',          '',                                 '', '1', '0', 'M', '0', '0', '',                       'tree-table', 103, 1, getdate(), NULL, NULL, '');
insert into sys_menu values('11617', '模型管理', '11616', '2', 'model',             'workflow/model/index',             '', '1', '1', 'C', '0', '0', 'workflow:model:list',    'tree-table', 103, 1, getdate(), NULL, NULL, '');
insert into sys_menu values('11618', '我的任务', '0', '7', 'task',              '',                                 '', '1', '0', 'M', '0', '0', '',                       'tree-table', 103, 1, getdate(), NULL, NULL, '');
insert into sys_menu values('11619', '我的待办', '11618', '2', 'taskWaiting',       'workflow/task/taskWaiting',              '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, getdate(), NULL, NULL, '');
insert into sys_menu values('11632', '我的已办', '11618', '3', 'taskFinish',       'workflow/task/taskFinish',              '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, getdate(), NULL, NULL, '');
insert into sys_menu values('11633', '我的抄送', '11618', '4', 'taskCopyList',       'workflow/task/taskCopyList',              '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, getdate(), NULL, NULL, '');
insert into sys_menu values('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, getdate(), NULL, NULL, '');
insert into sys_menu values('11621', '流程实例', '11630', '1', 'processInstance',   'workflow/processInstance/index',   '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, getdate(), NULL, NULL, '');
insert into sys_menu values('11622', '流程分类', '11616', '1', 'category',          'workflow/category/index',          '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'tree-table', 103, 1, getdate(), NULL, NULL, '');
insert into sys_menu values('11629', '我发起的', '11618', '1', 'myDocument',        'workflow/task/myDocument',         '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, getdate(), NULL, NULL, '');
insert into sys_menu values('11630', '流程监控', '11616', '4', 'monitor',           '',                                 '', '1', '0', 'M', '0', '0', '',                       'tree-table', 103, 1, getdate(), NULL, NULL, '');
insert into sys_menu values('11631', '待办任务', '11630', '2', 'allTaskWaiting',    'workflow/task/allTaskWaiting',     '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, getdate(), NULL, NULL, '');
-- æµç¨‹åˆ†ç±»ç®¡ç†ç›¸å…³æŒ‰é’®
insert into sys_menu values ('11623', '流程分类查询', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1, getdate(), null, null, '');
insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add',   '#', 103, 1, getdate(), null, null, '');
insert into sys_menu values ('11625', '流程分类修改', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit',  '#', 103, 1, getdate(), null, null, '');
insert into sys_menu values ('11626', '流程分类删除', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove','#', 103, 1, getdate(), null, null, '');
insert into sys_menu values ('11627', '流程分类导出', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export','#', 103, 1, getdate(), null, null, '');
-- è¯·å‡å•信息
DROP TABLE if EXISTS test_leave;
create table test_leave
(
    id          bigint        not null
        primary key,
    leave_type  nvarchar(255) not null,
    start_date  datetime2     not null,
    end_date    datetime2     not null,
    leave_days  int           not null,
    remark      nvarchar(255),
    create_dept bigint,
    create_by   bigint,
    create_time datetime2,
    update_by   bigint,
    update_time datetime2,
    tenant_id   nvarchar(20) default '000000'
)
go
exec sp_addextendedproperty 'MS_Description', N'请假申请表', 'SCHEMA', 'dbo', 'TABLE', 'test_leave'
go
exec sp_addextendedproperty 'MS_Description', N'主键', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN', 'id'
go
exec sp_addextendedproperty 'MS_Description', N'请假类型', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN',
     'leave_type'
go
exec sp_addextendedproperty 'MS_Description', N'开始时间', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN',
     'start_date'
go
exec sp_addextendedproperty 'MS_Description', N'结束时间', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN', 'end_date'
go
exec sp_addextendedproperty 'MS_Description', N'请假天数', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN',
     'leave_days'
go
exec sp_addextendedproperty 'MS_Description', N'请假原因', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN', 'remark'
go
exec sp_addextendedproperty 'MS_Description', N'创建部门', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN',
     'create_dept'
go
exec sp_addextendedproperty 'MS_Description', N'创建者', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN', 'create_by'
go
exec sp_addextendedproperty 'MS_Description', N'创建时间', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN',
     'create_time'
go
exec sp_addextendedproperty 'MS_Description', N'更新者', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN', 'update_by'
go
exec sp_addextendedproperty 'MS_Description', N'更新时间', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN',
     'update_time'
go
exec sp_addextendedproperty 'MS_Description', N'租户编号', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN', 'tenant_id'
go
-- æµç¨‹åˆ†ç±»ä¿¡æ¯è¡¨
DROP TABLE if EXISTS wf_category;
create table wf_category
(
    id            bigint not null
        primary key,
    category_name nvarchar(255),
    category_code nvarchar(255)
        constraint uni_category_code
        unique,
    parent_id     bigint,
    sort_num      int,
    tenant_id     nvarchar(20) default '000000',
    create_dept   bigint,
    create_by     bigint,
    create_time   datetime2,
    update_by     bigint,
    update_time   datetime2
)
go
exec sp_addextendedproperty 'MS_Description', N'流程分类', 'SCHEMA', 'dbo', 'TABLE', 'wf_category'
go
exec sp_addextendedproperty 'MS_Description', N'主键', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN', 'id'
go
exec sp_addextendedproperty 'MS_Description', N'分类名称', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN',
     'category_name'
go
exec sp_addextendedproperty 'MS_Description', N'分类编码', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN',
     'category_code'
go
exec sp_addextendedproperty 'MS_Description', N'父级id', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN', 'parent_id'
go
exec sp_addextendedproperty 'MS_Description', N'排序', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN', 'sort_num'
go
exec sp_addextendedproperty 'MS_Description', N'租户编号', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN',
     'tenant_id'
go
exec sp_addextendedproperty 'MS_Description', N'创建部门', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN',
     'create_dept'
go
exec sp_addextendedproperty 'MS_Description', N'创建者', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN', 'create_by'
go
exec sp_addextendedproperty 'MS_Description', N'创建时间', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN',
     'create_time'
go
exec sp_addextendedproperty 'MS_Description', N'更新者', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN', 'update_by'
go
exec sp_addextendedproperty 'MS_Description', N'更新时间', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN',
     'update_time'
go
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '请假申请', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'demo:leave:list', '#', 103, 1, getdate(), NULL, NULL, '请假申请菜单');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11639, '请假申请查询', 11638, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:query', '#', 103, 1, getdate(), NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11640, '请假申请新增', 11638, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:add', '#', 103, 1, getdate(), NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11641, '请假申请修改', 11638, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:edit', '#', 103, 1, getdate(), NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11642, '请假申请删除', 11638, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:remove', '#', 103, 1, getdate(), NULL, NULL, '');
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11643, '请假申请导出', 11638, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:export', '#', 103, 1, getdate(), NULL, NULL, '');