lyh
2025-02-24 3f5737664d799ece89224079860505a009dec32b
去除原有activiti 新增flowable工作流
已添加86个文件
已修改11个文件
已删除38个文件
14604 ■■■■■ 文件已修改
lxzn-boot-base-core/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-boot-base-core/src/main/java/org/jeecg/config/Swagger2Config.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-activiti/pom.xml 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-activiti/src/main/java/org/activiti/engine/impl/AbstractQuery.java 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-activiti/src/main/java/org/activiti/engine/impl/cfg/ProcessEngineConfigurationImpl.java 3541 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-activiti/src/main/java/org/activiti/engine/impl/db/DbSqlSession.java 1274 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-activiti/src/main/java/org/activiti/engine/impl/db/DbSqlSessionFactory.java 301 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/controller/ActivitiDefinitionController.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/controller/ActivitiSignExamineController.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/controller/AssignFileStreamController.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/entity/ActivitiDefinition.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/entity/ActivitiSignExamine.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/entity/AssignFileStream.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/entity/ToEquipmentTask.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/ext/ActTaskExt.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/ext/AssignFileStreamExt.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/mapper/ActivitiDefinitionMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/mapper/ActivitiSignExamineMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/mapper/AssignFileStreamMapper.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/mapper/ToEquipmentTaskMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/mapper/xml/AssignFileStreamMapper.xml 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/request/ActivitiDefinitionRequest.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/request/ApproveBatchRequest.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/request/AssignFileRequest.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/request/AssignFileStreamQueryRequest.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/request/TaskRequest.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/IActivitiDefinitionService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/IActivitiSignExamineService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/IAssignFileStreamService.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/IToEquipmentTaskService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/impl/ActivitiDefinitionServiceImpl.java 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/impl/ActivitiSignExamineServiceimpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/impl/AssignFileStreamServiceImpl.java 817 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/impl/ToEquipmentTaskServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/controller/DepartmentController.java 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/controller/DeviceGroupController.java 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/controller/UserPermButtonController.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/service/IDepartmentService.java 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/service/IDeviceGroupService.java 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/service/IUserPermButtonService.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/service/impl/DepartmentServiceImpl.java 340 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/service/impl/DeviceGroupServiceImpl.java 156 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/service/impl/UserPermButtonServiceImpl.java 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/pom.xml 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/JeecgFlowableCloudApplication.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/business/entity/FlowMyBusiness.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/business/entity/FlowMyBusinessDto.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/business/mapper/FlowMyBusinessMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/business/mapper/xml/FlowMyBusinessMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/business/service/IFlowMyBusinessService.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/business/service/impl/FlowMyBusinessServiceImpl.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/common/constant/ProcessConstants.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/common/enums/FlowComment.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/common/exception/CustomException.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/entity/ActStatus.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/entity/FlowBeforeParams.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/entity/SysCategory.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/entity/SysRole.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/entity/SysUser.java 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/service/FlowCallBackServiceI.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/service/FlowCommonService.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/service/IFlowThirdService.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/config/MyDefaultProcessDiagramCanvas.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/controller/FlowDefinitionController.java 243 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/controller/FlowInstanceController.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/controller/FlowTaskController.java 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/dto/FlowCommentDto.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/dto/FlowNextDto.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/dto/FlowProcDefDto.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/dto/FlowSaveXmlVo.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/dto/FlowTaskDto.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/dto/FlowViewerDto.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/vo/FlowTaskVo.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/factory/FlowServiceFactory.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/flow/CustomProcessDiagramCanvas.java 369 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/flow/CustomProcessDiagramGenerator.java 402 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/flow/FindNextNodeUtil.java 235 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/flow/FlowableConfig.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/flow/FlowableUtils.java 587 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/listener/UserTaskListener.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/service/IFlowDefinitionService.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/service/IFlowInstanceService.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/service/IFlowTaskService.java 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/service/impl/FlowDefinitionServiceImpl.java 406 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/service/impl/FlowInstanceServiceImpl.java 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/service/impl/FlowTaskServiceImpl.java 1429 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/business/entity/FlowMyBusiness.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/business/entity/FlowMyBusinessDto.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/business/mapper/FlowMyBusinessMapper.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/business/mapper/xml/FlowMyBusinessMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/business/service/IFlowMyBusinessService.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/business/service/impl/FlowMyBusinessServiceImpl.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/common/constant/ProcessConstants.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/common/enums/FlowComment.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/common/exception/CustomException.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/entity/ActStatus.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/entity/FlowBeforeParams.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/entity/SysCategory.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/entity/SysRole.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/entity/SysUser.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/service/FlowCallBackServiceI.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/service/FlowCommonService.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/service/IFlowThirdService.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/config/MyDefaultProcessDiagramCanvas.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/controller/FlowDefinitionController.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/controller/FlowInstanceController.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/controller/FlowTaskController.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowCommentDto$FlowCommentDtoBuilder.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowCommentDto.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowNextDto.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowProcDefDto.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowSaveXmlVo.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowTaskDto.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowViewerDto.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/vo/FlowTaskVo.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/factory/FlowServiceFactory.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/flow/CustomProcessDiagramCanvas.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/flow/CustomProcessDiagramGenerator.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/flow/FindNextNodeUtil.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/flow/FlowableConfig.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/flow/FlowableUtils.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/listener/UserTaskListener.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/service/IFlowDefinitionService.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/service/IFlowInstanceService.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/service/IFlowTaskService.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/service/impl/FlowDefinitionServiceImpl.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/service/impl/FlowInstanceServiceImpl.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/service/impl/FlowTaskServiceImpl.class 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-biz/pom.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/flow/FlowThirdServiceImpl.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-start/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-start/src/main/java/org/jeecg/JeecgSystemApplication.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-start/src/main/resources/application-dev.yml 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-boot-base-core/pom.xml
@@ -98,8 +98,8 @@
        <!--  sqlserver-->
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>sqljdbc4</artifactId>
            <version>${sqljdbc4.version}</version>
            <artifactId>mssql-jdbc</artifactId>
            <version>${mssql-jdbc.version}</version>
            <scope>runtime</scope>
        </dependency>
        <!-- oracle驱动 -->
@@ -221,4 +221,4 @@
        </dependency>
    </dependencies>
</project>
</project>
lxzn-boot-base-core/src/main/java/org/jeecg/config/Swagger2Config.java
@@ -132,7 +132,7 @@
                .apiInfo(apiInfo())
                .select()
                //此包路径下的类,才生成接口文档
                .apis(RequestHandlerSelectors.basePackage("org.jeecg.modules.activiti"))
                .apis(RequestHandlerSelectors.basePackage("org.jeecg.modules.flowable"))
                //加了ApiOperation注解的类,才生成接口文档
                .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
@@ -141,7 +141,7 @@
                .securitySchemes(Collections.singletonList(securityScheme()))
                .securityContexts(securityContexts())
                .globalOperationParameters(setHeaderToken())
                .groupName("Activiti模块");
                .groupName("Flowable模块");
    }
    /***
lxzn-module-activiti/pom.xml
ÎļþÒÑɾ³ý
lxzn-module-activiti/src/main/java/org/activiti/engine/impl/AbstractQuery.java
ÎļþÒÑɾ³ý
lxzn-module-activiti/src/main/java/org/activiti/engine/impl/cfg/ProcessEngineConfigurationImpl.java
ÎļþÒÑɾ³ý
lxzn-module-activiti/src/main/java/org/activiti/engine/impl/db/DbSqlSession.java
ÎļþÒÑɾ³ý
lxzn-module-activiti/src/main/java/org/activiti/engine/impl/db/DbSqlSessionFactory.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/pom.xml
@@ -22,8 +22,8 @@
        </dependency>
        <dependency>
            <groupId>org.jeecgframework.boot</groupId>
            <artifactId>lxzn-module-activiti</artifactId>
            <version>3.4.3</version>
            <artifactId>lxzn-module-flowable</artifactId>
            <version>${jeecgboot.version}</version>
        </dependency>
        <dependency>
            <groupId>org.graylog2</groupId>
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/controller/ActivitiDefinitionController.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/controller/ActivitiSignExamineController.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/controller/AssignFileStreamController.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/entity/ActivitiDefinition.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/entity/ActivitiSignExamine.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/entity/AssignFileStream.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/entity/ToEquipmentTask.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/ext/ActTaskExt.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/ext/AssignFileStreamExt.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/mapper/ActivitiDefinitionMapper.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/mapper/ActivitiSignExamineMapper.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/mapper/AssignFileStreamMapper.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/mapper/ToEquipmentTaskMapper.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/mapper/xml/AssignFileStreamMapper.xml
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/request/ActivitiDefinitionRequest.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/request/ApproveBatchRequest.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/request/AssignFileRequest.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/request/AssignFileStreamQueryRequest.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/request/TaskRequest.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/IActivitiDefinitionService.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/IActivitiSignExamineService.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/IAssignFileStreamService.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/IToEquipmentTaskService.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/impl/ActivitiDefinitionServiceImpl.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/impl/ActivitiSignExamineServiceimpl.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/impl/AssignFileStreamServiceImpl.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/activiti/service/impl/ToEquipmentTaskServiceImpl.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/controller/DepartmentController.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/controller/DeviceGroupController.java
@@ -80,27 +80,27 @@
        return new QueryListResponseResult<>(CommonCode.SUCCESS, list);
    }
    @AutoLog(value = "设备分组表-给分组分配部门权限")
    @ApiOperation(value = "设备分组表-给分组分配部门权限", notes = "设备分组表-给分组分配部门权限")
    @PostMapping("/assign/add/depart/{groupId}/{relativeFlag}")
    public ResponseResult assignAddDepartment(@PathVariable("groupId") String groupId, @PathVariable("relativeFlag") Integer relativeFlag, @RequestBody String[] departmentIds) {
        boolean b = deviceGroupService.assignAddDepartment(groupId, relativeFlag, departmentIds);
        if(b) {
            return new ResponseResult(CommonCode.SUCCESS);
        }
        return new ResponseResult(CommonCode.FAIL);
    }
    @AutoLog(value = "设备分组表-移除分组分配部门权限")
    @ApiOperation(value = "设备分组表-移除分组分配部门权限", notes = "设备分组表-移除分组分配部门权限")
    @PostMapping("/assign/remove/depart/{groupId}/{relativeFlag}")
    public ResponseResult assignRemoveDepartment(@PathVariable("groupId") String groupId, @PathVariable("relativeFlag") Integer relativeFlag, @RequestBody String[] departmentIds) {
        boolean b = deviceGroupService.assignRemoveDepartment(groupId, relativeFlag, departmentIds);
        if(b) {
            return new ResponseResult(CommonCode.SUCCESS);
        }
        return new ResponseResult(CommonCode.FAIL);
    }
//    @AutoLog(value = "设备分组表-给分组分配部门权限")
//    @ApiOperation(value = "设备分组表-给分组分配部门权限", notes = "设备分组表-给分组分配部门权限")
//    @PostMapping("/assign/add/depart/{groupId}/{relativeFlag}")
//    public ResponseResult assignAddDepartment(@PathVariable("groupId") String groupId, @PathVariable("relativeFlag") Integer relativeFlag, @RequestBody String[] departmentIds) {
//        boolean b = deviceGroupService.assignAddDepartment(groupId, relativeFlag, departmentIds);
//        if(b) {
//            return new ResponseResult(CommonCode.SUCCESS);
//        }
//        return new ResponseResult(CommonCode.FAIL);
//    }
//
//    @AutoLog(value = "设备分组表-移除分组分配部门权限")
//    @ApiOperation(value = "设备分组表-移除分组分配部门权限", notes = "设备分组表-移除分组分配部门权限")
//    @PostMapping("/assign/remove/depart/{groupId}/{relativeFlag}")
//    public ResponseResult assignRemoveDepartment(@PathVariable("groupId") String groupId, @PathVariable("relativeFlag") Integer relativeFlag, @RequestBody String[] departmentIds) {
//        boolean b = deviceGroupService.assignRemoveDepartment(groupId, relativeFlag, departmentIds);
//        if(b) {
//            return new ResponseResult(CommonCode.SUCCESS);
//        }
//        return new ResponseResult(CommonCode.FAIL);
//    }
}
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/controller/UserPermButtonController.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/service/IDepartmentService.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/service/IDeviceGroupService.java
@@ -108,23 +108,23 @@
     */
    List<Department> getDepartNonPermsList(String groupId);
    /**
     * ç»™åˆ†ç»„分配部门权限
     * @param groupId
     * @param relativeFlag
     * @param departmentIds
     * @return
     */
    boolean assignAddDepartment(String groupId, Integer relativeFlag, String[] departmentIds);
    /**
     * ç§»é™¤åˆ†ç»„分配部门权限
     * @param groupId
     * @param relativeFlag
     * @param departmentIds
     * @return
     */
    boolean assignRemoveDepartment(String groupId, Integer relativeFlag, String[] departmentIds);
//    /**
//     * ç»™åˆ†ç»„分配部门权限
//     * @param groupId
//     * @param relativeFlag
//     * @param departmentIds
//     * @return
//     */
//    boolean assignAddDepartment(String groupId, Integer relativeFlag, String[] departmentIds);
//
//    /**
//     * ç§»é™¤åˆ†ç»„分配部门权限
//     * @param groupId
//     * @param relativeFlag
//     * @param departmentIds
//     * @return
//     */
//    boolean assignRemoveDepartment(String groupId, Integer relativeFlag, String[] departmentIds);
    /**
     * ç»™åˆ†ç»„分配部门权限
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/service/IUserPermButtonService.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/service/impl/DepartmentServiceImpl.java
ÎļþÒÑɾ³ý
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/service/impl/DeviceGroupServiceImpl.java
@@ -39,8 +39,6 @@
    @Autowired
    private IDeviceGroupDepartService deviceGroupDepartService;
    @Autowired
    private IDepartmentService departmentService;
    @Autowired
    @Lazy
    private IDeviceInfoService deviceInfoService;
    @Autowired
@@ -375,83 +373,83 @@
        return deviceGroupDepartService.getDepartNonPermsByGroupId(groupId);
    }
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean assignAddDepartment(String groupId, Integer relativeFlag, String[] departmentIds) {
        if(!ValidateUtil.validateString(groupId) || !ValidateUtil.validateInteger(relativeFlag) || departmentIds == null || departmentIds.length < 1)
            ExceptionCast.cast(CommonCode.INVALID_PARAM);
        List<String> ids = new ArrayList<>(departmentIds.length);
        Collections.addAll(ids, departmentIds);
        Collection<Department> departmentList = departmentService.listByIds(ids);
        if(departmentList == null || departmentList.isEmpty() || departmentList.size() != departmentIds.length)
            ExceptionCast.cast(CommonCode.INVALID_PARAM);
        DeviceGroup deviceGroup = super.getById(groupId);
        if(deviceGroup == null)
            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_NOT_EXIST);
        boolean b1 = deviceInfoService.checkDevicePerm(1, deviceGroup.getGroupId());
        if(!b1) {
            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_PERM_ERROR);
        }
        b1 = assignAddDepartment(deviceGroup, departmentList);
        if(!b1) {
            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_PERM_ERROR);
        }
        if(relativeFlag == 1) {
            //获取分组下所有的子分组
            List<DeviceGroup> childrenList = getChildrenByParentId(deviceGroup.getGroupId());
            if(childrenList != null && !childrenList.isEmpty()) {
                childrenList.forEach(item -> {
                    boolean b = deviceInfoService.checkDevicePerm(1, item.getGroupId());
                    if(b) {
                        b = assignAddDepartment(item, departmentList);
                        if(!b) {
                            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_PERM_ERROR);
                        }
                    }
                });
            }
        }
        return true;
    }
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean assignRemoveDepartment(String groupId, Integer relativeFlag, String[] departmentIds) {
        if(!ValidateUtil.validateString(groupId) || !ValidateUtil.validateInteger(relativeFlag) || departmentIds == null || departmentIds.length < 1)
            ExceptionCast.cast(CommonCode.INVALID_PARAM);
        List<String> ids = new ArrayList<>(departmentIds.length);
        Collections.addAll(ids, departmentIds);
        Collection<Department> departmentList = departmentService.listByIds(ids);
        if(departmentList == null || departmentList.isEmpty() || departmentList.size() != departmentIds.length)
            ExceptionCast.cast(CommonCode.INVALID_PARAM);
        DeviceGroup deviceGroup = super.getById(groupId);
        if(deviceGroup == null)
            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_NOT_EXIST);
        boolean b1 = deviceInfoService.checkDevicePerm(1, deviceGroup.getGroupId());
        if(!b1) {
            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_PERM_ERROR);
        }
        b1 = assignRemoveDepartment(deviceGroup, departmentList);
        if(!b1) {
            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_PERM_ERROR);
        }
        if(relativeFlag == 1) {
            //获取分组下所有的子分组
            List<DeviceGroup> childrenList = getChildrenByParentId(deviceGroup.getGroupId());
            if(childrenList != null && !childrenList.isEmpty()) {
                childrenList.forEach(item -> {
                    boolean b = deviceInfoService.checkDevicePerm(1, item.getGroupId());
                    if(b) {
                        b = assignRemoveDepartment(item, departmentList);
                        if(!b) {
                            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_PERM_ERROR);
                        }
                    }
                });
            }
        }
        return true;
    }
//    @Override
//    @Transactional(rollbackFor = {Exception.class})
//    public boolean assignAddDepartment(String groupId, Integer relativeFlag, String[] departmentIds) {
//        if(!ValidateUtil.validateString(groupId) || !ValidateUtil.validateInteger(relativeFlag) || departmentIds == null || departmentIds.length < 1)
//            ExceptionCast.cast(CommonCode.INVALID_PARAM);
//        List<String> ids = new ArrayList<>(departmentIds.length);
//        Collections.addAll(ids, departmentIds);
//        Collection<Department> departmentList = departmentService.listByIds(ids);
//        if(departmentList == null || departmentList.isEmpty() || departmentList.size() != departmentIds.length)
//            ExceptionCast.cast(CommonCode.INVALID_PARAM);
//        DeviceGroup deviceGroup = super.getById(groupId);
//        if(deviceGroup == null)
//            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_NOT_EXIST);
//        boolean b1 = deviceInfoService.checkDevicePerm(1, deviceGroup.getGroupId());
//        if(!b1) {
//            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_PERM_ERROR);
//        }
//        b1 = assignAddDepartment(deviceGroup, departmentList);
//        if(!b1) {
//            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_PERM_ERROR);
//        }
//        if(relativeFlag == 1) {
//            //获取分组下所有的子分组
//            List<DeviceGroup> childrenList = getChildrenByParentId(deviceGroup.getGroupId());
//            if(childrenList != null && !childrenList.isEmpty()) {
//                childrenList.forEach(item -> {
//                    boolean b = deviceInfoService.checkDevicePerm(1, item.getGroupId());
//                    if(b) {
//                        b = assignAddDepartment(item, departmentList);
//                        if(!b) {
//                            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_PERM_ERROR);
//                        }
//                    }
//                });
//            }
//        }
//        return true;
//    }
//
//    @Override
//    @Transactional(rollbackFor = {Exception.class})
//    public boolean assignRemoveDepartment(String groupId, Integer relativeFlag, String[] departmentIds) {
//        if(!ValidateUtil.validateString(groupId) || !ValidateUtil.validateInteger(relativeFlag) || departmentIds == null || departmentIds.length < 1)
//            ExceptionCast.cast(CommonCode.INVALID_PARAM);
//        List<String> ids = new ArrayList<>(departmentIds.length);
//        Collections.addAll(ids, departmentIds);
//        Collection<Department> departmentList = departmentService.listByIds(ids);
//        if(departmentList == null || departmentList.isEmpty() || departmentList.size() != departmentIds.length)
//            ExceptionCast.cast(CommonCode.INVALID_PARAM);
//        DeviceGroup deviceGroup = super.getById(groupId);
//        if(deviceGroup == null)
//            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_NOT_EXIST);
//        boolean b1 = deviceInfoService.checkDevicePerm(1, deviceGroup.getGroupId());
//        if(!b1) {
//            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_PERM_ERROR);
//        }
//        b1 = assignRemoveDepartment(deviceGroup, departmentList);
//        if(!b1) {
//            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_PERM_ERROR);
//        }
//        if(relativeFlag == 1) {
//            //获取分组下所有的子分组
//            List<DeviceGroup> childrenList = getChildrenByParentId(deviceGroup.getGroupId());
//            if(childrenList != null && !childrenList.isEmpty()) {
//                childrenList.forEach(item -> {
//                    boolean b = deviceInfoService.checkDevicePerm(1, item.getGroupId());
//                    if(b) {
//                        b = assignRemoveDepartment(item, departmentList);
//                        if(!b) {
//                            ExceptionCast.cast(DeviceGroupCode.DEVICE_GROUP_PERM_ERROR);
//                        }
//                    }
//                });
//            }
//        }
//        return true;
//    }
    @Override
    @Transactional(rollbackFor = {Exception.class})
lxzn-module-dnc/src/main/java/org/jeecg/modules/dnc/service/impl/UserPermButtonServiceImpl.java
ÎļþÒÑɾ³ý
lxzn-module-flowable/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
<?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>
        <artifactId>lxzn-boot-parent</artifactId>
        <groupId>org.jeecgframework.boot</groupId>
        <version>3.4.3</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>lxzn-module-flowable</artifactId>
    <dependencies>
        <!--保持独立,只引入jeecg基础模块 -->
        <dependency>
            <groupId>org.jeecgframework.boot</groupId>
            <artifactId>lxzn-boot-base-core</artifactId>
        </dependency>
        <!--=======================================flow模块独立===================================-->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-engine</artifactId>
            <scope>compile</scope>
            <version>6.4.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter-basic</artifactId>
            <version>6.4.0</version>
        </dependency>
        <!--表达式解析工具-->
        <dependency>
            <groupId>org.eweb4j</groupId>
            <artifactId>fel</artifactId>
            <version>0.8</version>
        </dependency>
        <!--xml解析包:如果项目中已有,引起冲突,可注释-->
        <dependency>
            <groupId>xerces</groupId>
            <artifactId>xercesImpl</artifactId>
            <version>2.12.0</version>
        </dependency>
    </dependencies>
</project>
lxzn-module-flowable/src/main/java/org/jeecg/JeecgFlowableCloudApplication.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package org.jeecg;//package org.jeecg;
//
//import org.springframework.boot.SpringApplication;
//import org.springframework.boot.autoconfigure.SpringBootApplication;
//import org.springframework.cloud.openfeign.EnableFeignClients;
//
//@SpringBootApplication
//@EnableFeignClients
//public class JeecgFlowableCloudApplication {
//
//    public static void main(String[] args) {
//        SpringApplication.run(JeecgDemoCloudApplication.class, args);
//    }
//}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/business/entity/FlowMyBusiness.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,113 @@
package org.jeecg.modules.flowable.apithird.business.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
/**
 * @Description: æµç¨‹ä¸šåŠ¡æ‰©å±•è¡¨
 * @Author: jeecg-boot
 * @Date:   2021-11-25
 * @Version: V1.0
 */
@Data
@TableName("flow_my_business")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="flow_my_business对象", description="流程业务扩展表")
public class FlowMyBusiness implements Serializable {
    private static final long serialVersionUID = 1L;
    /**主键ID*/
    @TableId(type = IdType.ASSIGN_ID)
    @ApiModelProperty(value = "主键ID")
    private String id;
    /**创建人*/
    @ApiModelProperty(value = "创建人")
    private String createBy;
    /**创建时间*/
    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern="yyyy-MM-dd")
    @ApiModelProperty(value = "创建时间")
    private Date createTime;
    /**修改人*/
    @ApiModelProperty(value = "修改人")
    private String updateBy;
    /**修改时间*/
    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern="yyyy-MM-dd")
    @ApiModelProperty(value = "修改时间")
    private Date updateTime;
    /**流程定义key ä¸€ä¸ªkey会有多个版本的id*/
    @Excel(name = "流程定义key ä¸€ä¸ªkey会有多个版本的id", width = 15)
    @ApiModelProperty(value = "流程定义key ä¸€ä¸ªkey会有多个版本的id")
    private String processDefinitionKey;
    /**流程定义id ä¸€ä¸ªæµç¨‹å®šä¹‰å”¯ä¸€*/
    @Excel(name = "流程定义id ä¸€ä¸ªæµç¨‹å®šä¹‰å”¯ä¸€", width = 15)
    @ApiModelProperty(value = "流程定义id ä¸€ä¸ªæµç¨‹å®šä¹‰å”¯ä¸€")
    private String processDefinitionId;
    /**流程业务实例id ä¸€ä¸ªæµç¨‹ä¸šåŠ¡å”¯ä¸€ï¼Œæœ¬è¡¨ä¸­ä¹Ÿå”¯ä¸€*/
    @Excel(name = "流程业务实例id ä¸€ä¸ªæµç¨‹ä¸šåŠ¡å”¯ä¸€ï¼Œæœ¬è¡¨ä¸­ä¹Ÿå”¯ä¸€", width = 15)
    @ApiModelProperty(value = "流程业务实例id ä¸€ä¸ªæµç¨‹ä¸šåŠ¡å”¯ä¸€ï¼Œæœ¬è¡¨ä¸­ä¹Ÿå”¯ä¸€")
    private String processInstanceId;
    /**流程业务简要描述*/
    @Excel(name = "流程业务简要描述", width = 15)
    @ApiModelProperty(value = "流程业务简要描述")
    private String title;
    /**业务表id,理论唯一*/
    @Excel(name = "业务表id,理论唯一", width = 15)
    @ApiModelProperty(value = "业务表id,理论唯一")
    private String dataId;
    /**业务类名,用来获取spring容器里的服务对象*/
    @Excel(name = "业务类名,用来获取spring容器里的服务对象", width = 15)
    @ApiModelProperty(value = "业务类名,用来获取spring容器里的服务对象")
    private String serviceImplName;
    /**申请人*/
    @Excel(name = "申请人", width = 15)
    @ApiModelProperty(value = "申请人")
    private String proposer;
    /**流程状态说明,有:启动  æ’¤å›ž  é©³å›ž  å®¡æ‰¹ä¸­  å®¡æ‰¹é€šè¿‡  å®¡æ‰¹å¼‚常*/
    @Excel(name = "流程状态说明,有:启动  æ’¤å›ž  é©³å›ž  å®¡æ‰¹ä¸­  å®¡æ‰¹é€šè¿‡  å®¡æ‰¹å¼‚常", width = 15)
    @ApiModelProperty(value = "流程状态说明,有:启动  æ’¤å›ž  é©³å›ž  å®¡æ‰¹ä¸­  å®¡æ‰¹é€šè¿‡  å®¡æ‰¹å¼‚常")
    private String actStatus;
    /**当前的节点实例上的Id*/
    @Excel(name = "当前的节点Id", width = 15)
    @ApiModelProperty(value = "当前的节点Id")
    private String taskId;
    /**当前的节点*/
    @Excel(name = "当前的节点", width = 15)
    @ApiModelProperty(value = "当前的节点")
    private String taskName;
    /**当前的节点定义上的Id*/
    @Excel(name = "当前的节点", width = 15)
    @ApiModelProperty(value = "当前的节点")
    private String taskNameId;
    /**当前的节点可以处理的用户名,为username的集合json字符串*/
    @Excel(name = "当前的节点可以处理的用户名", width = 15)
    @ApiModelProperty(value = "当前的节点可以处理的用户名")
    private String todoUsers;
    /**处理过的人,为username的集合json字符串*/
    @Excel(name = "处理过的人", width = 15)
    @ApiModelProperty(value = "处理过的人")
    private String doneUsers;
    /**当前任务节点的优先级 æµç¨‹å®šä¹‰çš„æ—¶å€™æ‰€å¡«*/
    @Excel(name = "当前任务节点的优先级 æµç¨‹å®šä¹‰çš„æ—¶å€™æ‰€å¡«", width = 15)
    @ApiModelProperty(value = "当前任务节点的优先级 æµç¨‹å®šä¹‰çš„æ—¶å€™æ‰€å¡«")
    private String priority;
    /**流程变量*/
    @TableField(exist = false)
    private Map<String,Object> values;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/business/entity/FlowMyBusinessDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,100 @@
