|
@@ -0,0 +1,265 @@
|
|
|
+package cn.iocoder.yudao.module.bpm.service.definition;
|
|
|
+
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
|
+import cn.iocoder.yudao.framework.common.util.object.PageUtils;
|
|
|
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
|
|
|
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
|
|
|
+import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert;
|
|
|
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
|
|
|
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
|
|
|
+import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessDefinitionExtMapper;
|
|
|
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.flowable.bpmn.converter.BpmnXMLConverter;
|
|
|
+import org.flowable.bpmn.model.BpmnModel;
|
|
|
+import org.flowable.common.engine.impl.db.SuspensionState;
|
|
|
+import org.flowable.common.engine.impl.util.io.BytesStreamSource;
|
|
|
+import org.flowable.engine.RepositoryService;
|
|
|
+import org.flowable.engine.repository.Deployment;
|
|
|
+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 javax.validation.Valid;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
|
|
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
|
|
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_KEY_NOT_MATCH;
|
|
|
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_NAME_NOT_MATCH;
|
|
|
+import static java.util.Collections.emptyList;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 流程定义实现
|
|
|
+ * 主要进行 Flowable {@link ProcessDefinition} 和 {@link Deployment} 的维护
|
|
|
+ *
|
|
|
+ * @author yunlongn
|
|
|
+ * @author ZJQ
|
|
|
+ * @author 芋道源码
|
|
|
+ */
|
|
|
+@Service
|
|
|
+@Validated
|
|
|
+@Slf4j
|
|
|
+public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionService {
|
|
|
+
|
|
|
+ private static final String BPMN_FILE_SUFFIX = ".bpmn";
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private RepositoryService repositoryService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private BpmProcessDefinitionExtMapper processDefinitionMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private BpmFormService formService;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ProcessDefinition getProcessDefinition(String id) {
|
|
|
+ return repositoryService.getProcessDefinition(id);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) {
|
|
|
+ if (StrUtil.isEmpty(deploymentId)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> 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<Deployment> getDeployments(Set<String> ids) {
|
|
|
+ if (CollUtil.isEmpty(ids)) {
|
|
|
+ return emptyList();
|
|
|
+ }
|
|
|
+ List<Deployment> 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 BpmnModel getBpmnModel(String processDefinitionId) {
|
|
|
+ return repositoryService.getBpmnModel(processDefinitionId);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String createProcessDefinition(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO) {
|
|
|
+ // 创建 Deployment 部署
|
|
|
+ Deployment deploy = repositoryService.createDeployment()
|
|
|
+ .key(createReqDTO.getKey()).name(createReqDTO.getName()).category(createReqDTO.getCategory())
|
|
|
+ .addBytes(createReqDTO.getKey() + BPMN_FILE_SUFFIX, createReqDTO.getBpmnBytes())
|
|
|
+ .deploy();
|
|
|
+
|
|
|
+ // 设置 ProcessDefinition 的 category 分类
|
|
|
+ ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
|
|
|
+ repositoryService.setProcessDefinitionCategory(definition.getId(), createReqDTO.getCategory());
|
|
|
+ // 注意 1,ProcessDefinition 的 key 和 name 是通过 BPMN 中的 <bpmn2:process /> 的 id 和 name 决定
|
|
|
+ // 注意 2,目前该项目的设计上,需要保证 Model、Deployment、ProcessDefinition 使用相同的 key,保证关联性。
|
|
|
+ // 否则,会导致 ProcessDefinition 的分页无法查询到。
|
|
|
+ if (!Objects.equals(definition.getKey(), createReqDTO.getKey())) {
|
|
|
+ throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, createReqDTO.getKey(), definition.getKey());
|
|
|
+ }
|
|
|
+ if (!Objects.equals(definition.getName(), createReqDTO.getName())) {
|
|
|
+ throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, createReqDTO.getName(), definition.getName());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 插入拓展表
|
|
|
+ BpmProcessDefinitionExtDO definitionDO = BpmProcessDefinitionConvert.INSTANCE.convert2(createReqDTO)
|
|
|
+ .setProcessDefinitionId(definition.getId());
|
|
|
+ processDefinitionMapper.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 String getProcessDefinitionBpmnXML(String id) {
|
|
|
+ BpmnModel bpmnModel = repositoryService.getBpmnModel(id);
|
|
|
+ if (bpmnModel == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ BpmnXMLConverter converter = new BpmnXMLConverter();
|
|
|
+ return StrUtil.utf8Str(converter.convertToXML(bpmnModel));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean isProcessDefinitionEquals(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO) {
|
|
|
+ // 校验 name、description 是否更新
|
|
|
+ ProcessDefinition oldProcessDefinition = getActiveProcessDefinition(createReqDTO.getKey());
|
|
|
+ if (oldProcessDefinition == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ BpmProcessDefinitionExtDO oldProcessDefinitionExt = getProcessDefinitionExt(oldProcessDefinition.getId());
|
|
|
+ if (!StrUtil.equals(createReqDTO.getName(), oldProcessDefinition.getName())
|
|
|
+ || !StrUtil.equals(createReqDTO.getDescription(), oldProcessDefinitionExt.getDescription())
|
|
|
+ || !StrUtil.equals(createReqDTO.getCategory(), oldProcessDefinition.getCategory())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 校验 form 信息是否更新
|
|
|
+ if (!ObjectUtil.equal(createReqDTO.getFormType(), oldProcessDefinitionExt.getFormType())
|
|
|
+ || !ObjectUtil.equal(createReqDTO.getFormId(), oldProcessDefinitionExt.getFormId())
|
|
|
+ || !ObjectUtil.equal(createReqDTO.getFormConf(), oldProcessDefinitionExt.getFormConf())
|
|
|
+ || !ObjectUtil.equal(createReqDTO.getFormFields(), oldProcessDefinitionExt.getFormFields())
|
|
|
+ || !ObjectUtil.equal(createReqDTO.getFormCustomCreatePath(), oldProcessDefinitionExt.getFormCustomCreatePath())
|
|
|
+ || !ObjectUtil.equal(createReqDTO.getFormCustomViewPath(), oldProcessDefinitionExt.getFormCustomViewPath())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 校验 BPMN XML 信息
|
|
|
+ BpmnModel newModel = buildBpmnModel(createReqDTO.getBpmnBytes());
|
|
|
+ BpmnModel oldModel = getBpmnModel(oldProcessDefinition.getId());
|
|
|
+ //TODO 貌似 flowable 不修改这个也不同。需要看看。 sourceSystemId 不同
|
|
|
+ if (equals(oldModel, newModel)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 最终发现都一致,则返回 true
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建对应的 BPMN Model
|
|
|
+ *
|
|
|
+ * @param bpmnBytes 原始的 BPMN XML 字节数组
|
|
|
+ * @return BPMN Model
|
|
|
+ */
|
|
|
+ private BpmnModel buildBpmnModel(byte[] bpmnBytes) {
|
|
|
+ // 转换成 BpmnModel 对象
|
|
|
+ BpmnXMLConverter converter = new BpmnXMLConverter();
|
|
|
+ return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), true, true);
|
|
|
+ }
|
|
|
+ //TODO 移出去
|
|
|
+ public boolean equals(BpmnModel oldModel, BpmnModel newModel) {
|
|
|
+ // 由于 BpmnModel 未提供 equals 方法,所以只能转成字节数组,进行比较
|
|
|
+ return Arrays.equals(getBpmnBytes(oldModel), getBpmnBytes(newModel));
|
|
|
+ }
|
|
|
+ //TODO 移出去
|
|
|
+ public byte[] getBpmnBytes(BpmnModel model) {
|
|
|
+ if (model == null) {
|
|
|
+ return new byte[0];
|
|
|
+ }
|
|
|
+ BpmnXMLConverter converter = new BpmnXMLConverter();
|
|
|
+ return converter.convertToXML(model);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public BpmProcessDefinitionExtDO getProcessDefinitionExt(String id) {
|
|
|
+ return processDefinitionMapper.selectByProcessDefinitionId(id);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public PageResult<BpmProcessDefinitionPageItemRespVO> getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageVO) {
|
|
|
+ ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery();
|
|
|
+ if (StrUtil.isNotBlank(pageVO.getKey())) {
|
|
|
+ definitionQuery.processDefinitionKey(pageVO.getKey());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 执行查询
|
|
|
+ List<ProcessDefinition> processDefinitions = definitionQuery.orderByProcessDefinitionVersion().desc()
|
|
|
+ .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
|
|
|
+
|
|
|
+ if (CollUtil.isEmpty(processDefinitions)) {
|
|
|
+ return new PageResult<>(emptyList(), definitionQuery.count());
|
|
|
+ }
|
|
|
+ // 获得 Deployment Map
|
|
|
+ Set<String> deploymentIds = new HashSet<>();
|
|
|
+ processDefinitions.forEach(definition -> addIfNotNull(deploymentIds, definition.getDeploymentId()));
|
|
|
+ Map<String, Deployment> deploymentMap = getDeploymentMap(deploymentIds);
|
|
|
+
|
|
|
+ // 获得 BpmProcessDefinitionDO Map
|
|
|
+ List<BpmProcessDefinitionExtDO> processDefinitionDOs = processDefinitionMapper.selectListByProcessDefinitionIds(
|
|
|
+ convertList(processDefinitions, ProcessDefinition::getId));
|
|
|
+ Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap = convertMap(processDefinitionDOs,
|
|
|
+ BpmProcessDefinitionExtDO::getProcessDefinitionId);
|
|
|
+
|
|
|
+ // 获得 Form Map
|
|
|
+ Set<Long> formIds = convertSet(processDefinitionDOs, BpmProcessDefinitionExtDO::getFormId);
|
|
|
+ Map<Long, BpmFormDO> formMap = formService.getFormMap(formIds);
|
|
|
+
|
|
|
+ // 拼接结果
|
|
|
+ long definitionCount = definitionQuery.count();
|
|
|
+ return new PageResult<>(BpmProcessDefinitionConvert.INSTANCE.convertList(processDefinitions, deploymentMap,
|
|
|
+ processDefinitionDOMap, formMap), definitionCount);
|
|
|
+ }
|
|
|
+}
|