From 4936d228848e3af608bb328f37589eb5a2cff570 Mon Sep 17 00:00:00 2001 From: hongawen <83944980@qq.com> Date: Thu, 9 May 2024 14:23:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pojo/vo/govern/voltage/SgMachineVO.java | 1 - .../voltage/impl/SgMachineServiceImpl.java | 4 - pqs-bpm/bpm-api/pom.xml | 97 +++++ .../java/com/njcn/bpm/enums/BpmConstants.java | 40 +++ .../njcn/bpm/enums/BpmModelFormTypeEnum.java | 31 ++ .../com/njcn/bpm/enums/BpmResponseEnum.java | 50 +++ .../enums/BpmTaskCandidateStrategyEnum.java | 41 +++ .../njcn/bpm/enums/BpmnModelConstants.java | 26 ++ .../java/com/njcn/bpm/enums/ErrorCode.java | 29 ++ .../bpm/enums/GlobalErrorCodeConstants.java | 40 +++ .../com/njcn/bpm/enums/IntArrayValuable.java | 15 + .../bpm/pojo/dto/BpmModelMetaInfoRespDTO.java | 41 +++ .../com/njcn/bpm/pojo/dto/CommonResult.java | 116 ++++++ .../com/njcn/bpm/pojo/dto/PageResult.java | 39 +++ .../njcn/bpm/pojo/param/BpmCategoryParam.java | 96 +++++ .../com/njcn/bpm/pojo/param/BpmFormParam.java | 88 +++++ .../njcn/bpm/pojo/param/BpmModelParam.java | 87 +++++ .../param/BpmProcessDefinitionInfoParam.java | 112 ++++++ .../com/njcn/bpm/pojo/param/PageParam.java | 34 ++ .../com/njcn/bpm/pojo/po/BpmCategory.java | 57 +++ .../java/com/njcn/bpm/pojo/po/BpmForm.java | 64 ++++ .../bpm/pojo/po/BpmProcessDefinitionInfo.java | 95 +++++ .../java/com/njcn/bpm/pojo/po/WFForm.java | 48 +++ .../com/njcn/bpm/pojo/vo/BpmCategoryVO.java | 41 +++ .../java/com/njcn/bpm/pojo/vo/BpmFormVO.java | 26 ++ .../com/njcn/bpm/pojo/vo/BpmModelRespVO.java | 51 +++ .../pojo/vo/BpmProcessDefinitionInfoVO.java | 123 +++++++ .../java/com/njcn/bpm/utils/BeanUtils.java | 61 ++++ .../com/njcn/bpm/utils/CollectionUtils.java | 318 +++++++++++++++++ .../java/com/njcn/bpm/utils/DateUtils.java | 149 ++++++++ .../java/com/njcn/bpm/utils/NumberUtils.java | 64 ++++ .../com/njcn/bpm/utils/ProcessEnumUtil.java | 46 +++ .../com/njcn/bpm/utils/ValidationUtils.java | 55 +++ pqs-bpm/bpm-boot/pom.xml | 102 ++++++ .../java/com/njcn/bpm/BpmApplication.java | 27 ++ .../behavior/BpmActivityBehaviorFactory.java | 47 +++ .../BpmParallelMultiInstanceBehavior.java | 57 +++ .../BpmSequentialMultiInstanceBehavior.java | 52 +++ .../behavior/BpmUserTaskActivityBehavior.java | 67 ++++ .../bpm/config/BpmFlowableConfiguration.java | 91 +++++ .../bpm/controller/BpmCategoryController.java | 111 ++++++ .../bpm/controller/BpmFormController.java | 112 ++++++ .../bpm/controller/BpmModelController.java | 174 +++++++++ .../BpmProcessDefinitionController.java | 123 +++++++ .../BpmProcessInstanceEventPublisher.java | 23 ++ .../event/BpmProcessInstanceStatusEvent.java | 42 +++ .../njcn/bpm/mapper/BpmCategoryMapper.java | 22 ++ .../com/njcn/bpm/mapper/BpmFormMapper.java | 21 ++ .../BpmProcessDefinitionInfoMapper.java | 13 + .../bpm/mapper/mapping/BpmCategoryMapper.xml | 21 ++ .../njcn/bpm/mapper/mapping/BpmFormMapper.xml | 20 ++ .../njcn/bpm/service/IBpmCategoryService.java | 70 ++++ .../com/njcn/bpm/service/IBpmFormService.java | 77 ++++ .../njcn/bpm/service/IBpmModelService.java | 84 +++++ .../service/IBpmProcessDefinitionService.java | 163 +++++++++ .../service/impl/BpmCategoryServiceImpl.java | 143 ++++++++ .../bpm/service/impl/BpmFormServiceImpl.java | 139 ++++++++ .../bpm/service/impl/BpmModelServiceImpl.java | 283 +++++++++++++++ .../impl/BpmProcessDefinitionServiceImpl.java | 218 ++++++++++++ .../task/IBpmProcessInstanceService.java | 139 ++++++++ .../impl/BpmProcessInstanceServiceImpl.java | 292 +++++++++++++++ .../bpm/strategy/BpmTaskCandidateInvoker.java | 113 ++++++ .../BpmTaskCandidateRoleStrategy.java | 44 +++ ...mTaskCandidateStartUserSelectStrategy.java | 74 ++++ .../BpmTaskCandidateUserStrategy.java | 39 +++ .../strategy/IBpmTaskCandidateStrategy.java | 49 +++ .../com/njcn/bpm/utils/BpmModelConvert.java | 151 ++++++++ .../utils/BpmProcessDefinitionConvert.java | 100 ++++++ .../com/njcn/bpm/utils/BpmnModelUtils.java | 331 ++++++++++++++++++ .../com/njcn/bpm/utils/FlowableUtils.java | 178 ++++++++++ .../java/com/njcn/bpm/utils/JsonUtils.java | 202 +++++++++++ .../java/com/njcn/bpm/utils/StrUtils.java | 83 +++++ .../bpm-boot/src/main/resources/bootstrap.yml | 69 ++++ pqs-bpm/pom.xml | 28 ++ .../com/njcn/autocode/utils/GenerateCode.java | 8 +- .../njcn/web/filter/XssRequestWrapper.java | 2 +- pqs-device/device-boot/pom.xml | 12 +- .../pq/pojo/bo/excel/OracleTerminalExcel.java | 2 +- pqs-gateway/src/main/resources/bootstrap.yml | 13 +- .../src/main/resources/bootstrap.yml | 3 +- .../process/enums/ProcessResponseEnum.java | 19 +- .../pojo/dto/flowable/FlowProcDefDto.java | 2 +- .../pojo/dto/flowable/FlowSaveXmlVo.java | 2 +- .../process/factory/FlowServiceFactory.java | 4 + .../system/controller/DictTypeController.java | 15 + .../com/njcn/user/api/DeptFeignClient.java | 4 + .../com/njcn/user/api/UserFeignClient.java | 32 +- .../DeptFeignClientFallbackFactory.java | 7 + .../UserFeignClientFallbackFactory.java | 12 + .../njcn/user/controller/DeptController.java | 44 +++ .../njcn/user/controller/RoleController.java | 37 ++ .../njcn/user/controller/UserController.java | 98 ++++-- .../com/njcn/user/service/IDeptService.java | 4 + .../com/njcn/user/service/IRoleService.java | 3 + .../com/njcn/user/service/IUserService.java | 6 + .../user/service/impl/DeptServiceImpl.java | 97 ++--- .../user/service/impl/RoleServiceImpl.java | 14 + .../user/service/impl/UserServiceImpl.java | 69 +++- 98 files changed, 6780 insertions(+), 108 deletions(-) create mode 100644 pqs-bpm/bpm-api/pom.xml create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmConstants.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmModelFormTypeEnum.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmResponseEnum.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmTaskCandidateStrategyEnum.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmnModelConstants.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/ErrorCode.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/GlobalErrorCodeConstants.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/IntArrayValuable.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/BpmModelMetaInfoRespDTO.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/CommonResult.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/PageResult.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmCategoryParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmFormParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmModelParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmProcessDefinitionInfoParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/PageParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmCategory.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmForm.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmProcessDefinitionInfo.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/WFForm.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmCategoryVO.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmFormVO.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmModelRespVO.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmProcessDefinitionInfoVO.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/BeanUtils.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/CollectionUtils.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/DateUtils.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/NumberUtils.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/ProcessEnumUtil.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/ValidationUtils.java create mode 100644 pqs-bpm/bpm-boot/pom.xml create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/BpmApplication.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmActivityBehaviorFactory.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmParallelMultiInstanceBehavior.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmSequentialMultiInstanceBehavior.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmUserTaskActivityBehavior.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmFlowableConfiguration.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmCategoryController.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmFormController.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmModelController.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmProcessDefinitionController.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/event/BpmProcessInstanceEventPublisher.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/event/BpmProcessInstanceStatusEvent.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/BpmCategoryMapper.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/BpmFormMapper.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/BpmProcessDefinitionInfoMapper.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/mapping/BpmCategoryMapper.xml create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/mapping/BpmFormMapper.xml create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmCategoryService.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmFormService.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmModelService.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmProcessDefinitionService.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmCategoryServiceImpl.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmFormServiceImpl.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmModelServiceImpl.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmProcessDefinitionServiceImpl.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmProcessInstanceService.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateInvoker.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateRoleStrategy.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateStartUserSelectStrategy.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateUserStrategy.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/IBpmTaskCandidateStrategy.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmModelConvert.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmProcessDefinitionConvert.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmnModelUtils.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/FlowableUtils.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/JsonUtils.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/StrUtils.java create mode 100644 pqs-bpm/bpm-boot/src/main/resources/bootstrap.yml create mode 100644 pqs-bpm/pom.xml diff --git a/pqs-advance/advance-api/src/main/java/com/njcn/advance/pojo/vo/govern/voltage/SgMachineVO.java b/pqs-advance/advance-api/src/main/java/com/njcn/advance/pojo/vo/govern/voltage/SgMachineVO.java index 856bcea66..5dbf67785 100644 --- a/pqs-advance/advance-api/src/main/java/com/njcn/advance/pojo/vo/govern/voltage/SgMachineVO.java +++ b/pqs-advance/advance-api/src/main/java/com/njcn/advance/pojo/vo/govern/voltage/SgMachineVO.java @@ -1,6 +1,5 @@ package com.njcn.advance.pojo.vo.govern.voltage; -import com.njcn.advance.pojo.po.govern.voltage.SgSensitiveUnit; import lombok.Data; import java.io.Serializable; diff --git a/pqs-advance/advance-boot/src/main/java/com/njcn/advance/service/govern/voltage/impl/SgMachineServiceImpl.java b/pqs-advance/advance-boot/src/main/java/com/njcn/advance/service/govern/voltage/impl/SgMachineServiceImpl.java index 43d7178c0..7bad94484 100644 --- a/pqs-advance/advance-boot/src/main/java/com/njcn/advance/service/govern/voltage/impl/SgMachineServiceImpl.java +++ b/pqs-advance/advance-boot/src/main/java/com/njcn/advance/service/govern/voltage/impl/SgMachineServiceImpl.java @@ -9,13 +9,9 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.njcn.advance.enums.AdvanceResponseEnum; import com.njcn.advance.mapper.govern.voltage.SgMachineMapper; import com.njcn.advance.pojo.param.govern.voltage.SgMachineParam; -import com.njcn.advance.pojo.param.govern.voltage.SgProductLineParam; import com.njcn.advance.pojo.po.govern.voltage.SgMachine; -import com.njcn.advance.pojo.po.govern.voltage.SgProductLine; import com.njcn.advance.pojo.po.govern.voltage.SgSensitiveUnit; -import com.njcn.advance.pojo.po.govern.voltage.SgUser; import com.njcn.advance.pojo.vo.govern.voltage.SgMachineVO; -import com.njcn.advance.pojo.vo.govern.voltage.SgProductLineVO; import com.njcn.advance.service.govern.voltage.ISgMachineService; import com.njcn.advance.service.govern.voltage.ISgSensitiveUnitService; import com.njcn.common.pojo.enums.common.DataStateEnum; diff --git a/pqs-bpm/bpm-api/pom.xml b/pqs-bpm/bpm-api/pom.xml new file mode 100644 index 000000000..25dbb81b4 --- /dev/null +++ b/pqs-bpm/bpm-api/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + com.njcn + pqs + 1.0.0 + ../../pom.xml + + + bpm-api + + + 8 + 8 + UTF-8 + 6.8.0 + 1.5.5.Final + 2.2.11 + + + + com.njcn + common-core + ${project.version} + + + + com.njcn + common-db + ${project.version} + + + + com.njcn + common-microservice + ${project.version} + + + + + org.flowable + flowable-spring-boot-starter-process + ${flowable.version} + + + org.flowable + flowable-spring-boot-starter-actuator + ${flowable.version} + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.mapstruct + mapstruct-jdk8 + ${mapstruct.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + com.fhs-opensource + easy-trans-spring-boot-starter + ${easy-trans.version} + + + spring-context + org.springframework + + + spring-cloud-commons + org.springframework.cloud + + + + + com.fhs-opensource + easy-trans-mybatis-plus-extend + ${easy-trans.version} + + + com.fhs-opensource + easy-trans-anno + ${easy-trans.version} + + + + + \ No newline at end of file diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmConstants.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmConstants.java new file mode 100644 index 000000000..d420f102d --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmConstants.java @@ -0,0 +1,40 @@ +package com.njcn.bpm.enums; + +import org.flowable.engine.runtime.ProcessInstance; + +/** + * BPM 通用常量 + * + * @author 芋道源码 + */ +public class BpmConstants { + + /** + * 流程实例的变量 - 状态 + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS"; + /** + * 流程实例的变量 - 发起用户选择的审批人 Map + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; + + /** + * 任务的变量 - 状态 + * + * @see org.flowable.task.api.Task#getTaskLocalVariables() + */ + public static final String TASK_VARIABLE_STATUS = "TASK_STATUS"; + /** + * 任务的变量 - 理由 + * + * 例如说:审批通过、不通过的理由 + * + * @see org.flowable.task.api.Task#getTaskLocalVariables() + */ + public static final String TASK_VARIABLE_REASON = "TASK_REASON"; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmModelFormTypeEnum.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmModelFormTypeEnum.java new file mode 100644 index 000000000..fb2d0e433 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmModelFormTypeEnum.java @@ -0,0 +1,31 @@ +package com.njcn.bpm.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 模型的表单类型的枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum BpmModelFormTypeEnum implements IntArrayValuable { + + NORMAL(10, "流程表单"), // 对应 BpmFormDO + CUSTOM(20, "业务表单") // 业务自己定义的表单,自己进行数据的存储 + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmModelFormTypeEnum::getType).toArray(); + + private final Integer type; + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmResponseEnum.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmResponseEnum.java new file mode 100644 index 000000000..3c77a433b --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmResponseEnum.java @@ -0,0 +1,50 @@ +package com.njcn.bpm.enums; + +import lombok.Getter; + +/** + * 异常处理类 + * @author qijian + * @version 1.0.0 + * @date 2022年11月11日 09:56 + */ +@Getter +public enum BpmResponseEnum { + + /** + * 过程监督异常响应码的范围: + * A00550 ~ A00649 + */ + BPM_COMMON_ERROR("A00568","工作流模块异常"), + + BPM_XML_ERROR("A00568","流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!"), + + BPM_MODEL_REPEAT("A00568","流程标识已存在"), + + BPM_MODEL_NOT_EXIST("A00568","流程模型不存在"), + + PROCESS_DEFINITION_NOT_EXISTS("A00568","流程定义不存在"), + + FORM_NOT_EXISTS("A00568","动态表单不存在"), + + MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG("A00568","部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置"), + + BPM_START_EVENT_NOT_EXIST("A00568","起始事件不存在"), + + MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS("A00568","部署流程失败,原因:BPMN 流程图中,用户任务的名字不存在"), + + REPEAT_NAME_FORM("A00568","流程表单名称重复"), + + REPEAT_CATEGORY_NAME_FORM("A00568","流程类型名称重复"), + + ; + + private final String code; + + private final String message; + + BpmResponseEnum(String code, String message) { + this.code = code; + this.message = message; + } +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmTaskCandidateStrategyEnum.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmTaskCandidateStrategyEnum.java new file mode 100644 index 000000000..83af136a4 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmTaskCandidateStrategyEnum.java @@ -0,0 +1,41 @@ +package com.njcn.bpm.enums; + +import cn.hutool.core.util.ArrayUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * BPM 任务的候选人策略枚举 + * + * 例如说:分配给指定人审批 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum BpmTaskCandidateStrategyEnum { + + ROLE(10, "角色"), + DEPT_MEMBER(20, "部门的成员"), // 包括负责人 + DEPT_LEADER(21, "部门的负责人"), + POST(22, "岗位"), + USER(30, "用户"), + START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 + USER_GROUP(40, "用户组"), + EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager + ; + + /** + * 类型 + */ + private final Integer strategy; + /** + * 描述 + */ + private final String description; + + public static BpmTaskCandidateStrategyEnum valueOf(Integer strategy) { + return ArrayUtil.firstMatch(o -> o.getStrategy().equals(strategy), values()); + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmnModelConstants.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmnModelConstants.java new file mode 100644 index 000000000..b0be6c9ae --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmnModelConstants.java @@ -0,0 +1,26 @@ +package com.njcn.bpm.enums; + +/** + * BPMN XML 常量信息 + * + * @author 芋道源码 + */ +public interface BpmnModelConstants { + + String BPMN_FILE_SUFFIX = ".bpmn"; + + /** + * BPMN 中的命名空间 + */ + String NAMESPACE = "http://flowable.org/bpmn"; + + /** + * BPMN UserTask 的扩展属性,用于标记候选人策略 + */ + String USER_TASK_CANDIDATE_STRATEGY = "candidateStrategy"; + /** + * BPMN UserTask 的扩展属性,用于标记候选人参数 + */ + String USER_TASK_CANDIDATE_PARAM = "candidateParam"; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/ErrorCode.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/ErrorCode.java new file mode 100644 index 000000000..47f35b400 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/ErrorCode.java @@ -0,0 +1,29 @@ +package com.njcn.bpm.enums; + +import lombok.Data; + +/** + * 错误码对象 + * + * 全局错误码,占用 [0, 999], 参见 {@link GlobalErrorCodeConstants} + * + * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备 + */ +@Data +public class ErrorCode { + + /** + * 错误码 + */ + private final Integer code; + /** + * 错误提示 + */ + private final String msg; + + public ErrorCode(Integer code, String message) { + this.code = code; + this.msg = message; + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/GlobalErrorCodeConstants.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/GlobalErrorCodeConstants.java new file mode 100644 index 000000000..dbceebc14 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/GlobalErrorCodeConstants.java @@ -0,0 +1,40 @@ +package com.njcn.bpm.enums; + + +/** + * 全局错误码枚举 + * 0-999 系统异常编码保留 + * + * 一般情况下,使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status + * 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的 + * 比较特殊的是,因为之前一直使用 0 作为成功,就不使用 200 啦。 + * + * @author 芋道源码 + */ +public interface GlobalErrorCodeConstants { + + ErrorCode SUCCESS = new ErrorCode(0, "成功"); + + // ========== 客户端错误段 ========== + + ErrorCode BAD_REQUEST = new ErrorCode(400, "请求参数不正确"); + ErrorCode UNAUTHORIZED = new ErrorCode(401, "账号未登录"); + ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限"); + ErrorCode NOT_FOUND = new ErrorCode(404, "请求未找到"); + ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确"); + ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许 + ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试"); + + // ========== 服务端错误段 ========== + + ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常"); + ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启"); + ErrorCode ERROR_CONFIGURATION = new ErrorCode(502, "错误的配置项"); + + // ========== 自定义错误段 ========== + ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求 + ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作"); + + ErrorCode UNKNOWN = new ErrorCode(999, "未知错误"); + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/IntArrayValuable.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/IntArrayValuable.java new file mode 100644 index 000000000..dcec39b47 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/IntArrayValuable.java @@ -0,0 +1,15 @@ +package com.njcn.bpm.enums; + +/** + * 可生成 Int 数组的接口 + * + * @author 芋道源码 + */ +public interface IntArrayValuable { + + /** + * @return int 数组 + */ + int[] array(); + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/BpmModelMetaInfoRespDTO.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/BpmModelMetaInfoRespDTO.java new file mode 100644 index 000000000..6570a2f4c --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/BpmModelMetaInfoRespDTO.java @@ -0,0 +1,41 @@ +package com.njcn.bpm.pojo.dto; + +import lombok.Data; + +/** + * BPM 流程 MetaInfo Response DTO + * 主要用于 { Model#setMetaInfo(String)} 的存储 + * + * + * @author 芋道源码 + */ +@Data +public class BpmModelMetaInfoRespDTO { + + /** + * 流程图标 + */ + private String icon; + /** + * 流程描述 + */ + private String description; + + /** + * 表单类型 + */ + private Integer formType; + /** + * 表单编号 + */ + private String formId; + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + */ + private String formCustomCreatePath; + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + */ + private String formCustomViewPath; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/CommonResult.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/CommonResult.java new file mode 100644 index 000000000..bf78a5f45 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/CommonResult.java @@ -0,0 +1,116 @@ +package com.njcn.bpm.pojo.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.njcn.bpm.enums.ErrorCode; +import com.njcn.bpm.enums.GlobalErrorCodeConstants; +import com.njcn.common.pojo.exception.BusinessException; +import lombok.Data; +import org.springframework.util.Assert; + +import java.io.Serializable; +import java.util.Objects; + +/** + * 通用返回 + * + * @param 数据泛型 + */ +@Data +public class CommonResult implements Serializable { + + /** + * 错误码 + * + * @see ErrorCode#getCode() + */ + private Integer code; + /** + * 返回数据 + */ + private T data; + /** + * 错误提示,用户可阅读 + * + * @see ErrorCode#getMsg() () + */ + private String msg; + + /** + * 将传入的 result 对象,转换成另外一个泛型结果的对象 + * + * 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。 + * + * @param result 传入的 result 对象 + * @param 返回的泛型 + * @return 新的 CommonResult 对象 + */ + public static CommonResult error(CommonResult result) { + return error(result.getCode(), result.getMsg()); + } + + public static CommonResult error(Integer code, String message) { + Assert.isTrue(!GlobalErrorCodeConstants.SUCCESS.getCode().equals(code), "code 必须是错误的!"); + CommonResult result = new CommonResult<>(); + result.code = code; + result.msg = message; + return result; + } + + public static CommonResult error( String message) { + CommonResult result = new CommonResult<>(); + result.msg = message; + return result; + } + + public static CommonResult error(ErrorCode errorCode) { + return error(errorCode.getCode(), errorCode.getMsg()); + } + + public static CommonResult success(T data) { + CommonResult result = new CommonResult<>(); + result.code = GlobalErrorCodeConstants.SUCCESS.getCode(); + result.data = data; + result.msg = ""; + return result; + } + + public static boolean isSuccess(Integer code) { + return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode()); + } + + @JsonIgnore // 避免 jackson 序列化 + public boolean isSuccess() { + return isSuccess(code); + } + + @JsonIgnore // 避免 jackson 序列化 + public boolean isError() { + return !isSuccess(); + } + + // ========= 和 Exception 异常体系集成 ========= + + /** + */ + public void checkError() throws BusinessException { + if (isSuccess()) { + return; + } + // 业务异常 + throw new BusinessException(msg); + } + + /** + * 如果没有,则返回 {@link #data} 数据 + */ + @JsonIgnore // 避免 jackson 序列化 + public T getCheckedData() { + checkError(); + return data; + } + + public static CommonResult error(BusinessException serviceException) { + return error( serviceException.getMessage()); + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/PageResult.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/PageResult.java new file mode 100644 index 000000000..4850319df --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/PageResult.java @@ -0,0 +1,39 @@ +package com.njcn.bpm.pojo.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +@Schema(description = "分页结果") +@Data +public final class PageResult implements Serializable { + + private List list; + + private Long total; + + public PageResult() { + } + + public PageResult(List list, Long total) { + this.list = list; + this.total = total; + } + + public PageResult(Long total) { + this.list = new ArrayList<>(); + this.total = total; + } + + public static PageResult empty() { + return new PageResult<>(0L); + } + + public static PageResult empty(Long total) { + return new PageResult<>(total); + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmCategoryParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmCategoryParam.java new file mode 100644 index 000000000..fa7a964ed --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmCategoryParam.java @@ -0,0 +1,96 @@ +package com.njcn.bpm.pojo.param; + +import com.njcn.common.pojo.constant.PatternRegex; +import com.njcn.web.constant.ValidMessage; +import com.njcn.web.pojo.param.BaseParam; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import java.io.Serializable; + +/** + * BPM 流程分类 + * + * @author 芋道源码 + */ +@Data +public class BpmCategoryParam implements Serializable { + + + /** + * 分类名 + */ + @ApiModelProperty("分类名") + @NotNull(message = "分类名不能为空") + private String name; + + /** + * 分类标志 + */ + @ApiModelProperty("分类标志") + @NotNull(message = "分类标志不能为空") + private String code; + + /** + * 分类描述 + */ + @ApiModelProperty("分类描述") + private String description; + + /** + * 分类状态 + * + */ + @ApiModelProperty("分类状态") + private Integer status = 1; + /** + * 分类排序 + */ + @ApiModelProperty("分类排序") + private Integer sort = 100; + + /** + * 更新操作实体 + */ + @Data + @EqualsAndHashCode(callSuper = true) + public static class BpmCategoryUpdateParam extends BpmCategoryParam { + + /** + * 表Id + */ + @ApiModelProperty("id") + @NotBlank(message = ValidMessage.ID_NOT_BLANK) + @Pattern(regexp = PatternRegex.SYSTEM_ID, message = ValidMessage.ID_FORMAT_ERROR) + private String id; + + } + + + /** + * 分页查询实体 + */ + @Data + @EqualsAndHashCode(callSuper = true) + public static class BpmCategoryQueryParam extends BaseParam { + + /** + * 表单名称 + */ + @ApiModelProperty("分类名称") + private String name; + + + /** + * 分类标志 + */ + @ApiModelProperty("分类标志") + private String code; + + + } + +} \ No newline at end of file diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmFormParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmFormParam.java new file mode 100644 index 000000000..549225c5b --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmFormParam.java @@ -0,0 +1,88 @@ +package com.njcn.bpm.pojo.param; + +import com.njcn.common.pojo.constant.PatternRegex; +import com.njcn.web.constant.ValidMessage; +import com.njcn.web.pojo.param.BaseParam; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import java.io.Serializable; +import java.util.List; + +@Data +public class BpmFormParam implements Serializable { + + /** + * 表单名 + */ + @ApiModelProperty("表单名称") + @NotNull(message = "表单名称不能为空") + private String name; + + /** + * 状态 + */ + @ApiModelProperty("表单状态") + @NotNull(message = "表单状态不能为空") + private Integer status; + + /** + * 表单的配置 + */ + @ApiModelProperty("表单的配置-JSON 字符串") + @NotNull(message = "表单的配置不能为空") + private String conf; + + /** + * 表单项的数组 + * + */ + @ApiModelProperty("表单项的数组-JSON 字符串的数组") + @NotNull(message = "表单项的数组不能为空") + private List fields; + + /** + * 备注 + */ + @ApiModelProperty("备注") + private String remark; + + + /** + * 更新操作实体 + */ + @Data + @EqualsAndHashCode(callSuper = true) + public static class BpmFormUpdateParam extends BpmFormParam { + + /** + * 表Id + */ + @ApiModelProperty("id") + @NotBlank(message = ValidMessage.ID_NOT_BLANK) + @Pattern(regexp = PatternRegex.SYSTEM_ID, message = ValidMessage.ID_FORMAT_ERROR) + private String id; + + } + + + /** + * 分页查询实体 + */ + @Data + @EqualsAndHashCode(callSuper = true) + public static class BpmFormQueryParam extends BaseParam { + + /** + * 表单名称 + */ + @ApiModelProperty("表单名称") + private String name; + + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmModelParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmModelParam.java new file mode 100644 index 000000000..fbed09f9a --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmModelParam.java @@ -0,0 +1,87 @@ +package com.njcn.bpm.pojo.param; + +import com.njcn.common.pojo.constant.PatternRegex; +import com.njcn.web.constant.ValidMessage; +import com.njcn.web.pojo.param.BaseParam; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.URL; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import java.io.Serializable; + + +@Data +public class BpmModelParam implements Serializable { + + + @ApiModelProperty("流程标识") + @NotNull(message = "流程标识不能为空") + private String key; + + @ApiModelProperty("流程名称") + @NotNull(message = "流程名称不能为空") + private String name; + + @ApiModelProperty( "流程图标") + private String icon; + + @ApiModelProperty("流程分类") + private String category; + + @ApiModelProperty("BPMN XML") + private String bpmnXml; + + @ApiModelProperty("流程表单") + private String formId; + + @ApiModelProperty("流程描述") + private String description; + + private Integer formType = 10; + + + private String formCustomCreatePath; + + private String formCustomViewPath; + + /** + * 更新操作实体 + */ + @Data + @EqualsAndHashCode(callSuper = true) + public static class BpmModelUpdateParam extends BpmModelParam { + + /** + * 表Id + */ + @ApiModelProperty("id") + @NotBlank(message = ValidMessage.ID_NOT_BLANK) + private String id; + + } + + + /** + * 分页查询实体 + */ + @Data + @EqualsAndHashCode(callSuper = true) + public static class BpmModelQueryParam extends BaseParam { + + @ApiModelProperty("流程标识") + private String key; + + @ApiModelProperty("流程名称") + private String name; + + @ApiModelProperty("流程分类") + private String category; + + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmProcessDefinitionInfoParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmProcessDefinitionInfoParam.java new file mode 100644 index 000000000..52e9185d1 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmProcessDefinitionInfoParam.java @@ -0,0 +1,112 @@ +package com.njcn.bpm.pojo.param; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.njcn.common.pojo.constant.PatternRegex; +import com.njcn.db.bo.BaseEntity; +import com.njcn.web.constant.ValidMessage; +import com.njcn.web.pojo.param.BaseParam; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import java.io.Serializable; +import java.util.List; + +/** + * BPM 流程定义的拓信息 + * 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表 + */ +@Data +public class BpmProcessDefinitionInfoParam extends BaseEntity implements Serializable { + + + /** + * 流程定义的编号 + *

+ * 关联 ProcessDefinition 的 id 属性 + */ + private String processDefinitionId; + /** + * 流程模型的编号 + *

+ * 关联 Model 的 id 属性 + */ + private String modelId; + + /** + * 图标 + */ + private String icon; + /** + * 描述 + */ + private String description; + + /** + * 表单类型 + */ + private Integer formType; + + /** + * 动态表单编号 + */ + private String formId; + + /** + * 表单的配置 + */ + private String formConf; + + /** + * 表单项的数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List formFields; + + + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + */ + private String formCustomCreatePath; + + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + */ + private String formCustomViewPath; + + /** + * 更新操作实体 + */ + @Data + @EqualsAndHashCode(callSuper = true) + public static class BpmProcessDefinitionInfoUpdateParam extends BpmProcessDefinitionInfoParam { + + /** + * 表Id + */ + @ApiModelProperty("id") + @NotBlank(message = ValidMessage.ID_NOT_BLANK) + @Pattern(regexp = PatternRegex.SYSTEM_ID, message = ValidMessage.ID_FORMAT_ERROR) + private String id; + + } + + + /** + * 分页查询实体 + */ + @Data + @EqualsAndHashCode(callSuper = true) + public static class BpmProcessDefinitionInfoQueryParam extends BaseParam { + + @ApiModelProperty("标识-精准匹配") + private String key; + + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/PageParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/PageParam.java new file mode 100644 index 000000000..2991e568c --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/PageParam.java @@ -0,0 +1,34 @@ +package com.njcn.bpm.pojo.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +@Schema(description="分页参数") +@Data +public class PageParam implements Serializable { + + private static final Integer PAGE_NO = 1; + private static final Integer PAGE_SIZE = 10; + + /** + * 每页条数 - 不分页 + * + * 例如说,导出接口,可以设置 {@link #pageSize} 为 -1 不分页,查询所有数据。 + */ + public static final Integer PAGE_SIZE_NONE = -1; + + @NotNull(message = "页码不能为空") + @Min(value = 1, message = "页码最小值为 1") + private Integer pageNo = PAGE_NO; + + @NotNull(message = "每页条数不能为空") + @Min(value = 1, message = "每页条数最小值为 1") + @Max(value = 100, message = "每页条数最大值为 100") + private Integer pageSize = PAGE_SIZE; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmCategory.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmCategory.java new file mode 100644 index 000000000..5d22181fa --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmCategory.java @@ -0,0 +1,57 @@ +package com.njcn.bpm.pojo.po; + +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.njcn.db.bo.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * BPM 流程分类 DO + * + * @author 芋道源码 + */ +@TableName("bpm_category") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmCategory extends BaseEntity implements Serializable { + + /** + * 分类编号 + */ + @TableId + private String id; + /** + * 分类名 + */ + private String name; + /** + * 分类标志 + */ + private String code; + /** + * 分类描述 + */ + private String description; + /** + * 分类状态 + * + */ + private Integer status; + /** + * 分类排序 + */ + private Integer sort; + + /** + * 状态:0-删除 1-正常 + */ + private Integer state; +} \ No newline at end of file diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmForm.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmForm.java new file mode 100644 index 000000000..5f7bd58a2 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmForm.java @@ -0,0 +1,64 @@ +package com.njcn.bpm.pojo.po; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.njcn.db.bo.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * BPM 工作流的表单定义 + * 用于工作流的申请表单,需要动态配置的场景 + * + * @author 芋道源码 + */ +@TableName(value = "bpm_form", autoResultMap = true) +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmForm extends BaseEntity implements Serializable { + + /** + * 编号 + */ + @TableId + private String id; + /** + * 表单名 + */ + private String name; + /** + * 状态 + */ + private Integer status; + /** + * 表单的配置 + */ + private String conf; + /** + * 表单项的数组 + * + * 目前直接将 https://github.com/JakHuang/form-generator 生成的 JSON 串,直接保存 + * 定义:https://github.com/JakHuang/form-generator/issues/46 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List fields; + /** + * 备注 + */ + private String remark; + + /** + * 状态:0-删除 1-正常 + */ + private Integer state; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmProcessDefinitionInfo.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmProcessDefinitionInfo.java new file mode 100644 index 000000000..876f74e22 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmProcessDefinitionInfo.java @@ -0,0 +1,95 @@ +package com.njcn.bpm.pojo.po; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.njcn.db.bo.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * BPM 流程定义的拓信息 + * 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表 + * + */ +@TableName(value = "bpm_process_definition_info", autoResultMap = true) +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmProcessDefinitionInfo extends BaseEntity implements Serializable { + + /** + * 编号 + */ + @TableId + private String id; + /** + * 流程定义的编号 + * + * 关联 ProcessDefinition 的 id 属性 + */ + private String processDefinitionId; + /** + * 流程模型的编号 + * + * 关联 Model 的 id 属性 + */ + private String modelId; + + /** + * 图标 + */ + private String icon; + /** + * 描述 + */ + private String description; + + /** + * 表单类型 + * + */ + private Integer formType; + + /** + * 动态表单编号 + * + */ + private String formId; + + /** + * 表单的配置 + * + */ + private String formConf; + + /** + * 表单项的数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List formFields; + + + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + */ + private String formCustomCreatePath; + + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + */ + private String formCustomViewPath; + + /** + * 状态:0-删除 1-正常 + */ + private Integer state; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/WFForm.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/WFForm.java new file mode 100644 index 000000000..a68bcae91 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/WFForm.java @@ -0,0 +1,48 @@ +package com.njcn.bpm.pojo.po; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.njcn.db.bo.BaseEntity; +import lombok.Data; + +import java.io.Serializable; + +/** + *

+ * 流程表单 + *

+ * + * @author hongawen + * @since 2024-04-25 + */ +@Data +@TableName("wf_form") +public class WFForm extends BaseEntity implements Serializable{ + + private static final long serialVersionUID = 1L; + + /** + * 流程表单id + */ + private String id; + + /** + * 表单名称 + */ + private String name; + + /** + * 表单内容 + */ + private String content; + + /** + * 表单描述 + */ + private String remark; + + /** + * 状态:0-删除 1-正常 + */ + private Integer state; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmCategoryVO.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmCategoryVO.java new file mode 100644 index 000000000..1da698994 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmCategoryVO.java @@ -0,0 +1,41 @@ +package com.njcn.bpm.pojo.vo; + +import lombok.Data; + +import java.io.Serializable; + +/** + * BPM 流程分类 DO + * + * @author 芋道源码 + */ +@Data +public class BpmCategoryVO implements Serializable { + + /** + * 分类编号 + */ + private String id; + /** + * 分类名 + */ + private String name; + /** + * 分类标志 + */ + private String code; + /** + * 分类描述 + */ + private String description; + /** + * 分类状态 + */ + private Integer status; + /** + * 分类排序 + */ + private Integer sort; + + +} \ No newline at end of file diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmFormVO.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmFormVO.java new file mode 100644 index 000000000..e3e5c4eea --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmFormVO.java @@ -0,0 +1,26 @@ +package com.njcn.bpm.pojo.vo; + +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.List; + +@Data +public class BpmFormVO implements Serializable { + + private String id; + + private String name; + + private String conf; + + private List fields; + + private Integer status; // 参见 CommonStatusEnum 枚举 + + private String remark; + + private LocalDateTime createTime; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmModelRespVO.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmModelRespVO.java new file mode 100644 index 000000000..4a8c16194 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmModelRespVO.java @@ -0,0 +1,51 @@ +package com.njcn.bpm.pojo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 流程模型 Response VO") +@Data +public class BpmModelRespVO { + + private String id; + + private String key; + + private String name; + + @Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg") + private String icon; + + @Schema(description = "流程描述", example = "我是描述") + private String description; + + @Schema(description = "流程分类编码", example = "1") + private String category; + @Schema(description = "流程分类名字", example = "请假") + private String categoryName; + + @Schema(description = "表单类型-参见 bpm_model_form_type 数据字典", example = "1") + private Integer formType; + + @Schema(description = "表单编号", example = "1024") + private String formId; // 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空 + @Schema(description = "表单名字", example = "请假表单") + private String formName; + + @Schema(description = "自定义表单的提交路径", example = "/bpm/oa/leave/create") + private String formCustomCreatePath; // 使用 Vue 的路由地址-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空 + @Schema(description = "自定义表单的查看路径", example = "/bpm/oa/leave/view") + private String formCustomViewPath; // ,使用 Vue 的路由地址-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空 + + private LocalDateTime createTime; + + private String bpmnXml; + + /** + * 最新部署的流程定义 + */ + private BpmProcessDefinitionInfoVO processDefinition; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmProcessDefinitionInfoVO.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmProcessDefinitionInfoVO.java new file mode 100644 index 000000000..b20e60fbb --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmProcessDefinitionInfoVO.java @@ -0,0 +1,123 @@ +package com.njcn.bpm.pojo.vo; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.njcn.db.bo.BaseEntity; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.List; + +/** + * BPM 流程定义的拓信息 + * 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表 + * + */ +@Data +public class BpmProcessDefinitionInfoVO extends BaseEntity implements Serializable { + + /** + * 编号 + */ + private String id; + /** + * 流程定义的编号 + * + * 关联 ProcessDefinition 的 id 属性 + */ + private String processDefinitionId; + /** + * 流程模型的编号 + * + * 关联 Model 的 id 属性 + */ + private String modelId; + + /** + * 图标 + */ + private String icon; + /** + * 描述 + */ + private String description; + + /** + * 表单类型 + * + */ + private Integer formType; + + /** + * 动态表单编号 + * + */ + private String formId; + + /** + * 表单的配置 + * + */ + private String formConf; + + /** + * 表单项的数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List formFields; + + + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + */ + private String formCustomCreatePath; + + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + */ + private String formCustomViewPath; + + + private Integer version; + + private String name; + + private String key; + + + @ApiModelProperty("流程分类") + private String category; + + @ApiModelProperty("流程分类名字") + private String categoryName; + + + @ApiModelProperty("表单名字") + private String formName; + + + private Integer suspensionState; // 参见 SuspensionState 枚举 + + @ApiModelProperty("部署时间") + private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取,非必须返回 + + @ApiModelProperty("BPMN") + private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回 + + @ApiModelProperty("发起用户需要选择审批人的任务数组") + private List startUserSelectTasks; // 需要从对应的 BpmnModel 读取,非必须返回 + + @Schema(description = "BPMN UserTask 用户任务") + @Data + public static class UserTask { + + private String id; + + private String name; + + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/BeanUtils.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/BeanUtils.java new file mode 100644 index 000000000..942f3d39a --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/BeanUtils.java @@ -0,0 +1,61 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +import java.util.List; +import java.util.function.Consumer; + +/** + * Bean 工具类 + * + * 1. 默认使用 {@link BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能 + * 2. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现 + * + * @author 芋道源码 + */ +public class BeanUtils { + + public static T toBean(Object source, Class targetClass) { + return BeanUtil.toBean(source, targetClass); + } + + public static T toBean(Object source, Class targetClass, Consumer peek) { + T target = toBean(source, targetClass); + if (target != null) { + peek.accept(target); + } + return target; + } + + public static List toBean(List source, Class targetType) { + if (source == null) { + return null; + } + return CollectionUtils.convertList(source, s -> toBean(s, targetType)); + } + + public static List toBean(List source, Class targetType, Consumer peek) { + List list = toBean(source, targetType); + if (list != null) { + list.forEach(peek); + } + return list; + } +// +// public static Page toBean(Page source, Class targetType) { +// return toBean(source, targetType, null); +// } +// +// public static Page toBean(Page source, Class targetType, Consumer peek) { +// if (source == null) { +// return null; +// } +// List list = toBean(source.getRecords(), targetType); +// if (peek != null) { +// list.forEach(peek); +// } +// return new Page<>(list, source.getTotal()); +// } + +} \ No newline at end of file diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/CollectionUtils.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/CollectionUtils.java new file mode 100644 index 000000000..5ce0dcc67 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/CollectionUtils.java @@ -0,0 +1,318 @@ +package com.njcn.bpm.utils; + + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ArrayUtil; +import com.google.common.collect.ImmutableMap; + +import java.util.*; +import java.util.function.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class CollectionUtils { + + public static boolean containsAny(Object source, Object... targets) { + return asList(targets).contains(source); + } + + public static boolean isAnyEmpty(Collection... collections) { + return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty); + } + + public static boolean anyMatch(Collection from, Predicate predicate) { + return from.stream().anyMatch(predicate); + } + + public static List filterList(Collection from, Predicate predicate) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return from.stream().filter(predicate).collect(Collectors.toList()); + } + + public static List distinct(Collection from, Function keyMapper) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return distinct(from, keyMapper, (t1, t2) -> t1); + } + + public static List distinct(Collection from, Function keyMapper, BinaryOperator cover) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values()); + } + + public static List convertList(T[] from, Function func) { + if (ArrayUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return convertList(Arrays.asList(from), func); + } + + public static List convertList(Collection from, Function func) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList()); + } + + public static List convertList(Collection from, Function func, Predicate filter) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList()); + } + + public static List convertListByFlatMap(Collection from, + Function> func) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); + } + + public static List convertListByFlatMap(Collection from, + Function mapper, + Function> func) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); + } + + public static List mergeValuesFromMap(Map> map) { + return map.values() + .stream() + .flatMap(List::stream) + .collect(Collectors.toList()); + } + + public static Set convertSet(Collection from) { + return convertSet(from, v -> v); + } + + public static Set convertSet(Collection from, Function func) { + if (CollUtil.isEmpty(from)) { + return new HashSet<>(); + } + return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet()); + } + + public static Set convertSet(Collection from, Function func, Predicate filter) { + if (CollUtil.isEmpty(from)) { + return new HashSet<>(); + } + return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet()); + } + + public static Map convertMapByFilter(Collection from, Predicate filter, Function keyFunc) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return from.stream().filter(filter).collect(Collectors.toMap(keyFunc, v -> v)); + } + + public static Set convertSetByFlatMap(Collection from, + Function> func) { + if (CollUtil.isEmpty(from)) { + return new HashSet<>(); + } + return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); + } + + public static Set convertSetByFlatMap(Collection from, + Function mapper, + Function> func) { + if (CollUtil.isEmpty(from)) { + return new HashSet<>(); + } + return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); + } + + public static Map convertMap(Collection from, Function keyFunc) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return convertMap(from, keyFunc, Function.identity()); + } + + public static Map convertMap(Collection from, Function keyFunc, Supplier> supplier) { + if (CollUtil.isEmpty(from)) { + return supplier.get(); + } + return convertMap(from, keyFunc, Function.identity(), supplier); + } + + public static Map convertMap(Collection from, Function keyFunc, Function valueFunc) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1); + } + + public static Map convertMap(Collection from, Function keyFunc, Function valueFunc, BinaryOperator mergeFunction) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new); + } + + public static Map convertMap(Collection from, Function keyFunc, Function valueFunc, Supplier> supplier) { + if (CollUtil.isEmpty(from)) { + return supplier.get(); + } + return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier); + } + + public static Map convertMap(Collection from, Function keyFunc, Function valueFunc, BinaryOperator mergeFunction, Supplier> supplier) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier)); + } + + public static Map> convertMultiMap(Collection from, Function keyFunc) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList()))); + } + + public static Map> convertMultiMap(Collection from, Function keyFunc, Function valueFunc) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return from.stream() + .collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList()))); + } + + // 暂时没想好名字,先以 2 结尾噶 + public static Map> convertMultiMap2(Collection from, Function keyFunc, Function valueFunc) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet()))); + } + + public static Map convertImmutableMap(Collection from, Function keyFunc) { + if (CollUtil.isEmpty(from)) { + return Collections.emptyMap(); + } + ImmutableMap.Builder builder = ImmutableMap.builder(); + from.forEach(item -> builder.put(keyFunc.apply(item), item)); + return builder.build(); + } + + /** + * 对比老、新两个列表,找出新增、修改、删除的数据 + * + * @param oldList 老列表 + * @param newList 新列表 + * @param sameFunc 对比函数,返回 true 表示相同,返回 false 表示不同 + * 注意,same 是通过每个元素的“标识”,判断它们是不是同一个数据 + * @return [新增列表、修改列表、删除列表] + */ + public static List> diffList(Collection oldList, Collection newList, + BiFunction sameFunc) { + List createList = new LinkedList<>(newList); // 默认都认为是新增的,后续会进行移除 + List updateList = new ArrayList<>(); + List deleteList = new ArrayList<>(); + + // 通过以 oldList 为主遍历,找出 updateList 和 deleteList + for (T oldObj : oldList) { + // 1. 寻找是否有匹配的 + T foundObj = null; + for (Iterator iterator = createList.iterator(); iterator.hasNext(); ) { + T newObj = iterator.next(); + // 1.1 不匹配,则直接跳过 + if (!sameFunc.apply(oldObj, newObj)) { + continue; + } + // 1.2 匹配,则移除,并结束寻找 + iterator.remove(); + foundObj = newObj; + break; + } + // 2. 匹配添加到 updateList;不匹配则添加到 deleteList 中 + if (foundObj != null) { + updateList.add(foundObj); + } else { + deleteList.add(oldObj); + } + } + return asList(createList, updateList, deleteList); + } + + public static boolean containsAny(Collection source, Collection candidates) { + return org.springframework.util.CollectionUtils.containsAny(source, candidates); + } + + public static T getFirst(List from) { + return !CollectionUtil.isEmpty(from) ? from.get(0) : null; + } + + public static T findFirst(Collection from, Predicate predicate) { + return findFirst(from, predicate, Function.identity()); + } + + public static U findFirst(Collection from, Predicate predicate, Function func) { + if (CollUtil.isEmpty(from)) { + return null; + } + return from.stream().filter(predicate).findFirst().map(func).orElse(null); + } + + public static > V getMaxValue(Collection from, Function valueFunc) { + if (CollUtil.isEmpty(from)) { + return null; + } + assert !from.isEmpty(); // 断言,避免告警 + T t = from.stream().max(Comparator.comparing(valueFunc)).get(); + return valueFunc.apply(t); + } + + public static > V getMinValue(List from, Function valueFunc) { + if (CollUtil.isEmpty(from)) { + return null; + } + assert from.size() > 0; // 断言,避免告警 + T t = from.stream().min(Comparator.comparing(valueFunc)).get(); + return valueFunc.apply(t); + } + + public static > V getSumValue(List from, Function valueFunc, + BinaryOperator accumulator) { + return getSumValue(from, valueFunc, accumulator, null); + } + + public static > V getSumValue(Collection from, Function valueFunc, + BinaryOperator accumulator, V defaultValue) { + if (CollUtil.isEmpty(from)) { + return defaultValue; + } + assert !from.isEmpty(); // 断言,避免告警 + return from.stream().map(valueFunc).filter(Objects::nonNull).reduce(accumulator).orElse(defaultValue); + } + + public static void addIfNotNull(Collection coll, T item) { + if (item == null) { + return; + } + coll.add(item); + } + + public static Collection singleton(T obj) { + return obj == null ? Collections.emptyList() : Collections.singleton(obj); + } + + public static List newArrayList(List> list) { + return list.stream().flatMap(Collection::stream).collect(Collectors.toList()); + } + +} \ No newline at end of file diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/DateUtils.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/DateUtils.java new file mode 100644 index 000000000..6000f7f2c --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/DateUtils.java @@ -0,0 +1,149 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.date.LocalDateTimeUtil; + +import java.time.*; +import java.util.Calendar; +import java.util.Date; + +/** + * 时间工具类 + * + * @author 芋道源码 + */ +public class DateUtils { + + /** + * 时区 - 默认 + */ + public static final String TIME_ZONE_DEFAULT = "GMT+8"; + + /** + * 秒转换成毫秒 + */ + public static final long SECOND_MILLIS = 1000; + + public static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd"; + + public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss"; + + /** + * 将 LocalDateTime 转换成 Date + * + * @param date LocalDateTime + * @return LocalDateTime + */ + public static Date of(LocalDateTime date) { + if (date == null) { + return null; + } + // 将此日期时间与时区相结合以创建 ZonedDateTime + ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault()); + // 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳 + Instant instant = zonedDateTime.toInstant(); + // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间 + return Date.from(instant); + } + + /** + * 将 Date 转换成 LocalDateTime + * + * @param date Date + * @return LocalDateTime + */ + public static LocalDateTime of(Date date) { + if (date == null) { + return null; + } + // 转为时间戳 + Instant instant = date.toInstant(); + // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间 + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + } + + public static Date addTime(Duration duration) { + return new Date(System.currentTimeMillis() + duration.toMillis()); + } + + public static boolean isExpired(LocalDateTime time) { + LocalDateTime now = LocalDateTime.now(); + return now.isAfter(time); + } + + /** + * 创建指定时间 + * + * @param year 年 + * @param mouth 月 + * @param day 日 + * @return 指定时间 + */ + public static Date buildTime(int year, int mouth, int day) { + return buildTime(year, mouth, day, 0, 0, 0); + } + + /** + * 创建指定时间 + * + * @param year 年 + * @param mouth 月 + * @param day 日 + * @param hour 小时 + * @param minute 分钟 + * @param second 秒 + * @return 指定时间 + */ + public static Date buildTime(int year, int mouth, int day, + int hour, int minute, int second) { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, mouth - 1); + calendar.set(Calendar.DAY_OF_MONTH, day); + calendar.set(Calendar.HOUR_OF_DAY, hour); + calendar.set(Calendar.MINUTE, minute); + calendar.set(Calendar.SECOND, second); + calendar.set(Calendar.MILLISECOND, 0); // 一般情况下,都是 0 毫秒 + return calendar.getTime(); + } + + public static Date max(Date a, Date b) { + if (a == null) { + return b; + } + if (b == null) { + return a; + } + return a.compareTo(b) > 0 ? a : b; + } + + public static LocalDateTime max(LocalDateTime a, LocalDateTime b) { + if (a == null) { + return b; + } + if (b == null) { + return a; + } + return a.isAfter(b) ? a : b; + } + + /** + * 是否今天 + * + * @param date 日期 + * @return 是否 + */ + public static boolean isToday(LocalDateTime date) { + return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now()); + } + + /** + * 是否昨天 + * + * @param date 日期 + * @return 是否 + */ + public static boolean isYesterday(LocalDateTime date) { + return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now().minusDays(1)); + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/NumberUtils.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/NumberUtils.java new file mode 100644 index 000000000..f5a927f34 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/NumberUtils.java @@ -0,0 +1,64 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; + +import java.math.BigDecimal; + +/** + * 数字的工具类,补全 {@link NumberUtil} 的功能 + * + * @author 芋道源码 + */ +public class NumberUtils { + + public static Long parseLong(String str) { + return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null; + } + + public static Integer parseInt(String str) { + return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null; + } + + /** + * 通过经纬度获取地球上两点之间的距离 + * + * 参考 <DistanceUtil> 实现,目前它已经被 hutool 删除 + * + * @param lat1 经度1 + * @param lng1 纬度1 + * @param lat2 经度2 + * @param lng2 纬度2 + * @return 距离,单位:千米 + */ + public static double getDistance(double lat1, double lng1, double lat2, double lng2) { + double radLat1 = lat1 * Math.PI / 180.0; + double radLat2 = lat2 * Math.PI / 180.0; + double a = radLat1 - radLat2; + double b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0; + double distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + + Math.cos(radLat1) * Math.cos(radLat2) + * Math.pow(Math.sin(b / 2), 2))); + distance = distance * 6378.137; + distance = Math.round(distance * 10000d) / 10000d; + return distance; + } + + /** + * 提供精确的乘法运算 + * + * 和 hutool {@link NumberUtil#mul(BigDecimal...)} 的差别是,如果存在 null,则返回 null + * + * @param values 多个被乘值 + * @return 积 + */ + public static BigDecimal mul(BigDecimal... values) { + for (BigDecimal value : values) { + if (value == null) { + return null; + } + } + return NumberUtil.mul(values); + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/ProcessEnumUtil.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/ProcessEnumUtil.java new file mode 100644 index 000000000..cd95b3334 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/ProcessEnumUtil.java @@ -0,0 +1,46 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.util.StrUtil; +import com.njcn.bpm.enums.BpmResponseEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.EnumUtils; + +import javax.validation.constraints.NotNull; +import java.util.Objects; + +/** + * @author hongawen + * @version 1.0.0 + * @date 2021年12月20日 10:03 + */ +public class ProcessEnumUtil { + + /** + * 获取HarmonicResponseEnum实例 + */ + public static BpmResponseEnum getHarmonicEnumResponseEnumByMessage(@NotNull Object value) { + BpmResponseEnum harmonicResponseEnum; + try { + String message = value.toString(); + if(message.indexOf(StrUtil.C_COMMA)>0){ + value = message.substring(message.indexOf(StrUtil.C_COMMA)+1); + } + harmonicResponseEnum = EnumUtils.valueOf(BpmResponseEnum.class, value, BpmResponseEnum.class.getMethod(BusinessException.GET_MESSAGE_METHOD)); + return Objects.isNull(harmonicResponseEnum) ? BpmResponseEnum.BPM_COMMON_ERROR : harmonicResponseEnum; + } catch (NoSuchMethodException e) { + throw new BusinessException(CommonResponseEnum.INTERNAL_ERROR); + } + } + public static Enum getExceptionEnum(HttpResult result){ + //如果返回错误,且为内部错误,则直接抛出异常 + CommonResponseEnum commonResponseEnum = EnumUtils.getCommonResponseEnumByCode(result.getCode()); + if (commonResponseEnum == CommonResponseEnum.DEVICE_RESPONSE_ENUM) { + return getHarmonicEnumResponseEnumByMessage(result.getMessage()); + } + return commonResponseEnum; + } + + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/ValidationUtils.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/ValidationUtils.java new file mode 100644 index 000000000..88f9bb622 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/ValidationUtils.java @@ -0,0 +1,55 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import org.springframework.util.StringUtils; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validation; +import javax.validation.Validator; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * 校验工具类 + * + * @author 芋道源码 + */ +public class ValidationUtils { + + private static final Pattern PATTERN_MOBILE = Pattern.compile("^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[0,1,4-9])|(?:5[0-3,5-9])|(?:6[2,5-7])|(?:7[0-8])|(?:8[\\d])|(?:9[0-3,5-9]))\\d{8}$"); + + private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"); + + private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*"); + + public static boolean isMobile(String mobile) { + return StringUtils.hasText(mobile) + && PATTERN_MOBILE.matcher(mobile).matches(); + } + + public static boolean isURL(String url) { + return StringUtils.hasText(url) + && PATTERN_URL.matcher(url).matches(); + } + + public static boolean isXmlNCName(String str) { + return StringUtils.hasText(str) + && PATTERN_XML_NCNAME.matcher(str).matches(); + } + + public static void validate(Object object, Class... groups) { + Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + Assert.notNull(validator); + validate(validator, object, groups); + } + + public static void validate(Validator validator, Object object, Class... groups) { + Set> constraintViolations = validator.validate(object, groups); + if (CollUtil.isNotEmpty(constraintViolations)) { + throw new ConstraintViolationException(constraintViolations); + } + } + +} diff --git a/pqs-bpm/bpm-boot/pom.xml b/pqs-bpm/bpm-boot/pom.xml new file mode 100644 index 000000000..cb0c773f4 --- /dev/null +++ b/pqs-bpm/bpm-boot/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + + com.njcn + pqs-bpm + 1.0.0 + + + bpm-boot + + + 8 + 8 + UTF-8 + + + + + + com.njcn + common-web + ${project.version} + + + + com.njcn + bpm-api + ${project.version} + + + + com.njcn + user-api + ${project.version} + + + + + + bpmboot + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + -Xlint:unchecked + + + + org.springframework.boot + spring-boot-maven-plugin + + + package + + repackage + + + + + + + com.spotify + docker-maven-plugin + 1.0.0 + + + build-image + ${docker.operate} + + build + + + + + + http://${docker.repostory} + + ${docker.repostory}/${docker.registry.name}/${project.artifactId} + + latest + + ${docker.url} + ${basedir}/ + + + /ROOT + ${project.build.directory} + ${project.build.finalName}.jar + + + + + + + + + \ No newline at end of file diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/BpmApplication.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/BpmApplication.java new file mode 100644 index 000000000..301c54b96 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/BpmApplication.java @@ -0,0 +1,27 @@ +package com.njcn.bpm; + +import lombok.extern.slf4j.Slf4j; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.DependsOn; + +/** + * pqs + * + * @author cdf + * @date 2022/11/10 + */ +@Slf4j +@DependsOn("proxyMapperRegister") +@MapperScan("com.njcn.**.mapper") +@EnableFeignClients(basePackages = "com.njcn") +@SpringBootApplication(scanBasePackages = "com.njcn") +public class BpmApplication { + + public static void main(String[] args) { + SpringApplication.run(BpmApplication.class, args); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmActivityBehaviorFactory.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmActivityBehaviorFactory.java new file mode 100644 index 000000000..5b1377f3c --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmActivityBehaviorFactory.java @@ -0,0 +1,47 @@ +package com.njcn.bpm.behavior; + +import com.njcn.bpm.strategy.BpmTaskCandidateInvoker; +import lombok.Setter; +import org.flowable.bpmn.model.Activity; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; +import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory; + +/** + * 自定义的 ActivityBehaviorFactory 实现类,目的如下: + * 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}:实现自定义的流程任务的 assignee 负责人的分配 + * + * @author 芋道源码 + */ +@Setter +public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory { + + private BpmTaskCandidateInvoker taskCandidateInvoker; + + @Override + public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) { + BpmUserTaskActivityBehavior bpmUserTaskActivityBehavior = new BpmUserTaskActivityBehavior(userTask); + bpmUserTaskActivityBehavior.setTaskCandidateInvoker(taskCandidateInvoker); + return bpmUserTaskActivityBehavior; + } + + @Override + public ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(Activity activity, + AbstractBpmnActivityBehavior behavior) { + BpmParallelMultiInstanceBehavior bpmParallelMultiInstanceBehavior = new BpmParallelMultiInstanceBehavior(activity, behavior); + bpmParallelMultiInstanceBehavior.setTaskCandidateInvoker(taskCandidateInvoker); + return bpmParallelMultiInstanceBehavior; + } + + @Override + public SequentialMultiInstanceBehavior createSequentialMultiInstanceBehavior(Activity activity, + AbstractBpmnActivityBehavior behavior) { + BpmSequentialMultiInstanceBehavior bpmSequentialMultiInstanceBehavior = new BpmSequentialMultiInstanceBehavior(activity, behavior); + bpmSequentialMultiInstanceBehavior.setTaskCandidateInvoker(taskCandidateInvoker); + return bpmSequentialMultiInstanceBehavior; + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmParallelMultiInstanceBehavior.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmParallelMultiInstanceBehavior.java new file mode 100644 index 000000000..0106ac0ec --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmParallelMultiInstanceBehavior.java @@ -0,0 +1,57 @@ +package com.njcn.bpm.behavior; + +import com.njcn.bpm.strategy.BpmTaskCandidateInvoker; +import com.njcn.bpm.utils.FlowableUtils; +import lombok.Setter; +import org.flowable.bpmn.model.Activity; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; + +import java.util.List; +import java.util.Set; + +/** + * 自定义的【并行】的【多个】流程任务的 assignee 负责人的分配 + * 第一步,基于分配规则,计算出分配任务的【多个】候选人们。 + * 第二步,将【多个】任务候选人们,设置到 DelegateExecution 的 collectionVariable 变量中,以便 BpmUserTaskActivityBehavior 使用它 + * + * @author kemengkai + * @since 2022-04-21 16:57 + */ +@Setter +public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior { + + private BpmTaskCandidateInvoker taskCandidateInvoker; + + public BpmParallelMultiInstanceBehavior(Activity activity, + AbstractBpmnActivityBehavior innerActivityBehavior) { + super(activity, innerActivityBehavior); + } + + /** + * 重写该方法,主要实现两个功能: + * 1. 忽略原有的 collectionVariable、collectionElementVariable 表达式,而是采用自己定义的 + * 2. 获得任务的处理人,并设置到 collectionVariable 中,用于 BpmUserTaskActivityBehavior 从中可以获取任务的处理人 + * + * 注意,多个任务实例,每个任务实例对应一个处理人,所以返回的数量就是任务处理人的数量 + * + * @param execution 执行任务 + * @return 数量 + */ + @Override + protected int resolveNrOfInstances(DelegateExecution execution) { + // 第一步,设置 collectionVariable 和 CollectionVariable + // 从 execution.getVariable() 读取所有任务处理人的 key + super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); + // 从 execution.getVariable() 读取当前所有任务处理的人的 key + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); + + // 第二步,获取任务的所有处理人 + List assigneeUserIds = taskCandidateInvoker.calculateUsers(execution); + execution.setVariable(super.collectionVariable, assigneeUserIds); + return assigneeUserIds.size(); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmSequentialMultiInstanceBehavior.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmSequentialMultiInstanceBehavior.java new file mode 100644 index 000000000..bf8211dda --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmSequentialMultiInstanceBehavior.java @@ -0,0 +1,52 @@ +package com.njcn.bpm.behavior; + +import com.njcn.bpm.strategy.BpmTaskCandidateInvoker; +import com.njcn.bpm.utils.FlowableUtils; +import lombok.Setter; +import org.flowable.bpmn.model.Activity; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * 自定义的【串行】的【多个】流程任务的 assignee 负责人的分配 + * + * 本质上,实现和 {@link BpmParallelMultiInstanceBehavior} 一样,只是继承的类不一样 + * + * @author 芋道源码 + */ +@Setter +public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceBehavior { + + private BpmTaskCandidateInvoker taskCandidateInvoker; + + public BpmSequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) { + super(activity, innerActivityBehavior); + } + + /** + * 逻辑和 {@link BpmParallelMultiInstanceBehavior#resolveNrOfInstances(DelegateExecution)} 类似 + * + * 差异的点:是在【第二步】的时候,需要返回 LinkedHashSet 集合!因为它需要有序! + */ + @Override + protected int resolveNrOfInstances(DelegateExecution execution) { + // 第一步,设置 collectionVariable 和 CollectionVariable + // 从 execution.getVariable() 读取所有任务处理人的 key + super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); + // 从 execution.getVariable() 读取当前所有任务处理的人的 key + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); + + // 第二步,获取任务的所有处理人 + List assigneeUserIds = new ArrayList<>(taskCandidateInvoker.calculateUsers(execution)); // 保证有序!!! + execution.setVariable(super.collectionVariable, assigneeUserIds); + return assigneeUserIds.size(); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmUserTaskActivityBehavior.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmUserTaskActivityBehavior.java new file mode 100644 index 000000000..bd50dfabb --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/behavior/BpmUserTaskActivityBehavior.java @@ -0,0 +1,67 @@ +package com.njcn.bpm.behavior; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.RandomUtil; +import com.njcn.bpm.strategy.BpmTaskCandidateInvoker; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.UserTask; +import org.flowable.common.engine.impl.el.ExpressionManager; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.TaskHelper; +import org.flowable.task.service.TaskService; +import org.flowable.task.service.impl.persistence.entity.TaskEntity; + +import java.util.List; +import java.util.Set; + +/** + * 自定义的【单个】流程任务的 assignee 负责人的分配 + * 第一步,基于分配规则,计算出分配任务的【单个】候选人。如果找不到,则直接报业务异常,不继续执行后续的流程; + * 第二步,随机选择一个候选人,则选择作为 assignee 负责人。 + * + * @author 芋道源码 + */ +@Slf4j +public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { + + @Setter + private BpmTaskCandidateInvoker taskCandidateInvoker; + + public BpmUserTaskActivityBehavior(UserTask userTask) { + super(userTask); + } + + @Override + protected void handleAssignments(TaskService taskService, String assignee, String owner, + List candidateUsers, List candidateGroups, TaskEntity task, ExpressionManager expressionManager, + DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { + // 第一步,获得任务的候选用户 + String assigneeUserId = calculateTaskCandidateUsers(execution); + Assert.notNull(assigneeUserId, "任务处理人不能为空"); + // 第二步,设置作为负责人 + TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); + } + + private String calculateTaskCandidateUsers(DelegateExecution execution) { + // 情况一,如果是多实例的任务,例如说会签、或签等情况,则从 Variable 中获取。 + // 顺序审批可见 BpmSequentialMultiInstanceBehavior,并发审批可见 BpmSequentialMultiInstanceBehavior + if (super.multiInstanceActivityBehavior != null) { + return execution.getVariable(super.multiInstanceActivityBehavior.getCollectionElementVariable(), String.class); + } + + // 情况二,如果非多实例的任务,则计算任务处理人 + // 第一步,先计算可处理该任务的处理人们 + List candidateUserIds = taskCandidateInvoker.calculateUsers(execution); + // 第二步,后随机选择一个任务的处理人 + // 疑问:为什么一定要选择一个任务处理人? + // 解答:项目对 bpm 的任务是责任到人,所以每个任务有且仅有一个处理人。 + // 如果希望一个任务可以同时被多个人处理,可以考虑使用 BpmParallelMultiInstanceBehavior 实现的会签 or 或签。 + int index = RandomUtil.randomInt(candidateUserIds.size()); + return CollUtil.get(candidateUserIds, index); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmFlowableConfiguration.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmFlowableConfiguration.java new file mode 100644 index 000000000..2b8b90c9c --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmFlowableConfiguration.java @@ -0,0 +1,91 @@ +package com.njcn.bpm.config; + +import cn.hutool.core.collection.ListUtil; +import com.njcn.bpm.behavior.BpmActivityBehaviorFactory; +import com.njcn.bpm.event.BpmProcessInstanceEventPublisher; +import com.njcn.bpm.strategy.BpmTaskCandidateInvoker; +import com.njcn.bpm.strategy.IBpmTaskCandidateStrategy; +import com.njcn.user.api.UserFeignClient; +import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.flowable.spring.boot.EngineConfigurationConfigurer; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncListenableTaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.List; + +/** + * BPM 模块的 Flowable 配置类 + * + * @author jason + */ +@Configuration(proxyBeanMethods = false) +public class BpmFlowableConfiguration { + + /** + * 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类,创建对应的 AsyncListenableTaskExecutor Bean + * + * 如果不创建,会导致项目启动时,Flowable 报错的问题 + */ + @Bean(name = "applicationTaskExecutor") + @ConditionalOnMissingBean(name = "applicationTaskExecutor") + public AsyncListenableTaskExecutor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(8); + executor.setMaxPoolSize(8); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("flowable-task-Executor-"); + executor.setAwaitTerminationSeconds(30); + executor.setWaitForTasksToCompleteOnShutdown(true); + executor.setAllowCoreThreadTimeOut(true); + executor.initialize(); + return executor; + } + + /** + * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类: + * + * 1. 设置各种监听器 + * 2. 设置自定义的 ActivityBehaviorFactory 实现 + */ + @Bean + public EngineConfigurationConfigurer bpmProcessEngineConfigurationConfigurer( + ObjectProvider listeners, + BpmActivityBehaviorFactory bpmActivityBehaviorFactory) { + return configuration -> { + // 注册监听器,例如说 BpmActivityEventListener + configuration.setEventListeners(ListUtil.toList(listeners.iterator())); + // 设置 ActivityBehaviorFactory 实现类,用于流程任务的审核人的自定义 + configuration.setActivityBehaviorFactory(bpmActivityBehaviorFactory); + }; + } + + // =========== 审批人相关的 Bean ========== + + @Bean + public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskCandidateInvoker bpmTaskCandidateInvoker) { + BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory(); + bpmActivityBehaviorFactory.setTaskCandidateInvoker(bpmTaskCandidateInvoker); + return bpmActivityBehaviorFactory; + } + + @Bean + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") // adminUserApi 可以注入成功 + public BpmTaskCandidateInvoker bpmTaskCandidateInvoker(List strategyList, + UserFeignClient adminUserApi) { + return new BpmTaskCandidateInvoker(strategyList, adminUserApi); + } + + // =========== 自己拓展的 Bean ========== + + @Bean + public BpmProcessInstanceEventPublisher processInstanceEventPublisher(ApplicationEventPublisher publisher) { + return new BpmProcessInstanceEventPublisher(publisher); + } + +} \ No newline at end of file diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmCategoryController.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmCategoryController.java new file mode 100644 index 000000000..36ce1811e --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmCategoryController.java @@ -0,0 +1,111 @@ +package com.njcn.bpm.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.pojo.param.BpmCategoryParam; +import com.njcn.bpm.pojo.po.BpmCategory; +import com.njcn.bpm.pojo.vo.BpmCategoryVO; +import com.njcn.bpm.service.IBpmCategoryService; +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.constant.OperateType; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.HttpResultUtil; +import com.njcn.common.utils.LogUtil; +import com.njcn.web.controller.BaseController; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + + +@RestController +@RequestMapping("/bpm/category") +@Validated +@Slf4j +@Api(tags = "流程分类控制器") +@RequiredArgsConstructor +public class BpmCategoryController extends BaseController { + + private final IBpmCategoryService categoryService; + + @OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD) + @PostMapping("/add") + @ApiOperation("创建流程分类") + @ApiImplicitParam(name = "bpmCategoryParam", value = "流程分类数据", required = true) + public HttpResult add(@Valid @RequestBody BpmCategoryParam bpmCategoryParam) { + String methodDescribe = getMethodDescribe("add"); + String categoryId = categoryService.createCategory(bpmCategoryParam); + if (StrUtil.isNotBlank(categoryId)) { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, categoryId, methodDescribe); + } else { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe); + } + } + + + @OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE) + @PostMapping("/update") + @ApiOperation("更新流程分类") + @ApiImplicitParam(name = "updateParam", value = "流程分类数据", required = true) + public HttpResult update(@RequestBody @Validated BpmCategoryParam.BpmCategoryUpdateParam updateParam) { + String methodDescribe = getMethodDescribe("update"); + categoryService.updateCategory(updateParam); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + + + @OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE) + @PostMapping("/delete") + @ApiOperation("删除流程分类") + @ApiImplicitParam(name = "ids", value = "流程分类索引", required = true, dataTypeClass = List.class) + public HttpResult delete(@RequestBody List ids) { + String methodDescribe = getMethodDescribe("delete"); + LogUtil.njcnDebug(log, "{},流程分类ID数据为:{}", methodDescribe, String.join(StrUtil.COMMA, ids)); + categoryService.deleteCategory(ids); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + + @GetMapping("/getById") + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @Operation(summary = "获得流程分类") + @Parameter(name = "id", description = "编号", required = true) + public HttpResult getById(String id) { + String methodDescribe = getMethodDescribe("getById"); + BpmCategory bpmCategory = categoryService.getById(id); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, bpmCategory, methodDescribe); + } + + + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/list") + @ApiOperation("查询流程分类数据") + @ApiImplicitParam(name = "bpmCategoryQueryParam", value = "查询参数", required = true) + public HttpResult> list(@RequestBody BpmCategoryParam.BpmCategoryQueryParam bpmCategoryQueryParam) { + String methodDescribe = getMethodDescribe("list"); + LogUtil.njcnDebug(log, "{},查询流程分类数据为:{}", methodDescribe, bpmCategoryQueryParam); + Page result = categoryService.getCategoryPage(bpmCategoryQueryParam); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + + + @GetMapping("/simpleList") + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @Operation(summary = "获得动态表单的精简列表", description = "用于表单下拉框") + public HttpResult> getCategorySimpleList() { + String methodDescribe = getMethodDescribe("getCategorySimpleList"); + List list = categoryService.getCategoryList(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe); + } + +} \ No newline at end of file diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmFormController.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmFormController.java new file mode 100644 index 000000000..25591c31d --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmFormController.java @@ -0,0 +1,112 @@ +package com.njcn.bpm.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.pojo.param.BpmFormParam; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.vo.BpmFormVO; +import com.njcn.bpm.service.IBpmFormService; +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.constant.OperateType; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.HttpResultUtil; +import com.njcn.common.utils.LogUtil; +import com.njcn.web.controller.BaseController; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + + +@RestController +@RequestMapping("/bpm/form") +@Validated +@Slf4j +@Api(tags = "流程表单控制器") +@RequiredArgsConstructor +public class BpmFormController extends BaseController { + + + private final IBpmFormService formService; + + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/list") + @ApiOperation("查询流程表单数据") + @ApiImplicitParam(name = "bpmFormQueryParam", value = "查询参数", required = true) + public HttpResult> list(@RequestBody BpmFormParam.BpmFormQueryParam bpmFormQueryParam) { + String methodDescribe = getMethodDescribe("list"); + LogUtil.njcnDebug(log, "{},查询流程表单数据为:{}", methodDescribe, bpmFormQueryParam); + Page result = formService.getFormPage(bpmFormQueryParam); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + + + @OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD) + @PostMapping("/add") + @ApiOperation("新增流程表单") + @ApiImplicitParam(name = "bpmFormParam", value = "流程表单数据", required = true) + public HttpResult add(@Valid @RequestBody BpmFormParam bpmFormParam) { + String methodDescribe = getMethodDescribe("add"); + String wfFormId = formService.createForm(bpmFormParam); + if (StrUtil.isNotBlank(wfFormId)) { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, wfFormId, methodDescribe); + } else { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe); + } + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE) + @PostMapping("/update") + @ApiOperation("更新流程表单") + @ApiImplicitParam(name = "updateParam", value = "流程表单数据", required = true) + public HttpResult update(@RequestBody @Validated BpmFormParam.BpmFormUpdateParam updateParam) { + String methodDescribe = getMethodDescribe("update"); + formService.updateForm(updateParam); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE) + @PostMapping("/delete") + @ApiOperation("删除流程表单") + @ApiImplicitParam(name = "ids", value = "流程表单索引", required = true, dataTypeClass = List.class) + public HttpResult delete(@RequestBody List ids) { + String methodDescribe = getMethodDescribe("delete"); + LogUtil.njcnDebug(log, "{},流程表单ID数据为:{}", methodDescribe, String.join(StrUtil.COMMA, ids)); + formService.deleteForm(ids); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + + + + @GetMapping("/getById") + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @Operation(summary = "获得动态表单") + @Parameter(name = "id", description = "编号", required = true) + public HttpResult getById(String id) { + String methodDescribe = getMethodDescribe("getById"); + BpmForm form = formService.getById(id); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, form, methodDescribe); + } + + @GetMapping("/simpleList") + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @Operation(summary = "获得动态表单的精简列表", description = "用于表单下拉框") + public HttpResult> getFormSimpleList() { + String methodDescribe = getMethodDescribe("getFormSimpleList"); + List list = formService.getFormList(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe); + } + + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmModelController.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmModelController.java new file mode 100644 index 000000000..323005dbf --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmModelController.java @@ -0,0 +1,174 @@ +package com.njcn.bpm.controller; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO; +import com.njcn.bpm.pojo.param.BpmCategoryParam; +import com.njcn.bpm.pojo.param.BpmModelParam; +import com.njcn.bpm.pojo.po.BpmCategory; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.vo.BpmModelRespVO; +import com.njcn.bpm.service.IBpmCategoryService; +import com.njcn.bpm.service.IBpmModelService; +import com.njcn.bpm.service.IBpmFormService; +import com.njcn.bpm.service.IBpmProcessDefinitionService; +import com.njcn.bpm.utils.BpmModelConvert; +import com.njcn.bpm.utils.CollectionUtils; +import com.njcn.bpm.utils.JsonUtils; +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.constant.OperateType; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.HttpResultUtil; +import com.njcn.common.utils.LogUtil; +import com.njcn.web.controller.BaseController; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.njcn.bpm.pojo.dto.CommonResult.success; +import static com.njcn.bpm.utils.CollectionUtils.convertMap; +import static com.njcn.bpm.utils.CollectionUtils.convertSet; + + +@RestController +@RequestMapping("/bpm/model") +@Validated +@Slf4j +@Api(tags = "流程模型控制器") +@RequiredArgsConstructor +public class BpmModelController extends BaseController { + + private final IBpmModelService modelService; + + private final IBpmFormService formService; + + private final IBpmCategoryService categoryService; + + private final IBpmProcessDefinitionService processDefinitionService; + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/list") + @ApiOperation("获得模型分页") + @ApiImplicitParam(name = "bpmModelQueryParam", value = "查询参数", required = true) + public HttpResult> getModelPage(@RequestBody BpmModelParam.BpmModelQueryParam bpmModelQueryParam) { + String methodDescribe = getMethodDescribe("list"); + LogUtil.njcnDebug(log, "{},查询流程表单数据为:{}", methodDescribe, bpmModelQueryParam); + Page pageResult = modelService.getModelPage(bpmModelQueryParam); + if (CollUtil.isEmpty(pageResult.getRecords())) { + Page result = new Page<>(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + // 拼接数据 + // 获得 Form 表单 + Set formIds = convertSet(pageResult.getRecords(), model -> { + BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); + return metaInfo != null ? metaInfo.getFormId() : null; + }); + Map formMap = formService.getFormMap(formIds); + // 获得 Category Map + Map categoryMap = categoryService.getCategoryMap( + convertSet(pageResult.getRecords(), Model::getCategory)); + // 获得 Deployment Map + Set deploymentIds = new HashSet<>(); + pageResult.getRecords().forEach(model -> CollectionUtils.addIfNotNull(deploymentIds, model.getDeploymentId())); + + Map deploymentMap = processDefinitionService.getDeploymentMap(deploymentIds); + // 获得 ProcessDefinition Map + List processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds(deploymentIds); + Map processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmModelConvert.INSTANCE.buildModelPage(pageResult, formMap, categoryMap, deploymentMap, processDefinitionMap), methodDescribe); + } + + @GetMapping("/getById") + @Operation(summary = "获得模型") + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @Parameter(name = "id", description = "编号", required = true) + public HttpResult getById(String id) { + Model model = modelService.getModel(id); + String methodDescribe = getMethodDescribe("getById"); + if (model == null) { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + byte[] bpmnBytes = modelService.getModelBpmnXML(id); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmModelConvert.INSTANCE.buildModel(model, bpmnBytes), methodDescribe); + } + + @Operation(summary = "新建模型") + @OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD) + @PostMapping("/add") + @ApiOperation("新建模型") + @ApiImplicitParam(name = "createRetVO", value = "模型数据", required = true) + public HttpResult createModel(@Validated @RequestBody BpmModelParam bpmModelParam) { + String methodDescribe = getMethodDescribe("createModel"); + String modelId = modelService.createModel(bpmModelParam, null); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, modelId, methodDescribe); + } + + + @OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE) + @PostMapping("/update") + @ApiOperation("修改模型") + @ApiImplicitParam(name = "updateParam", value = "流程分类数据", required = true) + public HttpResult update( @Validated @RequestBody BpmModelParam.BpmModelUpdateParam updateParam) { + String methodDescribe = getMethodDescribe("update"); + modelService.updateModel(updateParam); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + + +// @PostMapping("/import") +// @Operation(summary = "导入模型") +// @PreAuthorize("@ss.hasPermission('bpm:model:import')") +// public CommonResult importModel(@Valid BpmModeImportReqVO importReqVO) throws IOException { +// BpmModelCreateReqVO createReqVO = BeanUtils.toBean(importReqVO, BpmModelCreateReqVO.class); +// // 读取文件 +// String bpmnXml = IoUtils.readUtf8(importReqVO.getBpmnFile().getInputStream(), false); +// return success(modelService.createModel(createReqVO, bpmnXml)); +// } + + + @PostMapping("/deploy") + @Operation(summary = "部署模型") + @Parameter(name = "id", description = "编号", required = true) + public HttpResult deployModel(String id) { + String methodDescribe = getMethodDescribe("deployModel"); + modelService.deployModel(id); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } +// +// @PutMapping("/update-state") +// @Operation(summary = "修改模型的状态", description = "实际更新的部署的流程定义的状态") +// @PreAuthorize("@ss.hasPermission('bpm:model:update')") +// public CommonResult updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) { +// modelService.updateModelState(reqVO.getId(), reqVO.getState()); +// return success(true); +// } +// +// @DeleteMapping("/delete") +// @Operation(summary = "删除模型") +// @Parameter(name = "id", description = "编号", required = true, example = "1024") +// @PreAuthorize("@ss.hasPermission('bpm:model:delete')") +// public CommonResult deleteModel(@RequestParam("id") String id) { +// modelService.deleteModel(id); +// return success(true); +// } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmProcessDefinitionController.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmProcessDefinitionController.java new file mode 100644 index 000000000..33d47f025 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmProcessDefinitionController.java @@ -0,0 +1,123 @@ +package com.njcn.bpm.controller; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.pojo.param.BpmProcessDefinitionInfoParam; +import com.njcn.bpm.pojo.po.BpmCategory; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo; +import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO; +import com.njcn.bpm.service.IBpmCategoryService; +import com.njcn.bpm.service.IBpmFormService; +import com.njcn.bpm.service.IBpmProcessDefinitionService; +import com.njcn.bpm.strategy.BpmTaskCandidateStartUserSelectStrategy; +import com.njcn.bpm.utils.BpmProcessDefinitionConvert; +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.HttpResultUtil; +import com.njcn.web.controller.BaseController; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static com.njcn.bpm.utils.CollectionUtils.convertSet; + + +@RestController +@RequestMapping("/bpm/processDefinition") +@Validated +@Slf4j +@Api(tags = "管理后台 - 流程定义") +@RequiredArgsConstructor +public class BpmProcessDefinitionController extends BaseController { + + private final IBpmProcessDefinitionService processDefinitionService; + + private final IBpmFormService formService; + + private final IBpmCategoryService categoryService; + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/page") + @ApiOperation("获得流程定义分页") + @ApiImplicitParam(name = "bpmProcessDefinitionInfoQueryParam", value = "查询参数", required = true) + public HttpResult> getProcessDefinitionPage( + BpmProcessDefinitionInfoParam.BpmProcessDefinitionInfoQueryParam bpmProcessDefinitionInfoQueryParam) { + String methodDescribe = getMethodDescribe("getProcessDefinitionPage"); + Page pageResult = processDefinitionService.getProcessDefinitionPage(bpmProcessDefinitionInfoQueryParam); + if (CollUtil.isEmpty(pageResult.getRecords())) { + Page result = new Page<>(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + // 获得 Category Map + Map categoryMap = categoryService.getCategoryMap( + convertSet(pageResult.getRecords(), ProcessDefinition::getCategory)); + // 获得 Deployment Map + Map deploymentMap = processDefinitionService.getDeploymentMap( + convertSet(pageResult.getRecords(), ProcessDefinition::getDeploymentId)); + // 获得 BpmProcessDefinitionInfoDO Map + Map processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getRecords(), ProcessDefinition::getId)); + // 获得 Form Map + Map formMap = formService.getFormMap( + convertSet(processDefinitionMap.values(), BpmProcessDefinitionInfo::getFormId)); + + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionPage( + pageResult, deploymentMap, processDefinitionMap, formMap, categoryMap), methodDescribe); + } + + @GetMapping("/list") + @Operation(summary = "获得流程定义列表") + @Parameter(name = "suspensionState", description = "挂起状态", required = true) // 参见 Flowable SuspensionState 枚举 + public HttpResult> getProcessDefinitionList(Integer suspensionState) { + String methodDescribe = getMethodDescribe("getProcessDefinitionList"); + List list = processDefinitionService.getProcessDefinitionListBySuspensionState(suspensionState); + if (CollUtil.isEmpty(list)) { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, new ArrayList<>(), methodDescribe); + } + + // 获得 BpmProcessDefinitionInfoDO Map + Map processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(list, ProcessDefinition::getId)); + + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionList( + list, null, processDefinitionMap, null, null), methodDescribe); + } + + @GetMapping("/get") + @Operation(summary = "获得流程定义") + @Parameter(name = "id", description = "流程编号", required = true, example = "1024") + @Parameter(name = "key", description = "流程定义标识", required = true, example = "1024") + public HttpResult getProcessDefinition( + @RequestParam(value = "id", required = false) String id, + @RequestParam(value = "key", required = false) String key) { + String methodDescribe = getMethodDescribe("getProcessDefinition"); + ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id) + : processDefinitionService.getActiveProcessDefinition(key); + if (processDefinition == null) { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId()); + List userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition( + processDefinition, null, null, null, null, bpmnModel, userTaskList), methodDescribe); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/event/BpmProcessInstanceEventPublisher.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/event/BpmProcessInstanceEventPublisher.java new file mode 100644 index 000000000..71edf0212 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/event/BpmProcessInstanceEventPublisher.java @@ -0,0 +1,23 @@ +package com.njcn.bpm.event; + +import lombok.AllArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; + +/** + * + * @author 芋道源码 + */ +@AllArgsConstructor +@Validated +public class BpmProcessInstanceEventPublisher { + + private final ApplicationEventPublisher publisher; + + public void sendProcessInstanceResultEvent(@Valid BpmProcessInstanceStatusEvent event) { + publisher.publishEvent(event); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/event/BpmProcessInstanceStatusEvent.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/event/BpmProcessInstanceStatusEvent.java new file mode 100644 index 000000000..ce90b861e --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/event/BpmProcessInstanceStatusEvent.java @@ -0,0 +1,42 @@ +package com.njcn.bpm.event; + +import lombok.Data; +import org.springframework.context.ApplicationEvent; + +import javax.validation.constraints.NotNull; + +/** + * 流程实例的状态(结果)发生变化的 Event + * + * @author 芋道源码 + */ +@SuppressWarnings("ALL") +@Data +public class BpmProcessInstanceStatusEvent extends ApplicationEvent { + + /** + * 流程实例的编号 + */ + @NotNull(message = "流程实例的编号不能为空") + private String id; + /** + * 流程实例的 key + */ + @NotNull(message = "流程实例的 key 不能为空") + private String processDefinitionKey; + /** + * 流程实例的结果 + */ + @NotNull(message = "流程实例的状态不能为空") + private Integer status; + /** + * 流程实例对应的业务标识 + * 例如说,请假 + */ + private String businessKey; + + public BpmProcessInstanceStatusEvent(Object source) { + super(source); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/BpmCategoryMapper.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/BpmCategoryMapper.java new file mode 100644 index 000000000..0d9021177 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/BpmCategoryMapper.java @@ -0,0 +1,22 @@ +package com.njcn.bpm.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.pojo.po.BpmCategory; +import com.njcn.bpm.pojo.vo.BpmCategoryVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + + +/** + * BPM 流程分类 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface BpmCategoryMapper extends BaseMapper { + + + Page page(@Param("page") Page objectPage, @Param("ew") QueryWrapper categoryVOQueryWrapper); +} \ No newline at end of file diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/BpmFormMapper.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/BpmFormMapper.java new file mode 100644 index 000000000..bc0bfc731 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/BpmFormMapper.java @@ -0,0 +1,21 @@ +package com.njcn.bpm.mapper; + + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.vo.BpmFormVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 动态表单 Mapper + * + * @author 风里雾里 + */ +@Mapper +public interface BpmFormMapper extends BaseMapper { + Page page(@Param("page")Page objectPage, @Param("ew") QueryWrapper categoryVOQueryWrapper); + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/BpmProcessDefinitionInfoMapper.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/BpmProcessDefinitionInfoMapper.java new file mode 100644 index 000000000..52ce6ca8f --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/BpmProcessDefinitionInfoMapper.java @@ -0,0 +1,13 @@ +package com.njcn.bpm.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo; +import org.apache.ibatis.annotations.Mapper; + + +@Mapper +public interface BpmProcessDefinitionInfoMapper extends BaseMapper { + + + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/mapping/BpmCategoryMapper.xml b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/mapping/BpmCategoryMapper.xml new file mode 100644 index 000000000..6b13f0b61 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/mapping/BpmCategoryMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/mapping/BpmFormMapper.xml b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/mapping/BpmFormMapper.xml new file mode 100644 index 000000000..3df44fa34 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/mapping/BpmFormMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmCategoryService.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmCategoryService.java new file mode 100644 index 000000000..4c5712331 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmCategoryService.java @@ -0,0 +1,70 @@ +package com.njcn.bpm.service; + + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.njcn.bpm.pojo.param.BpmCategoryParam; +import com.njcn.bpm.pojo.po.BpmCategory; +import com.njcn.bpm.pojo.vo.BpmCategoryVO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.njcn.bpm.utils.CollectionUtils.convertMap; + + +/** + * BPM 流程分类 Service 接口 + * + * @author 芋道源码 + */ +public interface IBpmCategoryService extends IService { + + /** + * 创建流程分类 + * + * @return 编号 + */ + String createCategory( BpmCategoryParam bpmCategoryParam); + + /** + * 更新流程分类 + * + */ + void updateCategory( BpmCategoryParam.BpmCategoryUpdateParam updateParam); + + /** + * 删除流程分类 + */ + void deleteCategory(List ids); + + + /** + * 获得流程分类分页 + * + * @return 流程分类分页 + */ + Page getCategoryPage(BpmCategoryParam.BpmCategoryQueryParam bpmCategoryQueryParam); + + /** + * 获得流程分类 Map,基于指定编码 + * + * @param codes 编号数组 + * @return 流程分类 Map + */ + default Map getCategoryMap(Collection codes) { + return convertMap(getCategoryListByCode(codes), BpmCategory::getCode); + } + + /** + * 获得流程分类列表,基于指定编码 + * + * @return 流程分类列表 + */ + List getCategoryListByCode(Collection codes); + + + + List getCategoryList(); +} \ No newline at end of file diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmFormService.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmFormService.java new file mode 100644 index 000000000..f5d6a0355 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmFormService.java @@ -0,0 +1,77 @@ +package com.njcn.bpm.service; + + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.njcn.bpm.pojo.param.BpmFormParam; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.vo.BpmFormVO; +import com.njcn.bpm.utils.CollectionUtils; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + + +/** + * 动态表单 Service 接口 + * + * @author @风里雾里 + */ +public interface IBpmFormService extends IService { + + /** + * 创建动态表单 + * + * @return 编号 + */ + String createForm(BpmFormParam bpmFormParam); + + /** + * 更新动态表单 + * + */ + void updateForm(BpmFormParam.BpmFormUpdateParam updateParam); + + /** + * 删除动态表单 + * + * @param id 编号 + */ + void deleteForm(List id); + + + + /** + * 获得动态表单列表 + * + * @return 动态表单列表 + */ + List getFormList(); + + /** + * 获得动态表单列表 + * + * @param ids 编号 + * @return 动态表单列表 + */ + List getFormList(Collection ids); + + /** + * 获得动态表单 Map + * + * @param ids 编号 + * @return 动态表单 Map + */ + default Map getFormMap(Collection ids) { + return CollectionUtils.convertMap(this.getFormList(ids), BpmForm::getId); + } + + /** + * 获得动态表单分页 + * + * @return 动态表单分页 + */ + Page getFormPage(BpmFormParam.BpmFormQueryParam bpmFormQueryParam); + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmModelService.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmModelService.java new file mode 100644 index 000000000..4e989b622 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmModelService.java @@ -0,0 +1,84 @@ +package com.njcn.bpm.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.pojo.param.BpmModelParam; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Model; + +import javax.validation.Valid; + +/** + * Flowable流程模型接口 + * + * @author yunlongn + */ +public interface IBpmModelService { + + /** + * 获得流程模型分页 + * + * @return 流程模型分页 + */ + Page getModelPage(BpmModelParam.BpmModelQueryParam bpmModelQueryParam); + + /** + * 创建流程模型 + * + * @param bpmnXml BPMN XML + * @return 创建的流程模型的编号 + */ + String createModel(BpmModelParam bpmModelParam, String bpmnXml); + + /** + * 获得流程模块 + * + * @param id 编号 + * @return 流程模型 + */ + Model getModel(String id); + + /** + * 获得流程模型的 BPMN XML + * + * @param id 编号 + * @return BPMN XML + */ + byte[] getModelBpmnXML(String id); + + /** + * 修改流程模型 + * + */ + void updateModel(BpmModelParam.BpmModelUpdateParam updateParam); + + /** + * 将流程模型,部署成一个流程定义 + * + * @param id 编号 + */ + void deployModel(String id); + + /** + * 删除模型 + * + * @param id 编号 + */ + void deleteModel(String id); + + /** + * 修改模型的状态,实际更新的部署的流程定义的状态 + * + * @param id 编号 + * @param state 状态 + */ + void updateModelState(String id, Integer state); + + /** + * 获得流程定义编号对应的 BPMN Model + * + * @param processDefinitionId 流程定义编号 + * @return BPMN Model + */ + BpmnModel getBpmnModelByDefinitionId(String processDefinitionId); + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmProcessDefinitionService.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmProcessDefinitionService.java new file mode 100644 index 000000000..144a10cd4 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/IBpmProcessDefinitionService.java @@ -0,0 +1,163 @@ +package com.njcn.bpm.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO; +import com.njcn.bpm.pojo.param.BpmProcessDefinitionInfoParam; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.njcn.bpm.utils.CollectionUtils.convertMap; + + +/** + * Flowable流程定义接口 + * + * @author yunlong.li + * @author ZJQ + * @author 芋道源码 + */ +public interface IBpmProcessDefinitionService extends IService { + + /** + * 获得流程定义分页 + * + * @return 流程定义 Page + */ + Page getProcessDefinitionPage(BpmProcessDefinitionInfoParam.BpmProcessDefinitionInfoQueryParam bpmProcessDefinitionInfoQueryParam); + + /** + * 获得流程定义列表 + * + * @param suspensionState 中断状态 + * @return 流程定义列表 + */ + List getProcessDefinitionListBySuspensionState(Integer suspensionState); + + /** + * 基于流程模型,创建流程定义 + * + * @param model 流程模型 + * @param modelMetaInfo 流程模型元信息 + * @param bpmnBytes BPMN XML 字节数组 + * @param form 表单 + * @return 流程编号 + */ + String createProcessDefinition(Model model, BpmModelMetaInfoRespDTO modelMetaInfo, byte[] bpmnBytes, BpmForm form); + + /** + * 更新流程定义状态 + * + * @param id 流程定义的编号 + * @param state 状态 + */ + void updateProcessDefinitionState(String id, Integer state); + + /** + * 获得流程定义对应的 BPMN + * + * @param id 流程定义编号 + * @return BPMN + */ + BpmnModel getProcessDefinitionBpmnModel(String id); + + /** + * 获得流程定义的信息 + * + * @param id 流程定义编号 + * @return 流程定义信息 + */ + BpmProcessDefinitionInfo getProcessDefinitionInfo(String id); + + /** + * 获得流程定义的信息 List + * + * @param ids 流程定义编号数组 + * @return 流程额定义信息数组 + */ + List getProcessDefinitionInfoList(Collection ids); + + default Map getProcessDefinitionInfoMap(Set ids) { + return convertMap(getProcessDefinitionInfoList(ids), BpmProcessDefinitionInfo::getProcessDefinitionId); + } + + /** + * 获得流程定义编号对应的 ProcessDefinition + * + * @param id 流程定义编号 + * @return 流程定义 + */ + ProcessDefinition getProcessDefinition(String id); + + /** + * 获得 ids 对应的 ProcessDefinition 数组 + * + * @param ids 编号的数组 + * @return 流程定义的数组 + */ + List getProcessDefinitionList(Set ids); + + default Map getProcessDefinitionMap(Set ids) { + return convertMap(getProcessDefinitionList(ids), ProcessDefinition::getId); + } + + /** + * 获得 deploymentId 对应的 ProcessDefinition + * + * @param deploymentId 部署编号 + * @return 流程定义 + */ + ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId); + + /** + * 获得 deploymentIds 对应的 ProcessDefinition 数组 + * + * @param deploymentIds 部署编号的数组 + * @return 流程定义的数组 + */ + List getProcessDefinitionListByDeploymentIds(Set deploymentIds); + + /** + * 获得流程定义标识对应的激活的流程定义 + * + * @param key 流程定义的标识 + * @return 流程定义 + */ + ProcessDefinition getActiveProcessDefinition(String key); + + /** + * 获得 ids 对应的 Deployment Map + * + * @param ids 部署编号的数组 + * @return 流程部署 Map + */ + default Map getDeploymentMap(Set ids) { + return convertMap(getDeploymentList(ids), Deployment::getId); + } + + /** + * 获得 ids 对应的 Deployment 数组 + * + * @param ids 部署编号的数组 + * @return 流程部署的数组 + */ + List getDeploymentList(Set ids); + + /** + * 获得 id 对应的 Deployment + * + * @param id 部署编号 + * @return 流程部署 + */ + Deployment getDeployment(String id); + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmCategoryServiceImpl.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmCategoryServiceImpl.java new file mode 100644 index 000000000..1153c2ab9 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmCategoryServiceImpl.java @@ -0,0 +1,143 @@ +package com.njcn.bpm.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.njcn.bpm.enums.BpmResponseEnum; +import com.njcn.bpm.mapper.BpmCategoryMapper; +import com.njcn.bpm.pojo.param.BpmCategoryParam; +import com.njcn.bpm.pojo.po.BpmCategory; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.vo.BpmCategoryVO; +import com.njcn.bpm.pojo.vo.BpmFormVO; +import com.njcn.bpm.service.IBpmCategoryService; +import com.njcn.bpm.utils.BeanUtils; +import com.njcn.common.pojo.enums.common.DataStateEnum; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.web.factory.PageFactory; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * BPM 流程分类 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class BpmCategoryServiceImpl extends ServiceImpl implements IBpmCategoryService { + + + @Override + public String createCategory(BpmCategoryParam bpmCategoryParam) { + // 校验唯一 + checkCategoryName(bpmCategoryParam, false); + checkCategoryCode(bpmCategoryParam, false); + // 插入 + BpmCategory category = BeanUtils.toBean(bpmCategoryParam, BpmCategory.class); + this.baseMapper.insert(category); + return category.getId(); + } + + @Override + public void updateCategory(BpmCategoryParam.BpmCategoryUpdateParam updateParam) { + // 校验唯一 + checkCategoryName(updateParam, true); + checkCategoryCode(updateParam, true); + // 更新 + BpmCategory updateObj = BeanUtils.toBean(updateParam, BpmCategory.class); + this.baseMapper.updateById(updateObj); + } + + + @Override + public void deleteCategory(List ids) { + this.lambdaUpdate().set(BpmCategory::getState, DataStateEnum.DELETED.getCode()) + .in(BpmCategory::getId, ids) + .update(); + } + + + @Override + public Page getCategoryPage(BpmCategoryParam.BpmCategoryQueryParam bpmCategoryQueryParam) { + QueryWrapper categoryVOQueryWrapper = new QueryWrapper<>(); + if (StrUtil.isNotBlank(bpmCategoryQueryParam.getName())) { + categoryVOQueryWrapper.like("bpm_category.name", bpmCategoryQueryParam.getName()); + } + + if (StrUtil.isNotBlank(bpmCategoryQueryParam.getCode())) { + categoryVOQueryWrapper.like("bpm_category.name", bpmCategoryQueryParam.getCode()); + } + categoryVOQueryWrapper.eq("bpm_category.state", DataStateEnum.ENABLE.getCode()); + categoryVOQueryWrapper.orderByDesc("bpm_category.update_time"); + return this.baseMapper.page(new Page<>(PageFactory.getPageNum(bpmCategoryQueryParam), PageFactory.getPageSize(bpmCategoryQueryParam)), categoryVOQueryWrapper); + + } + + @Override + public List getCategoryListByCode(Collection codes) { + if (CollUtil.isEmpty(codes)) { + return Collections.emptyList(); + } + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.in(BpmCategory::getCode,codes); + return this.baseMapper.selectList(lambdaQueryWrapper); + } + + @Override + public List getCategoryList() { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.select(BpmCategory::getId,BpmCategory::getName) + .eq(BpmCategory::getState,DataStateEnum.ENABLE.getCode()); + return this.baseMapper.selectList(lambdaQueryWrapper); + } + + + private void checkCategoryName(BpmCategoryParam bpmCategoryParam, boolean isExcludeSelf) { + LambdaQueryWrapper categoryLambdaQueryWrapper = new LambdaQueryWrapper<>(); + //判断流程表单的名称 + categoryLambdaQueryWrapper + .eq(BpmCategory::getName, bpmCategoryParam.getName()) + .eq(BpmCategory::getState, DataStateEnum.ENABLE.getCode()); + //更新的时候,需排除当前记录 + if (isExcludeSelf) { + if (bpmCategoryParam instanceof BpmCategoryParam.BpmCategoryUpdateParam) { + categoryLambdaQueryWrapper.ne(BpmCategory::getId, ((BpmCategoryParam.BpmCategoryUpdateParam) bpmCategoryParam).getId()); + } + } + int nameCountByAccount = this.count(categoryLambdaQueryWrapper); + //大于等于1个则表示重复 + if (nameCountByAccount >= 1) { + throw new BusinessException(BpmResponseEnum.REPEAT_CATEGORY_NAME_FORM); + } + } + + private void checkCategoryCode(BpmCategoryParam bpmCategoryParam, boolean isExcludeSelf) { + LambdaQueryWrapper categoryLambdaQueryWrapper = new LambdaQueryWrapper<>(); + //判断流程表单的名称 + categoryLambdaQueryWrapper + .eq(BpmCategory::getCode, bpmCategoryParam.getCode()) + .eq(BpmCategory::getState, DataStateEnum.ENABLE.getCode()); + //更新的时候,需排除当前记录 + if (isExcludeSelf) { + if (bpmCategoryParam instanceof BpmCategoryParam.BpmCategoryUpdateParam) { + categoryLambdaQueryWrapper.ne(BpmCategory::getId, ((BpmCategoryParam.BpmCategoryUpdateParam) bpmCategoryParam).getId()); + } + } + int nameCountByAccount = this.count(categoryLambdaQueryWrapper); + //大于等于1个则表示重复 + if (nameCountByAccount >= 1) { + throw new BusinessException(BpmResponseEnum.REPEAT_CATEGORY_NAME_FORM); + } + } + + +} \ No newline at end of file diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmFormServiceImpl.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmFormServiceImpl.java new file mode 100644 index 000000000..ffc914409 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmFormServiceImpl.java @@ -0,0 +1,139 @@ +package com.njcn.bpm.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.njcn.bpm.enums.BpmResponseEnum; +import com.njcn.bpm.mapper.BpmFormMapper; +import com.njcn.bpm.pojo.param.BpmFormParam; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.vo.BpmFormVO; +import com.njcn.bpm.service.IBpmFormService; +import com.njcn.bpm.utils.BeanUtils; +import com.njcn.common.pojo.enums.common.DataStateEnum; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.web.factory.PageFactory; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.*; + + +/** + * 动态表单 Service 实现类 + * + * @author 风里雾里 + */ +@Service +@Validated +public class BpmFormServiceImpl extends ServiceImpl implements IBpmFormService { + + @Override + public Page getFormPage(BpmFormParam.BpmFormQueryParam bpmFormQueryParam) { + QueryWrapper bpmFormVOQueryWrapper = new QueryWrapper<>(); + if (StrUtil.isNotBlank(bpmFormQueryParam.getName())) { + bpmFormVOQueryWrapper.like("bpm_form.name", bpmFormQueryParam.getName()); + } + bpmFormVOQueryWrapper.eq("bpm_form.state", DataStateEnum.ENABLE.getCode()); + bpmFormVOQueryWrapper.orderByDesc("bpm_form.update_time"); + return this.baseMapper.page(new Page<>(PageFactory.getPageNum(bpmFormQueryParam), PageFactory.getPageSize(bpmFormQueryParam)), bpmFormVOQueryWrapper); + } + + + @Override + public String createForm(BpmFormParam bpmFormParam) { +// this.validateFields(createReqVO.getFields()); + checkFormName(bpmFormParam, false); + // 插入 + BpmForm form = BeanUtils.toBean(bpmFormParam, BpmForm.class); + this.baseMapper.insert(form); + // 返回 + return form.getId(); + } + + private void checkFormName(BpmFormParam bpmFormParam, boolean isExcludeSelf) { + LambdaQueryWrapper categoryLambdaQueryWrapper = new LambdaQueryWrapper<>(); + //判断流程表单的名称 + categoryLambdaQueryWrapper + .eq(BpmForm::getName, bpmFormParam.getName()) + .eq(BpmForm::getState, DataStateEnum.ENABLE.getCode()); + //更新的时候,需排除当前记录 + if (isExcludeSelf) { + if (bpmFormParam instanceof BpmFormParam.BpmFormUpdateParam) { + categoryLambdaQueryWrapper.ne(BpmForm::getId, ((BpmFormParam.BpmFormUpdateParam) bpmFormParam).getId()); + } + } + int nameCountByAccount = this.count(categoryLambdaQueryWrapper); + //大于等于1个则表示重复 + if (nameCountByAccount >= 1) { + throw new BusinessException(BpmResponseEnum.REPEAT_NAME_FORM); + } + } + + @Override + public void updateForm(BpmFormParam.BpmFormUpdateParam updateParam) { + checkFormName(updateParam, true); + BpmForm updateObj = BeanUtils.toBean(updateParam, BpmForm.class); + this.updateById(updateObj); + } + + @Override + public void deleteForm(List ids) { + this.lambdaUpdate().set(BpmForm::getState, DataStateEnum.DELETED.getCode()) + .in(BpmForm::getId, ids) + .update(); + } + +// private void validateFormExists(Long id) { +// if (this.baseMapper.selectById(id) == null) { +// throw exception(ErrorCodeConstants.FORM_NOT_EXISTS); +// } +// } + + + + @Override + public List getFormList() { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.select(BpmForm::getId,BpmForm::getName) + .eq(BpmForm::getState,DataStateEnum.ENABLE.getCode()); + return this.baseMapper.selectList(lambdaQueryWrapper); + } + + @Override + public List getFormList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return this.baseMapper.selectBatchIds(ids); + } + + + + /** + * 校验 Field,避免 field 重复 + * + * @param fields field 数组 + */ + private void validateFields(List fields) { + if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验 + return; + } +// Map fieldMap = new HashMap<>(); // key 是 vModel,value 是 label +// for (String field : fields) { +// BpmFormFieldRespDTO fieldDTO = JsonUtils.parseObject(field, BpmFormFieldRespDTO.class); +// Assert.notNull(fieldDTO); +// String oldLabel = fieldMap.put(fieldDTO.getVModel(), fieldDTO.getLabel()); +// // 如果不存在,则直接返回 +// if (oldLabel == null) { +// continue; +// } +// // 如果存在,则报错 +// throw exception(ErrorCodeConstants.FORM_FIELD_REPEAT, oldLabel, fieldDTO.getLabel(), fieldDTO.getVModel()); +// } + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmModelServiceImpl.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmModelServiceImpl.java new file mode 100644 index 000000000..96b17c53c --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmModelServiceImpl.java @@ -0,0 +1,283 @@ +package com.njcn.bpm.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.enums.BpmModelFormTypeEnum; +import com.njcn.bpm.enums.BpmResponseEnum; +import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO; +import com.njcn.bpm.pojo.param.BpmModelParam; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.service.IBpmFormService; +import com.njcn.bpm.service.IBpmModelService; +import com.njcn.bpm.service.IBpmProcessDefinitionService; +import com.njcn.bpm.strategy.BpmTaskCandidateInvoker; +import com.njcn.bpm.utils.*; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.web.factory.PageFactory; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.StartEvent; +import org.flowable.bpmn.model.UserTask; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ModelQuery; +import org.flowable.engine.repository.ProcessDefinition; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; +import java.util.Objects; + + +/** + * Flowable流程模型实现 + * 主要进行 Flowable {@link Model} 的维护 + * + * @author yunlongn + * @author 芋道源码 + * @author jason + */ +@Service +@Validated +@Slf4j +public class BpmModelServiceImpl implements IBpmModelService { + + @Resource + private RepositoryService repositoryService; + @Resource + private IBpmProcessDefinitionService processDefinitionService; + @Resource + private IBpmFormService bpmFormService; + @Resource + private BpmTaskCandidateInvoker taskCandidateInvoker; + + @Override + public Page getModelPage(BpmModelParam.BpmModelQueryParam bpmModelQueryParam) { + ModelQuery modelQuery = repositoryService.createModelQuery(); + if (StrUtil.isNotBlank(bpmModelQueryParam.getKey())) { + modelQuery.modelKey(bpmModelQueryParam.getKey()); + } + if (StrUtil.isNotBlank(bpmModelQueryParam.getName())) { + modelQuery.modelNameLike("%" + bpmModelQueryParam.getName() + "%"); // 模糊匹配 + } + if (StrUtil.isNotBlank(bpmModelQueryParam.getCategory())) { + modelQuery.modelCategory(bpmModelQueryParam.getCategory()); + } + // 执行查询 + long count = modelQuery.count(); + if (count == 0) { + return new Page<>(); + } + int offset = PageFactory.getPageSize(bpmModelQueryParam) * (PageFactory.getPageNum(bpmModelQueryParam) - 1); + List models = modelQuery + .modelTenantId(ProcessEngineConfiguration.NO_TENANT_ID) + .orderByCreateTime().desc() + .listPage(offset, PageFactory.getPageSize(bpmModelQueryParam)); + + Page page = new Page<>(); + page.setRecords(models); + page.setTotal(count); + page.setSize(PageFactory.getPageSize(bpmModelQueryParam)); + page.setCurrent(PageFactory.getPageNum(bpmModelQueryParam)); + return page; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String createModel(BpmModelParam bpmModelParam, String bpmnXml) { + if (!ValidationUtils.isXmlNCName(bpmModelParam.getKey())) { + throw new BusinessException(BpmResponseEnum.BPM_XML_ERROR); + } + // 校验流程标识已经存在 + Model keyModel = getModelByKey(bpmModelParam.getKey()); + if (keyModel != null) { + throw new BusinessException(BpmResponseEnum.BPM_MODEL_REPEAT); + } + // 创建流程定义 + Model model = repositoryService.newModel(); + BpmModelConvert.INSTANCE.copyToCreateModel(model, bpmModelParam); + model.setTenantId(FlowableUtils.getTenantId()); + // 保存流程定义 + repositoryService.saveModel(model); + // 保存 BPMN XML + saveModelBpmnXml(model, bpmnXml); + return model.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 + public void updateModel(BpmModelParam.BpmModelUpdateParam updateParam) { + // 校验流程模型存在 + Model model = getModel(updateParam.getId()); + if (model == null) { + throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST); + } + // 修改流程定义 + BpmModelConvert.INSTANCE.copyToUpdateModel(model, updateParam); + // 更新模型 + repositoryService.saveModel(model); + // 更新 BPMN XML + saveModelBpmnXml(model, updateParam.getBpmnXml()); + } + + @Override + @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 + public void deployModel(String id) { + // 1.1 校验流程模型存在 + Model model = getModel(id); + if (ObjectUtils.isEmpty(model)) { + throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST); + } + // 1.2 校验流程图 + byte[] bpmnBytes = getModelBpmnXML(model.getId()); + validateBpmnXml(bpmnBytes); + // 1.3 校验表单已配 + BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); + BpmForm form = validateFormConfig(metaInfo); + + // 1.4 校验任务分配规则已配置 + taskCandidateInvoker.validateBpmnConfig(bpmnBytes); + + // 2.1 创建流程定义 + String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, form); + + // 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。 + updateProcessDefinitionSuspended(model.getDeploymentId()); + + // 2.3 更新 model 的 deploymentId,进行关联 + ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId); + model.setDeploymentId(definition.getDeploymentId()); + repositoryService.saveModel(model); + } + + private void validateBpmnXml(byte[] bpmnBytes) { + BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); + if (bpmnModel == null) { + throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST); + } + // 1. 没有 StartEvent + StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel); + if (startEvent == null) { + throw new BusinessException(BpmResponseEnum.BPM_START_EVENT_NOT_EXIST); + } + // 2. 校验 UserTask 的 name 都配置了 + List userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); + UserTask userTask1 = userTasks.get(0); + userTask1.getName(); + userTasks.forEach(userTask -> { + if (StrUtil.isEmpty(userTask.getName())) { + throw new BusinessException(BpmResponseEnum.MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS); + } + }); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteModel(String id) { + // 校验流程模型存在 + Model model = getModel(id); + if (model == null) { + throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST); + } + // 执行删除 + repositoryService.deleteModel(id); + // 禁用流程定义 + updateProcessDefinitionSuspended(model.getDeploymentId()); + } + + @Override + public void updateModelState(String id, Integer state) { + // 1.1 校验流程模型存在 + Model model = getModel(id); + if (model == null) { + throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST); + } + // 1.2 校验流程定义存在 + ProcessDefinition definition = processDefinitionService.getProcessDefinitionByDeploymentId(model.getDeploymentId()); + if (definition == null) { + throw new BusinessException(BpmResponseEnum.PROCESS_DEFINITION_NOT_EXISTS); + } + + // 2. 更新状态 + processDefinitionService.updateProcessDefinitionState(definition.getId(), state); + } + + @Override + public BpmnModel getBpmnModelByDefinitionId(String processDefinitionId) { + return repositoryService.getBpmnModel(processDefinitionId); + } + + /** + * 校验流程表单已配置 + * + * @param metaInfo 流程模型元数据 + * @return 表单配置 + */ + private BpmForm validateFormConfig(BpmModelMetaInfoRespDTO metaInfo) { + if (metaInfo == null || metaInfo.getFormType() == null) { + throw new BusinessException(BpmResponseEnum.MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); + } + // 校验表单存在 + if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) { + if (metaInfo.getFormId() == null) { + throw new BusinessException(BpmResponseEnum.MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); + } + BpmForm form = bpmFormService.getById(metaInfo.getFormId()); + if (form == null) { + throw new BusinessException(BpmResponseEnum.FORM_NOT_EXISTS); + } + return form; + } else { + if (StrUtil.isEmpty(metaInfo.getFormCustomCreatePath()) || StrUtil.isEmpty(metaInfo.getFormCustomViewPath())) { + throw new BusinessException(BpmResponseEnum.MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); + } + return null; + } + } + + private void saveModelBpmnXml(Model model, String bpmnXml) { + if (StrUtil.isEmpty(bpmnXml)) { + return; + } + repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(bpmnXml)); + } + + /** + * 挂起 deploymentId 对应的流程定义 + *

+ * 注意:这里一个 deploymentId 只关联一个流程定义 + * + * @param deploymentId 流程发布Id + */ + private void updateProcessDefinitionSuspended(String deploymentId) { + if (StrUtil.isEmpty(deploymentId)) { + return; + } + ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(deploymentId); + if (oldDefinition == null) { + return; + } + processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), SuspensionState.SUSPENDED.getStateCode()); + } + + private Model getModelByKey(String key) { + return repositoryService.createModelQuery().modelKey(key).singleResult(); + } + + @Override + public Model getModel(String id) { + return repositoryService.getModel(id); + } + + @Override + public byte[] getModelBpmnXML(String id) { + return repositoryService.getModelEditorSource(id); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmProcessDefinitionServiceImpl.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmProcessDefinitionServiceImpl.java new file mode 100644 index 000000000..33e945782 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/impl/BpmProcessDefinitionServiceImpl.java @@ -0,0 +1,218 @@ +package com.njcn.bpm.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.njcn.bpm.enums.BpmnModelConstants; +import com.njcn.bpm.mapper.BpmCategoryMapper; +import com.njcn.bpm.mapper.BpmProcessDefinitionInfoMapper; +import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO; +import com.njcn.bpm.pojo.param.BpmProcessDefinitionInfoParam; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo; +import com.njcn.bpm.service.IBpmProcessDefinitionService; +import com.njcn.bpm.utils.BeanUtils; +import com.njcn.bpm.utils.FlowableUtils; +import com.njcn.common.pojo.enums.common.DataStateEnum; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.web.factory.PageFactory; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.repository.ProcessDefinitionQuery; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.*; + +import static com.njcn.bpm.utils.CollectionUtils.addIfNotNull; +import static java.util.Collections.emptyList; + +/** + * 流程定义实现 + * 主要进行 Flowable {@link ProcessDefinition} 和 {@link Deployment} 的维护 + * + * @author yunlongn + * @author ZJQ + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class BpmProcessDefinitionServiceImpl extends ServiceImpl implements IBpmProcessDefinitionService { + + @Resource + private RepositoryService repositoryService; + + + @Override + public Page getProcessDefinitionPage(BpmProcessDefinitionInfoParam.BpmProcessDefinitionInfoQueryParam bpmProcessDefinitionInfoQueryParam) { + ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery(); + if (StrUtil.isNotBlank(bpmProcessDefinitionInfoQueryParam.getKey())) { + query.processDefinitionKey(bpmProcessDefinitionInfoQueryParam.getKey()); + } + // 执行查询 + long count = query.count(); + if (count == 0) { + return new Page<>(); + } + int offset = PageFactory.getPageSize(bpmProcessDefinitionInfoQueryParam) * (PageFactory.getPageNum(bpmProcessDefinitionInfoQueryParam) - 1); + List processDefinitionList = query.orderByProcessDefinitionVersion().desc() + .listPage(offset, PageFactory.getPageSize(bpmProcessDefinitionInfoQueryParam)); + Page page = new Page<>(); + page.setRecords(processDefinitionList); + page.setTotal(count); + page.setSize(PageFactory.getPageSize(bpmProcessDefinitionInfoQueryParam)); + page.setCurrent(PageFactory.getPageNum(bpmProcessDefinitionInfoQueryParam)); + return page; + } + + + @Override + public ProcessDefinition getProcessDefinition(String id) { + return repositoryService.getProcessDefinition(id); + } + + @Override + public List getProcessDefinitionList(Set ids) { + return repositoryService.createProcessDefinitionQuery().processDefinitionIds(ids).list(); + } + + @Override + public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) { + if (StrUtil.isEmpty(deploymentId)) { + return null; + } + return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult(); + } + + @Override + public List getProcessDefinitionListByDeploymentIds(Set deploymentIds) { + if (CollUtil.isEmpty(deploymentIds)) { + return emptyList(); + } + return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list(); + } + + @Override + public ProcessDefinition getActiveProcessDefinition(String key) { + return repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).active().singleResult(); + } + + @Override + public List getDeploymentList(Set ids) { + if (CollUtil.isEmpty(ids)) { + return emptyList(); + } + List list = new ArrayList<>(ids.size()); + for (String id : ids) { + addIfNotNull(list, getDeployment(id)); + } + return list; + } + + @Override + public Deployment getDeployment(String id) { + if (StrUtil.isEmpty(id)) { + return null; + } + return repositoryService.createDeploymentQuery().deploymentId(id).singleResult(); + } + + @Override + public String createProcessDefinition(Model model, BpmModelMetaInfoRespDTO modelMetaInfo, + byte[] bpmnBytes, BpmForm form) { + // 创建 Deployment 部署 + Deployment deploy = repositoryService.createDeployment() + .key(model.getKey()).name(model.getName()).category(model.getCategory()) + .addBytes(model.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, bpmnBytes) + .tenantId(FlowableUtils.getTenantId()) + .disableSchemaValidation() // 禁用 XML Schema 验证,因为有自定义的属性 + .deploy(); + + // 设置 ProcessDefinition 的 category 分类 + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery() + .deploymentId(deploy.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory()); + // 注意 1,ProcessDefinition 的 key 和 name 是通过 BPMN 中的 的 id 和 name 决定 + // 注意 2,目前该项目的设计上,需要保证 Model、Deployment、ProcessDefinition 使用相同的 key,保证关联性。 + // 否则,会导致 ProcessDefinition 的分页无法查询到。 + if (!Objects.equals(definition.getKey(), model.getKey())) { + throw new BusinessException("流程定义的标识期望是(" + model.getKey() + "),当前是(" + definition.getKey() + "),请修改 BPMN 流程图"); + } + if (!Objects.equals(definition.getName(), model.getName())) { + throw new BusinessException("流程定义的名字期望是(" + model.getName() + "),当前是(" + definition.getName() + "),请修改 BPMN 流程图"); + } + + // 插入拓展表 + BpmProcessDefinitionInfo definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfo.class); + definitionDO.setModelId(model.getId()); + definitionDO.setProcessDefinitionId(definition.getId()); + if (form != null) { + definitionDO.setFormFields(form.getFields()); + definitionDO.setFormConf(form.getConf()); + } + this.baseMapper.insert(definitionDO); + return definition.getId(); + } + + @Override + public void updateProcessDefinitionState(String id, Integer state) { + // 激活 + if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) { + repositoryService.activateProcessDefinitionById(id, false, null); + return; + } + // 挂起 + if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) { + // suspendProcessInstances = false,进行中的任务,不进行挂起。 + // 原因:只要新的流程不允许发起即可,老流程继续可以执行。 + repositoryService.suspendProcessDefinitionById(id, false, null); + return; + } + log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state); + } + + @Override + public BpmnModel getProcessDefinitionBpmnModel(String id) { + return repositoryService.getBpmnModel(id); + } + + @Override + public BpmProcessDefinitionInfo getProcessDefinitionInfo(String id) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(BpmProcessDefinitionInfo::getProcessDefinitionId, id); + return this.baseMapper.selectOne(lambdaQueryWrapper); + } + + @Override + public List getProcessDefinitionInfoList(Collection ids) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(BpmProcessDefinitionInfo::getProcessDefinitionId, ids) + .eq(BpmProcessDefinitionInfo::getState, DataStateEnum.ENABLE.getCode()); + return this.baseMapper.selectList(lambdaQueryWrapper); + } + + + @Override + public List getProcessDefinitionListBySuspensionState(Integer suspensionState) { + // 拼接查询条件 + ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery(); + if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), suspensionState)) { + query.suspended(); + } else if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), suspensionState)) { + query.active(); + } + // 执行查询 + query.processDefinitionTenantId(FlowableUtils.getTenantId()); + return query.list(); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmProcessInstanceService.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmProcessInstanceService.java new file mode 100644 index 000000000..20fbcdf5a --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmProcessInstanceService.java @@ -0,0 +1,139 @@ +package com.njcn.bpm.service.task; + +import org.flowable.engine.delegate.event.FlowableCancelledEvent; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; + +import javax.validation.Valid; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.njcn.bpm.utils.CollectionUtils.convertMap; + + +/** + * 流程实例 Service 接口 + * + * @author 芋道源码 + */ +public interface IBpmProcessInstanceService { + + /** + * 获得流程实例 + * + * @param id 流程实例的编号 + * @return 流程实例 + */ + ProcessInstance getProcessInstance(String id); +// +// /** +// * 获得流程实例列表 +// * +// * @param ids 流程实例的编号集合 +// * @return 流程实例列表 +// */ +// List getProcessInstances(Set ids); +// +// /** +// * 获得流程实例 Map +// * +// * @param ids 流程实例的编号集合 +// * @return 流程实例列表 Map +// */ +// default Map getProcessInstanceMap(Set ids) { +// return convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId); +// } +// +// /** +// * 获得历史的流程实例 +// * +// * @param id 流程实例的编号 +// * @return 历史的流程实例 +// */ +// HistoricProcessInstance getHistoricProcessInstance(String id); +// +// /** +// * 获得历史的流程实例列表 +// * +// * @param ids 流程实例的编号集合 +// * @return 历史的流程实例列表 +// */ +// List getHistoricProcessInstances(Set ids); +// +// /** +// * 获得历史的流程实例 Map +// * +// * @param ids 流程实例的编号集合 +// * @return 历史的流程实例列表 Map +// */ +// default Map getHistoricProcessInstanceMap(Set ids) { +// return convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId); +// } +// +// /** +// * 获得流程实例的分页 +// * +// * @param userId 用户编号 +// * @param pageReqVO 分页请求 +// * @return 流程实例的分页 +// */ +// PageResult getProcessInstancePage(Long userId, @Valid BpmProcessInstancePageReqVO pageReqVO); +// +// /** +// * 创建流程实例(提供给前端) +// * +// * @param userId 用户编号 +// * @param createReqVO 创建信息 +// * @return 实例的编号 +// */ +// String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO); +// +// /** +// * 创建流程实例(提供给内部) +// * +// * @param userId 用户编号 +// * @param createReqDTO 创建信息 +// * @return 实例的编号 +// */ +// String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO); +// +// /** +// * 发起人取消流程实例 +// * +// * @param userId 用户编号 +// * @param cancelReqVO 取消信息 +// */ +// void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO); +// +// /** +// * 管理员取消流程实例 +// * +// * @param userId 用户编号 +// * @param cancelReqVO 取消信息 +// */ +// void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO); +// +// /** +// * 更新 ProcessInstance 拓展记录为取消 +// * +// * @param event 流程取消事件 +// */ +// void updateProcessInstanceWhenCancel(FlowableCancelledEvent event); +// +// /** +// * 更新 ProcessInstance 拓展记录为完成 +// * +// * @param instance 流程任务 +// */ +// void updateProcessInstanceWhenApprove(ProcessInstance instance); +// +// /** +// * 更新 ProcessInstance 拓展记录为不通过 +// * +// * @param id 流程编号 +// * @param reason 理由。例如说,审批不通过时,需要传递该值 +// */ +// void updateProcessInstanceReject(String id, String reason); + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java new file mode 100644 index 000000000..9083efe05 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java @@ -0,0 +1,292 @@ +package com.njcn.bpm.service.task.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.njcn.bpm.service.task.IBpmProcessInstanceService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.HistoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.delegate.event.FlowableCancelledEvent; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.*; + + +/** + * 流程实例 Service 实现类 + * + * ProcessDefinition & ProcessInstance & Execution & Task 的关系: + * 1. + * + * HistoricProcessInstance & ProcessInstance 的关系: + * 1. + * + * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class BpmProcessInstanceServiceImpl implements IBpmProcessInstanceService { + + @Resource + private RuntimeService runtimeService; + @Resource + private HistoryService historyService; + +// @Resource +// private BpmProcessDefinitionService processDefinitionService; +// @Resource +// private BpmMessageService messageService; +// +// @Resource +// private AdminUserApi adminUserApi; +// +// @Resource +// private BpmProcessInstanceEventPublisher processInstanceEventPublisher; + + @Override + public ProcessInstance getProcessInstance(String id) { + return runtimeService.createProcessInstanceQuery() + .includeProcessVariables() + .processInstanceId(id) + .singleResult(); + } +// +// @Override +// public List getProcessInstances(Set ids) { +// return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); +// } +// +// @Override +// public HistoricProcessInstance getHistoricProcessInstance(String id) { +// return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); +// } +// +// @Override +// public List getHistoricProcessInstances(Set ids) { +// return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); +// } +// +// @Override +// public PageResult getProcessInstancePage(Long userId, +// BpmProcessInstancePageReqVO pageReqVO) { +// // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 +// HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() +// .includeProcessVariables() +// .processInstanceTenantId(FlowableUtils.getTenantId()) +// .orderByProcessInstanceStartTime().desc(); +// if (userId != null) { // 【我的流程】菜单时,需要传递该字段 +// processInstanceQuery.startedBy(String.valueOf(userId)); +// } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 +// processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); +// } +// if (StrUtil.isNotEmpty(pageReqVO.getName())) { +// processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); +// } +// if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) { +// processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%"); +// } +// if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { +// processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); +// } +// if (pageReqVO.getStatus() != null) { +// processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus()); +// } +// if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { +// processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); +// processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); +// } +// // 查询数量 +// long processInstanceCount = processInstanceQuery.count(); +// if (processInstanceCount == 0) { +// return PageResult.empty(processInstanceCount); +// } +// // 查询列表 +// List processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); +// return new PageResult<>(processInstanceList, processInstanceCount); +// } +// +// @Override +// @Transactional(rollbackFor = Exception.class) +// public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { +// // 获得流程定义 +// ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); +// // 发起流程 +// return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, +// createReqVO.getStartUserSelectAssignees()); +// } +// +// @Override +// public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { +// // 获得流程定义 +// ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); +// // 发起流程 +// return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), +// createReqDTO.getStartUserSelectAssignees()); +// } +// +// private String createProcessInstance0(Long userId, ProcessDefinition definition, +// Map variables, String businessKey, +// Map> startUserSelectAssignees) { +// // 1.1 校验流程定义 +// if (definition == null) { +// throw exception(PROCESS_DEFINITION_NOT_EXISTS); +// } +// if (definition.isSuspended()) { +// throw exception(PROCESS_DEFINITION_IS_SUSPENDED); +// } +// // 1.2 校验发起人自选审批人 +// validateStartUserSelectAssignees(definition, startUserSelectAssignees); +// +// // 2. 创建流程实例 +// if (variables == null) { +// variables = new HashMap<>(); +// } +// FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 +// variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 +// BpmProcessInstanceStatusEnum.RUNNING.getStatus()); +// if (CollUtil.isNotEmpty(startUserSelectAssignees)) { +// variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); +// } +// ProcessInstance instance = runtimeService.createProcessInstanceBuilder() +// .processDefinitionId(definition.getId()) +// .businessKey(businessKey) +// .name(definition.getName().trim()) +// .variables(variables) +// .start(); +// return instance.getId(); +// } +// +// private void validateStartUserSelectAssignees(ProcessDefinition definition, Map> startUserSelectAssignees) { +// // 1. 获得发起人自选审批人的 UserTask 列表 +// BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); +// List userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); +// if (CollUtil.isEmpty(userTaskList)) { +// return; +// } +// +// // 2. 校验发起人自选审批人的 UserTask 是否都配置了 +// userTaskList.forEach(userTask -> { +// List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null; +// if (CollUtil.isEmpty(assignees)) { +// throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName()); +// } +// Map userMap = adminUserApi.getUserMap(assignees); +// assignees.forEach(assignee -> { +// if (userMap.get(assignee) == null) { +// throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee); +// } +// }); +// }); +// } +// +// @Override +// public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { +// // 1.1 校验流程实例存在 +// ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); +// if (instance == null) { +// throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); +// } +// // 1.2 只能取消自己的 +// if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { +// throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); +// } +// +// // 2. 通过删除流程实例,实现流程实例的取消, +// // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 +// deleteProcessInstance(cancelReqVO.getId(), +// BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); +// +// // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 +// } +// +// @Override +// public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { +// // 1.1 校验流程实例存在 +// ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); +// if (instance == null) { +// throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); +// } +// // 1.2 管理员取消,不用校验是否为自己的 +// AdminUserRespDTO user = adminUserApi.getUser(userId); +// +// // 2. 通过删除流程实例,实现流程实例的取消, +// // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 +// deleteProcessInstance(cancelReqVO.getId(), +// BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); +// +// // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 +// } +// +// @Override +// public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) { +// // 1. 判断是否为 Reject 不通过。如果是,则不进行更新. +// // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了 +// if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) { +// return; +// } +// +// // 2. 更新流程实例 status +// runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, +// BpmProcessInstanceStatusEnum.CANCEL.getStatus()); +// +// // 3. 发送流程实例的状态事件 +// // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance +// HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); +// // 发送流程实例的状态事件 +// processInstanceEventPublisher.sendProcessInstanceResultEvent( +// BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus())); +// } +// +// @Override +// public void updateProcessInstanceWhenApprove(ProcessInstance instance) { +// // 1. 更新流程实例 status +// runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, +// BpmProcessInstanceStatusEnum.APPROVE.getStatus()); +// +// // 2. 发送流程被【通过】的消息 +// messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); +// +// // 3. 发送流程实例的状态事件 +// // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance +// HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); +// processInstanceEventPublisher.sendProcessInstanceResultEvent( +// BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus())); +// } +// +// @Override +// @Transactional(rollbackFor = Exception.class) +// public void updateProcessInstanceReject(String id, String reason) { +// // 1. 更新流程实例 status +// runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); +// +// // 2. 删除流程实例,以实现驳回任务时,取消整个审批流程 +// ProcessInstance processInstance = getProcessInstance(id); +// deleteProcessInstance(id, StrUtil.format(BpmDeleteReasonEnum.REJECT_TASK.format(reason))); +// +// // 3. 发送流程被【不通过】的消息 +// messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason)); +// +// // 4. 发送流程实例的状态事件 +// processInstanceEventPublisher.sendProcessInstanceResultEvent( +// BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); +// } +// +// private void deleteProcessInstance(String id, String reason) { +// runtimeService.deleteProcessInstance(id, reason); +// } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateInvoker.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateInvoker.java new file mode 100644 index 000000000..90a5a236c --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateInvoker.java @@ -0,0 +1,113 @@ +package com.njcn.bpm.strategy; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.google.common.annotations.VisibleForTesting; +import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum; +import com.njcn.bpm.utils.BpmnModelUtils; +import com.njcn.bpm.utils.CollectionUtils; +import com.njcn.common.pojo.enums.common.DataStateEnum; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.user.api.UserFeignClient; +import com.njcn.user.pojo.po.User; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.util.*; + + +/** + * @author 芋道源码 + */ +@Slf4j +public class BpmTaskCandidateInvoker { + + private final Map strategyMap = new HashMap<>(); + + private final UserFeignClient adminUserApi; + + public BpmTaskCandidateInvoker(List strategyList, + UserFeignClient adminUserApi) { + strategyList.forEach(strategy -> { + IBpmTaskCandidateStrategy oldStrategy = strategyMap.put(strategy.getStrategy(), strategy); + Assert.isNull(oldStrategy, "策略(%s) 重复", strategy.getStrategy()); + }); + this.adminUserApi = adminUserApi; + } + + /** + * 校验流程模型的任务分配规则全部都配置了 + * 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去! + * + * @param bpmnBytes BPMN XML + */ + public void validateBpmnConfig(byte[] bpmnBytes) { + BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); + assert bpmnModel != null; + List userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); + // 遍历所有的 UserTask,校验审批人配置 + userTaskList.forEach(userTask -> { + // 1. 非空校验 + Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask); + String param = BpmnModelUtils.parseCandidateParam(userTask); + if (strategy == null) { + throw new BusinessException("部署流程失败,原因:BPMN 流程图中,用户任务(" + userTask.getName() + ")的名字不存在"); + } + IBpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy); + if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) { + throw new BusinessException("部署流程失败,原因:用户任务(" + userTask.getName() + ")未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置"); + } + // 2. 具体策略校验 + getCandidateStrategy(strategy).validateParam(param); + }); + } + + /** + * 计算任务的候选人 + * + * @param execution 执行任务 + * @return 用户编号集合 + */ + public List calculateUsers(DelegateExecution execution) { + Integer strategy = BpmnModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement()); + String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement()); + // 1.1 计算任务的候选人 + List userIds = getCandidateStrategy(strategy).calculateUsers(execution, param); + // 1.2 移除被禁用的用户 + removeDisableUsers(userIds); + + // 2. 校验是否有候选人 + if (CollUtil.isEmpty(userIds)) { + log.error("[calculateUsers][流程任务({}/{}/{}) 任务规则({}/{}) 找不到候选人]", execution.getId(), + execution.getProcessDefinitionId(), execution.getCurrentActivityId(), strategy, param); + throw new BusinessException("操作失败,原因:找不到任务的审批人!"); + } + return userIds; + } + + @VisibleForTesting + void removeDisableUsers(List assigneeUserIds) { + if (CollUtil.isEmpty(assigneeUserIds)) { + return; + } + List users = adminUserApi.getUserByIdList(assigneeUserIds).getData(); + Map userMap = CollectionUtils.convertMap(users, User::getId); + assigneeUserIds.removeIf(id -> { + User user = userMap.get(id); + return user == null || !DataStateEnum.ENABLE.getCode().equals(user.getState()); + }); + } + + private IBpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) { + BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy); + Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy); + IBpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum); + Assert.notNull(strategyObj, "策略(%s) 不存在", strategy); + return strategyObj; + } + +} \ No newline at end of file diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateRoleStrategy.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateRoleStrategy.java new file mode 100644 index 000000000..8defa012b --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateRoleStrategy.java @@ -0,0 +1,44 @@ +package com.njcn.bpm.strategy; + +import com.njcn.user.api.UserFeignClient; +import com.njcn.bpm.utils.StrUtils; +import org.flowable.engine.delegate.DelegateExecution; +import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Set; + +/** + * 角色 {@link IBpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateRoleStrategy implements IBpmTaskCandidateStrategy { + + @Resource + private UserFeignClient userFeignClient; + +// @Resource +// private PermissionApi permissionApi; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.ROLE; + } + + @Override + public void validateParam(String param) { +// Set roleIds = StrUtils.splitToLongSet(param); +// roleApi.validRoleList(roleIds); + } + + @Override + public List calculateUsers(DelegateExecution execution, String param) { + List roleIds = StrUtils.splitToStringList(param); + return userFeignClient.getUserIdByRoleId(roleIds).getData(); + } + +} \ No newline at end of file diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateStartUserSelectStrategy.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateStartUserSelectStrategy.java new file mode 100644 index 000000000..a66d5c59a --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateStartUserSelectStrategy.java @@ -0,0 +1,74 @@ +package com.njcn.bpm.strategy; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum; +import com.njcn.bpm.service.task.IBpmProcessInstanceService; +import com.njcn.bpm.utils.BpmnModelUtils; +import com.njcn.bpm.utils.FlowableUtils; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.*; + +/** + * + * @author 芋道源码 + */ +@Component +public class BpmTaskCandidateStartUserSelectStrategy implements IBpmTaskCandidateStrategy { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private IBpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.START_USER_SELECT; + } + + @Override + public void validateParam(String param) {} + + @Override + public List calculateUsers(DelegateExecution execution, String param) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId()); + Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance); + Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", + execution.getProcessInstanceId()); + // 获得审批人 + List assignees = startUserSelectAssignees.get(execution.getCurrentActivityId()); + return new ArrayList<>(assignees); + } + + @Override + public boolean isParamRequired() { + return false; + } + + /** + * 获得发起人自选审批人的 UserTask 列表 + * + * @param bpmnModel BPMN 模型 + * @return UserTask 列表 + */ + public static List getStartUserSelectUserTaskList(BpmnModel bpmnModel) { + if (bpmnModel == null) { + return null; + } + List userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); + if (CollUtil.isEmpty(userTaskList)) { + return null; + } + userTaskList.removeIf(userTask -> !Objects.equals(BpmnModelUtils.parseCandidateStrategy(userTask), + BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())); + return userTaskList; + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateUserStrategy.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateUserStrategy.java new file mode 100644 index 000000000..8e8d85604 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateUserStrategy.java @@ -0,0 +1,39 @@ +package com.njcn.bpm.strategy; + +import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum; +import com.njcn.bpm.utils.StrUtils; +import com.njcn.user.api.UserFeignClient; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 用户 {@link IBpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateUserStrategy implements IBpmTaskCandidateStrategy { + + @Resource + private UserFeignClient adminUserApi; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.USER; + } + + @Override + public void validateParam(String param) { +// adminUserApi.validateUserList(StrUtils.splitToLongSet(param)); + //暂时默认均有效,后期可以加强管理,todo... + } + + @Override + public List calculateUsers(DelegateExecution execution, String param) { + return StrUtils.splitToStringList(param); + } + +} \ No newline at end of file diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/IBpmTaskCandidateStrategy.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/IBpmTaskCandidateStrategy.java new file mode 100644 index 000000000..34494cfc9 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/IBpmTaskCandidateStrategy.java @@ -0,0 +1,49 @@ +package com.njcn.bpm.strategy; + +import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum; +import org.flowable.engine.delegate.DelegateExecution; + +import java.util.List; +import java.util.Set; + +/** + * BPM 任务的候选人的策略接口 + * + * 例如说:分配审批人 + * + * @author 芋道源码 + */ +public interface IBpmTaskCandidateStrategy { + + /** + * 对应策略 + * + * @return 策略 + */ + BpmTaskCandidateStrategyEnum getStrategy(); + + /** + * 校验参数 + * + * @param param 参数 + */ + void validateParam(String param); + + /** + * 基于执行任务,获得任务的候选用户们 + * + * @param execution 执行任务 + * @return 用户编号集合 + */ + List calculateUsers(DelegateExecution execution, String param); + + /** + * 是否一定要输入参数 + * + * @return 是否 + */ + default boolean isParamRequired() { + return true; + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmModelConvert.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmModelConvert.java new file mode 100644 index 000000000..d2d06ac59 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmModelConvert.java @@ -0,0 +1,151 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.enums.BpmModelFormTypeEnum; +import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO; +import com.njcn.bpm.pojo.dto.PageResult; +import com.njcn.bpm.pojo.param.BpmModelParam; +import com.njcn.bpm.pojo.po.BpmCategory; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo; +import com.njcn.bpm.pojo.vo.BpmModelRespVO; +import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 流程模型 Convert + * + * @author yunlongn + */ +@Mapper +public interface BpmModelConvert { + + BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class); + + default Page buildModelPage(Page pageResult, + Map formMap, + Map categoryMap, Map deploymentMap, + Map processDefinitionMap) { + List list = CollectionUtils.convertList(pageResult.getRecords(), model -> { + BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model); + BpmForm form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null; + BpmCategory category = categoryMap.get(model.getCategory()); + Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null; + ProcessDefinition processDefinition = model.getDeploymentId() != null ? processDefinitionMap.get(model.getDeploymentId()) : null; + return buildModel0(model, metaInfo, form, category, deployment, processDefinition); + }); + Page voPage = new Page<>(); + voPage.setRecords(list); + voPage.setTotal(pageResult.getTotal()); + voPage.setSize(pageResult.getSize()); + voPage.setCurrent(pageResult.getCurrent()); + return voPage; + } + + default BpmModelRespVO buildModel(Model model, + byte[] bpmnBytes) { + BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model); + BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null); + if (ArrayUtil.isNotEmpty(bpmnBytes)) { + modelVO.setBpmnXml(new String(bpmnBytes)); + } + return modelVO; + } + + default BpmModelRespVO buildModel0(Model model, + BpmModelMetaInfoRespDTO metaInfo, BpmForm form, BpmCategory category, + Deployment deployment, ProcessDefinition processDefinition) { + BpmModelRespVO modelRespVO = new BpmModelRespVO(); + modelRespVO.setId(model.getId()); + modelRespVO.setName(model.getName()); + modelRespVO.setKey(model.getKey()); + modelRespVO.setCategory(model.getCategory()); + modelRespVO.setCreateTime(DateUtils.of(model.getCreateTime())); + // Form + if (metaInfo != null) { + modelRespVO.setFormType(metaInfo.getFormType()); + modelRespVO.setFormId(metaInfo.getFormId()); + modelRespVO.setFormCustomCreatePath(metaInfo.getFormCustomCreatePath()); + modelRespVO.setFormCustomViewPath(metaInfo.getFormCustomViewPath()); + modelRespVO.setIcon(metaInfo.getIcon()); + modelRespVO.setDescription(metaInfo.getDescription()); + } + if (form != null) { + modelRespVO.setFormId(form.getId()); + modelRespVO.setFormName(form.getName()); + } + // Category + if (category != null) { + modelRespVO.setCategoryName(category.getName()); + } + // ProcessDefinition + if (processDefinition != null) { + modelRespVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionInfoVO.class)); + modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ? + SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); + if (deployment != null) { + modelRespVO.getProcessDefinition().setDeploymentTime(DateUtils.of(deployment.getDeploymentTime())); + } + } + return modelRespVO; + } + + default void copyToCreateModel(Model model, BpmModelParam bean) { + model.setName(bean.getName()); + model.setKey(bean.getKey()); + model.setCategory(bean.getCategory()); +// model.setMetaInfo(buildMetaInfoStr(null, +// null, bean.getDescription(), +// null, null, null, null)); + //类型暂时写死为表单 todo... + model.setMetaInfo(buildMetaInfoStr(buildMetaInfo(model), + bean.getIcon(), bean.getDescription(), + BpmModelFormTypeEnum.NORMAL.getType(), bean.getFormId(), bean.getFormCustomCreatePath(), bean.getFormCustomViewPath())); + } + + default void copyToUpdateModel(Model model, BpmModelParam.BpmModelUpdateParam bean) { + model.setName(bean.getName()); + model.setCategory(bean.getCategory()); + model.setMetaInfo(buildMetaInfoStr(buildMetaInfo(model), + bean.getIcon(), bean.getDescription(), + bean.getFormType(), bean.getFormId(), bean.getFormCustomCreatePath(), bean.getFormCustomViewPath())); + } + + default String buildMetaInfoStr(BpmModelMetaInfoRespDTO metaInfo, + String icon, String description, + Integer formType, String formId, String formCustomCreatePath, String formCustomViewPath) { + if (metaInfo == null) { + metaInfo = new BpmModelMetaInfoRespDTO(); + } + // 只有非空,才进行设置,避免更新时的覆盖 + if (StrUtil.isNotEmpty(icon)) { + metaInfo.setIcon(icon); + } + if (StrUtil.isNotEmpty(description)) { + metaInfo.setDescription(description); + } + if (Objects.nonNull(formType)) { + metaInfo.setFormType(formType); + metaInfo.setFormId(formId); + metaInfo.setFormCustomCreatePath(formCustomCreatePath); + metaInfo.setFormCustomViewPath(formCustomViewPath); + } + return JsonUtils.toJsonString(metaInfo); + } + + default BpmModelMetaInfoRespDTO buildMetaInfo(Model model) { + return JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmProcessDefinitionConvert.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmProcessDefinitionConvert.java new file mode 100644 index 000000000..809989db0 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmProcessDefinitionConvert.java @@ -0,0 +1,100 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.map.MapUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.pojo.po.BpmCategory; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo; +import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.UserTask; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +/** + * Bpm 流程定义的 Convert + * + * @author yunlong.li + */ +@Mapper +public interface BpmProcessDefinitionConvert { + + BpmProcessDefinitionConvert INSTANCE = Mappers.getMapper(BpmProcessDefinitionConvert.class); + + default Page buildProcessDefinitionPage(Page page, + Map deploymentMap, + Map processDefinitionInfoMap, + Map formMap, + Map categoryMap) { + List list = buildProcessDefinitionList(page.getRecords(), deploymentMap, processDefinitionInfoMap, formMap, categoryMap); + Page voPage = new Page<>(); + voPage.setRecords(list); + voPage.setTotal(page.getTotal()); + voPage.setSize(page.getSize()); + voPage.setCurrent(page.getCurrent()); + return voPage; + } + + default List buildProcessDefinitionList(List list, + Map deploymentMap, + Map processDefinitionInfoMap, + Map formMap, + Map categoryMap) { + return CollectionUtils.convertList(list, definition -> { + Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class); + BpmProcessDefinitionInfo processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfo.class); + BpmForm form = null; + if (processDefinitionInfo != null) { + form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmForm.class); + } + BpmCategory category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategory.class); + return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null, null); + }); + } + + default BpmProcessDefinitionInfoVO buildProcessDefinition(ProcessDefinition definition, + Deployment deployment, + BpmProcessDefinitionInfo processDefinitionInfo, + BpmForm form, + BpmCategory category, + BpmnModel bpmnModel, + List startUserSelectUserTaskList) { + BpmProcessDefinitionInfoVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionInfoVO.class); + respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); + // Deployment + if (deployment != null) { + respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime())); + } + // BpmProcessDefinitionInfoDO + if (processDefinitionInfo != null) { + copyTo(processDefinitionInfo, respVO); + // Form + if (form != null) { + respVO.setFormName(form.getName()); + } + } + // Category + if (category != null) { + respVO.setCategoryName(category.getName()); + } + // BpmnModel + if (bpmnModel != null) { + respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel)); + respVO.setStartUserSelectTasks(BeanUtils.toBean(startUserSelectUserTaskList, BpmProcessDefinitionInfoVO.UserTask.class)); + } + return respVO; + } + + @Mapping(source = "from.id", target = "to.id", ignore = true) + void copyTo(BpmProcessDefinitionInfo from, @MappingTarget BpmProcessDefinitionInfoVO to); + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmnModelUtils.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmnModelUtils.java new file mode 100644 index 000000000..8be85c8de --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmnModelUtils.java @@ -0,0 +1,331 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import com.njcn.bpm.enums.BpmnModelConstants; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.*; +import org.flowable.common.engine.impl.util.io.BytesStreamSource; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 流程模型转操作工具类 + */ +public class BpmnModelUtils { + + public static Integer parseCandidateStrategy(FlowElement userTask) { + return NumberUtils.parseInt(userTask.getAttributeValue( + BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); + } + + public static String parseCandidateParam(FlowElement userTask) { + return userTask.getAttributeValue( + BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM); + } + + /** + * 根据节点,获取入口连线 + * + * @param source 起始节点 + * @return 入口连线列表 + */ + public static List getElementIncomingFlows(FlowElement source) { + if (source instanceof FlowNode) { + return ((FlowNode) source).getIncomingFlows(); + } + return new ArrayList<>(); + } + + /** + * 根据节点,获取出口连线 + * + * @param source 起始节点 + * @return 出口连线列表 + */ + public static List getElementOutgoingFlows(FlowElement source) { + if (source instanceof FlowNode) { + return ((FlowNode) source).getOutgoingFlows(); + } + return new ArrayList<>(); + } + + /** + * 获取流程元素信息 + * + * @param model bpmnModel 对象 + * @param flowElementId 元素 ID + * @return 元素信息 + */ + public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { + Process process = model.getMainProcess(); + return process.getFlowElement(flowElementId); + } + + /** + * 获得 BPMN 流程中,指定的元素们 + * + * @param model 模型 + * @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等 + * @return 元素们 + */ + public static List getBpmnModelElements(BpmnModel model, Class clazz) { + List result = new ArrayList<>(); + model.getProcesses().forEach(process -> { + process.getFlowElements().forEach(flowElement -> { + if (flowElement.getClass().isAssignableFrom(clazz)) { + result.add((T) flowElement); + } + }); + }); + return result; + } + + public static StartEvent getStartEvent(BpmnModel model) { + Process process = model.getMainProcess(); + // 从 initialFlowElement 找 + FlowElement startElement = process.getInitialFlowElement(); + if (startElement instanceof StartEvent) { + return (StartEvent) startElement; + } + // 从 flowElementList 找 + return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent); + } + + public static BpmnModel getBpmnModel(byte[] bpmnBytes) { + if (ArrayUtil.isEmpty(bpmnBytes)) { + return null; + } + BpmnXMLConverter converter = new BpmnXMLConverter(); + // 补充说明:由于在 Flowable 中自定义了属性,所以 validateSchema 传递 false + return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false); + } + + public static String getBpmnXml(BpmnModel model) { + if (model == null) { + return null; + } + BpmnXMLConverter converter = new BpmnXMLConverter(); + return new String(converter.convertToXML(model)); + } + + // ========== 遍历相关的方法 ========== + + /** + * 找到 source 节点之前的所有用户任务节点 + * + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 已找到的用户任务节点 + * @return 用户任务节点 数组 + */ + public static List getPreviousUserTaskList(FlowElement source, Set hasSequenceFlow, List userTaskList) { + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + if (sequenceFlows == null) { + return userTaskList; + } + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 类型为用户节点,则新增父级节点 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); + } + // 类型为子流程,则添加子流程开始节点出口处相连的节点 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + // 获取子流程用户任务节点 + List childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (CollUtil.isNotEmpty(childUserTaskList)) { + userTaskList.addAll(childUserTaskList); + } + } + // 继续迭代 + userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); + } + return userTaskList; + } + + /** + * 迭代获取子流程用户任务节点 + * + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return 用户任务节点 + */ + public static List findChildProcessUserTaskList(FlowElement source, Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (sequenceFlows == null) { + return userTaskList; + } + // 循环找到目标元素 + 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 childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (CollUtil.isNotEmpty(childUserTaskList)) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); + } + return userTaskList; + } + + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * + * @param source 起始节点 + * @param target 目标节点 + * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 + * @return 结果 + */ + public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set visitedElements) { + visitedElements = visitedElements == null ? new HashSet<>() : visitedElements; + // 不能是开始事件和子流程 + if (source instanceof StartEvent && isInEventSubprocess(source)) { + return false; + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + if (CollUtil.isEmpty(sequenceFlows)) { + return true; + } + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (visitedElements.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + visitedElements.add(sequenceFlow.getId()); + // 这条线路存在目标节点,这条线路完成,进入下个线路 + FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement(); + if (target.getId().equals(sourceFlowElement.getId())) { + continue; + } + // 如果目标节点为并行网关,则不继续 + if (sourceFlowElement instanceof ParallelGateway) { + return false; + } + // 否则就继续迭代 + if (!isSequentialReachable(sourceFlowElement, target, visitedElements)) { + return false; + } + } + return true; + } + + /** + * 判断当前节点是否属于不同的子流程 + * + * @param flowElement 被判断的节点 + * @return true 表示属于子流程 + */ + private static boolean isInEventSubprocess(FlowElement flowElement) { + FlowElementsContainer flowElementsContainer = flowElement.getParentContainer(); + while (flowElementsContainer != null) { + if (flowElementsContainer instanceof EventSubProcess) { + return true; + } + + if (flowElementsContainer instanceof FlowElement) { + flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer(); + } else { + flowElementsContainer = null; + } + } + return false; + } + + /** + * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 + * + * @param source 起始节点 + * @param runTaskKeyList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return 子级任务节点列表 + */ + public static List iteratorFindChildUserTasks(FlowElement source, List runTaskKeyList, + Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (sequenceFlows == null) { + return userTaskList; + } + // 循环找到目标元素 + 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 childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (CollUtil.isNotEmpty(childUserTaskList)) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + return userTaskList; + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/FlowableUtils.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/FlowableUtils.java new file mode 100644 index 000000000..3a4215234 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/FlowableUtils.java @@ -0,0 +1,178 @@ +package com.njcn.bpm.utils; + +import com.njcn.bpm.enums.BpmConstants; +import org.flowable.common.engine.api.delegate.Expression; +import org.flowable.common.engine.api.variable.VariableContainer; +import org.flowable.common.engine.impl.el.ExpressionManager; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.TaskInfo; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Flowable 相关的工具方法 + * + * @author 芋道源码 + */ +public class FlowableUtils { + + // ========== User 相关的工具方法 ========== + + public static void setAuthenticatedUserId(Long userId) { + Authentication.setAuthenticatedUserId(String.valueOf(userId)); + } + + public static void clearAuthenticatedUserId() { + Authentication.setAuthenticatedUserId(null); + } + + public static String getTenantId() { + return ProcessEngineConfiguration.NO_TENANT_ID; + } + + // ========== Execution 相关的工具方法 ========== + + /** + * 格式化多实例(并签、或签)的 collectionVariable 变量(多实例对应的多审批人列表) + * + * @param activityId 活动编号 + * @return collectionVariable 变量 + */ + public static String formatExecutionCollectionVariable(String activityId) { + return activityId + "_assignees"; + } + + /** + * 格式化多实例(并签、或签)的 collectionElementVariable 变量(当前实例对应的一个审批人) + * + * @param activityId 活动编号 + * @return collectionElementVariable 变量 + */ + public static String formatExecutionCollectionElementVariable(String activityId) { + return activityId + "_assignee"; + } + + // ========== ProcessInstance 相关的工具方法 ========== + + public static Integer getProcessInstanceStatus(ProcessInstance processInstance) { + return getProcessInstanceStatus(processInstance.getProcessVariables()); + } + + public static Integer getProcessInstanceStatus(HistoricProcessInstance processInstance) { + return getProcessInstanceStatus(processInstance.getProcessVariables()); + } + + /** + * 获得流程实例的状态 + * + * @param processVariables 流程实例的 variables + * @return 状态 + */ + private static Integer getProcessInstanceStatus(Map processVariables) { + return (Integer) processVariables.get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + } + + /** + * 获得流程实例的表单 + * + * @param processInstance 流程实例 + * @return 表单 + */ + public static Map getProcessInstanceFormVariable(HistoricProcessInstance processInstance) { + Map formVariables = new HashMap<>(processInstance.getProcessVariables()); + filterProcessInstanceFormVariable(formVariables); + return formVariables; + } + + /** + * 过滤流程实例的表单 + * + * 为什么要过滤?目前使用 processVariables 存储所有流程实例的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示 + * + * @param processVariables 流程实例的 variables + * @return 过滤后的表单 + */ + public static Map filterProcessInstanceFormVariable(Map processVariables) { + processVariables.remove(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + return processVariables; + } + + /** + * 获得流程实例的发起用户选择的审批人 Map + * + * @param processInstance 流程实例 + * @return 发起用户选择的审批人 Map + */ + @SuppressWarnings("unchecked") + public static Map> getStartUserSelectAssignees(ProcessInstance processInstance) { + return (Map>) processInstance.getProcessVariables().get( + BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); + } + + // ========== Task 相关的工具方法 ========== + + /** + * 获得任务的状态 + * + * @param task 任务 + * @return 状态 + */ + public static Integer getTaskStatus(TaskInfo task) { + return (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); + } + + /** + * 获得任务的审批原因 + * + * @param task 任务 + * @return 审批原因 + */ + public static String getTaskReason(TaskInfo task) { + return (String) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_REASON); + } + + /** + * 获得任务的表单 + * + * @param task 任务 + * @return 表单 + */ + public static Map getTaskFormVariable(TaskInfo task) { + Map formVariables = new HashMap<>(task.getTaskLocalVariables()); + filterTaskFormVariable(formVariables); + return formVariables; + } + + /** + * 过滤任务的表单 + * + * 为什么要过滤?目前使用 taskLocalVariables 存储所有任务的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示 + * + * @param taskLocalVariables 任务的 taskLocalVariables + * @return 过滤后的表单 + */ + public static Map filterTaskFormVariable(Map taskLocalVariables) { + taskLocalVariables.remove(BpmConstants.TASK_VARIABLE_STATUS); + taskLocalVariables.remove(BpmConstants.TASK_VARIABLE_REASON); + return taskLocalVariables; + } + + // ========== Expression 相关的工具方法 ========== + + public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) { + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); + assert processEngineConfiguration != null; + ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager(); + assert expressionManager != null; + Expression expression = expressionManager.createExpression(expressionString); + return expression.getValue(variableContainer); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/JsonUtils.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/JsonUtils.java new file mode 100644 index 000000000..a275bfe29 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/JsonUtils.java @@ -0,0 +1,202 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +/** + * JSON 工具类 + * + * @author 芋道源码 + */ +@Slf4j +public class JsonUtils { + + private static ObjectMapper objectMapper = new ObjectMapper(); + + static { + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值 + objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化 + } + + /** + * 初始化 objectMapper 属性 + *

+ * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean + * + * @param objectMapper ObjectMapper 对象 + */ + public static void init(ObjectMapper objectMapper) { + JsonUtils.objectMapper = objectMapper; + } + + @SneakyThrows + public static String toJsonString(Object object) { + return objectMapper.writeValueAsString(object); + } + + @SneakyThrows + public static byte[] toJsonByte(Object object) { + return objectMapper.writeValueAsBytes(object); + } + + @SneakyThrows + public static String toJsonPrettyString(Object object) { + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object); + } + + public static T parseObject(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return null; + } + try { + return objectMapper.readValue(text, clazz); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, String path, Class clazz) { + if (StrUtil.isEmpty(text)) { + return null; + } + try { + JsonNode treeNode = objectMapper.readTree(text); + JsonNode pathNode = treeNode.path(path); + return objectMapper.readValue(pathNode.toString(), clazz); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, Type type) { + if (StrUtil.isEmpty(text)) { + return null; + } + try { + return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type)); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + /** + * 将字符串解析成指定类型的对象 + * 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下, + * 如果 text 没有 class 属性,则会报错。此时,使用这个方法,可以解决。 + * + * @param text 字符串 + * @param clazz 类型 + * @return 对象 + */ + public static T parseObject2(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return null; + } + return JSONUtil.toBean(text, clazz); + } + + public static T parseObject(byte[] bytes, Class clazz) { + if (ArrayUtil.isEmpty(bytes)) { + return null; + } + try { + return objectMapper.readValue(bytes, clazz); + } catch (IOException e) { + log.error("json parse err,json:{}", bytes, e); + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, TypeReference typeReference) { + try { + return objectMapper.readValue(text, typeReference); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + /** + * 解析 JSON 字符串成指定类型的对象,如果解析失败,则返回 null + * + * @param text 字符串 + * @param typeReference 类型引用 + * @return 指定类型的对象 + */ + public static T parseObjectQuietly(String text, TypeReference typeReference) { + try { + return objectMapper.readValue(text, typeReference); + } catch (IOException e) { + return null; + } + } + + public static List parseArray(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return new ArrayList<>(); + } + try { + return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz)); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + public static List parseArray(String text, String path, Class clazz) { + if (StrUtil.isEmpty(text)) { + return null; + } + try { + JsonNode treeNode = objectMapper.readTree(text); + JsonNode pathNode = treeNode.path(path); + return objectMapper.readValue(pathNode.toString(), objectMapper.getTypeFactory().constructCollectionType(List.class, clazz)); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + public static JsonNode parseTree(String text) { + try { + return objectMapper.readTree(text); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + public static JsonNode parseTree(byte[] text) { + try { + return objectMapper.readTree(text); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + public static boolean isJson(String text) { + return JSONUtil.isTypeJSON(text); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/StrUtils.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/StrUtils.java new file mode 100644 index 000000000..b4d8ee4e9 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/StrUtils.java @@ -0,0 +1,83 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.text.StrPool; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 字符串工具类 + * + * @author 芋道源码 + */ +public class StrUtils { + + public static String maxLength(CharSequence str, int maxLength) { + return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好 + } + + /** + * 给定字符串是否以任何一个字符串开始 + * 给定字符串和数组为空都返回 false + * + * @param str 给定字符串 + * @param prefixes 需要检测的开始字符串 + * @since 3.0.6 + */ + public static boolean startWithAny(String str, Collection prefixes) { + if (StrUtil.isEmpty(str) || ArrayUtil.isEmpty(prefixes)) { + return false; + } + + for (CharSequence suffix : prefixes) { + if (StrUtil.startWith(str, suffix, false)) { + return true; + } + } + return false; + } + + public static List splitToLong(String value, CharSequence separator) { + long[] longs = StrUtil.splitToLong(value, separator); + return Arrays.stream(longs).boxed().collect(Collectors.toList()); + } + + public static Set splitToLongSet(String value) { + return splitToLongSet(value, StrPool.COMMA); + } + + public static Set splitToLongSet(String value, CharSequence separator) { + long[] longs = StrUtil.splitToLong(value, separator); + return Arrays.stream(longs).boxed().collect(Collectors.toSet()); + } + + public static List splitToInteger(String value, CharSequence separator) { + int[] integers = StrUtil.splitToInt(value, separator); + return Arrays.stream(integers).boxed().collect(Collectors.toList()); + } + + /** + * 移除字符串中,包含指定字符串的行 + * + * @param content 字符串 + * @param sequence 包含的字符串 + * @return 移除后的字符串 + */ + public static String removeLineContains(String content, String sequence) { + if (StrUtil.isEmpty(content) || StrUtil.isEmpty(sequence)) { + return content; + } + return Arrays.stream(content.split("\n")) + .filter(line -> !line.contains(sequence)) + .collect(Collectors.joining("\n")); + } + + public static List splitToStringList(String value) { + return StrUtil.split(value, StrPool.COMMA); + } +} diff --git a/pqs-bpm/bpm-boot/src/main/resources/bootstrap.yml b/pqs-bpm/bpm-boot/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..7ea185876 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/resources/bootstrap.yml @@ -0,0 +1,69 @@ +#当前服务的基本信息 +microservice: + ename: @artifactId@ + name: '@name@' + version: @version@ + sentinel: + url: @sentinel.url@ + gateway: + url: @gateway.url@ +server: + port: 10321 +#feign接口开启服务熔断降级处理 +feign: + sentinel: + enabled: true +spring: + application: + name: @artifactId@ + #nacos注册中心以及配置中心的指定 + cloud: + nacos: + discovery: + ip: @service.server.url@ + server-addr: @nacos.url@ + namespace: @nacos.namespace@ + config: + server-addr: @nacos.url@ + namespace: @nacos.namespace@ + file-extension: yaml + shared-configs: + - data-id: share-config.yaml + refresh: true + - data-Id: bpm-config.yaml + refresh: true + main: + allow-bean-definition-overriding: true + servlet: + multipart: + max-file-size: 100MB + max-request-size: 100MB + +flowable: + database-schema-update: false + db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置 + check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程 + history-level: full # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数 + async-executor-activate: false + +#项目日志的配置 +logging: + config: http://@nacos.url@/nacos/v1/cs/configs?tenant=@nacos.namespace@&group=DEFAULT_GROUP&dataId=logback.xml + level: + root: info + + +#mybatis配置信息 +mybatis-plus: + type-aliases-package: com.njcn.bpm.pojo + +gw: + url: dwzyywzt-pms3-proxy.com + code: 13B9B47F1E483324E05338297A0A0595 + +mqtt: + client-id: @artifactId@${random.value} + + + + diff --git a/pqs-bpm/pom.xml b/pqs-bpm/pom.xml new file mode 100644 index 000000000..327138036 --- /dev/null +++ b/pqs-bpm/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + com.njcn + pqs + 1.0.0 + + + + pqs-bpm + pom + + + bpm-api + bpm-boot + + + + + 8 + 8 + UTF-8 + + + \ No newline at end of file diff --git a/pqs-common/common-autocode/src/main/java/com/njcn/autocode/utils/GenerateCode.java b/pqs-common/common-autocode/src/main/java/com/njcn/autocode/utils/GenerateCode.java index 65cb93d26..7708c47f5 100644 --- a/pqs-common/common-autocode/src/main/java/com/njcn/autocode/utils/GenerateCode.java +++ b/pqs-common/common-autocode/src/main/java/com/njcn/autocode/utils/GenerateCode.java @@ -21,7 +21,7 @@ public class GenerateCode { private static final String TARGET_DIR = "D://code"; - private static final String DB_URL = "jdbc:mysql://192.168.1.24:13306/pqsinfo_hn"; + private static final String DB_URL = "jdbc:mysql://192.168.1.24:13306/pqsinfo_hn_process"; // private static final String DB_URL = "jdbc:oracle:thin:@192.168.1.170:1521:pqsbase"; private static final String USERNAME = "root"; @@ -30,9 +30,9 @@ public class GenerateCode { public static void main(String[] args) { List modules = Stream.of( - new Module("hongawen", "com.njcn.advance.govern", "voltage", Stream.of( - "sg_harmonic_file" - ).collect(Collectors.toList()), "") + new Module("hongawen", "com.njcn.process", "workflow", Stream.of( + "wf_deploy_form" + ).collect(Collectors.toList()), "wf_") ).collect(Collectors.toList()); generateJavaFile(modules); } diff --git a/pqs-common/common-web/src/main/java/com/njcn/web/filter/XssRequestWrapper.java b/pqs-common/common-web/src/main/java/com/njcn/web/filter/XssRequestWrapper.java index ca92150f5..7b42387b2 100644 --- a/pqs-common/common-web/src/main/java/com/njcn/web/filter/XssRequestWrapper.java +++ b/pqs-common/common-web/src/main/java/com/njcn/web/filter/XssRequestWrapper.java @@ -34,7 +34,7 @@ import java.util.stream.Stream; @Slf4j public class XssRequestWrapper extends HttpServletRequestWrapper { - private final static String[] WHITE_PARAMETER_NAME = {"password", "mxContent", "docContent", "bgImage","fileContent","flowableXml"}; + private final static String[] WHITE_PARAMETER_NAME = {"password", "mxContent", "docContent", "bgImage","fileContent","flowableXml","bpmnXml"}; public XssRequestWrapper(HttpServletRequest request) { diff --git a/pqs-device/device-boot/pom.xml b/pqs-device/device-boot/pom.xml index bed3679ea..afe8857d4 100644 --- a/pqs-device/device-boot/pom.xml +++ b/pqs-device/device-boot/pom.xml @@ -21,16 +21,16 @@ - - - com.njcn - pms-device-boot - ${project.version} + + + + + com.njcn diff --git a/pqs-device/pq-device/pq-device-api/src/main/java/com/njcn/device/pq/pojo/bo/excel/OracleTerminalExcel.java b/pqs-device/pq-device/pq-device-api/src/main/java/com/njcn/device/pq/pojo/bo/excel/OracleTerminalExcel.java index 1bc9cdc6f..a5f04d406 100644 --- a/pqs-device/pq-device/pq-device-api/src/main/java/com/njcn/device/pq/pojo/bo/excel/OracleTerminalExcel.java +++ b/pqs-device/pq-device/pq-device-api/src/main/java/com/njcn/device/pq/pojo/bo/excel/OracleTerminalExcel.java @@ -134,7 +134,7 @@ public class OracleTerminalExcel implements Serializable { @Excel(name = "合同号", width = 15) private String contract; - @Excel(name = "电度功能", replace = {"关闭_0", "开启_1"}, width = 15) + @Excel(name = "电度功能", replace = {"关闭_0", "开启_1","null_0"}, width = 15) private Integer electroplate; @Excel(name = "母线", width = 15) diff --git a/pqs-gateway/src/main/resources/bootstrap.yml b/pqs-gateway/src/main/resources/bootstrap.yml index 2800aceb1..985a2e1a3 100644 --- a/pqs-gateway/src/main/resources/bootstrap.yml +++ b/pqs-gateway/src/main/resources/bootstrap.yml @@ -172,6 +172,13 @@ spring: filters: - SwaggerHeaderFilter - StripPrefix=1 + - id: bpm-boot + uri: lb://bpm-boot + predicates: + - Path=/bpm-boot/** + filters: + - SwaggerHeaderFilter + - StripPrefix=1 #项目日志的配置 logging: config: http://@nacos.url@/nacos/v1/cs/configs?tenant=@nacos.namespace@&group=DEFAULT_GROUP&dataId=logback.xml @@ -211,7 +218,11 @@ whitelist: #- /event-boot/** #- /quality-boot/** #- /harmonic-prepare/** - #- /process-boot/** + - /process-boot/** + - /bpm-boot/** + - /system-boot/** + - /user-boot/** + - /user-boot/user/listAllUserByDeptId mqtt: diff --git a/pqs-harmonic/harmonic-boot/src/main/resources/bootstrap.yml b/pqs-harmonic/harmonic-boot/src/main/resources/bootstrap.yml index 4ef726b36..5f3b76d17 100644 --- a/pqs-harmonic/harmonic-boot/src/main/resources/bootstrap.yml +++ b/pqs-harmonic/harmonic-boot/src/main/resources/bootstrap.yml @@ -31,7 +31,8 @@ spring: - data-id: share-config.yaml refresh: true #数据中心使用 - # - data-Id: share-config-datasource-db.yaml +# - data-Id: share-config-datasource-db.yaml +# refresh: true #PMS使用 - data-Id: share-config-harmonic-db.yaml refresh: true diff --git a/pqs-process/process-api/src/main/java/com/njcn/process/enums/ProcessResponseEnum.java b/pqs-process/process-api/src/main/java/com/njcn/process/enums/ProcessResponseEnum.java index 5ce24073d..89954976b 100644 --- a/pqs-process/process-api/src/main/java/com/njcn/process/enums/ProcessResponseEnum.java +++ b/pqs-process/process-api/src/main/java/com/njcn/process/enums/ProcessResponseEnum.java @@ -26,8 +26,25 @@ public enum ProcessResponseEnum { SUPV_PLAN_REPEAT("A00568","监督计划名称已存在"), NO_UPDATE("A00568","该申请单非新建,驳回状态不准许更新"), + REPEAT_NAME_CATEGORY("A00568","流程表单名称重复"), + REPEAT_CODE_CATEGORY("A00568","流程表单编号重复"), - ; + FORM_NOT_EXIST("A00568","流程表单不存在"), + + MODEL_NO_EXIST("A00568","流程模型不存在"), + + NO_MODEL_XML("A00568","未获取到流程模型设计"), + + NO_XML("A00568","请先设计流程图"), + + NO_STARTER_MODEL_XML("A00568","开始节点不存在,请检查流程设计是否有误"), + + MODEL_HAS_NO_FORM("A00568","请配置流程表单"), + + LATEST_VERSION("A00568","当前版本已是最新版!"), + + PROCESS_DATASOURCE_MISS("A00568","数据源没找到!"), + ; private final String code; diff --git a/pqs-process/process-api/src/main/java/com/njcn/process/pojo/dto/flowable/FlowProcDefDto.java b/pqs-process/process-api/src/main/java/com/njcn/process/pojo/dto/flowable/FlowProcDefDto.java index 7fb06ba8b..8b1153b06 100644 --- a/pqs-process/process-api/src/main/java/com/njcn/process/pojo/dto/flowable/FlowProcDefDto.java +++ b/pqs-process/process-api/src/main/java/com/njcn/process/pojo/dto/flowable/FlowProcDefDto.java @@ -30,7 +30,7 @@ public class FlowProcDefDto implements Serializable { @ApiModelProperty("流程key") private String flowKey; - @ApiModelProperty("流程分类") + @ApiModelProperty("流程表单") private String category; @ApiModelProperty("配置表单名称") diff --git a/pqs-process/process-api/src/main/java/com/njcn/process/pojo/dto/flowable/FlowSaveXmlVo.java b/pqs-process/process-api/src/main/java/com/njcn/process/pojo/dto/flowable/FlowSaveXmlVo.java index 58f13839a..8f3e7dc7b 100644 --- a/pqs-process/process-api/src/main/java/com/njcn/process/pojo/dto/flowable/FlowSaveXmlVo.java +++ b/pqs-process/process-api/src/main/java/com/njcn/process/pojo/dto/flowable/FlowSaveXmlVo.java @@ -17,7 +17,7 @@ public class FlowSaveXmlVo implements Serializable { private String name; /** - * 流程分类 + * 流程表单 */ private String category; diff --git a/pqs-process/process-boot/src/main/java/com/njcn/process/factory/FlowServiceFactory.java b/pqs-process/process-boot/src/main/java/com/njcn/process/factory/FlowServiceFactory.java index 9e0ac9435..fb8e40b52 100644 --- a/pqs-process/process-boot/src/main/java/com/njcn/process/factory/FlowServiceFactory.java +++ b/pqs-process/process-boot/src/main/java/com/njcn/process/factory/FlowServiceFactory.java @@ -38,4 +38,8 @@ public class FlowServiceFactory { @Resource protected ProcessEngine processEngine; +// @Resource +// protected FormService formService; + + } diff --git a/pqs-system/system-boot/src/main/java/com/njcn/system/controller/DictTypeController.java b/pqs-system/system-boot/src/main/java/com/njcn/system/controller/DictTypeController.java index 2e6f8fffb..223f9dfd5 100644 --- a/pqs-system/system-boot/src/main/java/com/njcn/system/controller/DictTypeController.java +++ b/pqs-system/system-boot/src/main/java/com/njcn/system/controller/DictTypeController.java @@ -3,11 +3,13 @@ package com.njcn.system.controller; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.njcn.common.pojo.annotation.OperateInfo; import com.njcn.common.pojo.constant.OperateType; import com.njcn.common.pojo.dto.SimpleDTO; import com.njcn.common.pojo.dto.SimpleTreeDTO; +import com.njcn.common.pojo.enums.common.DataStateEnum; import com.njcn.common.pojo.enums.common.LogEnum; import com.njcn.common.pojo.enums.response.CommonResponseEnum; import com.njcn.common.pojo.response.HttpResult; @@ -60,6 +62,19 @@ public class DictTypeController extends BaseController { return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); } + /** + * 查询所有字典类型数据 + */ + @OperateInfo(info = LogEnum.SYSTEM_COMMON) + @GetMapping("/listAll") + @ApiOperation("查询所有字典类型数据") + public HttpResult> listAll() { + String methodDescribe = getMethodDescribe("listAll"); + LogUtil.njcnDebug(log, "{}", methodDescribe); + List dictTypeList = dictTypeService.list(new LambdaQueryWrapper().eq(DictType::getState, DataStateEnum.ENABLE.getCode())); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, dictTypeList, methodDescribe); + } + /** * 新增字典类型 diff --git a/pqs-user/user-api/src/main/java/com/njcn/user/api/DeptFeignClient.java b/pqs-user/user-api/src/main/java/com/njcn/user/api/DeptFeignClient.java index 0f112978d..8f6f86aca 100644 --- a/pqs-user/user-api/src/main/java/com/njcn/user/api/DeptFeignClient.java +++ b/pqs-user/user-api/src/main/java/com/njcn/user/api/DeptFeignClient.java @@ -1,5 +1,6 @@ package com.njcn.user.api; +import cn.hutool.core.lang.tree.Tree; import com.njcn.common.pojo.annotation.OperateInfo; import com.njcn.common.pojo.constant.ServerInfo; import com.njcn.common.pojo.enums.common.LogEnum; @@ -160,4 +161,7 @@ public interface DeptFeignClient { */ @GetMapping("/getAllDept") HttpResult> getAllDept(); + + @GetMapping("/orgTreeSelector") + HttpResult>> orgTreeSelector(); } diff --git a/pqs-user/user-api/src/main/java/com/njcn/user/api/UserFeignClient.java b/pqs-user/user-api/src/main/java/com/njcn/user/api/UserFeignClient.java index 8d45dd283..4dd313a6b 100644 --- a/pqs-user/user-api/src/main/java/com/njcn/user/api/UserFeignClient.java +++ b/pqs-user/user-api/src/main/java/com/njcn/user/api/UserFeignClient.java @@ -1,16 +1,26 @@ package com.njcn.user.api; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.common.pojo.annotation.OperateInfo; import com.njcn.common.pojo.constant.ServerInfo; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.HttpResultUtil; import com.njcn.user.api.fallback.UserFeignClientFallbackFactory; import com.njcn.user.pojo.dto.UserDTO; import com.njcn.user.pojo.po.User; import com.njcn.user.pojo.vo.UserVO; import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.Collection; import java.util.List; /** @@ -18,7 +28,7 @@ import java.util.List; * @version 1.0.0 * @date 2021年05月08日 15:11 */ -@FeignClient(value = ServerInfo.USER,path = "/user",fallbackFactory = UserFeignClientFallbackFactory.class,contextId = "user") +@FeignClient(value = ServerInfo.USER, path = "/user", fallbackFactory = UserFeignClientFallbackFactory.class, contextId = "user") public interface UserFeignClient { /** @@ -37,10 +47,11 @@ public interface UserFeignClient { * @return 用户基本信息 */ @GetMapping("/getUserByPhone/{phone}") - HttpResult getUserByPhone(@PathVariable("phone")String phone); + HttpResult getUserByPhone(@PathVariable("phone") String phone); /** * 认证后根据用户名判断用户状态 + * * @param loginName 登录名 * @return 校验结果 */ @@ -58,6 +69,7 @@ public interface UserFeignClient { /** * 根据用户id集合查询用户信息 + * * @param ids * @return */ @@ -66,10 +78,11 @@ public interface UserFeignClient { @PostMapping("/appuserByIdList") @ApiImplicitParam(name = "ids", value = "用户id集合", required = true) - HttpResult> appuserByIdList(@RequestBody List ids); + HttpResult> appuserByIdList(@RequestBody List ids); /** * 根据部门ids查询接收短信通知的用户信息 + * * @param deptId * @return */ @@ -78,14 +91,24 @@ public interface UserFeignClient { /** * 根据部门ids查询用户信息 + * * @param deptId * @return */ @PostMapping("/getUserInfoByDeptIds") HttpResult> getUserInfoByDeptIds(@RequestBody List deptId); + /** + * 根据角色id查询用户id + * + * @param roleId 角色id集合 + */ + @PostMapping("/getUserIdByRoleId") + HttpResult> getUserIdByRoleId(@RequestBody List roleId); + /** * 根据角色Code集合查询用户信息 + * * @param roleCode * @return */ @@ -95,4 +118,7 @@ public interface UserFeignClient { @GetMapping("/getUserById") HttpResult getUserById(@RequestParam("id") String id); + @PostMapping("/getUserListByIds") + HttpResult> getUserListByIds(@RequestBody List ids); + } diff --git a/pqs-user/user-api/src/main/java/com/njcn/user/api/fallback/DeptFeignClientFallbackFactory.java b/pqs-user/user-api/src/main/java/com/njcn/user/api/fallback/DeptFeignClientFallbackFactory.java index 84444c104..842256e8e 100644 --- a/pqs-user/user-api/src/main/java/com/njcn/user/api/fallback/DeptFeignClientFallbackFactory.java +++ b/pqs-user/user-api/src/main/java/com/njcn/user/api/fallback/DeptFeignClientFallbackFactory.java @@ -1,5 +1,6 @@ package com.njcn.user.api.fallback; +import cn.hutool.core.lang.tree.Tree; import com.njcn.common.pojo.enums.response.CommonResponseEnum; import com.njcn.common.pojo.exception.BusinessException; import com.njcn.common.pojo.response.HttpResult; @@ -146,6 +147,12 @@ public class DeptFeignClientFallbackFactory implements FallbackFactory>> orgTreeSelector() { + log.error("{}异常,降级处理,异常为:{}","获取所有单位树:",cause.toString()); + throw new BusinessException(finalExceptionEnum); + } }; } } diff --git a/pqs-user/user-api/src/main/java/com/njcn/user/api/fallback/UserFeignClientFallbackFactory.java b/pqs-user/user-api/src/main/java/com/njcn/user/api/fallback/UserFeignClientFallbackFactory.java index 0373dcd42..f2352f880 100644 --- a/pqs-user/user-api/src/main/java/com/njcn/user/api/fallback/UserFeignClientFallbackFactory.java +++ b/pqs-user/user-api/src/main/java/com/njcn/user/api/fallback/UserFeignClientFallbackFactory.java @@ -1,5 +1,6 @@ package com.njcn.user.api.fallback; +import cn.hutool.json.JSONObject; import com.njcn.common.pojo.enums.response.CommonResponseEnum; import com.njcn.common.pojo.exception.BusinessException; import com.njcn.common.pojo.response.HttpResult; @@ -86,6 +87,12 @@ public class UserFeignClientFallbackFactory implements FallbackFactory> getUserIdByRoleId(List roleId) { + log.error("{}异常,降级处理,异常为:{}","根据角色ids查询用户id",cause.toString()); + throw new BusinessException(finalExceptionEnum); + } + @Override public HttpResult> getUserListByRoleCode(String roleCode) { log.error("{}异常,降级处理,异常为:{}","根据角色Code集合查询用户信息",cause.toString()); @@ -98,6 +105,11 @@ public class UserFeignClientFallbackFactory implements FallbackFactory> getUserListByIds(List ids) { + log.error("{}异常,降级处理,异常为:{}","根据用户id集合获取用户集合",cause.toString()); + throw new BusinessException(finalExceptionEnum); + } }; } diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/controller/DeptController.java b/pqs-user/user-boot/src/main/java/com/njcn/user/controller/DeptController.java index a7dd14ac1..0a29d0d52 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/controller/DeptController.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/controller/DeptController.java @@ -1,7 +1,12 @@ package com.njcn.user.controller; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.lang.tree.TreeNode; +import cn.hutool.core.lang.tree.TreeUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.njcn.common.pojo.annotation.OperateInfo; import com.njcn.common.pojo.constant.OperateType; @@ -14,6 +19,7 @@ import com.njcn.system.pojo.dto.AreaTreeDTO; import com.njcn.user.pojo.dto.DeptDTO; import com.njcn.user.pojo.param.DeptParam; import com.njcn.user.pojo.po.Dept; +import com.njcn.user.pojo.po.Role; import com.njcn.user.pojo.vo.DeptAllTreeVO; import com.njcn.user.pojo.vo.DeptTreeVO; import com.njcn.user.pojo.vo.DeptVO; @@ -29,6 +35,7 @@ import springfox.documentation.annotations.ApiIgnore; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; /** *

@@ -155,6 +162,17 @@ public class DeptController extends BaseController { } } + @OperateInfo(info = LogEnum.SYSTEM_COMMON) + @PostMapping("/getDeptListByIds") + @ApiOperation("根据部门id集合查询部门信息") + @ApiImplicitParam(name = "ids", value = "部门id集合", required = true) + public HttpResult> getDeptListByIds(@RequestBody List ids) { + String methodDescribe = getMethodDescribe("getDeptListByIds"); + List users = deptService.list((new LambdaQueryWrapper().in(CollUtil.isNotEmpty(ids), Dept::getId, ids))); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe); + } + + /** * 判断是否有关联信息 */ @@ -187,6 +205,19 @@ public class DeptController extends BaseController { } + /** + * 获取部门树 + */ + @OperateInfo(info = LogEnum.SYSTEM_COMMON) + @GetMapping("/deptTreeSelector") + @ApiOperation("部门信息树") + public HttpResult>> deptTreeSelector() { + String methodDescribe = getMethodDescribe("deptTreeSelector"); + List> result = deptService.orgTreeSelector(); + //删除返回失败,查不到数据返回空数组,兼容治理项目没有部门直接报错的bug + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + + } @OperateInfo(info = LogEnum.SYSTEM_COMMON) @PostMapping("/existMonitorDeptTree") @@ -511,6 +542,19 @@ public class DeptController extends BaseController { return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); } + /** + * 获取所有的部门集合 + * @author xy + * @date 2023/12/11 + */ + @OperateInfo(info = LogEnum.SYSTEM_COMMON) + @GetMapping("/orgTreeSelector") + @ApiOperation("获取所有单位树") + public HttpResult>> orgTreeSelector() { + String methodDescribe = getMethodDescribe("orgTreeSelector"); + List> result = deptService.orgTreeSelector(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } /*++++++++++++++++++++++++++++++++pms专用+++++++++++++++++++++++++++++++end*/ } diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/controller/RoleController.java b/pqs-user/user-boot/src/main/java/com/njcn/user/controller/RoleController.java index 5677f284d..4ef3f4a2b 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/controller/RoleController.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/controller/RoleController.java @@ -1,6 +1,8 @@ package com.njcn.user.controller; +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.njcn.common.pojo.annotation.OperateInfo; import com.njcn.common.pojo.constant.OperateType; @@ -12,6 +14,7 @@ import com.njcn.common.utils.LogUtil; import com.njcn.user.pojo.param.DeptParam; import com.njcn.user.pojo.param.RoleParam; import com.njcn.user.pojo.po.Role; +import com.njcn.user.pojo.po.User; import com.njcn.user.pojo.vo.RoleVO; import com.njcn.user.service.IRoleService; import io.swagger.annotations.Api; @@ -114,6 +117,16 @@ public class RoleController extends BaseController { } } + @OperateInfo(info = LogEnum.SYSTEM_COMMON) + @PostMapping("/getRoleListByIds") + @ApiOperation("根据角色id集合查询角色信息") + @ApiImplicitParam(name = "ids", value = "角色id集合", required = true) + public HttpResult> getRoleListByIds(@RequestBody List ids) { + String methodDescribe = getMethodDescribe("getRoleListByIds"); + List users = roleService.list((new LambdaQueryWrapper().in(CollUtil.isNotEmpty(ids), Role::getId, ids))); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe); + } + /** * 根据角色id查询相关联的资源和组件 */ @@ -145,5 +158,29 @@ public class RoleController extends BaseController { List result = roleService.selectRoleDetail(id); return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); } + + /** + * 查询所有角色 + */ + @OperateInfo(info = LogEnum.SYSTEM_COMMON) + @GetMapping("/allRoleList") + @ApiOperation("查询所有角色") + public HttpResult allRoleList() { + String methodDescribe = getMethodDescribe("allRoleList"); + List result = roleService.allRoleList(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + + /** + * 查询所有角色 + */ + @OperateInfo(info = LogEnum.SYSTEM_COMMON) + @GetMapping("/simpleList") + @ApiOperation("查询所有角色作为下拉框") + public HttpResult simpleList() { + String methodDescribe = getMethodDescribe("simpleList"); + List result = roleService.simpleList(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } } diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/controller/UserController.java b/pqs-user/user-boot/src/main/java/com/njcn/user/controller/UserController.java index 165435c33..0c7a9023c 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/controller/UserController.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/controller/UserController.java @@ -3,6 +3,8 @@ package com.njcn.user.controller; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.njcn.common.pojo.annotation.OperateInfo; @@ -26,6 +28,7 @@ import com.njcn.user.pojo.dto.UserDTO; import com.njcn.user.pojo.param.UserInfoParm; import com.njcn.user.pojo.param.UserParam; import com.njcn.user.pojo.param.UserPasswordParam; +import com.njcn.user.pojo.po.Role; import com.njcn.user.pojo.po.User; import com.njcn.user.pojo.vo.UserVO; import com.njcn.user.service.IUserService; @@ -48,6 +51,7 @@ import java.security.NoSuchAlgorithmException; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; /** *

@@ -194,18 +198,19 @@ public class UserController extends BaseController { @OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPLOAD) @PostMapping("/uploadImage") @ApiOperation("上传头像") - public HttpResult uploadImage( MultipartFile issuesFile){ + public HttpResult uploadImage(MultipartFile issuesFile) { String methodDescribe = getMethodDescribe("uploadImage"); String filePath = userService.uploadImage(issuesFile); - return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,new MinIoUploadResDTO(issuesFile.getOriginalFilename(),filePath), methodDescribe); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, new MinIoUploadResDTO(issuesFile.getOriginalFilename(), filePath), methodDescribe); } + @OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPLOAD) @PostMapping("/getUrl") @ApiOperation("获取头像url") - public HttpResult getUrl(@RequestParam("headSculpture") String headSculpture){ + public HttpResult getUrl(@RequestParam("headSculpture") String headSculpture) { String methodDescribe = getMethodDescribe("getUrl"); String url = userService.getUrl(headSculpture); - return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,url, methodDescribe); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, url, methodDescribe); } @@ -221,6 +226,16 @@ public class UserController extends BaseController { } + @OperateInfo(info = LogEnum.SYSTEM_COMMON) + @GetMapping("/listAllUserByDeptId") + @ApiOperation("查询部门下所有状态正常的用户") + public HttpResult> listAllUserByDeptId(String deptId) { + String methodDescribe = getMethodDescribe("listAllUserByDeptId"); + List list = userService.listAllUserByDeptId(deptId); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe); + } + + @OperateInfo(info = LogEnum.SYSTEM_COMMON) @PostMapping("/exportUser") @ApiOperation("用户数据") @@ -228,19 +243,18 @@ public class UserController extends BaseController { public HttpResult exportUser(@RequestBody @Validated UserParam.UserQueryParam queryParam) { String methodDescribe = getMethodDescribe("exportUser"); LogUtil.njcnDebug(log, "{},查询数据为:{}", methodDescribe, queryParam); - return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, userService.exportUser(queryParam,methodDescribe), methodDescribe); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, userService.exportUser(queryParam, methodDescribe), methodDescribe); } - @OperateInfo(info = LogEnum.SYSTEM_COMMON,operateType = OperateType.DOWNLOAD) + @OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.DOWNLOAD) @GetMapping("/exportUserExcel") @ApiOperation("导出用户数据") @ApiImplicitParam(name = "filePath", value = "报表路径", required = true) public void exportUserExcel(String filePath, HttpServletResponse response) { - PoiUtil.exportFileByAbsolutePath(filePath,response); + PoiUtil.exportFileByAbsolutePath(filePath, response); } - @OperateInfo(info = LogEnum.SYSTEM_COMMON) @PostMapping("/checkUserList") @ApiOperation("审核用户列表") @@ -337,7 +351,7 @@ public class UserController extends BaseController { //秘钥先删除再添加 redisUtil.delete(loginName + ip); // 保存私钥到 redis - redisUtil.saveByKeyWithExpire(loginName + ip, privateKey, 5*60L); + redisUtil.saveByKeyWithExpire(loginName + ip, privateKey, 5 * 60L); return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, publicKey, methodDescribe); } @@ -356,6 +370,7 @@ public class UserController extends BaseController { /** * 密码二次确认 + * * @param password 确认密码 */ @OperateInfo @@ -366,7 +381,7 @@ public class UserController extends BaseController { String methodDescribe = getMethodDescribe("passwordConfirm"); LogUtil.njcnDebug(log, "{},用户输入的密码:{}", methodDescribe, password); boolean result = userService.passwordConfirm(password); - if (result){ + if (result) { return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); } else { return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe); @@ -376,7 +391,7 @@ public class UserController extends BaseController { /** * 首次登录修改密码 * - * @param userPasswordParam 用户信息 + * @param userPasswordParam 用户信息 */ @OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.UPDATE) @PutMapping("/updateFirstPassword") @@ -393,6 +408,7 @@ public class UserController extends BaseController { /** * 用于激活处于休眠、锁定、密码过期的用户 + * * @param id 用户ID * @return */ @@ -404,7 +420,7 @@ public class UserController extends BaseController { String methodDescribe = getMethodDescribe("activateUser"); LogUtil.njcnDebug(log, "{},用户输入的id:{}", methodDescribe, id); boolean result = userService.activateUser(id); - if (result){ + if (result) { return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); } else { return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe); @@ -415,13 +431,13 @@ public class UserController extends BaseController { @PostMapping("/userByIdList") @ApiOperation("根据用户id集合查询用户信息") @ApiImplicitParam(name = "ids", value = "用户id集合", required = true) - public HttpResult> getUserByIdList(@RequestBody List ids) { + public HttpResult> getUserByIdList(@RequestBody List ids) { String methodDescribe = getMethodDescribe("getUserByIdList"); List users = userService.list((new LambdaQueryWrapper() - .in(CollUtil.isNotEmpty(ids),User::getId,ids) + .in(CollUtil.isNotEmpty(ids), User::getId, ids) .isNotNull(User::getDeptId) - .ne(User::getDeptId,"") - .ne(User::getState,"0") + .ne(User::getDeptId, "") + .ne(User::getState, "0") )); return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe); } @@ -430,15 +446,24 @@ public class UserController extends BaseController { @PostMapping("/appuserByIdList") @ApiOperation("根据用户id集合查询用户信息") @ApiImplicitParam(name = "ids", value = "用户id集合", required = true) - public HttpResult> appuserByIdList(@RequestBody List ids) { + public HttpResult> appuserByIdList(@RequestBody List ids) { String methodDescribe = getMethodDescribe("appuserByIdList"); List users = userService.list((new LambdaQueryWrapper() - .in(CollUtil.isNotEmpty(ids),User::getId,ids) - + .in(CollUtil.isNotEmpty(ids), User::getId, ids) )); return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe); } + @OperateInfo(info = LogEnum.SYSTEM_COMMON) + @PostMapping("/getUserListByIds") + @ApiOperation("根据用户id集合查询用户信息") + @ApiImplicitParam(name = "ids", value = "用户id集合", required = true) + public HttpResult> getUserListByIds(@RequestBody List ids) { + String methodDescribe = getMethodDescribe("getUserListByIds"); + List users = userService.list((new LambdaQueryWrapper().in(CollUtil.isNotEmpty(ids), User::getId, ids))); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe); + } + @OperateInfo(info = LogEnum.SYSTEM_COMMON) @PostMapping("/getMarketList") @ApiOperation("获取营销用户列表") @@ -452,6 +477,7 @@ public class UserController extends BaseController { /** * 获取同级部门用户,以及下级部门所有用户 + * * @author cdf * @date 2023/7/31 */ @@ -468,6 +494,7 @@ public class UserController extends BaseController { /** * 根据部门ids查询用户信息 + * * @param deptId * @return */ @@ -478,9 +505,9 @@ public class UserController extends BaseController { public HttpResult> getUserByDeptIds(@RequestBody List deptId) { String methodDescribe = getMethodDescribe("getUserByDeptIds"); List users = userService.list(new LambdaQueryWrapper() - .in(User::getDeptId,deptId) - .eq(User::getSmsNotice,1) - .eq(User::getState,1) + .in(User::getDeptId, deptId) + .eq(User::getSmsNotice, 1) + .eq(User::getState, 1) ); return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe); } @@ -492,14 +519,25 @@ public class UserController extends BaseController { public HttpResult> getUserInfoByDeptIds(@RequestBody List deptId) { String methodDescribe = getMethodDescribe("getUserInfoByDeptIds"); List users = userService.list(new LambdaQueryWrapper() - .in(User::getDeptId,deptId) + .in(User::getDeptId, deptId) .eq(User::getState, DataStateEnum.ENABLE.getCode()) ); return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe); } + @OperateInfo(info = LogEnum.SYSTEM_COMMON) + @PostMapping("/getUserIdByRoleId") + @ApiOperation("根据角色id查询用户id") + @ApiImplicitParam(name = "roleId", value = "用户角色id", required = true) + public HttpResult> getUserIdByRoleId(@RequestBody List roleId) { + String methodDescribe = getMethodDescribe("getUserInfoByRoleId"); + List userId = userService.getUserIdByRoleId(roleId); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, userId, methodDescribe); + } + /** * 获取同级部门用户,以及下级部门所有用户 + * * @author cdf * @date 2023/7/31 */ @@ -516,6 +554,7 @@ public class UserController extends BaseController { /** * 根据角色类型获取用户 角色类型 type0:超级管理员;1:管理员;2:普通用户' 3:'审核角色', + * * @author cdf * @date 2024/3/29 */ @@ -528,5 +567,18 @@ public class UserController extends BaseController { List users = userService.getUserByRoleType(roleType); return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe); } + + + /** + * 查询所有用户 + */ + @OperateInfo(info = LogEnum.SYSTEM_COMMON) + @GetMapping("/simpleList") + @ApiOperation("查询所有用户作为下拉框") + public HttpResult simpleList() { + String methodDescribe = getMethodDescribe("simpleList"); + List result = userService.simpleList(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } } diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/service/IDeptService.java b/pqs-user/user-boot/src/main/java/com/njcn/user/service/IDeptService.java index 4f5cbcfd7..d13d6c3c2 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/service/IDeptService.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/service/IDeptService.java @@ -1,5 +1,6 @@ package com.njcn.user.service; +import cn.hutool.core.lang.tree.Tree; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.njcn.system.pojo.dto.AreaTreeDTO; @@ -247,4 +248,7 @@ public interface IDeptService extends IService { * @Date: 2023/12/11 14:50 */ List getAllDept(); + + List> orgTreeSelector(); + } diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/service/IRoleService.java b/pqs-user/user-boot/src/main/java/com/njcn/user/service/IRoleService.java index bfcdd3d51..f1e757710 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/service/IRoleService.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/service/IRoleService.java @@ -93,4 +93,7 @@ public interface IRoleService extends IService { */ Role getRoleByCode(String code); + List allRoleList(); + + List simpleList(); } diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/service/IUserService.java b/pqs-user/user-boot/src/main/java/com/njcn/user/service/IUserService.java index 88ebfa974..8ca56b737 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/service/IUserService.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/service/IUserService.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.njcn.common.pojo.response.HttpResult; import com.njcn.user.pojo.param.UserInfoParm; import com.njcn.user.pojo.param.UserParam; +import com.njcn.user.pojo.po.Role; import com.njcn.user.pojo.po.User; import com.njcn.user.pojo.dto.UserDTO; import com.njcn.user.pojo.vo.UserVO; @@ -190,4 +191,9 @@ public interface IUserService extends IService { List getUserByRoleType(Integer roleType); + List listAllUserByDeptId(String deptId); + + List getUserIdByRoleId(List roleId); + + List simpleList(); } diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/DeptServiceImpl.java b/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/DeptServiceImpl.java index 6fc4425d1..d83992734 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/DeptServiceImpl.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/DeptServiceImpl.java @@ -2,6 +2,9 @@ package com.njcn.user.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.lang.tree.TreeNode; +import cn.hutool.core.lang.tree.TreeUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -65,8 +68,6 @@ public class DeptServiceImpl extends ServiceImpl implements ID private final CommLedgerDeptClient commLedgerDeptClient; - - @Override public Page listDept(DeptParam.QueryParam queryParam) { QueryWrapper queryWrapper = new QueryWrapper<>(); @@ -82,7 +83,7 @@ public class DeptServiceImpl extends ServiceImpl implements ID queryWrapper.orderBy(true, queryParam.getOrderBy().equals(DbConstant.ASC), StrUtil.toUnderlineCase(queryParam.getSortBy())); } else { //没有排序参数,默认根据sort字段排序,没有排序字段的,根据updateTime更新时间排序 - queryWrapper.orderBy(true, queryParam.getOrderBy().equals(DbConstant.ASC), "sys_dept.sort"); + queryWrapper.orderBy(true, true, "sys_dept.sort"); } } queryWrapper.ne("sys_dept.state", DataStateEnum.DELETED.getCode()); @@ -105,7 +106,7 @@ public class DeptServiceImpl extends ServiceImpl implements ID } else { String pids = "," + deptParam.getPid(); String pid = this.baseMapper.getIdString(deptParam.getPid()); - if(StrUtil.isBlank(deptParam.getPid())){ + if (StrUtil.isBlank(deptParam.getPid())) { throw new BusinessException(UserResponseEnum.DEPT_PID_EXCEPTION); } @@ -114,7 +115,7 @@ public class DeptServiceImpl extends ServiceImpl implements ID } //默认为正常状态 dept.setState(DataStateEnum.ENABLE.getCode()); - if (StrUtil.isBlank(dept.getCode())){ + if (StrUtil.isBlank(dept.getCode())) { dept.setCode(dept.getId()); } return this.save(dept); @@ -129,7 +130,7 @@ public class DeptServiceImpl extends ServiceImpl implements ID Dept deptTem = this.getDeptById(updateParam.getId()); this.updateById(dept); - if(!updateParam.getName().equals(deptTem.getName())){ + if (!updateParam.getName().equals(deptTem.getName())) { //修改了部门名称需要修改台账信息中的单位部门 commLedgerDeptClient.update(dept); } @@ -181,20 +182,20 @@ public class DeptServiceImpl extends ServiceImpl implements ID @Override public List existMonitorDeptTree() { List deptTem = commLedgerDeptClient.existMonitorDeptTree().getData(); - if(CollectionUtil.isEmpty(deptTem)){ + if (CollectionUtil.isEmpty(deptTem)) { throw new BusinessException("当前没有部门存在监测点"); } List useMonitorIds = deptTem.stream().map(Dept::getId).collect(Collectors.toList()); - List resultDeptId = deptTem.stream().map(item->item.getPids().split(",")).flatMap(Arrays::stream).distinct().collect(Collectors.toList()); + List resultDeptId = deptTem.stream().map(item -> item.getPids().split(",")).flatMap(Arrays::stream).distinct().collect(Collectors.toList()); resultDeptId.addAll(useMonitorIds); List deptType = WebUtil.filterDeptType(); String deptIndex = RequestUtil.getDeptIndex(); List deptList = this.baseMapper.getDeptTree(deptIndex, deptType); - List finalDeptList = deptList.stream().filter(item->resultDeptId.contains(item.getId())).collect(Collectors.toList()); - List lastList = finalDeptList.stream().peek(item->{ - if(useMonitorIds.contains(item.getId())){ + List finalDeptList = deptList.stream().filter(item -> resultDeptId.contains(item.getId())).collect(Collectors.toList()); + List lastList = finalDeptList.stream().peek(item -> { + if (useMonitorIds.contains(item.getId())) { item.setLevel(2); } }).collect(Collectors.toList()); @@ -225,8 +226,8 @@ public class DeptServiceImpl extends ServiceImpl implements ID List areaTreeVO = deptArea(); HttpResult> areaTreeDTOS = areaFeignClient.areaDeptTree(id, type); List list = areaTreeDTOS.getData(); - if(CollectionUtils.isEmpty(list)){ - return new ArrayList<>(); + if (CollectionUtils.isEmpty(list)) { + return new ArrayList<>(); } for (AreaTreeDTO areaTreeVOList : list) { if (areaTreeVO.contains(areaTreeVOList.getId())) { @@ -251,14 +252,14 @@ public class DeptServiceImpl extends ServiceImpl implements ID List result = new ArrayList<>(); List deptBindAreaList = deptArea(); List areaList = areaFeignClient.getPidAreaList(id, type).getData(); - areaList.forEach(item->{ + areaList.forEach(item -> { AreaTreeDTO areaTreeDTO = new AreaTreeDTO(); areaTreeDTO.setId(item.getId()); areaTreeDTO.setPid(item.getPid()); - if(deptBindAreaList.contains(item.getId())){ + if (deptBindAreaList.contains(item.getId())) { areaTreeDTO.setName(item.getName() + "(已被绑定)"); areaTreeDTO.setIsFalse(1); - }else { + } else { areaTreeDTO.setName(item.getName()); areaTreeDTO.setIsFalse(0); } @@ -340,7 +341,7 @@ public class DeptServiceImpl extends ServiceImpl implements ID @Override public Dept getDeptByCode(String deptCode) { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); - lambdaQueryWrapper.eq(Dept::getCode,deptCode).eq(Dept::getState,DataStateEnum.ENABLE.getCode()); + lambdaQueryWrapper.eq(Dept::getCode, deptCode).eq(Dept::getState, DataStateEnum.ENABLE.getCode()); return this.baseMapper.selectOne(lambdaQueryWrapper); } @@ -406,7 +407,7 @@ public class DeptServiceImpl extends ServiceImpl implements ID @Override public List getDepSonSelfCodetByDeptId(String id) { Dept dept = this.getDeptById(id); - if(Objects.isNull(dept)){ + if (Objects.isNull(dept)) { throw new BusinessException(UserResponseEnum.DEPT_MISSING); } List sonIds = this.baseMapper.getDeptSonSlfeIds(id); @@ -418,11 +419,11 @@ public class DeptServiceImpl extends ServiceImpl implements ID public List getDepSonSelfCodetByCode(String code) { List codes = new ArrayList<>(); Dept dept = this.getDeptByCode(code); - if(Objects.isNull(dept)){ + if (Objects.isNull(dept)) { throw new BusinessException(UserResponseEnum.DEPT_MISSING); } - List deptList = this.baseMapper.getDeptDescendantIndexes(dept.getId(), Stream.of(0,1).collect(Collectors.toList())); - if(CollectionUtil.isNotEmpty(deptList)){ + List deptList = this.baseMapper.getDeptDescendantIndexes(dept.getId(), Stream.of(0, 1).collect(Collectors.toList())); + if (CollectionUtil.isNotEmpty(deptList)) { codes = deptList.stream().map(DeptDTO::getCode).collect(Collectors.toList()); codes.add(dept.getCode()); } @@ -441,10 +442,10 @@ public class DeptServiceImpl extends ServiceImpl implements ID @Override public List getDirectSonSelf(String deptId) { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); - lambdaQueryWrapper.and (Wrapper-> Wrapper. - eq (Dept::getPid,deptId). - or (). - eq(Dept::getId,deptId)).eq (Dept::getState, DataStateEnum.ENABLE.getCode()); + lambdaQueryWrapper.and(Wrapper -> Wrapper. + eq(Dept::getPid, deptId). + or(). + eq(Dept::getId, deptId)).eq(Dept::getState, DataStateEnum.ENABLE.getCode()); // lambdaQueryWrapper.eq(Dept::getPid,deptId).eq(Dept::getState, DataStateEnum.ENABLE.getCode()). // or().eq(Dept::getId,deptId); return this.list(lambdaQueryWrapper); @@ -453,14 +454,14 @@ public class DeptServiceImpl extends ServiceImpl implements ID @Override public List getSpecialDeptList() { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); - lambdaQueryWrapper.eq(Dept::getSpecialType,1).eq(Dept::getState, DataStateEnum.ENABLE.getCode()); + lambdaQueryWrapper.eq(Dept::getSpecialType, 1).eq(Dept::getState, DataStateEnum.ENABLE.getCode()); return this.list(lambdaQueryWrapper); } @Override - public Dept getRootDept(){ + public Dept getRootDept() { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); - lambdaQueryWrapper.eq(Dept::getState,DataStateEnum.ENABLE.getCode()).eq(Dept::getPid,'0'); + lambdaQueryWrapper.eq(Dept::getState, DataStateEnum.ENABLE.getCode()).eq(Dept::getPid, '0'); return this.getOne(lambdaQueryWrapper); } @@ -474,23 +475,23 @@ public class DeptServiceImpl extends ServiceImpl implements ID */ @Override public List getDepSonDetailByDeptId(String deptId) { - List result = new ArrayList<> (); + List result = new ArrayList<>(); LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); - lambdaQueryWrapper.eq (Dept::getPid, deptId). - eq (Dept::getState,DataStateEnum.ENABLE.getCode()); - result = this.baseMapper.selectList (lambdaQueryWrapper); - if(CollectionUtils.isEmpty (result)){ + lambdaQueryWrapper.eq(Dept::getPid, deptId). + eq(Dept::getState, DataStateEnum.ENABLE.getCode()); + result = this.baseMapper.selectList(lambdaQueryWrapper); + if (CollectionUtils.isEmpty(result)) { LambdaQueryWrapper deptLambdaQueryWrapper = new LambdaQueryWrapper<>(); - deptLambdaQueryWrapper.eq (Dept::getId, deptId). - eq (Dept::getState,DataStateEnum.ENABLE.getCode()); - result = this.baseMapper.selectList (deptLambdaQueryWrapper); + deptLambdaQueryWrapper.eq(Dept::getId, deptId). + eq(Dept::getState, DataStateEnum.ENABLE.getCode()); + result = this.baseMapper.selectList(deptLambdaQueryWrapper); } - List collect = result.stream ( ).map (temp -> { - DeptDTO deptDTO = new DeptDTO ( ); - BeanUtils.copyProperties (temp, deptDTO); + List collect = result.stream().map(temp -> { + DeptDTO deptDTO = new DeptDTO(); + BeanUtils.copyProperties(temp, deptDTO); return deptDTO; - }).collect (Collectors.toList ( )); + }).collect(Collectors.toList()); return collect; } @@ -503,16 +504,26 @@ public class DeptServiceImpl extends ServiceImpl implements ID @Override public List getDeptByCodeList(List list) { - return this.lambdaQuery().in(Dept::getCode,list).eq(Dept::getState,DataStateEnum.ENABLE.getCode()).list(); + return this.lambdaQuery().in(Dept::getCode, list).eq(Dept::getState, DataStateEnum.ENABLE.getCode()).list(); } @Override public List getDeptInfoListByIds(List deptIds) { - return this.lambdaQuery().in(Dept::getId,deptIds).eq(Dept::getState,DataStateEnum.ENABLE.getCode()).list(); + return this.lambdaQuery().in(Dept::getId, deptIds).eq(Dept::getState, DataStateEnum.ENABLE.getCode()).list(); } @Override public List getAllDept() { - return this.lambdaQuery().eq(Dept::getState,DataStateEnum.ENABLE.getCode()).list(); + return this.lambdaQuery().eq(Dept::getState, DataStateEnum.ENABLE.getCode()).list(); } + + @Override + public List> orgTreeSelector() { + List deptList = this.lambdaQuery().eq(Dept::getState, DataStateEnum.ENABLE.getCode()).list(); + List> treeNodeList = deptList.stream().map(dept -> + new TreeNode<>(dept.getId(), dept.getPid(), dept.getName(), dept.getSort())) + .collect(Collectors.toList()); + return TreeUtil.build(treeNodeList, "0"); + } + } diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/RoleServiceImpl.java b/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/RoleServiceImpl.java index c7c33e8c5..685e740b3 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/RoleServiceImpl.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/RoleServiceImpl.java @@ -204,6 +204,20 @@ public class RoleServiceImpl extends ServiceImpl implements IR return this.lambdaQuery().eq(Role::getCode,code).eq(Role::getState,1).one(); } + @Override + public List allRoleList() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.ne("sys_role.state", DataStateEnum.DELETED.getCode()); + return this.baseMapper.selectList(queryWrapper); + } + + @Override + public List simpleList() { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.select(Role::getId,Role::getName).eq(Role::getState,DataStateEnum.ENABLE.getCode()); + return this.baseMapper.selectList(lambdaQueryWrapper); + } + /** * 校验参数,检查是否存在相同编码的角色代码 */ diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/UserServiceImpl.java b/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/UserServiceImpl.java index 0b6cec36b..99b5e309b 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/UserServiceImpl.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/UserServiceImpl.java @@ -100,18 +100,18 @@ public class UserServiceImpl extends ServiceImpl implements IU } List roleNames = roleService.getRoleNameByUserId(user.getId()); UserSet userSet = userSetService.lambdaQuery().eq(UserSet::getUserId, user.getId()).one(); - return new UserDTO(user.getId(), user.getLoginName(), user.getName(), user.getPassword(), roleNames, userSet.getSecretKey(), userSet.getStandBy(), user.getDeptId(), user.getType(),user.getHeadSculpture()); + return new UserDTO(user.getId(), user.getLoginName(), user.getName(), user.getPassword(), roleNames, userSet.getSecretKey(), userSet.getStandBy(), user.getDeptId(), user.getType(), user.getHeadSculpture()); } @Override public UserDTO loadUserByPhone(String phone) { - User user = getUserByPhone(phone,false,null); + User user = getUserByPhone(phone, false, null); if (Objects.isNull(user)) { return null; } List roleNames = roleService.getRoleNameByUserId(user.getId()); UserSet userSet = userSetService.lambdaQuery().eq(UserSet::getUserId, user.getId()).one(); - return new UserDTO(user.getId(), user.getLoginName(), user.getName(), user.getPassword(), roleNames, userSet.getSecretKey(), userSet.getStandBy(), user.getDeptId(), user.getType(),user.getHeadSculpture()); + return new UserDTO(user.getId(), user.getLoginName(), user.getName(), user.getPassword(), roleNames, userSet.getSecretKey(), userSet.getStandBy(), user.getDeptId(), user.getType(), user.getHeadSculpture()); } @Override @@ -256,10 +256,10 @@ public class UserServiceImpl extends ServiceImpl implements IU return null; } BeanUtil.copyProperties(user, userVO); - if (!Objects.isNull(user.getDeptId())){ + if (!Objects.isNull(user.getDeptId())) { Dept dept = deptService.getDeptById(user.getDeptId()); //非自定义部门 - if (Objects.equals(dept.getType(),0)){ + if (Objects.equals(dept.getType(), 0)) { String areaId = deptService.getAreaIdByDeptId(user.getDeptId()); userVO.setAreaId(areaId); userVO.setAreaName(areaFeignClient.selectIdArea(areaId).getData().getName()); @@ -284,13 +284,12 @@ public class UserServiceImpl extends ServiceImpl implements IU Integer type = user.getType(); if (Objects.equals(UserType.SUPER_ADMINISTRATOR, type)) { type = UserType.ADMINISTRATOR; - //fixme 存在web用户和App用户,目前先通过管理员的登录名来区分开 - } else if (Objects.equals(UserType.ADMINISTRATOR, type) && !Objects.equals(user.getLoginName(),"njcnyw")) { + //fixme 存在web用户和App用户,目前先通过管理员的登录名来区分开 + } else if (Objects.equals(UserType.ADMINISTRATOR, type) && !Objects.equals(user.getLoginName(), "njcnyw")) { type = UserType.USER; - } else if (Objects.equals(UserType.ADMINISTRATOR, type) && Objects.equals(user.getLoginName(),"njcnyw")) { + } else if (Objects.equals(UserType.ADMINISTRATOR, type) && Objects.equals(user.getLoginName(), "njcnyw")) { type = UserType.APP; - } - else if (Objects.equals(UserType.USER, type) || Objects.equals(UserType.APP, type)) { + } else if (Objects.equals(UserType.USER, type) || Objects.equals(UserType.APP, type)) { return page; } if (ObjectUtil.isNotNull(queryParam)) { @@ -356,7 +355,7 @@ public class UserServiceImpl extends ServiceImpl implements IU @Override public boolean updatePassword(String id, String password) { - String secretPassword = userSetService.updatePassword(id, password,true); + String secretPassword = userSetService.updatePassword(id, password, true); User user = lambdaQuery().eq(User::getId, id).one(); user.setPassword(secretPassword); user.setPwdValidity(LocalDateTime.now()); @@ -431,7 +430,7 @@ public class UserServiceImpl extends ServiceImpl implements IU String fileName = methodDescribe + ".xlsx"; String targetDir = generalInfo.getBusinessTempPath() + File.separator + RequestUtil.getUserIndex(); File parentDir = new File(targetDir); - if(!parentDir.exists()){ + if (!parentDir.exists()) { parentDir.mkdirs(); } File excel = new File(targetDir, fileName); @@ -488,14 +487,14 @@ public class UserServiceImpl extends ServiceImpl implements IU @Override public List getUserListByDeptId(String deptId) { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); - lambdaQueryWrapper.like(Dept::getPids,deptId).select(Dept::getId); + lambdaQueryWrapper.like(Dept::getPids, deptId).select(Dept::getId); List deptIds = new ArrayList<>(); deptIds.add(deptId); List deptList = deptService.list(lambdaQueryWrapper); - if(CollectionUtil.isNotEmpty(deptIds)){ + if (CollectionUtil.isNotEmpty(deptIds)) { deptIds.addAll(deptList.stream().map(Dept::getId).distinct().collect(Collectors.toList())); } - return this.list(new LambdaQueryWrapper().in(User::getDeptId,deptIds).select(User::getId,User::getName,User::getLoginName)); + return this.list(new LambdaQueryWrapper().in(User::getDeptId, deptIds).select(User::getId, User::getName, User::getLoginName)); } @Override @@ -528,6 +527,42 @@ public class UserServiceImpl extends ServiceImpl implements IU return userRoleMapper.getUserByRoleType(roleType); } + @Override + public List listAllUserByDeptId(String deptId) { + //查询所有状态正常的用户信息,目前只要id和name + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(deptId)) { + lambdaQueryWrapper.eq(User::getDeptId, deptId); + } + lambdaQueryWrapper.eq(User::getState, DataStateEnum.ENABLE.getCode()) + .select(User::getName,User::getId); + List users = this.baseMapper.selectList(lambdaQueryWrapper); + if (CollectionUtil.isEmpty(users)) { + return new ArrayList<>(); + } else { + return BeanUtil.copyToList(users, UserVO.class); + } + } + + @Override + public List getUserIdByRoleId(List roleId) { + LambdaQueryWrapper userRoleLambdaQueryWrapper = new LambdaQueryWrapper<>(); + userRoleLambdaQueryWrapper.in(UserRole::getRoleId,roleId); + List userRole = userRoleService.list(userRoleLambdaQueryWrapper); + if(CollectionUtil.isEmpty(userRole)){ + return new ArrayList<>(); + }else{ + return userRole.stream().map(UserRole::getUserId).distinct().collect(Collectors.toList()); + } + } + + @Override + public List simpleList() { + LambdaQueryWrapper userLambdaQueryWrapper = new LambdaQueryWrapper<>(); + userLambdaQueryWrapper.select(User::getId,User::getName).eq(User::getState,DataStateEnum.ENABLE.getCode()); + return this.baseMapper.selectList(userLambdaQueryWrapper); + } + /** * 根据登录名查询用户 * @@ -576,8 +611,8 @@ public class UserServiceImpl extends ServiceImpl implements IU */ private void judgeIp(@NotNull User user, UserStrategy userStrategy) { String ipSection = user.getLimitIpStart() + "-" + user.getLimitIpEnd(); - log.error("用户实际ip:"+RequestUtil.getRealIp()); - log.error("用户限制ip:"+ipSection); + log.error("用户实际ip:" + RequestUtil.getRealIp()); + log.error("用户限制ip:" + ipSection); if (RequestUtil.getRealIp().equalsIgnoreCase(LogInfo.UNKNOWN_IP)) { //feign接口可能获取的IP是空的 throw new BusinessException(UserResponseEnum.INVALID_IP);