package org.jeecg.modules.flowable.apithird.business.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecgframework.poi.excel.annotation.Excel;
import java.io.Serializable;
import java.util.Map;
/**
 * @Description: æµç¨‹ä¸šåŠ¡æ‰©å±•è¡¨
 * @Author: jeecg-boot
 * @Date:   2021-11-25
 * @Version: V1.0
 */
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="flow_my_business对象", description="流程业务扩展表")
public class FlowMyBusinessDto implements Serializable {
    private static final long serialVersionUID = 1L;
    /**流程定义key ä¸€ä¸ªkey会有多个版本的id*/
    @TableField(exist = false)
    @Excel(name = "流程定义key ä¸€ä¸ªkey会有多个版本的id", width = 15)
    @ApiModelProperty(value = "流程定义key ä¸€ä¸ªkey会有多个版本的id")
    private String processDefinitionKey;
    /**流程定义id ä¸€ä¸ªæµç¨‹å®šä¹‰å”¯ä¸€*/
    @TableField(exist = false)
    @Excel(name = "流程定义id ä¸€ä¸ªæµç¨‹å®šä¹‰å”¯ä¸€", width = 15)
    @ApiModelProperty(value = "流程定义id ä¸€ä¸ªæµç¨‹å®šä¹‰å”¯ä¸€")
    private String processDefinitionId;
    /**流程业务实例id ä¸€ä¸ªæµç¨‹ä¸šåŠ¡å”¯ä¸€ï¼Œæœ¬è¡¨ä¸­ä¹Ÿå”¯ä¸€*/
    @TableField(exist = false)
    @Excel(name = "流程业务实例id ä¸€ä¸ªæµç¨‹ä¸šåŠ¡å”¯ä¸€ï¼Œæœ¬è¡¨ä¸­ä¹Ÿå”¯ä¸€", width = 15)
    @ApiModelProperty(value = "流程业务实例id ä¸€ä¸ªæµç¨‹ä¸šåŠ¡å”¯ä¸€ï¼Œæœ¬è¡¨ä¸­ä¹Ÿå”¯ä¸€")
    private String processInstanceId;
    /**流程业务简要描述*/
    @TableField(exist = false)
    @Excel(name = "流程业务简要描述", width = 15)
    @ApiModelProperty(value = "流程业务简要描述")
    private String title;
    /**业务表id,理论唯一*/
    @TableField(exist = false)
    @Excel(name = "业务表id,理论唯一", width = 15)
    @ApiModelProperty(value = "业务表id,理论唯一")
    private String dataId;
    /**业务类名,用来获取spring容器里的服务对象*/
    @TableField(exist = false)
    @Excel(name = "业务类名,用来获取spring容器里的服务对象", width = 15)
    @ApiModelProperty(value = "业务类名,用来获取spring容器里的服务对象")
    private String serviceImplName;
    /**申请人*/
    @TableField(exist = false)
    @Excel(name = "申请人", width = 15)
    @ApiModelProperty(value = "申请人")
    private String proposer;
    /**流程状态说明,有:启动  æ’¤å›ž  é©³å›ž  å®¡æ‰¹ä¸­  å®¡æ‰¹é€šè¿‡  å®¡æ‰¹å¼‚常*/
    @TableField(exist = false)
    @Excel(name = "流程状态说明,有:启动  æ’¤å›ž  é©³å›ž  å®¡æ‰¹ä¸­  å®¡æ‰¹é€šè¿‡  å®¡æ‰¹å¼‚常", width = 15)
    @ApiModelProperty(value = "流程状态说明,有:启动  æ’¤å›ž  é©³å›ž  å®¡æ‰¹ä¸­  å®¡æ‰¹é€šè¿‡  å®¡æ‰¹å¼‚常")
    private String actStatus;
    /**当前的节点实例上的Id*/
    @TableField(exist = false)
    @Excel(name = "当前的节点Id", width = 15)
    @ApiModelProperty(value = "当前的节点Id")
    private String taskId;
    /**当前的节点*/
    @TableField(exist = false)
    @Excel(name = "当前的节点", width = 15)
    @ApiModelProperty(value = "当前的节点")
    private String taskName;
    /**当前的节点定义上的Id*/
    @TableField(exist = false)
    @Excel(name = "当前的节点", width = 15)
    @ApiModelProperty(value = "当前的节点")
    private String taskNameId;
    /**当前的节点可以处理的用户名,为username的集合json字符串*/
    @TableField(exist = false)
    @Excel(name = "当前的节点可以处理的用户名", width = 15)
    @ApiModelProperty(value = "当前的节点可以处理的用户名")
    private String todoUsers;
    /**处理过的人,为username的集合json字符串*/
    @TableField(exist = false)
    @Excel(name = "处理过的人", width = 15)
    @ApiModelProperty(value = "处理过的人")
    private String doneUsers;
    /**当前任务节点的优先级 æµç¨‹å®šä¹‰çš„æ—¶å€™æ‰€å¡«*/
    @TableField(exist = false)
    @Excel(name = "当前任务节点的优先级 æµç¨‹å®šä¹‰çš„æ—¶å€™æ‰€å¡«", width = 15)
    @ApiModelProperty(value = "当前任务节点的优先级 æµç¨‹å®šä¹‰çš„æ—¶å€™æ‰€å¡«")
    private String priority;
    /**流程变量*/
    @TableField(exist = false)
    private Map<String,Object> values;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/business/mapper/FlowMyBusinessMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package org.jeecg.modules.flowable.apithird.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.flowable.apithird.business.entity.FlowMyBusiness;
/**
 * @Description: æµç¨‹ä¸šåŠ¡æ‰©å±•è¡¨
 * @Author: jeecg-boot
 * @Date:   2021-11-25
 * @Version: V1.0
 */
public interface FlowMyBusinessMapper extends BaseMapper<FlowMyBusiness> {
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/business/mapper/xml/FlowMyBusinessMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
<?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.jeecg.modules.flowable.apithird.business.mapper.FlowMyBusinessMapper">
</mapper>
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/business/service/IFlowMyBusinessService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package org.jeecg.modules.flowable.apithird.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.flowable.apithird.business.entity.FlowMyBusiness;
/**
 * @Description: æµç¨‹ä¸šåŠ¡æ‰©å±•è¡¨
 * @Author: jeecg-boot
 * @Date:   2021-11-25
 * @Version: V1.0
 */
public interface IFlowMyBusinessService extends IService<FlowMyBusiness> {
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/business/service/impl/FlowMyBusinessServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package org.jeecg.modules.flowable.apithird.business.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.flowable.apithird.business.entity.FlowMyBusiness;
import org.jeecg.modules.flowable.apithird.business.mapper.FlowMyBusinessMapper;
import org.jeecg.modules.flowable.apithird.business.service.IFlowMyBusinessService;
import org.springframework.stereotype.Service;
/**
 * @Description: æµç¨‹ä¸šåŠ¡æ‰©å±•è¡¨
 * @Author: jeecg-boot
 * @Date:   2021-11-25
 * @Version: V1.0
 */
@Service
public class FlowMyBusinessServiceImpl extends ServiceImpl<FlowMyBusinessMapper, FlowMyBusiness> implements IFlowMyBusinessService {
    public FlowMyBusiness getByDataId(String dataId) {
        LambdaQueryWrapper<FlowMyBusiness> flowMyBusinessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        flowMyBusinessLambdaQueryWrapper.eq(FlowMyBusiness::getDataId,dataId)
        ;
        //如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
        FlowMyBusiness business = this.getOne(flowMyBusinessLambdaQueryWrapper);
        return business;
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/common/constant/ProcessConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,77 @@
package org.jeecg.modules.flowable.apithird.common.constant;
/**
 * æµç¨‹å¸¸é‡ä¿¡æ¯
 *
 */
public class ProcessConstants {
    /**
     * çº¦å®šçš„开始节点标记名
     */
    public static final String START_NODE = "start";
    /**
     * åŠ¨æ€æ•°æ®
     */
    public static final String DATA_TYPE = "dynamic";
    /**
     * å•个审批人
     */
    public static final String USER_TYPE_ASSIGNEE = "assignee";
    /**
     * å€™é€‰äºº
     */
    public static final String USER_TYPE_USERS = "candidateUsers";
    /**
     * å®¡æ‰¹ç»„
     */
    public static final String USER_TYPE_ROUPS = "candidateGroups";
    /**
     * å•个审批人
     */
    public static final String PROCESS_APPROVAL = "approval";
    /**
     * ä¼šç­¾äººå‘˜
     */
    public static final String PROCESS_MULTI_INSTANCE_USER = "userList";
    /**
     * nameapace
     */
    public static final String NAMASPASE = "http://flowable.org/bpmn";
    /**
     * ä¼šç­¾èŠ‚ç‚¹
     */
    public static final String PROCESS_MULTI_INSTANCE = "multiInstance";
    /**
     * è‡ªå®šä¹‰å±žæ€§ dataType
     */
    public static final String PROCESS_CUSTOM_DATA_TYPE = "dataType";
    /**
     * è‡ªå®šä¹‰å±žæ€§ userType
     */
    public static final String PROCESS_CUSTOM_USER_TYPE = "userType";
    /**
     * åˆå§‹åŒ–人员
     */
    public static final String PROCESS_INITIATOR = "INITIATOR";
    /**
     * æµç¨‹è·³è¿‡
     */
    public static final String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED";
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/common/enums/FlowComment.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package org.jeecg.modules.flowable.apithird.common.enums;
/**
 * æµç¨‹æ„è§ç±»åž‹
 *
 */
public enum FlowComment {
    /**
     * è¯´æ˜Ž
     */
    NORMAL("1", "正常意见"),
    REBACK("2", "退回意见"),
    REJECT("3", "驳回意见"),
    DELEGATE("4", "委派意见"),
    ASSIGN("5", "转办意见"),
    STOP("6", "终止流程");
    /**
     * ç±»åž‹
     */
    private final String type;
    /**
     * è¯´æ˜Ž
     */
    private final String remark;
    FlowComment(String type, String remark) {
        this.type = type;
        this.remark = remark;
    }
    public String getType() {
        return type;
    }
    public String getRemark() {
        return remark;
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/common/exception/CustomException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package org.jeecg.modules.flowable.apithird.common.exception;
/**
 * è‡ªå®šä¹‰å¼‚常
 *
 */
public class CustomException extends RuntimeException
{
    private static final long serialVersionUID = 1L;
    private Integer code;
    private String message;
    public CustomException(String message)
    {
        this.message = message;
    }
    public CustomException(String message, Integer code)
    {
        this.message = message;
        this.code = code;
    }
    public CustomException(String message, Throwable e)
    {
        super(message, e);
        this.message = message;
    }
    @Override
    public String getMessage()
    {
        return message;
    }
    public Integer getCode()
    {
        return code;
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/entity/ActStatus.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package org.jeecg.modules.flowable.apithird.entity;
/**
 * @author PanMeiCheng
 * @version 1.0
 * @date 2021/11/26
 */
public interface ActStatus {
    //启动 æ’¤å›ž é©³å›ž å®¡æ‰¹ä¸­ å®¡æ‰¹é€šè¿‡ å®¡æ‰¹å¼‚常
    //本流程不应有启动状态,启动即进入审批,第一个节点就是发起人节点,未方便业务区分,设定为“启动”状态
    String start = "启动";
    String recall = "撤回";
    String reject = "驳回";
    String doing = "审批中";
    String pass = "审批通过";
    String err = "审批异常";
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/entity/FlowBeforeParams.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package org.jeecg.modules.flowable.apithird.entity;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
 *流程运行之前从业务层获取的相关参数
 *@author PanMeiCheng
 *@date 2021/12/3
 *@version 1.0
 */
@Data
public class FlowBeforeParams {
    /**指定下个节点的候选人,用户名username*/
    List<String> candidateUsernames;
    /**流程变量*/
    Map<String,Object> values;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/entity/SysCategory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package org.jeecg.modules.flowable.apithird.entity;
import lombok.Data;
/**
 *流程分类
 *@author PanMeiCheng
 *@date 2021/11/25
 *@version 1.0
 */
@Data
public class SysCategory {
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    private String id;
    /**
     * åˆ†ç±»åç§°
     */
    private String name;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/entity/SysRole.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package org.jeecg.modules.flowable.apithird.entity;
import lombok.Data;
/**
 * è§’色
 * @author pmc
 */
@Data
public class SysRole {
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    private String id;
    /**
     * è§’色名称
     */
    private String roleName;
    /**
     * è§’色编码
     */
    private String roleCode;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/entity/SysUser.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,138 @@
package org.jeecg.modules.flowable.apithird.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
 * ç”¨æˆ·å¯¹è±¡ <br/>
 *  //todo æš‚时支持用于jeecg,如需迁移其他框架,需要改动
 * @author pmc
 */
@Data
public class SysUser {
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    private String id;
    /**
     * ç™»å½•账号
     */
    private String username;
    /**
     * çœŸå®žå§“名
     */
    private String realname;
    /**
     * å¯†ç 
     */
    private String password;
    /**
     * md5密码盐
     */
    private String salt;
    /**
     * å¤´åƒ
     */
    private String avatar;
    /**
     * ç”Ÿæ—¥
     */
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
    /**
     * æ€§åˆ«ï¼ˆ1:男 2:女)
     */
    private Integer sex;
    /**
     * ç”µå­é‚®ä»¶
     */
    private String email;
    /**
     * ç”µè¯
     */
    private String phone;
    /**
     * éƒ¨é—¨code(当前选择登录部门)
     */
    private String orgCode;
    /**部门名称*/
    private transient String orgCodeTxt;
    /**
     * çŠ¶æ€(1:正常  2:冻结 ï¼‰
     */
    private Integer status;
    /**
     * åˆ é™¤çŠ¶æ€ï¼ˆ0,正常,1已删除)
     */
    private Integer delFlag;
    /**
     * å·¥å·ï¼Œå”¯ä¸€é”®
     */
    private String workNo;
    /**
     * èŒåŠ¡ï¼Œå…³è”èŒåŠ¡è¡¨
     */
    private String post;
    /**
     * åº§æœºå·
     */
    private String telephone;
    /**
     * åˆ›å»ºäºº
     */
    private String createBy;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    private Date createTime;
    /**
     * æ›´æ–°äºº
     */
    private String updateBy;
    /**
     * æ›´æ–°æ—¶é—´
     */
    private Date updateTime;
    /**
     * åŒæ­¥å·¥ä½œæµå¼•擎1同步0不同步
     */
    private Integer activitiSync;
    /**
     * èº«ä»½ï¼ˆ0 æ™®é€šæˆå‘˜ 1 ä¸Šçº§ï¼‰
     */
    private Integer userIdentity;
    /**
     * è´Ÿè´£éƒ¨é—¨
     */
    private String departIds;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/service/FlowCallBackServiceI.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package org.jeecg.modules.flowable.apithird.service;
import org.jeecg.modules.flowable.apithird.business.entity.FlowMyBusiness;
import java.util.List;
import java.util.Map;
/**
 * ä¸šåŠ¡å±‚å®žçŽ°æŽ¥å£æ–¹æ³•ï¼Œç”¨äºŽæµç¨‹å¤„ç†åŽçš„å›žè°ƒ
 * @author PanMeiCheng
 * @version 1.0
 * @date 2021/11/26
 */
public interface FlowCallBackServiceI {
    /**
     * æµç¨‹å¤„理完成后的回调
     * @param business é‡Œé¢åŒ…含流程运行的现状信息,业务层可根据其信息判断,书写增强业务逻辑的代码,<br/>
     *                 1、比如将其中关键信息存入业务表,即可单表业务操作,否则需要关联flow_my_business表获取流程信息<br/>
     *                 2、比如在流程进行到某个节点时(business.taskId),需要特别进行某些业务逻辑操作等等<br/>
     */
    void afterFlowHandle(FlowMyBusiness business);
    /**
     * æ ¹æ®ä¸šåŠ¡id返回业务表单数据<br/>
     * @param dataId
     * @return
     */
    Object getBusinessDataById(String dataId);
    /**
     * è¿”回当前节点的流程变量
     * @param taskNameId èŠ‚ç‚¹å®šä¹‰id
     * @param values å‰ç«¯ä¼ å…¥çš„变量,里面包含dataId
     * @return
     */
    Map<String, Object> flowValuesOfTask(String taskNameId, Map<String, Object> values);
    /**
     * è¿”回当前节点的候选人username
     * @param taskNameId èŠ‚ç‚¹å®šä¹‰id
     * @param values å‰ç«¯ä¼ å…¥çš„变量,里面包含dataId
     * @return
     */
    List<String> flowCandidateUsernamesOfTask(String taskNameId, Map<String, Object> values);
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/service/FlowCommonService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,86 @@
package org.jeecg.modules.flowable.apithird.service;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.jeecg.modules.flowable.apithird.business.entity.FlowMyBusiness;
import org.jeecg.modules.flowable.apithird.business.service.impl.FlowMyBusinessServiceImpl;
import org.jeecg.modules.flowable.apithird.common.exception.CustomException;
import org.jeecg.modules.flowable.service.impl.FlowInstanceServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 *业务模块调用API的集合
 *@author PanMeiCheng
 *@date 2021/11/22
 *@version 1.0
 */
@Service
public class FlowCommonService {
    @Autowired
    FlowMyBusinessServiceImpl flowMyBusinessService;
    @Autowired
    FlowInstanceServiceImpl flowInstanceService;
    /**
     * åˆå§‹ç”Ÿæˆæˆ–修改业务与流程的关联信息<br/>
     * å½“业务模块新增一条数据后调用,此时业务数据关联一个流程定义,以备后续流程使用
     * @return æ˜¯å¦æˆåŠŸ
     * @param title å¿…填。流程业务简要描述。例:2021å¹´11月26日xxxxx申请
     * @param dataId å¿…填。业务数据Id,如果是一对多业务关系,传入主表的数据Id
     * @param serviceImplName å¿…填。业务service注入spring容器的名称。
*                        ä¾‹å¦‚:@Service("demoService")则传入 demoService
     * @param processDefinitionKey å¿…填。流程定义Key,传入此值,未来启动的会是该类流程的最新一个版本
     * @param processDefinitionId é€‰å¡«ã€‚流程定义Id,传入此值,未来启动的为指定版本的流程
     */
    public boolean initActBusiness(String title,String dataId, String serviceImplName, String processDefinitionKey, String processDefinitionId){
        boolean hasBlank = StrUtil.hasBlank(title,dataId, serviceImplName, processDefinitionKey);
        if (hasBlank) throw new CustomException("流程关键参数未填完全!dataId, serviceImplName, processDefinitionKey");
        LambdaQueryWrapper<FlowMyBusiness> flowMyBusinessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        flowMyBusinessLambdaQueryWrapper.eq(FlowMyBusiness::getDataId, dataId)
        ;
        FlowMyBusiness flowMyBusiness = new FlowMyBusiness();
        FlowMyBusiness business = flowMyBusinessService.getOne(flowMyBusinessLambdaQueryWrapper);
        if (business!=null){
            flowMyBusiness = business;
        } else {
            flowMyBusiness.setId(IdUtil.fastSimpleUUID());
        }
        if (processDefinitionId==null){
            // ä»¥ä¾¿æ›´æ–°æµç¨‹
            processDefinitionId = "";
        }
        flowMyBusiness.setTitle(title)
                .setDataId(dataId)
                .setServiceImplName(serviceImplName)
                .setProcessDefinitionKey(processDefinitionKey)
                .setProcessDefinitionId(processDefinitionId)
                ;
        if (business!=null){
            return flowMyBusinessService.updateById(flowMyBusiness);
        } else {
            return flowMyBusinessService.save(flowMyBusiness);
        }
    }
    /**
     * åˆ é™¤æµç¨‹
     * @param dataId
     * @return
     */
    public boolean delActBusiness(String dataId){
        boolean hasBlank = StrUtil.hasBlank(dataId);
        if (hasBlank) throw new CustomException("流程关键参数未填完全!dataId");
        LambdaQueryWrapper<FlowMyBusiness> flowMyBusinessQueryWrapper = new LambdaQueryWrapper<>();
        flowMyBusinessQueryWrapper.eq(FlowMyBusiness::getDataId,dataId);
        FlowMyBusiness one = flowMyBusinessService.getOne(flowMyBusinessQueryWrapper);
        if (one.getProcessInstanceId()!=null){
            try {
                flowInstanceService.delete(one.getProcessInstanceId(),"删除流程");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return flowMyBusinessService.remove(flowMyBusinessQueryWrapper);
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/apithird/service/IFlowThirdService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package org.jeecg.modules.flowable.apithird.service;
import org.jeecg.modules.flowable.apithird.entity.SysCategory;
import org.jeecg.modules.flowable.apithird.entity.SysRole;
import org.jeecg.modules.flowable.apithird.entity.SysUser;
import java.util.List;
/**
 * ä¸šåŠ¡å±‚éœ€å®žçŽ°çš„æŽ¥å£å®šä¹‰<br/>
 *  æ”¯æ’‘工作流模块与业务的关联
 * @author pmc
 */
public interface IFlowThirdService {
    /**
     * èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·
     *
     * @return å½“前登录用户信息
     */
    public SysUser getLoginUser();
    /**
     * æ‰€æœ‰ç”¨æˆ·
     * @return
     */
    List<SysUser> getAllUser();
    /**
     * é€šè¿‡è§’色id获取用户
     * @return
     */
    List<SysUser> getUsersByRoleId(String roleId);
    /**
     * æ ¹æ®ç”¨æˆ·username查询用户信息
     * @param username
     * @return
     */
    SysUser getUserByUsername(String username);
    /**
     * èŽ·å–æ‰€æœ‰è§’è‰²
     * @return
     */
    public List<SysRole> getAllRole();
    /**
     * èŽ·å–æ‰€æœ‰æµç¨‹åˆ†ç±»
     * @return
     */
    List<SysCategory> getAllCategory();
    /**
     * é€šè¿‡ç”¨æˆ·è´¦å·æŸ¥è¯¢éƒ¨é—¨ name
     * @param username
     * @return éƒ¨é—¨ name
     */
    List<String> getDepartNamesByUsername(String username);
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/config/MyDefaultProcessDiagramCanvas.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
package org.jeecg.modules.flowable.config;
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 MyDefaultProcessDiagramCanvas extends DefaultProcessDiagramCanvas {
    //设置高亮线的颜色  è¿™é‡Œæˆ‘设置成绿色
    protected static Color HIGHLIGHT_SEQUENCEFLOW_COLOR = Color.GREEN;
    public MyDefaultProcessDiagramCanvas(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 MyDefaultProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType) {
        super(width, height, minX, minY, imageType);
    }
    /**
     * ç”»çº¿é¢œè‰²è®¾ç½®
     */
    @Override
    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(originalPaint);
            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);
    }
    /**
     * é«˜äº®èŠ‚ç‚¹è®¾ç½®
     */
    @Override
    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);
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/controller/FlowDefinitionController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,243 @@
package org.jeecg.modules.flowable.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.flowable.apithird.entity.SysCategory;
import org.jeecg.modules.flowable.apithird.entity.SysRole;
import org.jeecg.modules.flowable.apithird.entity.SysUser;
import org.jeecg.modules.flowable.apithird.service.IFlowThirdService;
import org.jeecg.modules.flowable.domain.dto.FlowProcDefDto;
import org.jeecg.modules.flowable.domain.dto.FlowSaveXmlVo;
import org.jeecg.modules.flowable.service.IFlowDefinitionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
/**
 * <p>
 * å·¥ä½œæµç¨‹å®šä¹‰
 * </p>
 *
 */
@Slf4j
@Api(tags = "流程定义")
@RestController
@RequestMapping("/flowable/definition")
public class FlowDefinitionController {
    @Autowired
    private IFlowDefinitionService flowDefinitionService;
    @Autowired
    private IFlowThirdService iFlowThirdService;
    @GetMapping(value = "/list")
    @ApiOperation(value = "流程定义列表", response = FlowProcDefDto.class)
    public Result list(@ApiParam(value = "当前页码", required = true) @RequestParam Integer pageNum,
                           @ApiParam(value = "每页条数", required = true) @RequestParam Integer pageSize,
                       FlowProcDefDto flowProcDefDto
    ) {
        return Result.OK(flowDefinitionService.list(pageNum, pageSize,flowProcDefDto));
    }
    @ApiOperation(value = "导入流程文件", notes = "上传bpmn20的xml文件")
    @PostMapping("/import")
    public Result importFile(@RequestParam(required = false) String name,
                                 @RequestParam(required = false) String category,
                                 MultipartFile file) {
        InputStream in = null;
        try {
            in = file.getInputStream();
            flowDefinitionService.importFile(name, category, in);
        } catch (Exception e) {
            log.error("导入失败:", e);
            return Result.OK(e.getMessage());
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                log.error("关闭输入流出错", e);
            }
        }
        return Result.OK("导入成功");
    }
    @ApiOperation(value = "读取xml文件")
    @GetMapping("/readXml/{deployId}")
    public Result readXml(@ApiParam(value = "流程定义id") @PathVariable(value = "deployId") String deployId) {
        try {
            return flowDefinitionService.readXml(deployId);
        } catch (Exception e) {
            return Result.error("加载xml文件异常");
        }
    }
    @ApiOperation(value = "读取xml文件")
    @GetMapping("/readXmlByDataId/{dataId}")
    public Result readXmlByDataId(@ApiParam(value = "流程定义id") @PathVariable(value = "dataId") String dataId) {
        try {
            return flowDefinitionService.readXmlByDataId(dataId);
        } catch (Exception e) {
            return Result.error("加载xml文件异常");
        }
    }
    @ApiOperation(value = "读取图片文件")
    @GetMapping("/readImage/{deployId}")
    public void readImage(@ApiParam(value = "流程定义id") @PathVariable(value = "deployId") String deployId, HttpServletResponse response) {
        OutputStream os = null;
        BufferedImage image = null;
        try {
            image = ImageIO.read(flowDefinitionService.readImage(deployId));
            response.setContentType("image/png");
            os = response.getOutputStream();
            if (image != null) {
                ImageIO.write(image, "png", os);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null) {
                    os.flush();
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    @ApiOperation(value = "读取图片文件")
    @GetMapping("/readImageByDataId/{dataId}")
    public void readImageByDataId(@ApiParam(value = "流程数据业务id") @PathVariable(value = "dataId") String dataId, HttpServletResponse response) {
        OutputStream os = null;
        BufferedImage image = null;
        try {
            image = ImageIO.read(flowDefinitionService.readImageByDataId(dataId));
            response.setContentType("image/png");
            os = response.getOutputStream();
            if (image != null) {
                ImageIO.write(image, "png", os);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null) {
                    os.flush();
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    @ApiOperation(value = "保存流程设计器内的xml文件")
    @PostMapping("/save")
    public Result save(@RequestBody FlowSaveXmlVo vo) {
        InputStream in = null;
        try {
            in = new ByteArrayInputStream(vo.getXml().getBytes(StandardCharsets.UTF_8));
            flowDefinitionService.importFile(vo.getName(), vo.getCategory(), in);
        } catch (Exception e) {
            log.error("导入失败:", e);
            return Result.OK(e.getMessage());
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                log.error("关闭输入流出错", e);
            }
        }
        return Result.OK("导入成功");
    }
    @ApiOperation(value = "根据流程定义id启动流程实例")
    @PostMapping("/startByProcDefId/{procDefId}")
    public Result startByProcDefId(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId,
                        @ApiParam(value = "变量集合,json对象") @RequestBody Map<String, Object> variables) {
        return flowDefinitionService.startProcessInstanceById(procDefId, variables);
    }
    @ApiOperation(value = "根据流程定义key启动流程实例")
    @PostMapping("/startByProcDefKey/{procDefKey}")
    public Result startByProcDefKey(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefKey") String procDefKey,
                        @ApiParam(value = "变量集合,json对象") @RequestBody Map<String, Object> variables) {
        return flowDefinitionService.startProcessInstanceByKey(procDefKey, variables);
    }
    @ApiOperation(value = "根据数据Id启动流程实例")
    @PostMapping("/startByDataId/{dataId}")
    public Result startByDataId(@ApiParam(value = "流程定义id") @PathVariable(value = "dataId") String dataId,
                        @ApiParam(value = "变量集合,json对象") @RequestBody Map<String, Object> variables) {
        variables.put("dataId",dataId);
        return flowDefinitionService.startProcessInstanceByDataId(dataId, variables);
    }
    @ApiOperation(value = "激活或挂起流程定义")
    @PutMapping(value = "/updateState")
    public Result updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state,
                                  @ApiParam(value = "流程部署ID", required = true) @RequestParam String deployId) {
        flowDefinitionService.updateState(state, deployId);
        return Result.OK("操作成功");
    }
    @ApiOperation(value = "删除流程")
    @DeleteMapping(value = "/delete")
    public Result delete(@ApiParam(value = "流程部署ID", required = true) @RequestParam String deployId) {
        flowDefinitionService.delete(deployId);
        return Result.OK();
    }
    @ApiOperation(value = "指定流程办理人员列表")
    @GetMapping("/userList")
    public Result userList(SysUser user) {
        List<SysUser> list = iFlowThirdService.getAllUser();
        return Result.OK(list);
    }
    @ApiOperation(value = "指定流程办理组列表")
    @GetMapping("/roleList")
    public Result roleList(SysRole role) {
        List<SysRole> list = iFlowThirdService.getAllRole();
        return Result.OK(list);
    }
    @ApiOperation(value = "指定流程办理组列表")
    @GetMapping("/categoryList")
    public Result categoryList(SysCategory category) {
        List<SysCategory> list = iFlowThirdService.getAllCategory();
        return Result.OK(list);
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/controller/FlowInstanceController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package org.jeecg.modules.flowable.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.flowable.service.IFlowInstanceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
 * <p>工作流流程实例管理<p>
 */
@Slf4j
@Api(tags = "工作流流程实例管理")
@RestController
@RequestMapping("/flowable/instance")
public class FlowInstanceController {
    @Autowired
    private IFlowInstanceService flowInstanceService;
    /*@ApiOperation(value = "根据流程定义id启动流程实例")
    @PostMapping("/startBy/{procDefId}")
    public Result startById(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId,
                                @ApiParam(value = "变量集合,json对象") @RequestBody Map<String, Object> variables) {
        return flowInstanceService.startProcessInstanceById(procDefId, variables);
    }*/
    @ApiOperation(value = "激活或挂起流程实例")
    @PostMapping(value = "/updateState")
    public Result updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state,
                              @ApiParam(value = "流程实例ID", required = true) @RequestParam String instanceId) {
        flowInstanceService.updateState(state,instanceId);
        return Result.OK();
    }
    /*@ApiOperation("结束流程实例")
    @PostMapping(value = "/stopProcessInstance")
    public Result stopProcessInstance(@RequestBody FlowTaskVo flowTaskVo) {
        flowInstanceService.stopProcessInstance(flowTaskVo);
        return Result.OK();
    }*/
    @ApiOperation(value = "删除流程实例")
    @DeleteMapping(value = "/delete")
    public Result delete(@ApiParam(value = "流程实例ID", required = true) @RequestParam String instanceId,
                             @ApiParam(value = "删除原因") @RequestParam(required = false) String deleteReason) {
        flowInstanceService.delete(instanceId,deleteReason);
        return Result.OK();
    }
    @ApiOperation(value = "删除流程实例")
    @PostMapping(value = "/deleteByDataId")
    public Result deleteByDataId(@ApiParam(value = "流程实例关联业务ID", required = true) @RequestParam String dataId,
                             @ApiParam(value = "删除原因") @RequestParam(required = false) String deleteReason) {
        flowInstanceService.deleteByDataId(dataId,deleteReason);
        return Result.OK();
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/controller/FlowTaskController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,219 @@
package org.jeecg.modules.flowable.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.flowable.domain.dto.FlowTaskDto;
import org.jeecg.modules.flowable.domain.vo.FlowTaskVo;
import org.jeecg.modules.flowable.service.IFlowTaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
 * <p>工作流任务管理<p>
 *
 */
@Slf4j
@Api(tags = "工作流流程任务管理")
@RestController
@RequestMapping("/flowable/task")
public class FlowTaskController {
    @Autowired
    private IFlowTaskService flowTaskService;
    @ApiOperation(value = "我发起的流程", response = FlowTaskDto.class)
    @GetMapping(value = "/myProcess")
    public Result myProcess(@ApiParam(value = "当前页码", required = true) @RequestParam Integer pageNum,
                                @ApiParam(value = "每页条数", required = true) @RequestParam Integer pageSize) {
        return flowTaskService.myProcess(pageNum, pageSize);
    }
    @ApiOperation(value = "取消申请", response = FlowTaskDto.class)
    @PostMapping(value = "/stopProcess")
    public Result stopProcess(@RequestBody FlowTaskVo flowTaskVo) {
        return flowTaskService.stopProcess(flowTaskVo);
    }
    @ApiOperation(value = "撤回流程", response = FlowTaskDto.class)
    @PostMapping(value = "/revokeProcess")
    public Result revokeProcess(@RequestBody FlowTaskVo flowTaskVo) {
        return flowTaskService.revokeProcess(flowTaskVo);
    }
    @ApiOperation(value = "获取待办列表", response = FlowTaskDto.class)
    @GetMapping(value = "/todoList")
    public Result todoList(@ApiParam(value = "当前页码", required = true) @RequestParam Integer pageNum,
                               @ApiParam(value = "每页条数", required = true) @RequestParam Integer pageSize) {
        return flowTaskService.todoList(pageNum, pageSize);
    }
    @ApiOperation(value = "获取已办任务", response = FlowTaskDto.class)
    @GetMapping(value = "/finishedList")
    public Result finishedList(@ApiParam(value = "当前页码", required = true) @RequestParam Integer pageNum,
                                   @ApiParam(value = "每页条数", required = true) @RequestParam Integer pageSize) {
        return flowTaskService.finishedList(pageNum, pageSize);
    }
    @ApiOperation(value = "流程历史流转记录", response = FlowTaskDto.class)
    @GetMapping(value = "/flowRecord")
    public Result flowRecord(String dataId) {
        return flowTaskService.flowRecord(dataId);
    }
    @ApiOperation(value = "获取流程变量", response = FlowTaskDto.class)
    @GetMapping(value = "/processVariables/{taskId}")
    public Result processVariables(@ApiParam(value = "流程任务Id")  @PathVariable(value = "taskId") String taskId) {
        return flowTaskService.processVariables(taskId);
    }
    @ApiOperation(value = "审批任务")
    @PostMapping(value = "/complete")
    public Result complete(@RequestBody FlowTaskVo flowTaskVo) {
        return flowTaskService.complete(flowTaskVo);
    }
    @ApiOperation(value = "审批任务")
    @PostMapping(value = "/completeByDateId")
    public Result completeByDateId(@RequestBody FlowTaskVo flowTaskVo) {
        return flowTaskService.completeByDateId(flowTaskVo);
    }
    @ApiOperation(value = "驳回任务")
    @PostMapping(value = "/reject")
    public Result taskReject(@RequestBody FlowTaskVo flowTaskVo) {
        flowTaskService.taskReject(flowTaskVo);
        return Result.OK();
    }
    @ApiOperation(value = "驳回任务")
    @PostMapping(value = "/taskRejectByDataId")
    public Result taskRejectByDataId(@RequestBody FlowTaskVo flowTaskVo) {
        flowTaskService.taskRejectByDataId(flowTaskVo);
        return Result.OK();
    }
    @ApiOperation(value = "退回任务")
    @PostMapping(value = "/return")
    public Result taskReturn(@RequestBody FlowTaskVo flowTaskVo) {
        flowTaskService.taskReturn(flowTaskVo);
        return Result.OK();
    }
    @ApiOperation(value = "退回任务")
    @PostMapping(value = "/taskReturnByDataId")
    public Result taskReturnByDataId(@RequestBody FlowTaskVo flowTaskVo) {
        flowTaskService.taskReturnByDataId(flowTaskVo);
        return Result.OK();
    }
    @ApiOperation(value = "获取所有可回退的节点")
    @PostMapping(value = "/returnList")
    public Result findReturnTaskList(@RequestBody FlowTaskVo flowTaskVo) {
        return flowTaskService.findReturnTaskList(flowTaskVo);
    }
    @ApiOperation(value = "获取所有可回退的节点")
    @PostMapping(value = "/findReturnTaskListByDataId")
    public Result findReturnTaskListByDataId(@RequestBody FlowTaskVo flowTaskVo) {
        return flowTaskService.findReturnTaskListByDataId(flowTaskVo);
    }
    @ApiOperation(value = "删除任务")
    @DeleteMapping(value = "/delete")
    public Result delete(@RequestBody FlowTaskVo flowTaskVo) {
        flowTaskService.deleteTask(flowTaskVo);
        return Result.OK();
    }
    @ApiOperation(value = "认领/签收任务")
    @PostMapping(value = "/claim")
    public Result claim(@RequestBody FlowTaskVo flowTaskVo) {
        flowTaskService.claim(flowTaskVo);
        return Result.OK();
    }
    @ApiOperation(value = "取消认领/签收任务")
    @PostMapping(value = "/unClaim")
    public Result unClaim(@RequestBody FlowTaskVo flowTaskVo) {
        flowTaskService.unClaim(flowTaskVo);
        return Result.OK();
    }
    @ApiOperation(value = "委派任务")
    @PostMapping(value = "/delegate")
    public Result delegate(@RequestBody FlowTaskVo flowTaskVo) {
        flowTaskService.delegateTask(flowTaskVo);
        return Result.OK();
    }
    @ApiOperation(value = "转办任务")
    @PostMapping(value = "/assign")
    public Result assign(@RequestBody FlowTaskVo flowTaskVo) {
        flowTaskService.assignTask(flowTaskVo);
        return Result.OK();
    }
    @ApiOperation(value = "获取下一节点")
    @PostMapping(value = "/nextFlowNode")
    public Result getNextFlowNode(@RequestBody FlowTaskVo flowTaskVo) {
        return flowTaskService.getNextFlowNode(flowTaskVo);
    }
    /**
     * ç”Ÿæˆæµç¨‹å›¾
     *
     * @param processId ä»»åŠ¡ID
     */
    @RequestMapping("/diagram/{processId}")
    public void genProcessDiagram(HttpServletResponse response,
                                  @PathVariable("processId") String processId) {
        InputStream inputStream =  flowTaskService.diagram(processId);
        OutputStream os = null;
        BufferedImage image = null;
        try {
            image = ImageIO.read(inputStream);
            response.setContentType("image/png");
            os = response.getOutputStream();
            if (image != null) {
                ImageIO.write(image, "png", os);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if (os != null) {
                    os.flush();
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * ç”Ÿæˆæµç¨‹å›¾
     *
     * @param procInsId ä»»åŠ¡ID
     */
    @RequestMapping("/flowViewer/{procInsId}")
    public Result getFlowViewer(@PathVariable("procInsId") String procInsId) {
        return Result.OK(flowTaskService.getFlowViewer(procInsId));
    }
    /**
     * ç”Ÿæˆæµç¨‹å›¾
     *
     * @param dataId ä»»åŠ¡æ•°æ®ID
     */
    @RequestMapping("/flowViewerByDataId/{dataId}")
    public Result getFlowViewerByDataId(@PathVariable("dataId") String dataId) {
        return Result.OK(flowTaskService.getFlowViewerByDataId(dataId));
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/dto/FlowCommentDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package org.jeecg.modules.flowable.domain.dto;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
/**
 * åŸºç¡€å¸¸é‡
 */
@Data
@Builder
public class FlowCommentDto implements Serializable {
    /**
     * æ„è§ç±»åˆ« 0 æ­£å¸¸æ„è§  1 é€€å›žæ„è§ 2 é©³å›žæ„è§
     */
    private String type;
    /**
     * æ„è§å†…容
     */
    private String comment;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/dto/FlowNextDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package org.jeecg.modules.flowable.domain.dto;
import lombok.Data;
import org.flowable.bpmn.model.UserTask;
import org.jeecg.modules.flowable.apithird.entity.SysUser;
import java.io.Serializable;
import java.util.List;
/**
 * äººå‘˜ã€ç»„
 */
@Data
public class FlowNextDto implements Serializable {
    /**
     * èŠ‚ç‚¹å¯¹è±¡
     */
    private UserTask userTask;
    /**
     * å¾…办人员
     */
    private List<SysUser> userList;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/dto/FlowProcDefDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package org.jeecg.modules.flowable.domain.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
 * <p>流程定义<p>
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("流程定义")
public class FlowProcDefDto implements Serializable {
    @ApiModelProperty("流程id")
    private String id;
    @ApiModelProperty("流程名称")
    private String name;
    @ApiModelProperty("流程key")
    private String key;
    @ApiModelProperty("流程分类")
    private String category;
    @ApiModelProperty("配置表单名称")
    private String formName;
    @ApiModelProperty("配置表单id")
    private Long formId;
    @ApiModelProperty("版本")
    private int version;
    @ApiModelProperty("部署ID")
    private String deploymentId;
    @ApiModelProperty("流程定义状态: 1:激活 , 2:中止")
    private int suspensionState;
    @ApiModelProperty("流程定义是否最新版本")
    private int isLastVersion;
    @ApiModelProperty("部署时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date deploymentTime;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/dto/FlowSaveXmlVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package org.jeecg.modules.flowable.domain.dto;
import lombok.Data;
import java.io.Serializable;
/**
 * æµç¨‹è§„范xml
 */
@Data
public class FlowSaveXmlVo implements Serializable {
    /**
     * æµç¨‹åç§°
     */
    private String name;
    /**
     * æµç¨‹åˆ†ç±»
     */
    private String category;
    /**
     * xml æ–‡ä»¶
     */
    private String xml;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/dto/FlowTaskDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
package org.jeecg.modules.flowable.domain.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.Date;
/**
 * <p>工作流任务<p>
 *
 */
@Getter
@Setter
@ApiModel("工作流任务相关-返回参数")
public class FlowTaskDto implements Serializable {
    @ApiModelProperty("任务编号")
    private String taskId;
    @ApiModelProperty("任务名称")
    private String taskName;
    @ApiModelProperty("任务Key")
    private String taskDefKey;
    @ApiModelProperty("任务执行人Id")
    private String assigneeId;
    @ApiModelProperty("部门名称")
    private String deptName;
    @ApiModelProperty("流程发起人部门名称")
    private String startDeptName;
    @ApiModelProperty("任务执行人名称")
    private String assigneeName;
    @ApiModelProperty("流程发起人Id")
    private String startUserId;
    @ApiModelProperty("流程发起人名称")
    private String startUserName;
    @ApiModelProperty("流程类型")
    private String category;
    @ApiModelProperty("流程变量信息")
    private Object procVars;
    @ApiModelProperty("局部变量信息")
    private Object taskLocalVars;
    @ApiModelProperty("流程部署编号")
    private String deployId;
    @ApiModelProperty("流程ID")
    private String procDefId;
    @ApiModelProperty("流程key")
    private String procDefKey;
    @ApiModelProperty("流程定义名称")
    private String procDefName;
    @ApiModelProperty("流程定义内置使用版本")
    private int procDefVersion;
    @ApiModelProperty("流程实例ID")
    private String procInsId;
    @ApiModelProperty("历史流程实例ID")
    private String hisProcInsId;
    @ApiModelProperty("任务耗时")
    private String duration;
    @ApiModelProperty("任务意见")
    private FlowCommentDto comment;
    @ApiModelProperty("候选执行人")
    private String candidate;
    @ApiModelProperty("任务创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    @ApiModelProperty("任务完成时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date finishTime;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/dto/FlowViewerDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package org.jeecg.modules.flowable.domain.dto;
import lombok.Data;
import java.io.Serializable;
/**
 */
@Data
public class FlowViewerDto implements Serializable {
    private String key;
    private boolean completed;
    private boolean back;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/domain/vo/FlowTaskVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package org.jeecg.modules.flowable.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
 * <p>流程任务<p>
 *
 */
@Data
@ApiModel("工作流任务相关--请求参数")
public class FlowTaskVo {
    @ApiModelProperty("数据Id")
    private String dataId;
    @ApiModelProperty("任务Id")
    private String taskId;
    @ApiModelProperty("用户Id")
    private String userId;
    @ApiModelProperty("任务意见")
    private String comment;
    @ApiModelProperty("流程实例Id")
    private String instanceId;
    @ApiModelProperty("节点")
    private String targetKey;
    @ApiModelProperty("流程变量信息")
    private Map<String, Object> values;
    @ApiModelProperty("审批人")
    private String assignee;
    @ApiModelProperty("候选人")
    private List<String> candidateUsers;
    @ApiModelProperty("审批组")
    private List<String> candidateGroups;
    private String xiugaiId;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/factory/FlowServiceFactory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package org.jeecg.modules.flowable.factory;
import lombok.Getter;
import org.flowable.engine.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
 * flowable å¼•擎注入封装
 */
@Component
@Getter
public class FlowServiceFactory {
    @Resource
    protected RepositoryService repositoryService;
    @Resource
    protected RuntimeService runtimeService;
    @Resource
    protected IdentityService identityService;
    @Resource
    protected TaskService taskService;
    @Resource
    protected FormService formService;
    @Resource
    protected HistoryService historyService;
    @Resource
    protected ManagementService managementService;
    @Qualifier("processEngine")
    @Resource
    protected ProcessEngine processEngine;
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/flow/CustomProcessDiagramCanvas.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,369 @@
package org.jeecg.modules.flowable.flow;
import org.flowable.bpmn.model.AssociationDirection;
import org.flowable.bpmn.model.GraphicInfo;
import org.flowable.image.impl.DefaultProcessDiagramCanvas;
import org.flowable.image.util.ReflectUtil;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
/**
 * åŸºç¡€é…ç½®
 */
public class CustomProcessDiagramCanvas extends DefaultProcessDiagramCanvas {
    //定义走过流程连线颜色为绿色
    protected static Color HIGHLIGHT_SequenceFlow_COLOR = Color.GREEN;
    //设置未走过流程的连接线颜色
    protected static Color CONNECTION_COLOR = Color.BLACK;
    //设置flows连接线字体颜色red
    protected static Color LABEL_COLOR = new Color(0, 0, 0);
    //高亮显示task框颜色
    protected static Color HIGHLIGHT_COLOR = Color.GREEN;
    protected static Color HIGHLIGHT_COLOR1 = Color.RED;
    public CustomProcessDiagramCanvas(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);
        this.initialize(imageType);
    }
    /**
     * é‡å†™ç»˜åˆ¶è¿žçº¿çš„æ–¹å¼,设置绘制颜色
     * @param xPoints
     * @param yPoints
     * @param conditional
     * @param isDefault
     * @param connectionType
     * @param associationDirection
     * @param highLighted
     * @param scaleFactor
     */
    @Override
    public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType, AssociationDirection associationDirection, boolean highLighted, double scaleFactor) {
        Paint originalPaint = this.g.getPaint();
        Stroke originalStroke = this.g.getStroke();
        this.g.setPaint(CONNECTION_COLOR);
        if (connectionType.equals("association")) {
            this.g.setStroke(ASSOCIATION_STROKE);
        } else if (highLighted) {
            this.g.setPaint(HIGHLIGHT_SequenceFlow_COLOR);
            this.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];
            java.awt.geom.Line2D.Double line = new java.awt.geom.Line2D.Double((double) sourceX, (double) sourceY, (double) targetX, (double) targetY);
            this.g.draw(line);
        }
        java.awt.geom.Line2D.Double line;
        if (isDefault) {
            line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]);
            this.drawDefaultSequenceFlowIndicator(line, scaleFactor);
        }
        if (conditional) {
            line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]);
            this.drawConditionalSequenceFlowIndicator(line, scaleFactor);
        }
        if (associationDirection.equals(AssociationDirection.ONE) || associationDirection.equals(AssociationDirection.BOTH)) {
            line = new java.awt.geom.Line2D.Double((double) xPoints[xPoints.length - 2], (double) yPoints[xPoints.length - 2], (double) xPoints[xPoints.length - 1], (double) yPoints[xPoints.length - 1]);
            this.drawArrowHead(line, scaleFactor);
        }
        if (associationDirection.equals(AssociationDirection.BOTH)) {
            line = new java.awt.geom.Line2D.Double((double) xPoints[1], (double) yPoints[1], (double) xPoints[0], (double) yPoints[0]);
            this.drawArrowHead(line, scaleFactor);
        }
        this.g.setPaint(originalPaint);
        this.g.setStroke(originalStroke);
    }
    /**
     * è®¾ç½®å­—体大小图标颜色
     * @param imageType
     */
    @Override
    public void initialize(String imageType) {
        if ("png".equalsIgnoreCase(imageType)) {
            this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 2);
        } else {
            this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 1);
        }
        this.g = this.processDiagram.createGraphics();
        if (!"png".equalsIgnoreCase(imageType)) {
            this.g.setBackground(new Color(255, 255, 255, 0));
            this.g.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
        }
        this.g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        //修改图标颜色,修改图标字体大小
        this.g.setPaint(Color.black);
        Font font = new Font(this.activityFontName, 10, 14);
        this.g.setFont(font);
        this.fontMetrics = this.g.getFontMetrics();
        //修改连接线字体大小
        LABEL_FONT = new Font(this.labelFontName, 10, 15);
        ANNOTATION_FONT = new Font(this.annotationFontName, 0, 11);
        try {
            USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/userTask.png", this.customClassLoader));
            SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/scriptTask.png", this.customClassLoader));
            SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/serviceTask.png", this.customClassLoader));
            RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/receiveTask.png", this.customClassLoader));
            SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/sendTask.png", this.customClassLoader));
            MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/manualTask.png", this.customClassLoader));
            BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/businessRuleTask.png", this.customClassLoader));
            SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/shellTask.png", this.customClassLoader));
            DMN_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/dmnTask.png", this.customClassLoader));
            CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/camelTask.png", this.customClassLoader));
            MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/muleTask.png", this.customClassLoader));
            HTTP_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/httpTask.png", this.customClassLoader));
            TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/timer.png", this.customClassLoader));
            COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate-throw.png", this.customClassLoader));
            COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate.png", this.customClassLoader));
            ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error-throw.png", this.customClassLoader));
            ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error.png", this.customClassLoader));
            MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message-throw.png", this.customClassLoader));
            MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message.png", this.customClassLoader));
            SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal-throw.png", this.customClassLoader));
            SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal.png", this.customClassLoader));
        } catch (IOException var4) {
            LOGGER.warn("Could not load image for process diagram creation: {}", var4.getMessage());
        }
    }
    /**
     * è®¾ç½®è¿žæŽ¥çº¿å­—体
     * @param text
     * @param graphicInfo
     * @param centered
     */
    @Override
    public void drawLabel(String text, GraphicInfo graphicInfo, boolean centered) {
        float interline = 1.0f;
        // text
        if (text != null && text.length() > 0) {
            Paint originalPaint = g.getPaint();
            Font originalFont = g.getFont();
            g.setPaint(LABEL_COLOR);
            g.setFont(LABEL_FONT);
            int wrapWidth = 100;
            int textY = (int) graphicInfo.getY();
            // TODO: use drawMultilineText()
            AttributedString as = new AttributedString(text);
            as.addAttribute(TextAttribute.FOREGROUND, g.getPaint());
            as.addAttribute(TextAttribute.FONT, g.getFont());
            AttributedCharacterIterator aci = as.getIterator();
            FontRenderContext frc = new FontRenderContext(null, true, false);
            LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
            while (lbm.getPosition() < text.length()) {
                TextLayout tl = lbm.nextLayout(wrapWidth);
                textY += tl.getAscent();
                Rectangle2D bb = tl.getBounds();
                double tX = graphicInfo.getX();
                if (centered) {
                    tX += (int) (graphicInfo.getWidth() / 2 - bb.getWidth() / 2);
                }
                tl.draw(g, (float) tX, textY);
                textY += tl.getDescent() + tl.getLeading() + (interline - 1.0f) * tl.getAscent();
            }
            // restore originals
            g.setFont(originalFont);
            g.setPaint(originalPaint);
        }
    }
    /**
     * é«˜äº®æ˜¾ç¤ºtask框完成的
     * @param x
     * @param y
     * @param width
     * @param height
     */
    @Override
    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);
    }
    /**
     * è‡ªå®šä¹‰task框当前的位置
     * @param x
     * @param y
     * @param width
     * @param height
     */
    public void drawHighLightNow(int x, int y, int width, int height) {
        Paint originalPaint = g.getPaint();
        Stroke originalStroke = g.getStroke();
        g.setPaint(HIGHLIGHT_COLOR1);
        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);
    }
    /**
     * è‡ªå®šä¹‰ç»“束节点
     * @param x
     * @param y
     * @param width
     * @param height
     */
    public void drawHighLightEnd(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);
    }
    /**
     * task框自定义文字
     * @param name
     * @param graphicInfo
     * @param thickBorder
     * @param scaleFactor
     */
    @Override
    protected void drawTask(String name, GraphicInfo graphicInfo, boolean thickBorder, double scaleFactor) {
        Paint originalPaint = g.getPaint();
        int x = (int) graphicInfo.getX();
        int y = (int) graphicInfo.getY();
        int width = (int) graphicInfo.getWidth();
        int height = (int) graphicInfo.getHeight();
        // Create a new gradient paint for every task box, gradient depends on x and y and is not relative
        g.setPaint(TASK_BOX_COLOR);
        int arcR = 6;
        if (thickBorder) {
            arcR = 3;
        }
        // shape
        RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcR, arcR);
        g.fill(rect);
        g.setPaint(TASK_BORDER_COLOR);
        if (thickBorder) {
            Stroke originalStroke = g.getStroke();
            g.setStroke(THICK_TASK_BORDER_STROKE);
            g.draw(rect);
            g.setStroke(originalStroke);
        } else {
            g.draw(rect);
        }
        g.setPaint(originalPaint);
        // text
        if (scaleFactor == 1.0 && name != null && name.length() > 0) {
            int boxWidth = width - (2 * TEXT_PADDING);
            int boxHeight = height - 16 - ICON_PADDING - ICON_PADDING - MARKER_WIDTH - 2 - 2;
            int boxX = x + width / 2 - boxWidth / 2;
            int boxY = y + height / 2 - boxHeight / 2 + ICON_PADDING + ICON_PADDING - 2 - 2;
            drawMultilineCentredText(name, boxX, boxY, boxWidth, boxHeight);
        }
    }
    protected static Color EVENT_COLOR = new Color(255, 255, 255);
    /**
     * é‡å†™å¼€å§‹äº‹ä»¶
     * @param graphicInfo
     * @param image
     * @param scaleFactor
     */
    @Override
    public void drawStartEvent(GraphicInfo graphicInfo, BufferedImage image, double scaleFactor) {
        Paint originalPaint = g.getPaint();
        g.setPaint(EVENT_COLOR);
        Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(),
                graphicInfo.getWidth(), graphicInfo.getHeight());
        g.fill(circle);
        g.setPaint(EVENT_BORDER_COLOR);
        g.draw(circle);
        g.setPaint(originalPaint);
        if (image != null) {
            // calculate coordinates to center image
            int imageX = (int) Math.round(graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / (2 * scaleFactor)));
            int imageY = (int) Math.round(graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / (2 * scaleFactor)));
            g.drawImage(image, imageX, imageY,
                    (int) (image.getWidth() / scaleFactor), (int) (image.getHeight() / scaleFactor), null);
        }
    }
    /**
     * é‡å†™ç»“束事件
     * @param graphicInfo
     * @param scaleFactor
     */
    @Override
    public void drawNoneEndEvent(GraphicInfo graphicInfo, double scaleFactor) {
        Paint originalPaint = g.getPaint();
        Stroke originalStroke = g.getStroke();
        g.setPaint(EVENT_COLOR);
        Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(),
                graphicInfo.getWidth(), graphicInfo.getHeight());
        g.fill(circle);
        g.setPaint(EVENT_BORDER_COLOR);
//        g.setPaint(HIGHLIGHT_COLOR);
        if (scaleFactor == 1.0) {
            g.setStroke(END_EVENT_STROKE);
        } else {
            g.setStroke(new BasicStroke(2.0f));
        }
        g.draw(circle);
        g.setStroke(originalStroke);
        g.setPaint(originalPaint);
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/flow/CustomProcessDiagramGenerator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,402 @@
package org.jeecg.modules.flowable.flow;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.image.impl.DefaultProcessDiagramCanvas;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import java.util.Iterator;
import java.util.List;
/**
 */
public class CustomProcessDiagramGenerator extends DefaultProcessDiagramGenerator {
    @Override
    protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        this.prepareBpmnModel(bpmnModel);
        DefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
        Iterator var13 = bpmnModel.getPools().iterator();
        while (var13.hasNext()) {
            Pool process = (Pool) var13.next();
            GraphicInfo subProcesses = bpmnModel.getGraphicInfo(process.getId());
            processDiagramCanvas.drawPoolOrLane(process.getName(), subProcesses, scaleFactor);
        }
        var13 = bpmnModel.getProcesses().iterator();
        Process process1;
        Iterator subProcesses1;
        while (var13.hasNext()) {
            process1 = (Process) var13.next();
            subProcesses1 = process1.getLanes().iterator();
            while (subProcesses1.hasNext()) {
                Lane artifact = (Lane) subProcesses1.next();
                GraphicInfo subProcess = bpmnModel.getGraphicInfo(artifact.getId());
                processDiagramCanvas.drawPoolOrLane(artifact.getName(), subProcess, scaleFactor);
            }
        }
        var13 = bpmnModel.getProcesses().iterator();
        while (var13.hasNext()) {
            process1 = (Process) var13.next();
            subProcesses1 = process1.findFlowElementsOfType(FlowNode.class).iterator();
            while (subProcesses1.hasNext()) {
                FlowNode artifact1 = (FlowNode) subProcesses1.next();
                if (!this.isPartOfCollapsedSubProcess(artifact1, bpmnModel)) {
                    this.drawActivity(processDiagramCanvas, bpmnModel, artifact1, highLightedActivities, highLightedFlows, scaleFactor, Boolean.valueOf(drawSequenceFlowNameWithNoLabelDI));
                }
            }
        }
        var13 = bpmnModel.getProcesses().iterator();
        label75:
        while (true) {
            List subProcesses2;
            do {
                if (!var13.hasNext()) {
                    return processDiagramCanvas;
                }
                process1 = (Process) var13.next();
                subProcesses1 = process1.getArtifacts().iterator();
                while (subProcesses1.hasNext()) {
                    Artifact artifact2 = (Artifact) subProcesses1.next();
                    this.drawArtifact(processDiagramCanvas, bpmnModel, artifact2);
                }
                subProcesses2 = process1.findFlowElementsOfType(SubProcess.class, true);
            } while (subProcesses2 == null);
            Iterator artifact3 = subProcesses2.iterator();
            while (true) {
                GraphicInfo graphicInfo;
                SubProcess subProcess1;
                do {
                    do {
                        if (!artifact3.hasNext()) {
                            continue label75;
                        }
                        subProcess1 = (SubProcess) artifact3.next();
                        graphicInfo = bpmnModel.getGraphicInfo(subProcess1.getId());
                    } while (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded().booleanValue());
                } while (this.isPartOfCollapsedSubProcess(subProcess1, bpmnModel));
                Iterator var19 = subProcess1.getArtifacts().iterator();
                while (var19.hasNext()) {
                    Artifact subProcessArtifact = (Artifact) var19.next();
                    this.drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact);
                }
            }
        }
    }
    protected static DefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
        double minX = 1.7976931348623157E308D;
        double maxX = 0.0D;
        double minY = 1.7976931348623157E308D;
        double maxY = 0.0D;
        GraphicInfo nrOfLanes;
        for (Iterator flowNodes = bpmnModel.getPools().iterator(); flowNodes.hasNext(); maxY = nrOfLanes.getY() + nrOfLanes.getHeight()) {
            Pool artifacts = (Pool) flowNodes.next();
            nrOfLanes = bpmnModel.getGraphicInfo(artifacts.getId());
            minX = nrOfLanes.getX();
            maxX = nrOfLanes.getX() + nrOfLanes.getWidth();
            minY = nrOfLanes.getY();
        }
        List var23 = gatherAllFlowNodes(bpmnModel);
        Iterator var24 = var23.iterator();
        label155:
        while (var24.hasNext()) {
            FlowNode var26 = (FlowNode) var24.next();
            GraphicInfo artifact = bpmnModel.getGraphicInfo(var26.getId());
            if (artifact.getX() + artifact.getWidth() > maxX) {
                maxX = artifact.getX() + artifact.getWidth();
            }
            if (artifact.getX() < minX) {
                minX = artifact.getX();
            }
            if (artifact.getY() + artifact.getHeight() > maxY) {
                maxY = artifact.getY() + artifact.getHeight();
            }
            if (artifact.getY() < minY) {
                minY = artifact.getY();
            }
            Iterator process = var26.getOutgoingFlows().iterator();
            while (true) {
                List l;
                do {
                    if (!process.hasNext()) {
                        continue label155;
                    }
                    SequenceFlow graphicInfoList = (SequenceFlow) process.next();
                    l = bpmnModel.getFlowLocationGraphicInfo(graphicInfoList.getId());
                } while (l == null);
                Iterator graphicInfo = l.iterator();
                while (graphicInfo.hasNext()) {
                    GraphicInfo graphicInfo1 = (GraphicInfo) graphicInfo.next();
                    if (graphicInfo1.getX() > maxX) {
                        maxX = graphicInfo1.getX();
                    }
                    if (graphicInfo1.getX() < minX) {
                        minX = graphicInfo1.getX();
                    }
                    if (graphicInfo1.getY() > maxY) {
                        maxY = graphicInfo1.getY();
                    }
                    if (graphicInfo1.getY() < minY) {
                        minY = graphicInfo1.getY();
                    }
                }
            }
        }
        List var25 = gatherAllArtifacts(bpmnModel);
        Iterator var27 = var25.iterator();
        GraphicInfo var37;
        while (var27.hasNext()) {
            Artifact var29 = (Artifact) var27.next();
            GraphicInfo var31 = bpmnModel.getGraphicInfo(var29.getId());
            if (var31 != null) {
                if (var31.getX() + var31.getWidth() > maxX) {
                    maxX = var31.getX() + var31.getWidth();
                }
                if (var31.getX() < minX) {
                    minX = var31.getX();
                }
                if (var31.getY() + var31.getHeight() > maxY) {
                    maxY = var31.getY() + var31.getHeight();
                }
                if (var31.getY() < minY) {
                    minY = var31.getY();
                }
            }
            List var33 = bpmnModel.getFlowLocationGraphicInfo(var29.getId());
            if (var33 != null) {
                Iterator var35 = var33.iterator();
                while (var35.hasNext()) {
                    var37 = (GraphicInfo) var35.next();
                    if (var37.getX() > maxX) {
                        maxX = var37.getX();
                    }
                    if (var37.getX() < minX) {
                        minX = var37.getX();
                    }
                    if (var37.getY() > maxY) {
                        maxY = var37.getY();
                    }
                    if (var37.getY() < minY) {
                        minY = var37.getY();
                    }
                }
            }
        }
        int var28 = 0;
        Iterator var30 = bpmnModel.getProcesses().iterator();
        while (var30.hasNext()) {
            Process var32 = (Process) var30.next();
            Iterator var34 = var32.getLanes().iterator();
            while (var34.hasNext()) {
                Lane var36 = (Lane) var34.next();
                ++var28;
                var37 = bpmnModel.getGraphicInfo(var36.getId());
                if (var37.getX() + var37.getWidth() > maxX) {
                    maxX = var37.getX() + var37.getWidth();
                }
                if (var37.getX() < minX) {
                    minX = var37.getX();
                }
                if (var37.getY() + var37.getHeight() > maxY) {
                    maxY = var37.getY() + var37.getHeight();
                }
                if (var37.getY() < minY) {
                    minY = var37.getY();
                }
            }
        }
        if (var23.isEmpty() && bpmnModel.getPools().isEmpty() && var28 == 0) {
            minX = 0.0D;
            minY = 0.0D;
        }
        return new CustomProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
    }
    private static void drawHighLight(DefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
        processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
    }
    private static void drawHighLightNow(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
        processDiagramCanvas.drawHighLightNow((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
    }
    private static void drawHighLightEnd(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
        processDiagramCanvas.drawHighLightEnd((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
    }
    @Override
    protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel,
                                FlowNode flowNode, List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) {
        DefaultProcessDiagramGenerator.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())) {
                if (highLightedActivities.get(highLightedActivities.size() - 1).equals(flowNode.getId())
                        && !"endenv".equals(flowNode.getId())) {
                    if ((flowNode.getId().contains("Event_"))) {
                        drawHighLightEnd((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
                    } else {
                        drawHighLightNow((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
                    }
                } else {
                    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 && !(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);
                }
            }
        }
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/flow/FindNextNodeUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,235 @@
package org.jeecg.modules.flowable.flow;
import com.google.common.collect.Maps;
import com.greenpineyu.fel.FelEngine;
import com.greenpineyu.fel.FelEngineImpl;
import com.greenpineyu.fel.context.FelContext;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.ProcessDefinition;
import java.util.*;
/**
 */
public class FindNextNodeUtil {
    /**
     * èŽ·å–ä¸‹ä¸€æ­¥éª¤çš„ç”¨æˆ·ä»»åŠ¡
     *
     * @param repositoryService
     * @param map
     * @return
     */
    public static List<UserTask> getNextUserTasks(RepositoryService repositoryService, org.flowable.task.api.Task task, Map<String, Object> map) {
        List<UserTask> data = new ArrayList<>();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        Process mainProcess = bpmnModel.getMainProcess();
        Collection<FlowElement> flowElements = mainProcess.getFlowElements();
        String key = task.getTaskDefinitionKey();
        FlowElement flowElement = bpmnModel.getFlowElement(key);
        next(flowElements, flowElement, map, data);
        return data;
    }
    public static void next(Collection<FlowElement> flowElements, FlowElement flowElement, Map<String, Object> map, List<UserTask> nextUser) {
        //如果是结束节点
        if (flowElement instanceof EndEvent) {
            //如果是子任务的结束节点
            if (getSubProcess(flowElements, flowElement) != null) {
                flowElement = getSubProcess(flowElements, flowElement);
            }
        }
        //获取Task的出线信息--可以拥有多个
        List<SequenceFlow> outGoingFlows = null;
        if (flowElement instanceof Task) {
            outGoingFlows = ((Task) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof Gateway) {
            outGoingFlows = ((Gateway) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof StartEvent) {
            outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof SubProcess) {
            outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof CallActivity) {
            outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows();
        }
        if (outGoingFlows != null && outGoingFlows.size() > 0) {
            //遍历所有的出线--找到可以正确执行的那一条
            for (SequenceFlow sequenceFlow : outGoingFlows) {
                //1.有表达式,且为true
                //2.无表达式
                String expression = sequenceFlow.getConditionExpression();
                if (expression == null ||Boolean.parseBoolean(
                                String.valueOf(
                                        result(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))))) {
                    //出线的下一节点
                    String nextFlowElementID = sequenceFlow.getTargetRef();
                    if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) {
                        continue;
                    }
                    //查询下一节点的信息
                    FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements);
                    //调用流程
                    if (nextFlowElement instanceof CallActivity) {
                        CallActivity ca = (CallActivity) nextFlowElement;
                        if (ca.getLoopCharacteristics() != null) {
                            UserTask userTask = new UserTask();
                            userTask.setId(ca.getId());
                            userTask.setId(ca.getId());
                            userTask.setLoopCharacteristics(ca.getLoopCharacteristics());
                            userTask.setName(ca.getName());
                            nextUser.add(userTask);
                        }
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //用户任务
                    if (nextFlowElement instanceof UserTask) {
                        nextUser.add((UserTask) nextFlowElement);
                    }
                    //排他网关
                    else if (nextFlowElement instanceof ExclusiveGateway) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //并行网关
                    else if (nextFlowElement instanceof ParallelGateway) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //接收任务
                    else if (nextFlowElement instanceof ReceiveTask) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //服务任务
                    else if (nextFlowElement instanceof ServiceTask) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //子任务的起点
                    else if (nextFlowElement instanceof StartEvent) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //结束节点
                    else if (nextFlowElement instanceof EndEvent) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                }
            }
        }
    }
    /**
     * åˆ¤æ–­æ˜¯å¦æ˜¯å¤šå®žä¾‹å­æµç¨‹å¹¶ä¸”需要设置集合类型变量
     */
    public static boolean checkSubProcess(String Id, Collection<FlowElement> flowElements, List<UserTask> nextUser) {
        for (FlowElement flowElement1 : flowElements) {
            if (flowElement1 instanceof SubProcess && flowElement1.getId().equals(Id)) {
                SubProcess sp = (SubProcess) flowElement1;
                if (sp.getLoopCharacteristics() != null) {
                    String inputDataItem = sp.getLoopCharacteristics().getInputDataItem();
                    UserTask userTask = new UserTask();
                    userTask.setId(sp.getId());
                    userTask.setLoopCharacteristics(sp.getLoopCharacteristics());
                    userTask.setName(sp.getName());
                    nextUser.add(userTask);
                    return true;
                }
            }
        }
        return false;
    }
    /**
     * æŸ¥è¯¢ä¸€ä¸ªèŠ‚ç‚¹çš„æ˜¯å¦å­ä»»åŠ¡ä¸­çš„èŠ‚ç‚¹ï¼Œå¦‚æžœæ˜¯ï¼Œè¿”å›žå­ä»»åŠ¡
     *
     * @param flowElements å…¨æµç¨‹çš„节点集合
     * @param flowElement  å½“前节点
     * @return
     */
    public static FlowElement getSubProcess(Collection<FlowElement> flowElements, FlowElement flowElement) {
        for (FlowElement flowElement1 : flowElements) {
            if (flowElement1 instanceof SubProcess) {
                for (FlowElement flowElement2 : ((SubProcess) flowElement1).getFlowElements()) {
                    if (flowElement.equals(flowElement2)) {
                        return flowElement1;
                    }
                }
            }
        }
        return null;
    }
    /**
     * æ ¹æ®ID查询流程节点对象, å¦‚果是子任务,则返回子任务的开始节点
     *
     * @param Id           èŠ‚ç‚¹ID
     * @param flowElements æµç¨‹èŠ‚ç‚¹é›†åˆ
     * @return
     */
    public static FlowElement getFlowElementById(String Id, Collection<FlowElement> flowElements) {
        for (FlowElement flowElement : flowElements) {
            if (flowElement.getId().equals(Id)) {
                //如果是子任务,则查询出子任务的开始节点
                if (flowElement instanceof SubProcess) {
                    return getStartFlowElement(((SubProcess) flowElement).getFlowElements());
                }
                return flowElement;
            }
            if (flowElement instanceof SubProcess) {
                FlowElement flowElement1 = getFlowElementById(Id, ((SubProcess) flowElement).getFlowElements());
                if (flowElement1 != null) {
                    return flowElement1;
                }
            }
        }
        return null;
    }
    /**
     * è¿”回流程的开始节点
     *
     * @param flowElements èŠ‚ç‚¹é›†åˆ
     * @description:
     */
    public static FlowElement getStartFlowElement(Collection<FlowElement> flowElements) {
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof StartEvent) {
                return flowElement;
            }
        }
        return null;
    }
    /**
     * æ ¡éªŒel表达示例
     *
     * @param map
     * @param expression
     * @return
     */
    public static Object result(Map<String, Object> map, String expression) {
        FelEngine fel = new FelEngineImpl();
        FelContext ctx = fel.getContext();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            ctx.set(entry.getKey(), entry.getValue());
        }
        Object result = fel.eval(expression);
        return result;
    }
    public static void main(String[] args) {
        HashMap<String, Object> objectObjectHashMap = Maps.newHashMap();
        objectObjectHashMap.put("a",100);
        objectObjectHashMap.put("b",200);
        objectObjectHashMap.put("c","abc");
        Object result = result(objectObjectHashMap, "a!=00&&b=='200'&&c=='abc'");
        System.out.println(result);
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/flow/FlowableConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package org.jeecg.modules.flowable.flow;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;
/**
 */
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
    @Override
    public void configure(SpringProcessEngineConfiguration engineConfiguration) {
        engineConfiguration.setActivityFontName("宋体");
        engineConfiguration.setLabelFontName("宋体");
        engineConfiguration.setAnnotationFontName("宋体");
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/flow/FlowableUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,587 @@
package org.jeecg.modules.flowable.flow;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.*;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.flowable.task.api.history.HistoricTaskInstance;
import java.util.*;
/**
 */
@Slf4j
public class FlowableUtils {
    /**
     * æ ¹æ®èŠ‚ç‚¹ï¼ŒèŽ·å–å…¥å£è¿žçº¿
     * @param source
     * @return
     */
    public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {
        List<SequenceFlow> sequenceFlows = null;
        if (source instanceof FlowNode) {
            sequenceFlows = ((FlowNode) source).getIncomingFlows();
        } else if (source instanceof Gateway) {
            sequenceFlows = ((Gateway) source).getIncomingFlows();
        } else if (source instanceof SubProcess) {
            sequenceFlows = ((SubProcess) source).getIncomingFlows();
        } else if (source instanceof StartEvent) {
            sequenceFlows = ((StartEvent) source).getIncomingFlows();
        } else if (source instanceof EndEvent) {
            sequenceFlows = ((EndEvent) source).getIncomingFlows();
        }
        return sequenceFlows;
    }
    /**
     * æ ¹æ®èŠ‚ç‚¹ï¼ŒèŽ·å–å‡ºå£è¿žçº¿
     * @param source
     * @return
     */
    public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {
        List<SequenceFlow> sequenceFlows = null;
        if (source instanceof FlowNode) {
            sequenceFlows = ((FlowNode) source).getOutgoingFlows();
        } else if (source instanceof Gateway) {
            sequenceFlows = ((Gateway) source).getOutgoingFlows();
        } else if (source instanceof SubProcess) {
            sequenceFlows = ((SubProcess) source).getOutgoingFlows();
        } else if (source instanceof StartEvent) {
            sequenceFlows = ((StartEvent) source).getOutgoingFlows();
        } else if (source instanceof EndEvent) {
            sequenceFlows = ((EndEvent) source).getOutgoingFlows();
        }
        return sequenceFlows;
    }
    /**
     * èŽ·å–å…¨éƒ¨èŠ‚ç‚¹åˆ—è¡¨ï¼ŒåŒ…å«å­æµç¨‹èŠ‚ç‚¹
     * @param flowElements
     * @param allElements
     * @return
     */
    public static Collection<FlowElement> getAllElements(Collection<FlowElement> flowElements, Collection<FlowElement> allElements) {
        allElements = allElements == null ? new ArrayList<>() : allElements;
        for (FlowElement flowElement : flowElements) {
            allElements.add(flowElement);
            if (flowElement instanceof SubProcess) {
                // ç»§ç»­æ·±å…¥å­æµç¨‹ï¼Œè¿›ä¸€æ­¥èŽ·å–å­æµç¨‹
                allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements);
            }
        }
        return allElements;
    }
    /**
     * è¿­ä»£èŽ·å–çˆ¶çº§ä»»åŠ¡èŠ‚ç‚¹åˆ—è¡¨ï¼Œå‘å‰æ‰¾
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param userTaskList å·²æ‰¾åˆ°çš„用户任务节点
     * @return
     */
    public static List<UserTask> iteratorFindParentUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
        userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        // å¦‚果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
        if (source instanceof StartEvent && source.getSubProcess() != null) {
            userTaskList = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList);
        }
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å…¥å£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
        if (sequenceFlows != null) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // ç±»åž‹ä¸ºç”¨æˆ·èŠ‚ç‚¹ï¼Œåˆ™æ–°å¢žçˆ¶çº§èŠ‚ç‚¹
                if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
                    userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement());
                    continue;
                }
                // ç±»åž‹ä¸ºå­æµç¨‹ï¼Œåˆ™æ·»åŠ å­æµç¨‹å¼€å§‹èŠ‚ç‚¹å‡ºå£å¤„ç›¸è¿žçš„èŠ‚ç‚¹
                if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
                    // èŽ·å–å­æµç¨‹ç”¨æˆ·ä»»åŠ¡èŠ‚ç‚¹
                    List<UserTask> childUserTaskList = findChildProcessUserTasks((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null);
                    // å¦‚果找到节点,则说明该线路找到节点,不继续向下找,反之继续
                    if (childUserTaskList != null && childUserTaskList.size() > 0) {
                        userTaskList.addAll(childUserTaskList);
                        continue;
                    }
                }
                // ç»§ç»­è¿­ä»£
                userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList);
            }
        }
        return userTaskList;
    }
    /**
     * æ ¹æ®æ­£åœ¨è¿è¡Œçš„任务节点,迭代获取子级任务节点列表,向后找
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param runTaskKeyList æ­£åœ¨è¿è¡Œçš„任务 Key,用于校验任务节点是否是正在运行的节点
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param userTaskList éœ€è¦æ’¤å›žçš„用户任务列表
     * @return
     */
    public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
        // å¦‚果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
        if (source instanceof EndEvent && source.getSubProcess() != null) {
            userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList);
        }
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å‡ºå£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
        if (sequenceFlows != null) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // å¦‚果为用户任务类型,且任务节点的 Key æ­£åœ¨è¿è¡Œçš„任务中存在,添加
                if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) {
                    userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
                    continue;
                }
                // å¦‚果节点为子流程节点情况,则从节点中的第一个节点开始获取
                if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
                    List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null);
                    // å¦‚果找到节点,则说明该线路找到节点,不继续向下找,反之继续
                    if (childUserTaskList != null && childUserTaskList.size() > 0) {
                        userTaskList.addAll(childUserTaskList);
                        continue;
                    }
                }
                // ç»§ç»­è¿­ä»£
                userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList);
            }
        }
        return userTaskList;
    }
    /**
     * è¿­ä»£èŽ·å–å­æµç¨‹ç”¨æˆ·ä»»åŠ¡èŠ‚ç‚¹
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param userTaskList éœ€è¦æ’¤å›žçš„用户任务列表
     * @return
     */
    public static List<UserTask> findChildProcessUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å‡ºå£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
        if (sequenceFlows != null) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // å¦‚果为用户任务类型,且任务节点的 Key æ­£åœ¨è¿è¡Œçš„任务中存在,添加
                if (sequenceFlow.getTargetFlowElement() instanceof UserTask) {
                    userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
                    continue;
                }
                // å¦‚果节点为子流程节点情况,则从节点中的第一个节点开始获取
                if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
                    List<UserTask> childUserTaskList = findChildProcessUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null);
                    // å¦‚果找到节点,则说明该线路找到节点,不继续向下找,反之继续
                    if (childUserTaskList != null && childUserTaskList.size() > 0) {
                        userTaskList.addAll(childUserTaskList);
                        continue;
                    }
                }
                // ç»§ç»­è¿­ä»£
                userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList);
            }
        }
        return userTaskList;
    }
    /**
     * ä»ŽåŽå‘前寻路,获取所有脏线路上的点
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param passRoads å·²ç»ç»è¿‡çš„点集合
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param targets ç›®æ ‡è„çº¿è·¯ç»ˆç‚¹
     * @param dirtyRoads ç¡®å®šä¸ºè„æ•°æ®çš„点,因为不需要重复,因此使用 set å­˜å‚¨
     * @return
     */
    public static Set<String> iteratorFindDirtyRoads(FlowElement source, List<String> passRoads, Set<String> hasSequenceFlow, List<String> targets, Set<String> dirtyRoads) {
        passRoads = passRoads == null ? new ArrayList<>() : passRoads;
        dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads;
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        // å¦‚果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
        if (source instanceof StartEvent && source.getSubProcess() != null) {
            dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, dirtyRoads);
        }
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å…¥å£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
        if (sequenceFlows != null) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // æ–°å¢žç»è¿‡çš„路线
                passRoads.add(sequenceFlow.getSourceFlowElement().getId());
                // å¦‚果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线
                if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) {
                    dirtyRoads.addAll(passRoads);
                    continue;
                }
                // å¦‚果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
                if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
                    dirtyRoads = findChildProcessAllDirtyRoad((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, dirtyRoads);
                    // æ˜¯å¦å­˜åœ¨å­æµç¨‹ä¸Šï¼Œtrue æ˜¯ï¼Œfalse å¦
                    Boolean isInChildProcess = dirtyTargetInChildProcess((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, targets, null);
                    if (isInChildProcess) {
                        // å·²åœ¨å­æµç¨‹ä¸Šæ‰¾åˆ°ï¼Œè¯¥è·¯çº¿ç»“束
                        continue;
                    }
                }
                // ç»§ç»­è¿­ä»£
                dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, targets, dirtyRoads);
            }
        }
        return dirtyRoads;
    }
    /**
     * è¿­ä»£èŽ·å–å­æµç¨‹è„è·¯çº¿
     * è¯´æ˜Žï¼Œå‡å¦‚回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param dirtyRoads ç¡®å®šä¸ºè„æ•°æ®çš„点,因为不需要重复,因此使用 set å­˜å‚¨
     * @return
     */
    public static Set<String> findChildProcessAllDirtyRoad(FlowElement source, Set<String> hasSequenceFlow, Set<String> dirtyRoads) {
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads;
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å‡ºå£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
        if (sequenceFlows != null) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // æ·»åŠ è„è·¯çº¿
                dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId());
                // å¦‚果节点为子流程节点情况,则从节点中的第一个节点开始获取
                if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
                    dirtyRoads = findChildProcessAllDirtyRoad((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, dirtyRoads);
                }
                // ç»§ç»­è¿­ä»£
                dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, dirtyRoads);
            }
        }
        return dirtyRoads;
    }
    /**
     * åˆ¤æ–­è„è·¯çº¿ç»“束节点是否在子流程上
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param targets åˆ¤æ–­è„è·¯çº¿èŠ‚ç‚¹æ˜¯å¦å­˜åœ¨å­æµç¨‹ä¸Šï¼Œåªè¦å­˜åœ¨ä¸€ä¸ªï¼Œè¯´æ˜Žè„è·¯çº¿åªåˆ°å­æµç¨‹ä¸ºæ­¢
     * @param inChildProcess æ˜¯å¦å­˜åœ¨å­æµç¨‹ä¸Šï¼Œtrue æ˜¯ï¼Œfalse å¦
     * @return
     */
    public static Boolean dirtyTargetInChildProcess(FlowElement source, Set<String> hasSequenceFlow, List<String> targets, Boolean inChildProcess) {
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        inChildProcess = inChildProcess == null ? false : inChildProcess;
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å‡ºå£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
        if (sequenceFlows != null && !inChildProcess) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // å¦‚果发现目标点在子流程上存在,说明只到子流程为止
                if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) {
                    inChildProcess = true;
                    break;
                }
                // å¦‚果节点为子流程节点情况,则从节点中的第一个节点开始获取
                if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
                    inChildProcess = dirtyTargetInChildProcess((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, targets, inChildProcess);
                }
                // ç»§ç»­è¿­ä»£
                inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, targets, inChildProcess);
            }
        }
        return inChildProcess;
    }
    /**
     * è¿­ä»£ä»ŽåŽå‘前扫描,判断目标节点相对于当前节点是否是串行
     * ä¸å­˜åœ¨ç›´æŽ¥å›žé€€åˆ°å­æµç¨‹ä¸­çš„æƒ…况,但存在从子流程出去到父流程情况
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param isSequential æ˜¯å¦ä¸²è¡Œ
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param targetKsy ç›®æ ‡èŠ‚ç‚¹
     * @return
     */
    public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, Set<String> hasSequenceFlow, Boolean isSequential) {
        isSequential = isSequential == null ? true : isSequential;
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        // å¦‚果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
        if (source instanceof StartEvent && source.getSubProcess() != null) {
            isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, isSequential);
        }
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å…¥å£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
        if (sequenceFlows != null) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // å¦‚果目标节点已被判断为并行,后面都不需要执行,直接返回
                if (isSequential == false) {
                    break;
                }
                // è¿™æ¡çº¿è·¯å­˜åœ¨ç›®æ ‡èŠ‚ç‚¹ï¼Œè¿™æ¡çº¿è·¯å®Œæˆï¼Œè¿›å…¥ä¸‹ä¸ªçº¿è·¯
                if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) {
                    continue;
                }
                if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) {
                    isSequential = false;
                    break;
                }
                // å¦åˆ™å°±ç»§ç»­è¿­ä»£
                isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, hasSequenceFlow, isSequential);
            }
        }
        return isSequential;
    }
    /**
     * ä»ŽåŽå‘前寻路,获取到达节点的所有路线
     * ä¸å­˜åœ¨ç›´æŽ¥å›žé€€åˆ°å­æµç¨‹ï¼Œä½†æ˜¯å­˜åœ¨å›žé€€åˆ°çˆ¶çº§æµç¨‹çš„æƒ…况
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param passRoads å·²ç»ç»è¿‡çš„点集合
     * @param roads è·¯çº¿
     * @return
     */
    public static List<List<UserTask>> findRoad(FlowElement source, List<UserTask> passRoads, Set<String> hasSequenceFlow, List<List<UserTask>> roads) {
        passRoads = passRoads == null ? new ArrayList<>() : passRoads;
        roads = roads == null ? new ArrayList<>() : roads;
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        // å¦‚果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
        if (source instanceof StartEvent && source.getSubProcess() != null) {
            roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads);
        }
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å…¥å£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
        if (sequenceFlows != null && sequenceFlows.size() != 0) {
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // æ·»åŠ ç»è¿‡è·¯çº¿
                if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
                    passRoads.add((UserTask) sequenceFlow.getSourceFlowElement());
                }
                // ç»§ç»­è¿­ä»£
                roads = findRoad(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, roads);
            }
        } else {
            // æ·»åŠ è·¯çº¿
            roads.add(passRoads);
        }
        return roads;
    }
    /**
     * åŽ†å²èŠ‚ç‚¹æ•°æ®æ¸…æ´—ï¼Œæ¸…æ´—æŽ‰åˆå›žæ»šå¯¼è‡´çš„è„æ•°æ®
     * @param allElements å…¨éƒ¨èŠ‚ç‚¹ä¿¡æ¯
     * @param historicTaskInstanceList åŽ†å²ä»»åŠ¡å®žä¾‹ä¿¡æ¯ï¼Œæ•°æ®é‡‡ç”¨å¼€å§‹æ—¶é—´å‡åº
     * @return
     */
    public static List<String> historicTaskInstanceClean(Collection<FlowElement> allElements, List<HistoricTaskInstance> historicTaskInstanceList) {
        // ä¼šç­¾èŠ‚ç‚¹æ”¶é›†
        List<String> multiTask = new ArrayList<>();
        allElements.forEach(flowElement -> {
            if (flowElement instanceof UserTask) {
                // å¦‚果该节点的行为为会签行为,说明该节点为会签节点
                if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) {
                    multiTask.add(flowElement.getId());
                }
            }
        });
        // å¾ªçŽ¯æ”¾å…¥æ ˆï¼Œæ ˆ LIFO:后进先出
        Stack<HistoricTaskInstance> stack = new Stack<>();
        historicTaskInstanceList.forEach(item -> stack.push(item));
        // æ¸…洗后的历史任务实例
        List<String> lastHistoricTaskInstanceList = new ArrayList<>();
        // ç½‘关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗
        // ä¸´æ—¶ç”¨æˆ·ä»»åŠ¡ key
        StringBuilder userTaskKey = null;
        // ä¸´æ—¶è¢«åˆ æŽ‰çš„任务 key,存在并行情况
        List<String> deleteKeyList = new ArrayList<>();
        // ä¸´æ—¶è„æ•°æ®çº¿è·¯
        List<Set<String>> dirtyDataLineList = new ArrayList<>();
        // ç”±æŸä¸ªç‚¹è·³åˆ°ä¼šç­¾ç‚¹,此时出现多个会签实例对应 1 ä¸ªè·³è½¬æƒ…况,需要把这些连续脏数据都找到
        // ä¼šç­¾ç‰¹æ®Šå¤„理下标
        int multiIndex = -1;
        // ä¼šç­¾ç‰¹æ®Šå¤„理 key
        StringBuilder multiKey = null;
        // ä¼šç­¾ç‰¹æ®Šå¤„理操作标识
        boolean multiOpera = false;
        while (!stack.empty()) {
            // ä»Žè¿™é‡Œå¼€å§‹ userTaskKey éƒ½è¿˜æ˜¯ä¸Šä¸ªæ ˆçš„ key
            // æ˜¯å¦æ˜¯è„æ•°æ®çº¿è·¯ä¸Šçš„点
            final boolean[] isDirtyData = {false};
            for (Set<String> oldDirtyDataLine : dirtyDataLineList) {
                if (oldDirtyDataLine.contains(stack.peek().getTaskDefinitionKey())) {
                    isDirtyData[0] = true;
                }
            }
            // åˆ é™¤åŽŸå› ä¸ä¸ºç©ºï¼Œè¯´æ˜Žä»Žè¿™æ¡æ•°æ®å¼€å§‹å›žè·³æˆ–è€…å›žé€€çš„
            // MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内
            if (stack.peek().getDeleteReason() != null && !stack.peek().getDeleteReason().equals("MI_END")) {
                // å¯ä»¥ç†è§£ä¸ºè„çº¿è·¯èµ·ç‚¹
                String dirtyPoint = "";
                if (stack.peek().getDeleteReason().indexOf("Change activity to ") >= 0) {
                    dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", "");
                }
                // ä¼šç­¾å›žé€€åˆ é™¤åŽŸå› æœ‰ç‚¹ä¸åŒ
                if (stack.peek().getDeleteReason().indexOf("Change parent activity to ") >= 0) {
                    dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", "");
                }
                FlowElement dirtyTask = null;
                // èŽ·å–å˜æ›´èŠ‚ç‚¹çš„å¯¹åº”çš„å…¥å£å¤„è¿žçº¿
                // å¦‚果是网关并行回退情况,会变成两条脏数据路线,效果一样
                for (FlowElement flowElement : allElements) {
                    if (flowElement.getId().equals(stack.peek().getTaskDefinitionKey())) {
                        dirtyTask = flowElement;
                    }
                }
                // èŽ·å–è„æ•°æ®çº¿è·¯
                Set<String> dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, Arrays.asList(dirtyPoint.split(",")), null);
                // è‡ªå·±æœ¬èº«ä¹Ÿæ˜¯è„çº¿è·¯ä¸Šçš„点,加进去
                dirtyDataLine.add(stack.peek().getTaskDefinitionKey());
                log.info(stack.peek().getTaskDefinitionKey() + "点脏路线集合:" + dirtyDataLine);
                // æ˜¯å…¨æ–°çš„需要添加的脏线路
                boolean isNewDirtyData = true;
                for (int i = 0; i < dirtyDataLineList.size(); i++) {
                    // å¦‚果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回
                    // è¿™æ—¶ï¼Œéƒ½ä»¥ä¹‹å‰çš„脏线路节点为标准,只需合并脏线路即可,也就是路线补全
                    if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) {
                        isNewDirtyData = false;
                        dirtyDataLineList.get(i).addAll(dirtyDataLine);
                    }
                }
                // å·²ç¡®å®šæ—¶å…¨æ–°çš„脏线路
                if (isNewDirtyData) {
                    // deleteKey å•一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey å…¶å®žæ˜¯ç”±å¤šä¸ªå€¼ç»„成
                    // æŒ‰ç…§é€»è¾‘,回退后立刻生成的实例记录就是回退的记录
                    // è‡³äºŽé©³å›žæ‰€ç”Ÿæˆçš„ Key,直接从删除原因中获取,因为存在驳回到并行的情况
                    deleteKeyList.add(dirtyPoint + ",");
                    dirtyDataLineList.add(dirtyDataLine);
                }
                // æ·»åŠ åŽï¼ŒçŽ°åœ¨è¿™ä¸ªç‚¹å˜æˆè„çº¿è·¯ä¸Šçš„ç‚¹äº†
                isDirtyData[0] = true;
            }
            // å¦‚果不是脏线路上的点,说明是有效数据,添加历史实例 Key
            if (!isDirtyData[0]) {
                lastHistoricTaskInstanceList.add(stack.peek().getTaskDefinitionKey());
            }
            // æ ¡éªŒè„çº¿è·¯æ˜¯å¦ç»“束
            for (int i = 0; i < deleteKeyList.size(); i ++) {
                // å¦‚果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始
                if (multiKey == null && multiTask.contains(stack.peek().getTaskDefinitionKey())
                        && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) {
                    multiIndex = i;
                    multiKey = new StringBuilder(stack.peek().getTaskDefinitionKey());
                }
                // ä¼šç­¾è„æ•°æ®å¤„理,节点退回会签清空
                // å¦‚果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉
                if (multiKey != null && !multiKey.toString().equals(stack.peek().getTaskDefinitionKey())) {
                    deleteKeyList.set(multiIndex , deleteKeyList.get(multiIndex).replace(stack.peek().getTaskDefinitionKey() + ",", ""));
                    multiKey = null;
                    // ç»“束进行下校验删除
                    multiOpera = true;
                }
                // å…¶ä»–脏数据处理
                // å‘现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息
                // è„æ•°æ®äº§ç”Ÿçš„æ–°å®žä¾‹ä¸­æ˜¯å¦åŒ…含这条数据
                if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) {
                    // åˆ é™¤åŒ¹é…åˆ°çš„部分
                    deleteKeyList.set(i , deleteKeyList.get(i).replace(stack.peek().getTaskDefinitionKey() + ",", ""));
                }
                // å¦‚果每组中的元素都以匹配过,说明脏数据结束
                if ("".equals(deleteKeyList.get(i))) {
                    // åŒæ—¶åˆ é™¤è„æ•°æ®
                    deleteKeyList.remove(i);
                    dirtyDataLineList.remove(i);
                    break;
                }
            }
            // ä¼šç­¾æ•°æ®å¤„理需要在循环外处理,否则可能导致溢出
            // ä¼šç­¾çš„æ•°æ®è‚¯å®šæ˜¯ä¹‹å‰æ”¾è¿›åŽ»çš„æ‰€ä»¥ç†è®ºä¸Šä¸ä¼šæº¢å‡ºï¼Œä½†è¿˜æ˜¯æ ¡éªŒä¸‹
            if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) {
                // åŒæ—¶åˆ é™¤è„æ•°æ®
                deleteKeyList.remove(multiIndex);
                dirtyDataLineList.remove(multiIndex);
                multiIndex = -1;
                multiOpera = false;
            }
            // pop() æ–¹æ³•与 peek() æ–¹æ³•不同,在返回值的同时,会把值从栈中移除
            // ä¿å­˜æ–°çš„ userTaskKey åœ¨ä¸‹ä¸ªå¾ªçŽ¯ä¸­ä½¿ç”¨
            userTaskKey = new StringBuilder(stack.pop().getTaskDefinitionKey());
        }
        log.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList);
        return lastHistoricTaskInstanceList;
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/listener/UserTaskListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package org.jeecg.modules.flowable.listener;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.stereotype.Component;
/**
 */
public class UserTaskListener implements TaskListener{
    @Override
    public void notify(DelegateTask delegateTask) {
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/service/IFlowDefinitionService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package org.jeecg.modules.flowable.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.flowable.domain.dto.FlowProcDefDto;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
 */
public interface IFlowDefinitionService {
    boolean exist(String processDefinitionKey);
    /**
     * æµç¨‹å®šä¹‰åˆ—表
     *
     * @param pageNum  å½“前页码
     * @param pageSize æ¯é¡µæ¡æ•°
     * @param flowProcDefDto
     * @return æµç¨‹å®šä¹‰åˆ†é¡µåˆ—表数据
     */
    Page<FlowProcDefDto> list(Integer pageNum, Integer pageSize, FlowProcDefDto flowProcDefDto);
    /**
     * å¯¼å…¥æµç¨‹æ–‡ä»¶
     *
     * @param name
     * @param category
     * @param in
     */
    void importFile(String name, String category, InputStream in);
    /**
     * è¯»å–xml
     * @param deployId
     * @return
     */
    Result readXml(String deployId) throws IOException;
    Result readXmlByDataId(String dataId) throws IOException;
    /**
     * æ ¹æ®æµç¨‹å®šä¹‰Key启动流程实例
     *启动最新一个版本
     * @param procDefKey
     * @param variables
     * @return
     */
    Result startProcessInstanceByKey(String procDefKey, Map<String, Object> variables);
    /**
     * æ ¹æ®æµç¨‹å®šä¹‰ID启动流程实例
     *
     * @param procDefId
     * @param variables
     * @return
     */
    Result startProcessInstanceById(String procDefId, Map<String, Object> variables);
    /**
     * æ ¹æ®æµç¨‹å…³è”的数据ID启动流程实例
     * @param dataId
     * @param variables
     * @return
     */
    Result startProcessInstanceByDataId(String dataId, Map<String, Object> variables);
    /**
     * æ¿€æ´»æˆ–挂起流程定义
     *
     * @param state    çŠ¶æ€
     * @param deployId æµç¨‹éƒ¨ç½²ID
     */
    void updateState(Integer state, String deployId);
    /**
     * åˆ é™¤æµç¨‹å®šä¹‰
     *
     * @param deployId æµç¨‹éƒ¨ç½²ID act_ge_bytearray è¡¨ä¸­ deployment_id值
     */
    void delete(String deployId);
    /**
     * è¯»å–图片文件
     * @param deployId
     * @return
     */
    InputStream readImage(String deployId);
    InputStream readImageByDataId(String dataId);
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/service/IFlowInstanceService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package org.jeecg.modules.flowable.service;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.task.api.Task;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.flowable.domain.vo.FlowTaskVo;
import java.util.List;
import java.util.Map;
/**
 */
public interface IFlowInstanceService {
    List<Task> queryListByInstanceId(String instanceId);
    /**
     * ç»“束流程实例
     *
     * @param vo
     */
    void stopProcessInstance(FlowTaskVo vo);
    /**
     * æ¿€æ´»æˆ–挂起流程实例
     *
     * @param state      çŠ¶æ€
     * @param instanceId æµç¨‹å®žä¾‹ID
     */
    void updateState(Integer state, String instanceId);
    /**
     * åˆ é™¤æµç¨‹å®žä¾‹ID
     *
     * @param instanceId   æµç¨‹å®žä¾‹ID
     * @param deleteReason åˆ é™¤åŽŸå› 
     */
    void delete(String instanceId, String deleteReason);
    void deleteByDataId(String dataId, String deleteReason);
    /**
     * æ ¹æ®å®žä¾‹ID查询历史实例数据
     *
     * @param processInstanceId
     * @return
     */
    HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId);
    /**
     * æ ¹æ®æµç¨‹å®šä¹‰ID启动流程实例
     *
     * @param procDefId æµç¨‹å®šä¹‰Id
     * @param variables æµç¨‹å˜é‡
     * @return
     */
    Result startProcessInstanceById(String procDefId, Map<String, Object> variables);
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/service/IFlowTaskService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,172 @@
package org.jeecg.modules.flowable.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.flowable.task.api.Task;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.flowable.domain.dto.FlowViewerDto;
import org.jeecg.modules.flowable.domain.vo.FlowTaskVo;
import java.io.InputStream;
import java.util.List;
/**
 */
public interface IFlowTaskService {
    /**
     * å®¡æ‰¹ä»»åŠ¡
     *
     * @param task è¯·æ±‚实体参数
     */
    Result complete(FlowTaskVo task);
    Result completeByDateId(FlowTaskVo flowTaskVo);
    /**
     * é©³å›žä»»åŠ¡
     *
     * @param flowTaskVo
     */
    void taskReject(FlowTaskVo flowTaskVo);
    void taskRejectByDataId(FlowTaskVo flowTaskVo);
    /**
     * é€€å›žä»»åŠ¡
     *
     * @param flowTaskVo è¯·æ±‚实体参数
     */
    void taskReturn(FlowTaskVo flowTaskVo);
    void taskReturnByDataId(FlowTaskVo flowTaskVo);
    /**
     * èŽ·å–æ‰€æœ‰å¯å›žé€€çš„èŠ‚ç‚¹
     *
     * @param flowTaskVo
     * @return
     */
    Result findReturnTaskList(FlowTaskVo flowTaskVo);
    Result findReturnTaskListByDataId(FlowTaskVo flowTaskVo);
    /**
     * åˆ é™¤ä»»åŠ¡
     *
     * @param flowTaskVo è¯·æ±‚实体参数
     */
    void deleteTask(FlowTaskVo flowTaskVo);
    /**
     * è®¤é¢†/签收任务
     *
     * @param flowTaskVo è¯·æ±‚实体参数
     */
    void claim(FlowTaskVo flowTaskVo);
    /**
     * å–消认领/签收任务
     *
     * @param flowTaskVo è¯·æ±‚实体参数
     */
    void unClaim(FlowTaskVo flowTaskVo);
    /**
     * å§”派任务
     *
     * @param flowTaskVo è¯·æ±‚实体参数
     */
    void delegateTask(FlowTaskVo flowTaskVo);
    /**
     * è½¬åŠžä»»åŠ¡
     *
     * @param flowTaskVo è¯·æ±‚实体参数
     */
    void assignTask(FlowTaskVo flowTaskVo);
    /**
     * æˆ‘发起的流程
     * @param pageNum
     * @param pageSize
     * @return
     */
    Result myProcess(Integer pageNum, Integer pageSize);
    /**
     * å–消申请
     * @param flowTaskVo
     * @return
     */
    Result stopProcess(FlowTaskVo flowTaskVo);
    /**
     * æ’¤å›žæµç¨‹
     * @param flowTaskVo
     * @return
     */
    Result revokeProcess(FlowTaskVo flowTaskVo);
    /**
     * ä»£åŠžä»»åŠ¡åˆ—è¡¨
     *
     * @param pageNum  å½“前页码
     * @param pageSize æ¯é¡µæ¡æ•°
     * @return
     */
    Result todoList(Integer pageNum, Integer pageSize);
    /**
     * å·²åŠžä»»åŠ¡åˆ—è¡¨
     *
     * @param pageNum  å½“前页码
     * @param pageSize æ¯é¡µæ¡æ•°
     * @return
     */
    Result finishedList(Integer pageNum, Integer pageSize);
    /**
     * æµç¨‹åŽ†å²æµè½¬è®°å½•
     *
     * @param procInsId æµç¨‹å®žä¾‹Id
     * @return
     */
    Result flowRecord(String dataId);
    /**
     * æ ¹æ®ä»»åŠ¡ID查询挂载的表单信息
     *
     * @param taskId ä»»åŠ¡Id
     * @return
     */
    Task getTaskForm(String taskId);
    /**
     * èŽ·å–æµç¨‹è¿‡ç¨‹å›¾
     * @param processId
     * @return
     */
    InputStream diagram(String processId);
    /**
     * èŽ·å–æµç¨‹æ‰§è¡Œè¿‡ç¨‹
     * @param procInsId
     * @return
     */
    List<FlowViewerDto> getFlowViewer(String procInsId);
    List<FlowViewerDto> getFlowViewerByDataId(String dataId);
    /**
     * èŽ·å–æµç¨‹å˜é‡
     * @param taskId
     * @return
     */
    Result processVariables(String taskId);
    /**
     * èŽ·å–ä¸‹ä¸€èŠ‚ç‚¹
     * @param flowTaskVo ä»»åŠ¡
     * @return
     */
    Result getNextFlowNode(FlowTaskVo flowTaskVo);
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/service/impl/FlowDefinitionServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,406 @@
package org.jeecg.modules.flowable.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.io.IOUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.flowable.apithird.business.entity.FlowMyBusiness;
import org.jeecg.modules.flowable.apithird.business.service.impl.FlowMyBusinessServiceImpl;
import org.jeecg.modules.flowable.apithird.entity.ActStatus;
import org.jeecg.modules.flowable.apithird.entity.SysUser;
import org.jeecg.modules.flowable.apithird.service.FlowCallBackServiceI;
import org.jeecg.modules.flowable.apithird.service.IFlowThirdService;
import org.jeecg.modules.flowable.apithird.common.constant.ProcessConstants;
import org.jeecg.modules.flowable.apithird.common.enums.FlowComment;
import org.jeecg.modules.flowable.domain.dto.FlowNextDto;
import org.jeecg.modules.flowable.domain.dto.FlowProcDefDto;
import org.jeecg.modules.flowable.factory.FlowServiceFactory;
import org.jeecg.modules.flowable.service.IFlowDefinitionService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
/**
 * æµç¨‹å®šä¹‰
 *
 */
@Service
public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFlowDefinitionService {
    @Autowired
    IFlowThirdService iFlowThirdService;
    @Autowired
    FlowMyBusinessServiceImpl flowMyBusinessService;
    @Autowired
    FlowTaskServiceImpl flowTaskService;
    private static final String BPMN_FILE_SUFFIX = ".bpmn";
    @Override
    public boolean exist(String processDefinitionKey) {
        ProcessDefinitionQuery processDefinitionQuery
                = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey);
        long count = processDefinitionQuery.count();
        return count > 0 ? true : false;
    }
    /**
     * æµç¨‹å®šä¹‰åˆ—表
     *
     * @param pageNum  å½“前页码
     * @param pageSize æ¯é¡µæ¡æ•°
     * @param flowProcDefDto
     * @return æµç¨‹å®šä¹‰åˆ†é¡µåˆ—表数据
     */
    @Override
    public Page<FlowProcDefDto> list(Integer pageNum, Integer pageSize, FlowProcDefDto flowProcDefDto) {
        Page<FlowProcDefDto> page = new Page<>();
        // æµç¨‹å®šä¹‰åˆ—表数据查询
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        processDefinitionQuery
                //.processDefinitionId("cs:5:15e953ed-4d09-11ec-85b8-e884a5deddfc")
                .orderByProcessDefinitionKey().asc().orderByProcessDefinitionVersion().desc();
        /*=====参数=====*/
        if (StrUtil.isNotBlank(flowProcDefDto.getName())){
            processDefinitionQuery.processDefinitionNameLike("%"+flowProcDefDto.getName()+"%");
        }
        if (StrUtil.isNotBlank(flowProcDefDto.getCategory())){
            processDefinitionQuery.processDefinitionCategory(flowProcDefDto.getCategory());
        }
        if (flowProcDefDto.getSuspensionState() == 1){
            processDefinitionQuery.active();
        }
        if (StrUtil.isNotBlank(flowProcDefDto.getKey())){
            processDefinitionQuery.processDefinitionKey(flowProcDefDto.getKey());
        }
        if (flowProcDefDto.getIsLastVersion() == 1){
            processDefinitionQuery.latestVersion();
        }
        /*============*/
        page.setTotal(processDefinitionQuery.count());
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.listPage((pageNum - 1) * pageSize, pageSize);
        List<FlowProcDefDto> dataList = new ArrayList<>();
        for (ProcessDefinition processDefinition : processDefinitionList) {
            String deploymentId = processDefinition.getDeploymentId();
            Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
            FlowProcDefDto reProcDef = new FlowProcDefDto();
            BeanUtils.copyProperties(processDefinition, reProcDef);
            // æµç¨‹å®šä¹‰æ—¶é—´
            reProcDef.setDeploymentTime(deployment.getDeploymentTime());
            dataList.add(reProcDef);
        }
        page.setRecords(dataList);
        return page;
    }
    /**
     * å¯¼å…¥æµç¨‹æ–‡ä»¶
     *
     * @param name
     * @param category
     * @param in
     */
    @Override
    public void importFile(String name, String category, InputStream in) {
        Deployment deploy = repositoryService.createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in).name(name).category(category).deploy();
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
        repositoryService.setProcessDefinitionCategory(definition.getId(), category);
    }
    /**
     * è¯»å–xml
     *
     * @param deployId
     * @return
     */
    @Override
    public Result readXml(String deployId) throws IOException {
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
        InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName());
        String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
        return Result.OK("", result);
    }
    @Override
    public Result readXmlByDataId(String dataId) throws IOException {
        LambdaQueryWrapper<FlowMyBusiness> flowMyBusinessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        flowMyBusinessLambdaQueryWrapper.eq(FlowMyBusiness::getDataId,dataId)
        ;
        //如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
        FlowMyBusiness business = flowMyBusinessService.getOne(flowMyBusinessLambdaQueryWrapper);
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().processDefinitionId(business.getProcessDefinitionId()).singleResult();
        InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName());
        String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
        return Result.OK("", result);
    }
    /**
     * è¯»å–xml æ ¹æ®ä¸šåŠ¡Id
     *
     * @param dataId
     * @return
     */
    @Override
    public InputStream readImageByDataId(String dataId) {
        FlowMyBusiness business = flowMyBusinessService.getByDataId(dataId);
        String processId = business.getProcessInstanceId();
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        //流程走完的 æ˜¾ç¤ºå…¨å›¾
        if (pi == null) {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(business.getProcessDefinitionId()).singleResult();
            return this.readImage(processDefinition.getDeploymentId());
        }
        List<HistoricActivityInstance> historyProcess = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processId).list();
        List<String> activityIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        for (HistoricActivityInstance hi : historyProcess) {
            String activityType = hi.getActivityType();
            if (activityType.equals("sequenceFlow") || activityType.equals("exclusiveGateway")) {
                flows.add(hi.getActivityId());
            } else if (activityType.equals("userTask") || activityType.equals("startEvent")) {
                activityIds.add(hi.getActivityId());
            }
        }
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(processId).list();
        for (Task task : tasks) {
            activityIds.add(task.getTaskDefinitionKey());
        }
        ProcessEngineConfiguration engConf = processEngine.getProcessEngineConfiguration();
        //定义流程画布生成器
        ProcessDiagramGenerator processDiagramGenerator = engConf.getProcessDiagramGenerator();
        InputStream in = processDiagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engConf.getActivityFontName(), engConf.getLabelFontName(), engConf.getAnnotationFontName(), engConf.getClassLoader(), 1.0, true);
        return in;
    }
    /**
     * è¯»å–xml
     *
     * @param deployId
     * @return
     */
    @Override
    public InputStream readImage(String deployId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
        //获得图片流
        DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        //输出为图片
        return diagramGenerator.generateDiagram(
                bpmnModel,
                "png",
                Collections.emptyList(),
                Collections.emptyList(),
                "宋体",
                "宋体",
                "宋体",
                null,
                1.0,
                false);
    }
    /**
     * æ ¹æ®æµç¨‹å®šä¹‰ID启动流程实例
     *
     * @param procDefKey æµç¨‹å®šä¹‰Id
     * @param variables æµç¨‹å˜é‡
     * @return
     */
    @Override
    public Result startProcessInstanceByKey(String procDefKey, Map<String, Object> variables) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(procDefKey)
                .latestVersion().singleResult();
        return startProcessInstanceById(processDefinition.getId(),variables);
    }
    /**
     * æ ¹æ®æµç¨‹å®šä¹‰ID启动流程实例
     *
     * @param procDefId æµç¨‹å®šä¹‰Id
     * @param variables æµç¨‹å˜é‡
     * @return
     */
    @Override
    @Transactional
    public Result startProcessInstanceById(String procDefId, Map<String, Object> variables) {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(procDefId)
                    .singleResult();
            if (Objects.nonNull(processDefinition) && processDefinition.isSuspended()) {
                return Result.error("流程已被挂起,请先激活流程");
            }
//           variables.put("skip", true);
//           variables.put(ProcessConstants.FLOWABLE_SKIP_EXPRESSION_ENABLED, true);
            // è®¾ç½®æµç¨‹å‘起人Id到流程中
            SysUser sysUser = iFlowThirdService.getLoginUser();
            identityService.setAuthenticatedUserId(sysUser.getUsername());
            variables.put(ProcessConstants.PROCESS_INITIATOR, sysUser.getUsername());
            ProcessInstance processInstance = runtimeService.startProcessInstanceById(procDefId, variables);
            // ç»™ç¬¬ä¸€æ­¥ç”³è¯·äººèŠ‚ç‚¹è®¾ç½®ä»»åŠ¡æ‰§è¡Œäººå’Œæ„è§
            Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().singleResult();
            if (Objects.nonNull(task)) {
                taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), sysUser.getRealname() + "发起流程申请");
                taskService.setAssignee(task.getId(), sysUser.getUsername());
                //taskService.complete(task.getId(), variables);
            }
            //设置所有申请人
        /*======================todo å¯åŠ¨ä¹‹åŽ  å›žè°ƒä»¥åŠå…³é”®æ•°æ®ä¿å­˜======================*/
        //业务数据id
        String dataId = variables.get("dataId").toString();
        //如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
        FlowMyBusiness business = flowMyBusinessService.getByDataId(dataId);
        //设置数据
        FlowNextDto nextFlowNode = flowTaskService.getNextFlowNode(task.getId(), variables);
        taskService.complete(task.getId(), variables);
        //下一个实例节点  å¤šå®žä¾‹ä¼šæ˜¯ä¸€ä¸ªlist,随意取一个即可  æ•°ç»„中定义Key是一致的
        //Task task2 = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().singleResult();
        List<Task> task2List = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().list();
        Task task2 = null;
        if(task2List.size()>0) task2 = task2List.get(0);
        String doneUsers = business.getDoneUsers();
        // å¤„理过流程的人
        JSONArray doneUserList = new JSONArray();
        if (StrUtil.isNotBlank(doneUsers)){
            doneUserList = JSON.parseArray(doneUsers);
        }
        if (!doneUserList.contains(sysUser.getUsername())){
            doneUserList.add(sysUser.getUsername());
        }
        if (nextFlowNode!=null){
            //**有下一个节点
            UserTask nextTask = nextFlowNode.getUserTask();
            //能够处理下个节点的候选人
            List<SysUser> nextFlowNodeUserList = nextFlowNode.getUserList();
            List<String> collect_username = nextFlowNodeUserList.stream().map(SysUser::getUsername).collect(Collectors.toList());
            //spring容器类名
            String serviceImplName = business.getServiceImplName();
            FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
            List<String> beforeParamsCandidateUsernames = flowCallBackService.flowCandidateUsernamesOfTask(task2.getTaskDefinitionKey(), variables);
            if (CollUtil.isNotEmpty(beforeParamsCandidateUsernames)){
                // åˆ é™¤åŽé‡å†™
                for (Task task2One : task2List) {
                    for (String oldUser : collect_username) {
                        taskService.deleteCandidateUser(task2One.getId(),oldUser);
                    }
                }
                // ä¸šåŠ¡å±‚æœ‰æŒ‡å®šå€™é€‰äººï¼Œè¦†ç›–
                for (Task task2One : task2List) {
                    for (String newUser : beforeParamsCandidateUsernames) {
                        taskService.addCandidateUser(task2One.getId(),newUser);
                    }
                }
                business.setTodoUsers(JSON.toJSONString(beforeParamsCandidateUsernames));
            }
            business.setProcessDefinitionId(procDefId)
                    .setProcessInstanceId(processInstance.getProcessInstanceId())
                    .setActStatus(ActStatus.start)
                    .setProposer(sysUser.getUsername())
                    .setTaskId(task2.getId())
                    .setTaskName(nextTask.getName())
                    .setTaskNameId(nextTask.getId())
                    .setPriority(nextTask.getPriority())
                    .setDoneUsers(doneUserList.toJSONString())
                    .setTodoUsers(JSON.toJSONString(collect_username))
            ;
        } else {
        //    **没有下一个节点,流程已经结束了
            business.setProcessDefinitionId(procDefId)
                    .setProcessInstanceId(processInstance.getProcessInstanceId())
                    .setActStatus(ActStatus.pass)
                    .setProposer(sysUser.getUsername())
                    .setDoneUsers(doneUserList.toJSONString())
            ;
        }
        flowMyBusinessService.updateById(business);
        //spring容器类名
        String serviceImplName = business.getServiceImplName();
        FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
        // æµç¨‹å¤„理完后,进行回调业务层
        business.setValues(variables);
        if (flowCallBackService!=null)flowCallBackService.afterFlowHandle(business);
        return Result.OK("流程启动成功");
    }
    @Override
    public Result startProcessInstanceByDataId(String dataId, Map<String, Object> variables) {
        LambdaQueryWrapper<FlowMyBusiness> flowMyBusinessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        flowMyBusinessLambdaQueryWrapper.eq(FlowMyBusiness::getDataId, dataId)
        ;
        FlowMyBusiness business = flowMyBusinessService.getOne(flowMyBusinessLambdaQueryWrapper);
        if (business==null){
            return Result.error("未找到dataId:"+dataId);
        }
        if (StrUtil.isNotBlank(business.getProcessDefinitionId())){
            return this.startProcessInstanceById(business.getProcessDefinitionId(),variables);
        }
        return this.startProcessInstanceByKey(business.getProcessDefinitionKey(),variables);
    }
    /**
     * æ¿€æ´»æˆ–挂起流程定义
     *
     * @param state    çŠ¶æ€ æ¿€æ´»1 æŒ‚èµ·2
     * @param deployId æµç¨‹éƒ¨ç½²ID
     */
    @Override
    public void updateState(Integer state, String deployId) {
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
        // æ¿€æ´»
        if (state == 1) {
            repositoryService.activateProcessDefinitionById(procDef.getId(), true, null);
        }
        // æŒ‚èµ·
        if (state == 2) {
            repositoryService.suspendProcessDefinitionById(procDef.getId(), true, null);
        }
    }
    /**
     * åˆ é™¤æµç¨‹å®šä¹‰
     *
     * @param deployId æµç¨‹éƒ¨ç½²ID act_ge_bytearray è¡¨ä¸­ deployment_id值
     */
    @Override
    public void delete(String deployId) {
        // true å…è®¸çº§è”删除 ,不设置会导致数据库外键关联异常
        repositoryService.deleteDeployment(deployId, true);
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/service/impl/FlowInstanceServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,187 @@
package org.jeecg.modules.flowable.service.impl;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.shiro.SecurityUtils;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.flowable.apithird.business.entity.FlowMyBusiness;
import org.jeecg.modules.flowable.apithird.business.service.impl.FlowMyBusinessServiceImpl;
import org.jeecg.modules.flowable.apithird.entity.ActStatus;
import org.jeecg.modules.flowable.apithird.entity.SysUser;
import org.jeecg.modules.flowable.apithird.service.FlowCallBackServiceI;
import org.jeecg.modules.flowable.apithird.service.IFlowThirdService;
import org.jeecg.modules.flowable.apithird.common.exception.CustomException;
import org.jeecg.modules.flowable.domain.vo.FlowTaskVo;
import org.jeecg.modules.flowable.factory.FlowServiceFactory;
import org.jeecg.modules.flowable.service.IFlowInstanceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
 * <p>工作流流程实例管理<p>
 *
 */
@Service
@Slf4j
public class FlowInstanceServiceImpl extends FlowServiceFactory implements IFlowInstanceService {
    @Autowired
    IFlowThirdService iFlowThirdService;
    @Autowired
    FlowMyBusinessServiceImpl flowMyBusinessService;
    @Override
    public List<Task> queryListByInstanceId(String instanceId) {
        List<Task> list = taskService.createTaskQuery().processInstanceId(instanceId).active().list();
        return list;
    }
    /**
     * ç»“束流程实例
     *
     * @param vo
     */
    @Override
    public void stopProcessInstance(FlowTaskVo vo) {
        String taskId = vo.getTaskId();
    }
    /**
     * æ¿€æ´»æˆ–挂起流程实例
     *
     * @param state      çŠ¶æ€
     * @param instanceId æµç¨‹å®žä¾‹ID
     */
    @Override
    public void updateState(Integer state, String instanceId) {
        // æ¿€æ´»
        if (state == 1) {
            runtimeService.activateProcessInstanceById(instanceId);
        }
        // æŒ‚èµ·
        if (state == 2) {
            runtimeService.suspendProcessInstanceById(instanceId);
        }
    }
    /**
     * åˆ é™¤æµç¨‹å®žä¾‹ID
     *
     * @param instanceId   æµç¨‹å®žä¾‹ID
     * @param deleteReason åˆ é™¤åŽŸå› 
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(String instanceId, String deleteReason) {
        List<Task> task = taskService.createTaskQuery().processInstanceId(instanceId).list();
        if (CollectionUtils.isEmpty(task)) {
            throw new CustomException("流程未启动或已执行完成,取消申请失败");
        }
        // æŸ¥è¯¢åŽ†å²æ•°æ®
        HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(instanceId);
        if (historicProcessInstance.getEndTime() != null) {
            historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());
            return;
        }
        // åˆ é™¤æµç¨‹å®žä¾‹
        runtimeService.deleteProcessInstance(instanceId, deleteReason);
        // åˆ é™¤åŽ†å²æµç¨‹å®žä¾‹
        historyService.deleteHistoricProcessInstance(instanceId);
        /*======================撤回删除 å›žè°ƒä»¥åŠå…³é”®æ•°æ®ä¿å­˜======================*/
        LambdaQueryWrapper<FlowMyBusiness> flowMyBusinessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        flowMyBusinessLambdaQueryWrapper.eq(FlowMyBusiness::getProcessInstanceId,instanceId)
        ;
        //如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
        FlowMyBusiness business = flowMyBusinessService.getOne(flowMyBusinessLambdaQueryWrapper);
        //设置数据
        String doneUsers = business.getDoneUsers();
        SysUser sysUser = iFlowThirdService.getLoginUser();
        // å¤„理过流程的人
        JSONArray doneUserList = new JSONArray();
        if (StrUtil.isNotBlank(doneUsers)){
            doneUserList = JSON.parseArray(doneUsers);
        }
        if (!doneUserList.contains(sysUser.getUsername())){
            doneUserList.add(sysUser.getUsername());
        }
            business
                    .setActStatus(ActStatus.recall)
                    .setTaskId("")
                    .setTaskName("已撤回")
                    .setPriority("")
                    .setDoneUsers(doneUserList.toJSONString())
                    .setTodoUsers("")
            ;
        flowMyBusinessService.updateById(business);
        //spring容器类名
        String serviceImplName = business.getServiceImplName();
        FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
        // æµç¨‹å¤„理完后,进行回调业务层
        if (flowCallBackService!=null)flowCallBackService.afterFlowHandle(business);
    }
    @Override
    public void deleteByDataId(String dataId, String deleteReason) {
        LambdaQueryWrapper<FlowMyBusiness> flowMyBusinessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        flowMyBusinessLambdaQueryWrapper.eq(FlowMyBusiness::getDataId, dataId)
        ;
        //如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
        FlowMyBusiness business = flowMyBusinessService.getOne(flowMyBusinessLambdaQueryWrapper);
        this.delete(business.getProcessInstanceId(),deleteReason);
    }
    /**
     * æ ¹æ®å®žä¾‹ID查询历史实例数据
     *
     * @param processInstanceId
     * @return
     */
    @Override
    public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance =
                historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (Objects.isNull(historicProcessInstance)) {
            throw new FlowableObjectNotFoundException("流程实例不存在: " + processInstanceId);
        }
        return historicProcessInstance;
    }
    /**
     * æ ¹æ®æµç¨‹å®šä¹‰ID启动流程实例
     *
     * @param procDefId æµç¨‹å®šä¹‰Id
     * @param variables æµç¨‹å˜é‡
     * @return
     */
    @Override
    public Result startProcessInstanceById(String procDefId, Map<String, Object> variables) {
            // è®¾ç½®æµç¨‹å‘起人Id到流程中
            LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
            String username = user.getUsername();
//            identityService.setAuthenticatedUserId(userId.toString());
            variables.put("initiator",username);
            variables.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", true);
            ProcessInstance processInstance = runtimeService.startProcessInstanceById(procDefId, variables);
            processInstance.getProcessInstanceId();
            return Result.OK("流程启动成功");
    }
}
lxzn-module-flowable/src/main/java/org/jeecg/modules/flowable/service/impl/FlowTaskServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1429 @@
package org.jeecg.modules.flowable.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.IdentityLinkType;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.DelegationState;
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.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.flowable.apithird.business.entity.FlowMyBusiness;
import org.jeecg.modules.flowable.apithird.business.service.impl.FlowMyBusinessServiceImpl;
import org.jeecg.modules.flowable.apithird.entity.ActStatus;
import org.jeecg.modules.flowable.apithird.entity.SysUser;
import org.jeecg.modules.flowable.apithird.service.FlowCallBackServiceI;
import org.jeecg.modules.flowable.apithird.service.IFlowThirdService;
import org.jeecg.modules.flowable.apithird.common.constant.ProcessConstants;
import org.jeecg.modules.flowable.apithird.common.enums.FlowComment;
import org.jeecg.modules.flowable.apithird.common.exception.CustomException;
import org.jeecg.modules.flowable.domain.dto.FlowCommentDto;
import org.jeecg.modules.flowable.domain.dto.FlowNextDto;
import org.jeecg.modules.flowable.domain.dto.FlowTaskDto;
import org.jeecg.modules.flowable.domain.dto.FlowViewerDto;
import org.jeecg.modules.flowable.domain.vo.FlowTaskVo;
import org.jeecg.modules.flowable.factory.FlowServiceFactory;
import org.jeecg.modules.flowable.flow.CustomProcessDiagramGenerator;
import org.jeecg.modules.flowable.flow.FindNextNodeUtil;
import org.jeecg.modules.flowable.flow.FlowableUtils;
import org.jeecg.modules.flowable.service.IFlowTaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
 **/
@Service
@Slf4j
@Transactional
public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTaskService {
    @Resource
    private IFlowThirdService iFlowThirdService;
    @Autowired
    FlowMyBusinessServiceImpl flowMyBusinessService;
    /**
     * å®Œæˆä»»åŠ¡
     *
     * @param taskVo è¯·æ±‚实体参数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result complete(FlowTaskVo taskVo) {
        Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)){
            return Result.error("任务不存在");
        }
        if(StringUtils.isNotEmpty(taskVo.getComment())){
            task.setDescription(taskVo.getComment());
            taskService.saveTask(task);
        }
        SysUser loginUser = iFlowThirdService.getLoginUser();
        if (DelegationState.PENDING.equals(task.getDelegationState())) {
            taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.DELEGATE.getType(), taskVo.getComment());
            //taskService.resolveTask(taskVo.getTaskId(), taskVo.getValues());
        } else {
            System.out.println("taskVo.getTaskId()--->taskVo.getInstanceId()--->FlowComment.NORMAL.getType()--->taskVo.getComment()"+taskVo.getTaskId() + "---" + taskVo.getInstanceId() + "---" + FlowComment.NORMAL.getType() + "---"+taskVo.getComment() );
            taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment());
            taskService.setAssignee(taskVo.getTaskId(), loginUser.getUsername());
            //taskService.complete(taskVo.getTaskId(), taskVo.getValues());
        }
        /*======================审批通过  å›žè°ƒä»¥åŠå…³é”®æ•°æ®ä¿å­˜======================*/
        //业务数据id
        String dataId = taskVo.getDataId();
        //如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
        FlowMyBusiness business = flowMyBusinessService.getByDataId(dataId);
        //spring容器类名
        String serviceImplName = business.getServiceImplName();
        FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
        // æµç¨‹å˜é‡
        Map<String, Object> flowBeforeParamsValues = flowCallBackService.flowValuesOfTask(business.getTaskNameId(),taskVo.getValues());
        //设置数据
        Map<String, Object> values = taskVo.getValues();
        if (MapUtil.isNotEmpty(flowBeforeParamsValues)){
        //    ä¸šåŠ¡å±‚æœ‰è®¾ç½®å˜é‡ï¼Œä½¿ç”¨ä¸šåŠ¡å±‚çš„å˜é‡
            values = flowBeforeParamsValues;
        }
        FlowNextDto nextFlowNode = this.getNextFlowNode(task.getId(), values);
        //下一个实例节点
        if (DelegationState.PENDING.equals(task.getDelegationState())) {
            taskService.resolveTask(taskVo.getTaskId(), values);
        } else {
            taskService.complete(taskVo.getTaskId(), values);
        }
        List<Task> task2List = taskService.createTaskQuery().processInstanceId(business.getProcessInstanceId()).active().list();
        Task task2 = null;
        if (CollUtil.isNotEmpty(task2List)){
            task2 = task2List.get(0);
        }
        // ä¸‹ä¸ªèŠ‚ç‚¹å€™é€‰äºº
        List<String> beforeParamsCandidateUsernames = Lists.newArrayList();
        if(task2!=null){
            beforeParamsCandidateUsernames = flowCallBackService.flowCandidateUsernamesOfTask(task2.getTaskDefinitionKey(),taskVo.getValues());
        }
        List<String> candidateUsers = taskVo.getCandidateUsers();
        if (CollUtil.isNotEmpty(candidateUsers)){
            //    å‰ç«¯ä¼ å…¥å€™é€‰äºº è¦†ç›–
            beforeParamsCandidateUsernames = candidateUsers;
        }
        String doneUsers = business.getDoneUsers();
        // å¤„理过流程的人
        JSONArray doneUserList = new JSONArray();
        if (StrUtil.isNotBlank(doneUsers)){
            doneUserList = JSON.parseArray(doneUsers);
        }
        if (!doneUserList.contains(loginUser.getUsername())){
            doneUserList.add(loginUser.getUsername());
        }
        if (task2!=null && task.getTaskDefinitionKey().equals(task2.getTaskDefinitionKey())){
        //    * å½“前节点是会签节点,没有走完
            business.setActStatus(ActStatus.doing)
                    .setTaskId(task2.getId())
                    .setDoneUsers(doneUserList.toJSONString())
            ;
            String todoUsersStr = business.getTodoUsers();
            JSONArray todosArr = JSON.parseArray(todoUsersStr);
            // åˆ é™¤åŽé‡å†™
            for (Task task2One : task2List) {
                for (Object oldUser : todosArr) {
                    taskService.deleteCandidateUser(task2One.getId(),oldUser.toString());
                }
            }
            // é‡å†™
            if (CollUtil.isNotEmpty(beforeParamsCandidateUsernames)){
                beforeParamsCandidateUsernames.remove(loginUser.getUsername());
                // ä¸šåŠ¡å±‚æœ‰æŒ‡å®šå€™é€‰äººï¼Œè¦†ç›–
                for (Task task2One : task2List) {
                    for (String newUser : beforeParamsCandidateUsernames) {
                        taskService.addCandidateUser(task2One.getId(),newUser);
                    }
                }
                business.setTodoUsers(JSON.toJSONString(beforeParamsCandidateUsernames));
            } else {
                todosArr.remove(loginUser.getUsername());
                for (Task task2One : task2List) {
                    for (Object oldUser : todosArr) {
                        taskService.addCandidateUser(task2One.getId(),oldUser.toString());
                    }
                }
                business.setTodoUsers(todosArr.toJSONString());
            }
        } else {
        //    * ä¸‹ä¸€èŠ‚ç‚¹æ˜¯ä¼šç­¾èŠ‚ç‚¹ æˆ– æ™®é€šç”¨æˆ·èŠ‚ç‚¹ï¼Œé€»è¾‘ä¸€è‡´
            if (nextFlowNode!=null){
                //**有下一个节点
                UserTask nextTask = nextFlowNode.getUserTask();
                //能够处理下个节点的候选人
                List<SysUser> nextFlowNodeUserList = nextFlowNode.getUserList();
                List<String> collect_username = nextFlowNodeUserList.stream().map(SysUser::getUsername).collect(Collectors.toList());
                if (CollUtil.isNotEmpty(candidateUsers)){
                    //    å‰ç«¯ä¼ å…¥å€™é€‰äºº
                    collect_username = candidateUsers;
                }
                business.setActStatus(ActStatus.doing)
                        .setTaskId(task2.getId())
                        .setTaskNameId(nextTask.getId())
                        .setTaskName(nextTask.getName())
                        .setPriority(nextTask.getPriority())
                        .setDoneUsers(doneUserList.toJSONString())
                        .setTodoUsers(JSON.toJSONString(collect_username))
                ;
                // åˆ é™¤åŽé‡å†™
                for (Task task2One : task2List) {
                    for (String oldUser : collect_username) {
                        taskService.deleteCandidateUser(task2One.getId(),oldUser);
                    }
                }
                if (CollUtil.isEmpty(candidateUsers)&&CollUtil.isNotEmpty(beforeParamsCandidateUsernames)){
                    // å‰ç«¯æ²¡æœ‰ä¼ å…¥å€™é€‰äºº && ä¸šåŠ¡å±‚æœ‰æŒ‡å®šå€™é€‰äººï¼Œè¦†ç›–
                    for (Task task2One : task2List) {
                        for (String newUser : beforeParamsCandidateUsernames) {
                            taskService.addCandidateUser(task2One.getId(),newUser);
                        }
                    }
                    business.setTodoUsers(JSON.toJSONString(beforeParamsCandidateUsernames));
                } else {
                    for (Task task2One : task2List) {
                        for (String oldUser : collect_username) {
                            taskService.addCandidateUser(task2One.getId(),oldUser);
                        }
                    }
                }
            } else {
                //    **没有下一个节点,流程已经结束了
                business.setActStatus(ActStatus.pass)
                        .setDoneUsers(doneUserList.toJSONString())
                        .setTodoUsers("")
                        .setTaskId("")
                        .setTaskNameId("")
                        .setTaskName("")
                ;
            }
        }
        flowMyBusinessService.updateById(business);
        // æµç¨‹å¤„理完后,进行回调业务层
        business.setValues(values);
        if (flowCallBackService!=null)flowCallBackService.afterFlowHandle(business);
        return Result.OK();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result completeByDateId(FlowTaskVo flowTaskVo){
        return this.complete(flowTaskVo);
    }
    @Override
    public void taskRejectByDataId(FlowTaskVo flowTaskVo){
        FlowMyBusiness business = flowMyBusinessService.getByDataId(flowTaskVo.getDataId());
        flowTaskVo.setTaskId(business.getTaskId());
        this.taskReject(flowTaskVo);
    }
    /**
     * é©³å›žä»»åŠ¡
     *
     * @param flowTaskVo
     */
    @Override
    public void taskReject(FlowTaskVo flowTaskVo) {
        if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) {
            throw new CustomException("任务处于挂起状态");
        }
        // å½“前任务 task
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        // èŽ·å–æµç¨‹å®šä¹‰ä¿¡æ¯
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // èŽ·å–æ‰€æœ‰èŠ‚ç‚¹ä¿¡æ¯
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        // èŽ·å–å…¨éƒ¨èŠ‚ç‚¹åˆ—è¡¨ï¼ŒåŒ…å«å­èŠ‚ç‚¹
        Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
        // èŽ·å–å½“å‰ä»»åŠ¡èŠ‚ç‚¹å…ƒç´ 
        FlowElement source = null;
        if (allElements != null) {
            for (FlowElement flowElement : allElements) {
                // ç±»åž‹ä¸ºç”¨æˆ·èŠ‚ç‚¹
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    // èŽ·å–èŠ‚ç‚¹ä¿¡æ¯
                    source = flowElement;
                }
            }
        }
        // ç›®çš„获取所有跳转到的节点 targetIds
        // èŽ·å–å½“å‰èŠ‚ç‚¹çš„æ‰€æœ‰çˆ¶çº§ç”¨æˆ·ä»»åŠ¡èŠ‚ç‚¹
        // æ·±åº¦ä¼˜å…ˆç®—法思想:延边迭代深入
        List<UserTask> parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null);
        if (parentUserTaskList == null || parentUserTaskList.size() == 0) {
            throw new CustomException("当前节点为初始任务节点,不能驳回");
        }
        // èŽ·å–æ´»åŠ¨ ID å³èŠ‚ç‚¹ Key
        List<String> parentUserTaskKeyList = new ArrayList<>();
        parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId()));
        // èŽ·å–å…¨éƒ¨åŽ†å²èŠ‚ç‚¹æ´»åŠ¨å®žä¾‹ï¼Œå³å·²ç»èµ°è¿‡çš„èŠ‚ç‚¹åŽ†å²ï¼Œæ•°æ®é‡‡ç”¨å¼€å§‹æ—¶é—´å‡åº
        List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list();
        // æ•°æ®æ¸…洗,将回滚导致的脏数据清洗掉
        List<String> lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList);
        // æ­¤æ—¶åŽ†å²ä»»åŠ¡å®žä¾‹ä¸ºå€’åºï¼ŒèŽ·å–æœ€åŽèµ°çš„èŠ‚ç‚¹
        List<String> targetIds = new ArrayList<>();
        // å¾ªçŽ¯ç»“æŸæ ‡è¯†ï¼Œé‡åˆ°å½“å‰ç›®æ ‡èŠ‚ç‚¹çš„æ¬¡æ•°
        int number = 0;
        StringBuilder parentHistoricTaskKey = new StringBuilder();
        for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
            // å½“会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过
            if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
                continue;
            }
            parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
            if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
                number++;
            }
            // åœ¨æ•°æ®æ¸…洗后,历史节点就是唯一一条从起始到当前节点的历史记录,理论上每个点只会出现一次
            // åœ¨æµç¨‹ä¸­å¦‚果出现循环,那么每次循环中间的点也只会出现一次,再出现就是下次循环
            // number == 1,第一次遇到当前节点
            // number == 2,第二次遇到,代表最后一次的循环范围
            if (number == 2) {
                break;
            }
            // å¦‚果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点
            if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) {
                targetIds.add(historicTaskInstanceKey);
            }
        }
        // ç›®çš„获取所有需要被跳转的节点 currentIds
        // å–其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路
        UserTask oneUserTask = parentUserTaskList.get(0);
        // èŽ·å–æ‰€æœ‰æ­£å¸¸è¿›è¡Œçš„ä»»åŠ¡èŠ‚ç‚¹ Key,这些任务不能直接使用,需要找出其中需要撤回的任务
        List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        List<String> runTaskKeyList = new ArrayList<>();
        runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
        // éœ€é©³å›žä»»åŠ¡åˆ—è¡¨
        List<String> currentIds = new ArrayList<>();
        // é€šè¿‡çˆ¶çº§ç½‘关的出口连线,结合 runTaskList æ¯”对,获取需要撤回的任务
        List<UserTask> currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null);
        currentUserTaskList.forEach(item -> currentIds.add(item.getId()));
        // è§„定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况
        if (targetIds.size() > 1 && currentIds.size() > 1) {
            throw new CustomException("任务出现多对多情况,无法撤回");
        }
        // å¾ªçŽ¯èŽ·å–é‚£äº›éœ€è¦è¢«æ’¤å›žçš„èŠ‚ç‚¹çš„ID,用来设置驳回原因
        List<String> currentTaskIds = new ArrayList<>();
        currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
            if (currentId.equals(runTask.getTaskDefinitionKey())) {
                currentTaskIds.add(runTask.getId());
            }
        }));
        // è®¾ç½®é©³å›žæ„è§
        currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskVo.getComment()));
        SysUser loginUser = iFlowThirdService.getLoginUser();
        try {
            // è®¾ç½®å¤„理人
            taskService.setAssignee(task.getId(), loginUser.getUsername());
            // å¦‚果父级任务多于 1 ä¸ªï¼Œè¯´æ˜Žå½“前节点不是并行节点,原因为不考虑多对多情况
            if (targetIds.size() > 1) {
                // 1 å¯¹ å¤šä»»åŠ¡è·³è½¬ï¼ŒcurrentIds å½“前节点(1),targetIds è·³è½¬åˆ°çš„节点(多)
                runtimeService.createChangeActivityStateBuilder()
                        .processInstanceId(task.getProcessInstanceId()).
                        moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState();
            }
            // å¦‚果父级任务只有一个,因此当前任务可能为网关中的任务
            if (targetIds.size() == 1) {
                // 1 å¯¹ 1 æˆ– å¤š å¯¹ 1 æƒ…况,currentIds å½“前要跳转的节点列表(1或多),targetIds.get(0) è·³è½¬åˆ°çš„节点(1)
                runtimeService.createChangeActivityStateBuilder()
                        .processInstanceId(task.getProcessInstanceId())
                        .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState();
            }
            /*======================驳回  å›žè°ƒä»¥åŠå…³é”®æ•°æ®ä¿å­˜======================*/
            //业务数据id
            String dataId = flowTaskVo.getDataId();
            if (dataId==null) return;
            //如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
            FlowMyBusiness business = flowMyBusinessService.getByDataId(dataId);
            // é©³å›žåˆ°äº†ä¸Šä¸€ä¸ªèŠ‚ç‚¹ç­‰å¾…å¤„ç†
            List<Task> task2List = taskService.createTaskQuery().processInstanceId(business.getProcessInstanceId()).active().list();
            Task task2 = task2List.get(0);
            //spring容器类名
            String serviceImplName = business.getServiceImplName();
            FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
            Map<String, Object> values = flowTaskVo.getValues();
            if (values ==null){
                values = MapUtil.newHashMap();
                values.put("dataId",dataId);
            } else {
                values.put("dataId",dataId);
            }
            List<String> beforeParamsCandidateUsernames = flowCallBackService.flowCandidateUsernamesOfTask(task2.getTaskDefinitionKey(), values);
            //设置数据
            String doneUsers = business.getDoneUsers();
            // å¤„理过流程的人
            JSONArray doneUserList = new JSONArray();
            if (StrUtil.isNotBlank(doneUsers)){
                doneUserList = JSON.parseArray(doneUsers);
            }
            if (!doneUserList.contains(loginUser.getUsername())){
                doneUserList.add(loginUser.getUsername());
            }
            business.setActStatus(ActStatus.reject)
                    .setTaskId(task2.getId())
                    .setTaskNameId(task2.getTaskDefinitionKey())
                    .setTaskName(task2.getName())
                    .setDoneUsers(doneUserList.toJSONString())
            ;
            FlowElement targetElement = null;
            if (allElements != null) {
                for (FlowElement flowElement : allElements) {
                    // ç±»åž‹ä¸ºç”¨æˆ·èŠ‚ç‚¹
                    if (flowElement.getId().equals(task2.getTaskDefinitionKey())) {
                        // èŽ·å–èŠ‚ç‚¹ä¿¡æ¯
                        targetElement = flowElement;
                    }
                }
            }
            if (targetElement!=null){
                UserTask targetTask = (UserTask) targetElement;
                business.setPriority(targetTask.getPriority());
                if (StrUtil.equals(business.getTaskNameId(),ProcessConstants.START_NODE)){
                    //    å¼€å§‹èŠ‚ç‚¹ã€‚è®¾ç½®å¤„ç†äººä¸ºç”³è¯·äºº
                    business.setTodoUsers(JSON.toJSONString(Lists.newArrayList(business.getProposer())));
                    taskService.setAssignee(business.getTaskId(),business.getProposer());
                } else {
                    List<SysUser> sysUserFromTask = getSysUserFromTask(targetTask);
                    List<String> collect_username = sysUserFromTask.stream().map(SysUser::getUsername).collect(Collectors.toList());
                    // å‰ç«¯å­˜å…¥çš„候选人
                    List<String> candidateUsers = flowTaskVo.getCandidateUsers();
                    if (CollUtil.isNotEmpty(candidateUsers)){
                        collect_username = candidateUsers;
                    }
                    business.setTodoUsers(JSON.toJSONString(collect_username));
                    // åˆ é™¤åŽé‡å†™
                    for (Task task2One : task2List) {
                        for (String oldUser : collect_username) {
                            taskService.deleteCandidateUser(task2One.getId(),oldUser);
                        }
                    }
                    if (CollUtil.isNotEmpty(beforeParamsCandidateUsernames)){
                        if (CollUtil.isNotEmpty(candidateUsers)){
                            beforeParamsCandidateUsernames = candidateUsers;
                        }
                        // ä¸šåŠ¡å±‚æœ‰æŒ‡å®šå€™é€‰äººï¼Œè¦†ç›–
                        for (Task task2One : task2List) {
                            for (String newUser : beforeParamsCandidateUsernames) {
                                taskService.addCandidateUser(task2One.getId(), newUser);
                            }
                        }
                        business.setTodoUsers(JSON.toJSONString(beforeParamsCandidateUsernames));
                    } else {
                        for (Task task2One : task2List) {
                            for (String oldUser : collect_username) {
                                taskService.addCandidateUser(task2One.getId(), oldUser);
                            }
                        }
                    }
                }
            }
            flowMyBusinessService.updateById(business);
           // æµç¨‹å¤„理完后,进行回调业务层
            business.setValues(values);
            if (flowCallBackService!=null) flowCallBackService.afterFlowHandle(business);
        } catch (FlowableObjectNotFoundException e) {
            throw new CustomException("未找到流程实例,流程可能已发生变化");
        } catch (FlowableException e) {
            throw new CustomException("无法取消或开始活动");
        }
    }
    @Override
    public void taskReturnByDataId(FlowTaskVo flowTaskVo){
        //如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
        FlowMyBusiness business = flowMyBusinessService.getByDataId(flowTaskVo.getDataId());
        flowTaskVo.setTaskId(business.getTaskId());
        taskReturn(flowTaskVo);
    }
    /**
     * é€€å›žä»»åŠ¡
     *
     * @param flowTaskVo è¯·æ±‚实体参数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void taskReturn(FlowTaskVo flowTaskVo) {
        if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) {
            throw new CustomException("任务处于挂起状态");
        }
        // å½“前任务 task
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        // èŽ·å–æµç¨‹å®šä¹‰ä¿¡æ¯
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // èŽ·å–æ‰€æœ‰èŠ‚ç‚¹ä¿¡æ¯
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        // èŽ·å–å…¨éƒ¨èŠ‚ç‚¹åˆ—è¡¨ï¼ŒåŒ…å«å­èŠ‚ç‚¹
        Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
        // èŽ·å–å½“å‰ä»»åŠ¡èŠ‚ç‚¹å…ƒç´ 
        FlowElement source = null;
        // èŽ·å–è·³è½¬çš„èŠ‚ç‚¹å…ƒç´ 
        FlowElement target = null;
        if (allElements != null) {
            for (FlowElement flowElement : allElements) {
                // å½“前任务节点元素
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    source = flowElement;
                }
                // è·³è½¬çš„节点元素
                if (flowElement.getId().equals(flowTaskVo.getTargetKey())) {
                    target = flowElement;
                }
            }
        }
        // ä»Žå½“前节点向前扫描
        // å¦‚果存在路线上不存在目标节点,说明目标节点是在网关上或非同一路线上,不可跳转
        // å¦åˆ™ç›®æ ‡èŠ‚ç‚¹ç›¸å¯¹äºŽå½“å‰èŠ‚ç‚¹ï¼Œå±žäºŽä¸²è¡Œ
        Boolean isSequential = FlowableUtils.iteratorCheckSequentialReferTarget(source, flowTaskVo.getTargetKey(), null, null);
        if (!isSequential) {
            throw new CustomException("当前节点相对于目标节点,不属于串行关系,无法回退");
        }
        // èŽ·å–æ‰€æœ‰æ­£å¸¸è¿›è¡Œçš„ä»»åŠ¡èŠ‚ç‚¹ Key,这些任务不能直接使用,需要找出其中需要撤回的任务
        List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        List<String> runTaskKeyList = new ArrayList<>();
        runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
        // éœ€é€€å›žä»»åŠ¡åˆ—è¡¨
        List<String> currentIds = new ArrayList<>();
        // é€šè¿‡çˆ¶çº§ç½‘关的出口连线,结合 runTaskList æ¯”对,获取需要撤回的任务
        List<UserTask> currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null);
        currentUserTaskList.forEach(item -> {
            currentIds.add(item.getId());
        });
        // å¾ªçŽ¯èŽ·å–é‚£äº›éœ€è¦è¢«æ’¤å›žçš„èŠ‚ç‚¹çš„ID,用来设置驳回原因
        List<String> currentTaskIds = new ArrayList<>();
        currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
            if (currentId.equals(runTask.getTaskDefinitionKey())) {
                currentTaskIds.add(runTask.getId());
            }
        }));
        // è®¾ç½®å›žé€€æ„è§
        for (String currentTaskId : currentTaskIds) {
            taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), flowTaskVo.getComment());
        }
        SysUser loginUser = iFlowThirdService.getLoginUser();
        try {
            // è®¾ç½®å¤„理人
            taskService.setAssignee(task.getId(), loginUser.getUsername());
            // 1 å¯¹ 1 æˆ– å¤š å¯¹ 1 æƒ…况,currentIds å½“前要跳转的节点列表(1或多),targetKey è·³è½¬åˆ°çš„节点(1)
            runtimeService.createChangeActivityStateBuilder()
                    .processInstanceId(task.getProcessInstanceId())
                    .moveActivityIdsToSingleActivityId(currentIds, flowTaskVo.getTargetKey()).changeState();
            /*======================退回  å›žè°ƒä»¥åŠå…³é”®æ•°æ®ä¿å­˜======================*/
            //业务数据id
            String dataId = flowTaskVo.getDataId();
            if (dataId==null) return;
            //如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
            FlowMyBusiness business = flowMyBusinessService.getByDataId(dataId);
            //spring容器类名
            String serviceImplName = business.getServiceImplName();
            FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
            //设置数据
            String doneUsers = business.getDoneUsers();
            // å¤„理过流程的人
            JSONArray doneUserList = new JSONArray();
            if (StrUtil.isNotBlank(doneUsers)){
                doneUserList = JSON.parseArray(doneUsers);
            }
            if (!doneUserList.contains(loginUser.getUsername())){
                doneUserList.add(loginUser.getUsername());
            }
                //**跳转到目标节点
            List<Task> task2List = taskService.createTaskQuery().processInstanceId(business.getProcessInstanceId()).active().list();
            Task targetTask = task2List.get(0);
                business.setActStatus(ActStatus.reject)
                        .setTaskId(targetTask.getId())
                        .setTaskNameId(targetTask.getTaskDefinitionKey())
                        .setTaskName(targetTask.getName())
                        .setPriority(targetTask.getPriority()+"")
                        .setDoneUsers(doneUserList.toJSONString())
                ;
            if (target!=null){
                UserTask target2 = (UserTask) target;
                business.setPriority(target2.getPriority());
                if (StrUtil.equals(business.getTaskNameId(),ProcessConstants.START_NODE)){
                //    å¼€å§‹èŠ‚ç‚¹ã€‚è®¾ç½®å¤„ç†äººä¸ºç”³è¯·äºº
                    business.setTodoUsers(JSON.toJSONString(Lists.newArrayList(business.getProposer())));
                    taskService.setAssignee(business.getTaskId(),business.getProposer());
                } else {
                    List<SysUser> sysUserFromTask = getSysUserFromTask(target2);
                    List<String> collect_username = sysUserFromTask.stream().map(SysUser::getUsername).collect(Collectors.toList());
                    List<String> candidateUsers = flowTaskVo.getCandidateUsers();
                    if (CollUtil.isNotEmpty(candidateUsers)){
                        collect_username = candidateUsers;
                    }
                    business.setTodoUsers(JSON.toJSONString(collect_username));
                    // åˆ é™¤åŽé‡å†™
                    for (Task task2One : task2List) {
                        for (String oldUser : collect_username) {
                            taskService.deleteCandidateUser(task2One.getId(),oldUser);
                        }
                    }
                    Map<String, Object> values = flowTaskVo.getValues();
                    if (values==null){
                        values = MapUtil.newHashMap();
                        values.put("dataId",dataId);
                    } else {
                        values.put("dataId",dataId);
                    }
                    List<String> beforeParamsCandidateUsernames = flowCallBackService.flowCandidateUsernamesOfTask(targetTask.getTaskDefinitionKey(), values);
                    if (CollUtil.isNotEmpty(beforeParamsCandidateUsernames)){
                        if (CollUtil.isNotEmpty(candidateUsers)){
                            beforeParamsCandidateUsernames = candidateUsers;
                        }
                        // ä¸šåŠ¡å±‚æœ‰æŒ‡å®šå€™é€‰äººï¼Œè¦†ç›–
                        for (Task task2One : task2List) {
                            for (String newUser : beforeParamsCandidateUsernames) {
                                taskService.addCandidateUser(task2One.getId(),newUser);
                            }
                        }
                        business.setTodoUsers(JSON.toJSONString(beforeParamsCandidateUsernames));
                    } else {
                        for (Task task2One : task2List) {
                            for (String oldUser : collect_username) {
                                taskService.addCandidateUser(task2One.getId(), oldUser);
                            }
                        }
                    }
                }
            }
            flowMyBusinessService.updateById(business);
            // æµç¨‹å¤„理完后,进行回调业务层
            business.setValues(flowTaskVo.getValues());
            if (flowCallBackService!=null) flowCallBackService.afterFlowHandle(business);
        } catch (FlowableObjectNotFoundException e) {
            throw new CustomException("未找到流程实例,流程可能已发生变化");
        } catch (FlowableException e) {
            throw new CustomException("无法取消或开始活动");
        }
    }
    @Override
    public Result findReturnTaskListByDataId(FlowTaskVo flowTaskVo) {
        FlowMyBusiness business = flowMyBusinessService.getByDataId(flowTaskVo.getDataId());
        flowTaskVo.setTaskId(business.getTaskId());
        return findReturnTaskList(flowTaskVo);
    }
    /**
     * èŽ·å–æ‰€æœ‰å¯å›žé€€çš„èŠ‚ç‚¹
     *
     * @param flowTaskVo
     * @return
     */
    @Override
    public Result findReturnTaskList(FlowTaskVo flowTaskVo) {
        // å½“前任务 task
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        // èŽ·å–æµç¨‹å®šä¹‰ä¿¡æ¯
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // èŽ·å–æ‰€æœ‰èŠ‚ç‚¹ä¿¡æ¯ï¼Œæš‚ä¸è€ƒè™‘å­æµç¨‹æƒ…å†µ
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        // èŽ·å–å½“å‰ä»»åŠ¡èŠ‚ç‚¹å…ƒç´ 
        UserTask source = null;
        if (flowElements != null) {
            for (FlowElement flowElement : flowElements) {
                // ç±»åž‹ä¸ºç”¨æˆ·èŠ‚ç‚¹
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    source = (UserTask) flowElement;
                }
            }
        }
        // èŽ·å–èŠ‚ç‚¹çš„æ‰€æœ‰è·¯çº¿
        List<List<UserTask>> roads = FlowableUtils.findRoad(source, null, null, null);
        // å¯å›žé€€çš„节点列表
        List<UserTask> userTaskList = new ArrayList<>();
        for (List<UserTask> road : roads) {
            if (userTaskList.size() == 0) {
                // è¿˜æ²¡æœ‰å¯å›žé€€èŠ‚ç‚¹ç›´æŽ¥æ·»åŠ 
                userTaskList = road;
            } else {
                // å¦‚果已有回退节点,则比对取交集部分
                userTaskList.retainAll(road);
            }
        }
        return Result.OK(userTaskList);
    }
    /**
     * åˆ é™¤ä»»åŠ¡
     *
     * @param flowTaskVo è¯·æ±‚实体参数
     */
    @Override
    public void deleteTask(FlowTaskVo flowTaskVo) {
        // todo å¾…确认删除任务是物理删除任务 è¿˜æ˜¯é€»è¾‘删除,让这个任务直接通过?
        taskService.deleteTask(flowTaskVo.getTaskId(),flowTaskVo.getComment());
    }
    /**
     * è®¤é¢†/签收任务
     *
     * @param flowTaskVo è¯·æ±‚实体参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void claim(FlowTaskVo flowTaskVo) {
        taskService.claim(flowTaskVo.getTaskId(), flowTaskVo.getUserId());
    }
    /**
     * å–消认领/签收任务
     *
     * @param flowTaskVo è¯·æ±‚实体参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unClaim(FlowTaskVo flowTaskVo) {
        taskService.unclaim(flowTaskVo.getTaskId());
    }
    /**
     * å§”派任务
     *
     * @param flowTaskVo è¯·æ±‚实体参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delegateTask(FlowTaskVo flowTaskVo) {
        taskService.delegateTask(flowTaskVo.getTaskId(), flowTaskVo.getAssignee());
    }
    /**
     * è½¬åŠžä»»åŠ¡
     *
     * @param flowTaskVo è¯·æ±‚实体参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assignTask(FlowTaskVo flowTaskVo) {
        taskService.setAssignee(flowTaskVo.getTaskId(),flowTaskVo.getAssignee());
    }
    /**
     * æˆ‘发起的流程
     *
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Override
    public Result myProcess(Integer pageNum, Integer pageSize) {
        Page<FlowTaskDto> page = new Page<>();
        String username = iFlowThirdService.getLoginUser().getUsername();
        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
                .startedBy(username)
                .orderByProcessInstanceStartTime()
                .desc();
        List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery.listPage((pageNum - 1)*pageSize, pageSize);
        page.setTotal(historicProcessInstanceQuery.count());
        List<FlowTaskDto> flowList = new ArrayList<>();
        for (HistoricProcessInstance hisIns : historicProcessInstances) {
            FlowTaskDto flowTask = new FlowTaskDto();
            flowTask.setCreateTime(hisIns.getStartTime());
            flowTask.setFinishTime(hisIns.getEndTime());
            flowTask.setProcInsId(hisIns.getId());
            // è®¡ç®—耗时
            if (Objects.nonNull(hisIns.getEndTime())) {
                long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime();
                flowTask.setDuration(getDate(time));
            } else {
                long time = System.currentTimeMillis() - hisIns.getStartTime().getTime();
                flowTask.setDuration(getDate(time));
            }
            // æµç¨‹å®šä¹‰ä¿¡æ¯
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(hisIns.getProcessDefinitionId())
                    .singleResult();
            flowTask.setDeployId(pd.getDeploymentId());
            flowTask.setProcDefName(pd.getName());
            flowTask.setProcDefVersion(pd.getVersion());
            flowTask.setCategory(pd.getCategory());
            flowTask.setProcDefVersion(pd.getVersion());
            // å½“前所处流程 todo: æœ¬åœ°å¯åŠ¨æ”¾å¼€ä»¥ä¸‹æ³¨é‡Š
            List<Task> taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list();
            if (CollectionUtils.isNotEmpty(taskList)) {
                flowTask.setTaskId(taskList.get(0).getId());
            } else {
                List<HistoricTaskInstance> historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list();
                flowTask.setTaskId(historicTaskInstance.get(0).getId());
            }
            flowList.add(flowTask);
        }
        page.setRecords(flowList);
        return Result.OK(page);
    }
    /**
     * å–消申请
     *
     * @param flowTaskVo
     * @return
     */
    @Override
    public Result stopProcess(FlowTaskVo flowTaskVo) {
        List<Task> task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).list();
        if (CollectionUtils.isEmpty(task)) {
            throw new CustomException("流程未启动或已执行完成,取消申请失败");
        }
        SysUser loginUser = iFlowThirdService.getLoginUser();
        ProcessInstance processInstance =
                runtimeService.createProcessInstanceQuery().processInstanceId(flowTaskVo.getInstanceId()).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        if (Objects.nonNull(bpmnModel)) {
            Process process = bpmnModel.getMainProcess();
            List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
            if (CollectionUtils.isNotEmpty(endNodes)) {
                Authentication.setAuthenticatedUserId(loginUser.getUsername());
                taskService.addComment(task.get(0).getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(),
                        StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment());
                String endId = endNodes.get(0).getId();
                List<Execution> executions =
                        runtimeService.createExecutionQuery().parentId(processInstance.getProcessInstanceId()).list();
                List<String> executionIds = new ArrayList<>();
                executions.forEach(execution -> executionIds.add(execution.getId()));
                runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds,
                        endId).changeState();
            }
        }
        return Result.OK();
    }
    /**
     * æ’¤å›žæµç¨‹  todo ç›®å‰å­˜åœ¨é”™è¯¯
     *
     * @param flowTaskVo
     * @return
     */
    @Override
    public Result revokeProcess(FlowTaskVo flowTaskVo) {
        Task task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).singleResult();
        if (task == null) {
            throw new CustomException("流程未启动或已执行完成,无法撤回");
        }
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .orderByTaskCreateTime()
                .asc()
                .list();
        String myTaskId = null;
        HistoricTaskInstance myTask = null;
        for (HistoricTaskInstance hti : htiList) {
            if (loginUser.getUsername().toString().equals(hti.getAssignee())) {
                myTaskId = hti.getId();
                myTask = hti;
                break;
            }
        }
        if (null == myTaskId) {
            throw new CustomException("该任务非当前用户提交,无法撤回");
        }
        String processDefinitionId = myTask.getProcessDefinitionId();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        //变量
//      Map<String, VariableInstance> variables = runtimeService.getVariableInstances(currentTask.getExecutionId());
        String myActivityId = null;
        List<HistoricActivityInstance> haiList = historyService.createHistoricActivityInstanceQuery()
                .executionId(myTask.getExecutionId()).finished().list();
        for (HistoricActivityInstance hai : haiList) {
            if (myTaskId.equals(hai.getTaskId())) {
                myActivityId = hai.getActivityId();
                break;
            }
        }
        FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);
        Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
        String activityId = execution.getActivityId();
        FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
        //记录原活动方向
        List<SequenceFlow> oriSequenceFlows = new ArrayList<>(flowNode.getOutgoingFlows());
        return Result.OK();
    }
    /**
     * ä»£åŠžä»»åŠ¡åˆ—è¡¨
     *
     * @param pageNum  å½“前页码
     * @param pageSize æ¯é¡µæ¡æ•°
     * @return
     */
    @Override
    public Result todoList(Integer pageNum, Integer pageSize) {
        Page<FlowTaskDto> page = new Page<>();
        String username = iFlowThirdService.getLoginUser().getUsername();
        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .includeProcessVariables()
                .taskAssignee(username)
                .orderByTaskCreateTime().desc();
        page.setTotal(taskQuery.count());
        List<Task> taskList = taskQuery.listPage((pageNum - 1)*pageSize, pageSize);
        List<FlowTaskDto> flowList = new ArrayList<>();
        for (Task task : taskList) {
            FlowTaskDto flowTask = new FlowTaskDto();
            // å½“前流程信息
            flowTask.setTaskId(task.getId());
            flowTask.setTaskDefKey(task.getTaskDefinitionKey());
            flowTask.setCreateTime(task.getCreateTime());
            flowTask.setProcDefId(task.getProcessDefinitionId());
            flowTask.setTaskName(task.getName());
            // æµç¨‹å®šä¹‰ä¿¡æ¯
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(task.getProcessDefinitionId())
                    .singleResult();
            flowTask.setDeployId(pd.getDeploymentId());
            flowTask.setProcDefName(pd.getName());
            flowTask.setProcDefVersion(pd.getVersion());
            flowTask.setProcInsId(task.getProcessInstanceId());
            // æµç¨‹å‘起人信息
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(task.getProcessInstanceId())
                    .singleResult();
            SysUser startUser = iFlowThirdService.getUserByUsername(historicProcessInstance.getStartUserId());
            List<String> departNamesByUsername = iFlowThirdService.getDepartNamesByUsername(historicProcessInstance.getStartUserId());
            flowTask.setStartUserId(startUser.getUsername());
            flowTask.setStartUserName(startUser.getRealname());
            flowTask.setStartDeptName(CollUtil.join(departNamesByUsername,","));
            flowList.add(flowTask);
        }
        page.setRecords(flowList);
        return Result.OK(page);
    }
    /**
     * å·²åŠžä»»åŠ¡åˆ—è¡¨
     *
     * @param pageNum  å½“前页码
     * @param pageSize æ¯é¡µæ¡æ•°
     * @return
     */
    @Override
    public Result finishedList(Integer pageNum, Integer pageSize) {
        Page<FlowTaskDto> page = new Page<>();
        String username = iFlowThirdService.getLoginUser().getUsername();
        HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .includeProcessVariables()
                .finished()
                .taskAssignee(username)
                .orderByHistoricTaskInstanceEndTime()
                .desc();
        List<HistoricTaskInstance> historicTaskInstanceList = taskInstanceQuery.listPage((pageNum - 1)*pageSize, pageSize);
        List<FlowTaskDto> hisTaskList = Lists.newArrayList();
        for (HistoricTaskInstance histTask : historicTaskInstanceList) {
            FlowTaskDto flowTask = new FlowTaskDto();
            // å½“前流程信息
            flowTask.setTaskId(histTask.getId());
            // å®¡æ‰¹äººå‘˜ä¿¡æ¯
            flowTask.setCreateTime(histTask.getCreateTime());
            flowTask.setFinishTime(histTask.getEndTime());
            flowTask.setDuration(getDate(histTask.getDurationInMillis()));
            flowTask.setProcDefId(histTask.getProcessDefinitionId());
            flowTask.setTaskDefKey(histTask.getTaskDefinitionKey());
            flowTask.setTaskName(histTask.getName());
            // æµç¨‹å®šä¹‰ä¿¡æ¯
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(histTask.getProcessDefinitionId())
                    .singleResult();
            flowTask.setDeployId(pd.getDeploymentId());
            flowTask.setProcDefName(pd.getName());
            flowTask.setProcDefVersion(pd.getVersion());
            flowTask.setProcInsId(histTask.getProcessInstanceId());
            flowTask.setHisProcInsId(histTask.getProcessInstanceId());
            // æµç¨‹å‘起人信息
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(histTask.getProcessInstanceId())
                    .singleResult();
            SysUser startUser = iFlowThirdService.getUserByUsername(historicProcessInstance.getStartUserId());
            flowTask.setStartUserId(startUser.getUsername());
            flowTask.setStartUserName(startUser.getRealname());
            List<String> departNamesByUsername = iFlowThirdService.getDepartNamesByUsername(historicProcessInstance.getStartUserId());
            flowTask.setStartDeptName(CollUtil.join(departNamesByUsername,","));
            hisTaskList.add(flowTask);
        }
        page.setTotal(hisTaskList.size());
        page.setRecords(hisTaskList);
//        Map<String, Object> result = new HashMap<>();
//        result.put("result",page);
//        result.put("finished",true);
        return Result.OK(page);
    }
    private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Set<Object> seen = ConcurrentHashMap.newKeySet();
        return t -> seen.add(keyExtractor.apply(t));
    }
    /**
     * æµç¨‹åŽ†å²æµè½¬è®°å½•
     *
     * @param dataId æµç¨‹æ•°æ®Id
     * @return
     */
    @Override
    public Result flowRecord(String dataId) {
        FlowMyBusiness business = flowMyBusinessService.getByDataId(dataId);
        String procInsId = business.getProcessInstanceId();
        Map<String, Object> map = new HashMap<String, Object>();
        if (StringUtils.isNotBlank(procInsId)) {
            List<HistoricActivityInstance> list = historyService
                    .createHistoricActivityInstanceQuery()
                    .processInstanceId(procInsId)
                    .orderByHistoricActivityInstanceStartTime()
                    .desc().list();
            List<FlowTaskDto> hisFlowList = new ArrayList<>();
            for (HistoricActivityInstance histIns : list) {
                if (StringUtils.isNotBlank(histIns.getTaskId())) {
                    FlowTaskDto flowTask = new FlowTaskDto();
                    flowTask.setTaskId(histIns.getTaskId());
                    flowTask.setTaskName(histIns.getActivityName());
                    flowTask.setTaskDefKey(histIns.getActivityId());
                    flowTask.setCreateTime(histIns.getStartTime());
                    flowTask.setFinishTime(histIns.getEndTime());
                    if (StringUtils.isNotBlank(histIns.getAssignee())) {
                        SysUser sysUser = iFlowThirdService.getUserByUsername(histIns.getAssignee());
                        flowTask.setAssigneeId(sysUser.getUsername());
                        flowTask.setAssigneeName(sysUser.getRealname());
                        List<String> departNamesByUsername = iFlowThirdService.getDepartNamesByUsername(histIns.getAssignee());
                        flowTask.setDeptName(CollUtil.join(departNamesByUsername,","));
                        if (StrUtil.equals(histIns.getActivityId(),ProcessConstants.START_NODE)){
                        //    å¼€å§‹èŠ‚ç‚¹ï¼ŒæŠŠå€™é€‰äººè®¾ç½®ä¸ºå‘èµ·äººï¼Œè¿™ä¸ªå€¼å·²è¢«å…¶ä»–åœ°æ–¹è®¾ç½®è¿‡ï¼Œä¸Žå®žé™…åŠžç†äººä¸€è‡´å³å¯
                            flowTask.setCandidate(sysUser.getRealname());
                        }
                    }
                    // å±•示审批人员
                    List<HistoricIdentityLink> linksForTask = historyService.getHistoricIdentityLinksForTask(histIns.getTaskId());
                    StringBuilder stringBuilder = new StringBuilder();
                    for (HistoricIdentityLink identityLink : linksForTask) {
                        if (IdentityLinkType.CANDIDATE.equals(identityLink.getType())) {
                            if (StringUtils.isNotBlank(identityLink.getUserId())) {
                                SysUser sysUser = iFlowThirdService.getUserByUsername(identityLink.getUserId());
                                stringBuilder.append(sysUser.getRealname()).append(",");
                            }
                            /*已经全部设置到 CANDIDATE äº†ï¼Œä¸æ‹¿ç»„了*/
                            /*if (StringUtils.isNotBlank(identityLink.getGroupId())) {
                                List<SysRole> allRole = iFlowThirdService.getAllRole();
                                SysRole sysRole = allRole.stream().filter(o -> StringUtils.equals(identityLink.getGroupId(), o.getId())).findAny().orElse(new SysRole());
                                stringBuilder.append(sysRole.getRoleName()).append(",");
                            }*/
                        }
                    }
                    if (StringUtils.isNotBlank(stringBuilder)) {
                        flowTask.setCandidate(stringBuilder.substring(0, stringBuilder.length() - 1));
                    }
                    flowTask.setDuration(histIns.getDurationInMillis() == null || histIns.getDurationInMillis() == 0 ? null : getDate(histIns.getDurationInMillis()));
                    // èŽ·å–æ„è§è¯„è®ºå†…å®¹
                    List<Comment> commentList = taskService.getProcessInstanceComments(histIns.getProcessInstanceId());
                    commentList.forEach(comment -> {
                        if (histIns.getTaskId().equals(comment.getTaskId())) {
                            flowTask.setComment(FlowCommentDto.builder().type(comment.getType()).comment(comment.getFullMessage()).build());
                        }
                    });
                    hisFlowList.add(flowTask);
                }
            }
            map.put("flowList", hisFlowList);
        }
        // èŽ·å–åˆå§‹åŒ–è¡¨å•
        String serviceImplName = business.getServiceImplName();
        FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
        // æµç¨‹å¤„理完后,进行回调业务层
        if (flowCallBackService!=null){
            Object businessDataById = flowCallBackService.getBusinessDataById(dataId);
            map.put("formData",businessDataById);
        }
        return Result.OK(map);
    }
    /**
     * æ ¹æ®ä»»åŠ¡ID查询挂载的表单信息
     *
     * @param taskId ä»»åŠ¡Id
     * @return
     */
    @Override
    public Task getTaskForm(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        return task;
    }
    /**
     * èŽ·å–æµç¨‹è¿‡ç¨‹å›¾
     *
     * @param processId
     * @return
     */
    @Override
    public InputStream diagram(String processId) {
        String processDefinitionId;
        // èŽ·å–å½“å‰çš„æµç¨‹å®žä¾‹
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        // å¦‚果流程已经结束,则得到结束节点
        if (Objects.isNull(processInstance)) {
            HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();
            processDefinitionId = pi.getProcessDefinitionId();
        } else {// å¦‚果流程没有结束,则取当前活动节点
            // æ ¹æ®æµç¨‹å®žä¾‹ID获得当前处于活动状态的ActivityId合集
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
            processDefinitionId = pi.getProcessDefinitionId();
        }
        // èŽ·å¾—æ´»åŠ¨çš„èŠ‚ç‚¹
        List<HistoricActivityInstance> highLightedFlowList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();
        List<String> highLightedFlows = new ArrayList<>();
        List<String> highLightedNodes = new ArrayList<>();
        //高亮线
        for (HistoricActivityInstance tempActivity : highLightedFlowList) {
            if ("sequenceFlow".equals(tempActivity.getActivityType())) {
                //高亮线
                highLightedFlows.add(tempActivity.getActivityId());
            } else {
                //高亮节点
                highLightedNodes.add(tempActivity.getActivityId());
            }
        }
        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
        //获取自定义图片生成器
        ProcessDiagramGenerator diagramGenerator = new CustomProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodes, highLightedFlows, configuration.getActivityFontName(),
                configuration.getLabelFontName(), configuration.getAnnotationFontName(), configuration.getClassLoader(), 1.0, true);
        return in;
    }
    /**
     * èŽ·å–æµç¨‹æ‰§è¡Œè¿‡ç¨‹
     *
     * @param procInsId
     * @return
     */
    @Override
    public List<FlowViewerDto> getFlowViewer(String procInsId) {
        List<FlowViewerDto> flowViewerList = new ArrayList<>();
        FlowViewerDto flowViewerDto;
        // èŽ·å¾—æ´»åŠ¨çš„èŠ‚ç‚¹
        List<HistoricActivityInstance> hisActIns = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(procInsId)
                .orderByHistoricActivityInstanceStartTime()
                .asc().list();
        for (HistoricActivityInstance activityInstance : hisActIns) {
            if (!"sequenceFlow".equals(activityInstance.getActivityType())) {
                flowViewerDto = new FlowViewerDto();
                flowViewerDto.setKey(activityInstance.getActivityId());
                flowViewerDto.setCompleted(!Objects.isNull(activityInstance.getEndTime()));
                for (FlowViewerDto viewerDto : flowViewerList) {
                    String key = viewerDto.getKey();
                    if (key.equals(flowViewerDto.getKey())){
                    //    é‡å¤åˆ é™¤åŽé¢æ›´æ–°
                        flowViewerList.remove(viewerDto);
                        break;
                    }
                }
                flowViewerList.add(flowViewerDto);
            }
        }
        return flowViewerList;
    }
    @Override
    public List<FlowViewerDto> getFlowViewerByDataId(String dataId) {
        LambdaQueryWrapper<FlowMyBusiness> flowMyBusinessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        flowMyBusinessLambdaQueryWrapper.eq(FlowMyBusiness::getDataId,dataId)
        ;
        //如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
        FlowMyBusiness business = flowMyBusinessService.getOne(flowMyBusinessLambdaQueryWrapper);
        // 1.执行过的步骤
        List<FlowViewerDto> flowViewers = this.getFlowViewer(business.getProcessInstanceId());
        // 2.获取所有节点信息,根据所有节点 æŒ‰é¡ºåº  å’Œæ‰§è¡Œè¿‡çš„æ¯”较,驳回的节点就被跳过
        Process process = repositoryService.getBpmnModel(business.getProcessDefinitionId()).getProcesses().get(0);
        List<FlowElement> flowElements = Lists.newArrayList(process.getFlowElements());
        // èŽ·å–å½“å‰ä»»åŠ¡èŠ‚ç‚¹å…ƒç´ 
        List<FlowViewerDto> reflowViewers = Lists.newArrayList();
        // *顺序的Key
        List<String> orderKeys = Lists.newArrayList();
        if (flowElements != null) {
            for (FlowElement flowElement : flowElements) {
                try {
                    // å¼€å§‹èŠ‚ç‚¹
                    StartEvent stev = (StartEvent) flowElement;
                    //第一个key节点,
                    String firstKey = stev.getId();
                    orderKeys.add(firstKey);
                    //顺序获取节点
                    this.appendKeys(orderKeys, firstKey,flowElements);
                } catch (Exception e) {
                    break;
                }
            }
            for (String key : orderKeys) {
                Optional<FlowViewerDto> any = flowViewers.stream().filter(o -> StrUtil.equals(o.getKey(), key)).findAny();
                if(any.isPresent()){
                    FlowViewerDto viewerDto = any.get();
                    reflowViewers.add(viewerDto);
                    if (!viewerDto.isCompleted()){
                    //    å·²åˆ°æ­£åœ¨ç­‰å¾…执行的节点,后面的不要了
                        break;
                    }
                }
            }
        }
        for (FlowViewerDto flowViewer : flowViewers) {
            boolean present = reflowViewers.stream().filter(o -> StrUtil.equals(o.getKey(), flowViewer.getKey())).findAny().isPresent();
            flowViewer.setBack(!present);
        }
        //return reflowViewers;
        return flowViewers;
    }
    /**
     * é¡ºåºæŠ½å–节点
     * @param orderKeys å®¹å™¨
     * @param sourceKey æº
     * @param flowElements æ‰€æœ‰çš„节点对象
     */
    private void appendKeys(List<String> orderKeys, String sourceKey, List<FlowElement> flowElements) {
        for (FlowElement flowElement : flowElements) {
            try {
                SequenceFlow sf = (SequenceFlow) flowElement;
                String sourceRef = sf.getSourceRef();
                String targetRef = sf.getTargetRef();
                if (sourceKey.equals(sourceRef)&&targetRef!=null){
                    orderKeys.add(targetRef);
                    this.appendKeys(orderKeys,targetRef,flowElements);
                }
            } catch (Exception e) {
                continue;
            }
        }
    }
    /**
     * èŽ·å–æµç¨‹å˜é‡
     *
     * @param taskId
     * @return
     */
    @Override
    public Result processVariables(String taskId) {
        // æµç¨‹å˜é‡
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().includeProcessVariables().finished().taskId(taskId).singleResult();
        if (Objects.nonNull(historicTaskInstance)) {
            return Result.OK(historicTaskInstance.getProcessVariables());
        } else {
            Map<String, Object> variables = taskService.getVariables(taskId);
            return Result.OK(variables);
        }
    }
    /**
     * èŽ·å–ä¸‹ä¸€èŠ‚ç‚¹
     *
     * @param flowTaskVo ä»»åŠ¡
     * @return
     */
    @Override
    public Result getNextFlowNode(FlowTaskVo flowTaskVo) {
        // todo ä¼¼ä¹Žé€»è¾‘未写完,待检查
        FlowNextDto flowNextDto = this.getNextFlowNode(flowTaskVo.getTaskId(), flowTaskVo.getValues());
        if (flowNextDto==null) {
            return Result.OK("流程已完结", null);
        }
        return Result.OK(flowNextDto);
    }
    /**
     * èŽ·å–ä¸‹ä¸€ä¸ªèŠ‚ç‚¹ä¿¡æ¯,流程定义上的节点信息
     * @param taskId å½“前节点id
     * @param values æµç¨‹å˜é‡
     * @return å¦‚果返回null,表示没有下一个节点,流程结束
     */
    public FlowNextDto getNextFlowNode(String taskId, Map<String, Object> values) {
        //当前节点
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (Objects.nonNull(task)) {
            // ä¸‹ä¸ªä»»åŠ¡èŠ‚ç‚¹
            List<UserTask> nextUserTask = FindNextNodeUtil.getNextUserTasks(repositoryService, task, values);
            if (CollectionUtils.isNotEmpty(nextUserTask)) {
                FlowNextDto flowNextDto = new FlowNextDto();
                for (UserTask userTask : nextUserTask) {
                    flowNextDto.setUserTask(userTask);
                    //待办人员
                    List<SysUser> sysUserFromTask = this.getSysUserFromTask(userTask);
                    flowNextDto.setUserList(sysUserFromTask);
                    MultiInstanceLoopCharacteristics   multiInstance =  userTask.getLoopCharacteristics();
                    if (Objects.nonNull(multiInstance)) {
                    //    ä¼šç­¾  å¤šå®žä¾‹
                        String collectionString = multiInstance.getInputDataItem();
                        Object colObj = values.get(collectionString);
                        List<String> userNameList = null;
                        if(colObj!=null){
                            userNameList = (List) colObj;
                        }
                        if (CollUtil.isNotEmpty(userNameList)){
                            // å¾…办人员从变量中获取  å¦åˆ™å°±æ˜¯èŠ‚ç‚¹ä¸­é…ç½®çš„ç”¨æˆ· sysUserFromTask
                            List<SysUser> userList = Lists.newArrayList();
                            for (String username : userNameList) {
                                SysUser userByUsername = iFlowThirdService.getUserByUsername(username);
                                if (userByUsername==null){
                                    throw new CustomException(username + " ç”¨æˆ·åæœªæ‰¾åˆ°");
                                } else {
                                    userList.add(userByUsername);
                                }
                            }
                            flowNextDto.setUserList(userList);
                        } else {
                            // å˜é‡ä¸­æ²¡æœ‰ä¼ å…¥ï¼Œå†™å…¥èŠ‚ç‚¹ä¸­é…ç½®çš„ç”¨æˆ·
                            List<String> collect_username = sysUserFromTask.stream().map(SysUser::getUsername).collect(Collectors.toList());
                            values.put(collectionString,collect_username);
                        }
                    } else {
                        // todo è¯»å–自定义节点属性做些啥?
                        //String dataType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_DATA_TYPE);
                        String userType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_USER_TYPE);
                    }
                }
                return flowNextDto;
            }
        }
        return null;
    }
    public List<SysUser> getSysUserFromTask(UserTask userTask) {
        String assignee = userTask.getAssignee();
        if (StrUtil.isNotBlank(assignee)){
            // æŒ‡å®šå•人
            SysUser userByUsername = iFlowThirdService.getUserByUsername(assignee);
            return Lists.newArrayList(userByUsername);
        }
        List<String> candidateUsers = userTask.getCandidateUsers();
        if (CollUtil.isNotEmpty(candidateUsers)){
            // æŒ‡å®šå¤šäºº
            List<SysUser> list = iFlowThirdService.getAllUser();
            return list.stream().filter(o->candidateUsers.contains(o.getUsername())).collect(Collectors.toList());
        }
        List<String> candidateGroups = userTask.getCandidateGroups();
        if (CollUtil.isNotEmpty(candidateGroups)){
        //    æŒ‡å®šå¤šç»„
            List<SysUser> userList = Lists.newArrayList();
            for (String candidateGroup : candidateGroups) {
                List<SysUser> usersByRoleId = iFlowThirdService.getUsersByRoleId(candidateGroup);
                userList.addAll(usersByRoleId);
            }
            return userList;
        }
        return Lists.newArrayList();
    }
    /**
     * æµç¨‹å®Œæˆæ—¶é—´å¤„理
     *
     * @param ms
     * @return
     */
    private String getDate(long ms) {
        long day = ms / (24 * 60 * 60 * 1000);
        long hour = (ms / (60 * 60 * 1000) - day * 24);
        long minute = ((ms / (60 * 1000)) - day * 24 * 60 - hour * 60);
        long second = (ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60);
        if (day > 0) {
            return day + "天" + hour + "小时" + minute + "分钟";
        }
        if (hour > 0) {
            return hour + "小时" + minute + "分钟";
        }
        if (minute > 0) {
            return minute + "分钟";
        }
        if (second > 0) {
            return second + "秒";
        } else {
            return 0 + "秒";
        }
    }
}
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/business/entity/FlowMyBusiness.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/business/entity/FlowMyBusinessDto.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/business/mapper/FlowMyBusinessMapper.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/business/mapper/xml/FlowMyBusinessMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
<?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.jeecg.modules.flowable.apithird.business.mapper.FlowMyBusinessMapper">
</mapper>
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/business/service/IFlowMyBusinessService.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/business/service/impl/FlowMyBusinessServiceImpl.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/common/constant/ProcessConstants.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/common/enums/FlowComment.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/common/exception/CustomException.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/entity/ActStatus.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/entity/FlowBeforeParams.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/entity/SysCategory.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/entity/SysRole.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/entity/SysUser.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/service/FlowCallBackServiceI.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/service/FlowCommonService.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/apithird/service/IFlowThirdService.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/config/MyDefaultProcessDiagramCanvas.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/controller/FlowDefinitionController.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/controller/FlowInstanceController.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/controller/FlowTaskController.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowCommentDto$FlowCommentDtoBuilder.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowCommentDto.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowNextDto.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowProcDefDto.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowSaveXmlVo.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowTaskDto.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/dto/FlowViewerDto.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/domain/vo/FlowTaskVo.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/factory/FlowServiceFactory.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/flow/CustomProcessDiagramCanvas.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/flow/CustomProcessDiagramGenerator.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/flow/FindNextNodeUtil.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/flow/FlowableConfig.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/flow/FlowableUtils.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/listener/UserTaskListener.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/service/IFlowDefinitionService.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/service/IFlowInstanceService.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/service/IFlowTaskService.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/service/impl/FlowDefinitionServiceImpl.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/service/impl/FlowInstanceServiceImpl.class
Binary files differ
lxzn-module-flowable/target/classes/org/jeecg/modules/flowable/service/impl/FlowTaskServiceImpl.class
Binary files differ
lxzn-module-system/lxzn-system-biz/pom.xml
@@ -34,11 +34,17 @@
            <groupId>org.jeecgframework.jimureport</groupId>
            <artifactId>jimureport-spring-boot-starter</artifactId>
        </dependency>
        <!-- ç§¯æœ¨æŠ¥è¡¨ mongo redis æ”¯æŒåŒ…
        <dependency>
            <groupId>org.jeecgframework.jimureport</groupId>
            <artifactId>jimureport-nosql-starter</artifactId>
        </dependency>-->
        <dependency>
            <groupId>org.jeecgframework.boot</groupId>
            <artifactId>lxzn-module-flowable</artifactId>
            <version>3.4.3</version>
            <scope>compile</scope>
        </dependency>
        <!-- ç§¯æœ¨æŠ¥è¡¨ mongo redis æ”¯æŒåŒ…
        <dependency>
            <groupId>org.jeecgframework.jimureport</groupId>
            <artifactId>jimureport-nosql-starter</artifactId>
        </dependency>-->
    </dependencies>
</project>
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/flow/FlowThirdServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,112 @@
package org.jeecg.modules.flow;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.flowable.apithird.entity.SysCategory;
import org.jeecg.modules.flowable.apithird.entity.SysRole;
import org.jeecg.modules.flowable.apithird.entity.SysUser;
import org.jeecg.modules.flowable.apithird.service.IFlowThirdService;
import org.jeecg.modules.system.service.impl.SysRoleServiceImpl;
import org.jeecg.modules.system.service.impl.SysUserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
 * flowable模块必需实现类
 *@author PanMeiCheng
 *@date 2021/11/22
 *@version 1.0
 */
@Service
public class FlowThirdServiceImpl implements IFlowThirdService {
    @Autowired
    ISysBaseAPI sysBaseAPI;
    @Autowired
    SysUserServiceImpl sysUserService;
    @Autowired
    SysRoleServiceImpl sysRoleService;
    @Override
    public SysUser getLoginUser() {
        LoginUser sysUser = null;
        SysUser copyProperties = null;
        try {
            sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
            System.out.println( );
            copyProperties = BeanUtil.copyProperties(sysUser, SysUser.class);
        } catch (Exception e) {
            //e.printStackTrace();
        }
        return copyProperties;
    }
    @Override
    public List<SysUser> getAllUser() {
        List<org.jeecg.modules.system.entity.SysUser> list = sysUserService.list();
        List<SysUser> userList = list.stream().map(o -> BeanUtil.copyProperties(o, SysUser.class)).collect(Collectors.toList());
        return userList;
    }
    @Override
    public List<SysUser> getUsersByRoleId(String roleId) {
        Page<org.jeecg.modules.system.entity.SysUser> page = new Page<>(1,Integer.MAX_VALUE);
        IPage<org.jeecg.modules.system.entity.SysUser> userByRoleId = sysUserService.getUserByRoleId(page, roleId, null);
        List<org.jeecg.modules.system.entity.SysUser> records = userByRoleId.getRecords();
        List<SysUser> userList = records.stream().map(o -> BeanUtil.copyProperties(o, SysUser.class)).collect(Collectors.toList());
        return userList;
    }
    @Override
    public SysUser getUserByUsername(String username) {
        LoginUser userByName = sysBaseAPI.getUserByName(username);
        return userByName==null?null:BeanUtil.copyProperties(userByName, SysUser.class);
    }
    @Override
    public List<SysRole> getAllRole() {
        List<org.jeecg.modules.system.entity.SysRole> list = sysRoleService.list();
        List<SysRole> roleList = list.stream().map(o -> BeanUtil.copyProperties(o, SysRole.class)).collect(Collectors.toList());
        return roleList;
    }
    @Override
    public List<SysCategory> getAllCategory() {
        // todo èŽ·å–æµç¨‹åˆ†ç±»ä¿¡æ¯ï¼Œæ­¤å¤„ä¸ºä¾‹å­
        SysCategory category1 = new SysCategory();
        category1.setId("drApproval");
        category1.setName("导入审批流程");
        SysCategory category2 = new SysCategory();
        category2.setId("ggApproval");
        category2.setName("设计更改单流程");
        SysCategory category3 = new SysCategory();
        category3.setId("jjApproval");
        category3.setName("机加工艺规程流程");
        SysCategory category4 = new SysCategory();
        category4.setId("lsApproval");
        category4.setName("临时工艺规程流程");
        SysCategory category5 = new SysCategory();
        category5.setId("fxApproval");
        category5.setName("返修工艺规程流程");
        SysCategory category6 = new SysCategory();
        category6.setId("jjggApproval");
        category6.setName("机加更改单流程");
        ArrayList<SysCategory> sysCategories = Lists.newArrayList(category1,category2,category3,category4,category5,category6);
        return sysCategories;
    }
    @Override
    public List<String> getDepartNamesByUsername(String username) {
        List<String> departNamesByUsername = sysBaseAPI.getDepartNamesByUsername(username);
        return departNamesByUsername;
    }
}
lxzn-module-system/lxzn-system-start/pom.xml
@@ -30,10 +30,10 @@
            <artifactId>lxzn-module-dnc</artifactId>
            <version>${jeecgboot.version}</version>
        </dependency>
        <!-- activiti æ¨¡å— -->
        <!-- flowable æ¨¡å— -->
        <dependency>
            <groupId>org.jeecgframework.boot</groupId>
            <artifactId>lxzn-module-activiti</artifactId>
            <artifactId>lxzn-module-flowable</artifactId>
            <version>${jeecgboot.version}</version>
        </dependency>
lxzn-module-system/lxzn-system-start/src/main/java/org/jeecg/JeecgSystemApplication.java
@@ -1,7 +1,6 @@
package org.jeecg;
import lombok.extern.slf4j.Slf4j;
import org.activiti.spring.boot.SecurityAutoConfiguration;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
@@ -20,8 +19,7 @@
* æŠ¥é”™æé†’: æœªé›†æˆmongo报错,可以打开启动类上面的注释 exclude={MongoAutoConfiguration.class}
*/
@Slf4j
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, SecurityAutoConfiguration.class,
        SecurityAutoConfiguration.class,
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,
        ManagementWebSecurityAutoConfiguration.class})
//@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class})
public class JeecgSystemApplication extends SpringBootServletInitializer {
lxzn-module-system/lxzn-system-start/src/main/resources/application-dev.yml
@@ -126,7 +126,7 @@
        connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
      datasource:
        master:
          url: jdbc:sqlserver://192.168.1.118:1433;databasename=LXZN_TEST_HANGYU
          url: jdbc:sqlserver://192.168.1.118:1433;databasename=LXZN_TEST_HANGYU;nullCatalogMeansCurrent=true
          username: sa
          password: 123
          driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
@@ -136,22 +136,26 @@
    host: 127.0.0.1
    port: 6379
    password: 123456
  activiti:
    #1.flase:默认值。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常
    #2.true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建
    #3.create_drop: åœ¨activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
    #4.drop-create: åœ¨activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
    database-schema-update: true
    # æ£€æµ‹åŽ†å²ä¿¡æ¯è¡¨æ˜¯å¦å­˜åœ¨ï¼Œactiviti7默认不生成历史信息表,开启历史表
    db-history-used: true
    # åŽ†å²è®°å½•å­˜å‚¨ç­‰çº§
    history-level: full
    check-process-definitions: true
    #在流程引擎启动就激活AsyncExecutor,异步 true  false å…³é—­  ï¼ˆåˆ‡è®°å…³é—­ï¼‰
    async-executor-activate: false
    async-executor-enabled: true
activiti:
      enable: false # true å¯ç”¨å®¡æ‰¹ false ä¸å¯ç”¨å®¡æ‰¹
#  activiti:
#    #1.flase:默认值。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常
#    #2.true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建
#    #3.create_drop: åœ¨activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
#    #4.drop-create: åœ¨activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
#    database-schema-update: true
#    # æ£€æµ‹åŽ†å²ä¿¡æ¯è¡¨æ˜¯å¦å­˜åœ¨ï¼Œactiviti7默认不生成历史信息表,开启历史表
#    db-history-used: true
#    # åŽ†å²è®°å½•å­˜å‚¨ç­‰çº§
#    history-level: full
#    check-process-definitions: true
#    #在流程引擎启动就激活AsyncExecutor,异步 true  false å…³é—­  ï¼ˆåˆ‡è®°å…³é—­ï¼‰
#    async-executor-activate: false
#    async-executor-enabled: true
flowable:
  #关闭定时任务JOB
  async-executor-activate: false
  database-schema-update: true
#activiti:
#      enable: false # true å¯ç”¨å®¡æ‰¹ false ä¸å¯ç”¨å®¡æ‰¹
#mybatis plus è®¾ç½®
mybatis-plus:
  mapper-locations: classpath*:org/jeecg/modules/**/xml/*Mapper.xml
pom.xml
@@ -31,7 +31,7 @@
        <!-- æ•°æ®åº“驱动 -->
        <postgresql.version>42.2.25</postgresql.version>
        <ojdbc6.version>11.2.0.3</ojdbc6.version>
        <sqljdbc4.version>4.0</sqljdbc4.version>
        <mssql-jdbc.version>8.4.1.jre8</mssql-jdbc.version>
        <mysql-connector-java.version>8.0.27</mysql-connector-java.version>
        <hutool.version>5.3.8</hutool.version>
@@ -66,7 +66,7 @@
        <module>lxzn-module-system</module>
        <module>lxzn-module-mdc</module>
        <module>lxzn-module-dnc</module>
        <module>lxzn-module-activiti</module>
        <module>lxzn-module-flowable</module>
    </modules>
    <repositories>