浏览代码

完成流程图上传的功能

YunaiV 3 年之前
父节点
当前提交
cfbef058b5

+ 1 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/ProcessDefinitionController.java → yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/BpmDefinitionController.java

@@ -22,7 +22,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 @RestController
 @RequestMapping("/bpm/definition")
 @Validated
-public class ProcessDefinitionController {
+public class BpmDefinitionController {
 
     @Resource
     private BpmDefinitionService bpmDefinitionService;

+ 13 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/model/BpmModelController.java

@@ -1,9 +1,11 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.controller.model;
 
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.model.vo.*;
+import cn.iocoder.yudao.adminserver.modules.bpm.convert.model.BpmModelConvert;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.model.BpmModelService;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.io.IoUtils;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiOperation;
@@ -11,6 +13,7 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import java.io.IOException;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
@@ -23,7 +26,7 @@ public class BpmModelController {
     @Resource
     private BpmModelService bpmModelService;
 
-    // TODO @芋艿:权限
+    // TODO @芋艿:权限、参数校验
 
     @GetMapping("/page")
     @ApiOperation(value = "获得模型分页")
@@ -46,6 +49,15 @@ public class BpmModelController {
         return success(bpmModelService.createModel(createRetVO));
     }
 
+    @PostMapping("/import")
+    @ApiOperation(value = "导入模型")
+    public CommonResult<String> importModel(BpmModeImportReqVO importReqVO) throws IOException {
+        BpmModelCreateReqVO createReqVO = BpmModelConvert.INSTANCE.convert(importReqVO);
+        // 读取文件
+        createReqVO.setBpmnXml(IoUtils.readUtf8(importReqVO.getBpmnFile().getInputStream(), false));
+        return success(bpmModelService.createModel(createReqVO));
+    }
+
     @PutMapping("/update")
     @ApiOperation(value = "修改模型")
     public CommonResult<Boolean> updateModel(@RequestBody BpmModelUpdateReqVO modelVO) {

+ 22 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/model/vo/BpmModeImportReqVO.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.model.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.constraints.NotNull;
+
+@ApiModel("流程模型的导入 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class BpmModeImportReqVO extends BpmModelBaseVO {
+
+    @ApiModelProperty(value = "BPMN 文件", required = true)
+    @NotNull(message = "BPMN 文件不能为空")
+    private MultipartFile bpmnFile;
+
+}

+ 5 - 6
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/model/ModelConvert.java → yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/model/BpmModelConvert.java

@@ -1,9 +1,6 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.convert.model;
 
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.model.vo.BpmModelCreateReqVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.model.vo.BpmModelPageItemRespVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.model.vo.BpmModelRespVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.model.vo.BpmModelUpdateReqVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.model.vo.*;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmDefinitionCreateReqDTO;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.model.dto.BpmModelMetaInfoRespDTO;
@@ -26,9 +23,9 @@ import java.util.Map;
  * @author yunlongn
  */
 @Mapper
-public interface ModelConvert {
+public interface BpmModelConvert {
 
-    ModelConvert INSTANCE = Mappers.getMapper(ModelConvert.class);
+    BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class);
 
     default List<BpmModelPageItemRespVO> convertList(List<Model> list, Map<Long, BpmFormDO> formMap,
                                                      Map<String, Deployment> deploymentMap,
@@ -116,4 +113,6 @@ public interface ModelConvert {
 
     BpmModelPageItemRespVO.ProcessDefinition convert(ProcessDefinition bean);
 
+    BpmModelCreateReqVO convert(BpmModeImportReqVO bean);
+
 }

+ 0 - 20
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/ProcessService.java

@@ -1,20 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.service.definition;
-
-import org.springframework.web.multipart.MultipartFile;
-
-/**
- * 流程基础管理
- *
- * @author ZJQ
- * @date 2021/9/5 21:00
- */
-@Deprecated
-public interface ProcessService {
-
-    /**
-     * 上传流程文件,进行流程模型部署
-     * @param multipartFile 上传文件
-     */
-    void deployProcess(MultipartFile multipartFile);
-
-}

+ 0 - 85
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/ProcessServiceImpl.java

@@ -1,85 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.service.definition;
-
-import lombok.extern.slf4j.Slf4j;
-import org.activiti.engine.RepositoryService;
-import org.activiti.engine.repository.Deployment;
-import org.activiti.engine.repository.ProcessDefinition;
-import org.apache.commons.io.FilenameUtils;
-import org.springframework.stereotype.Service;
-import org.springframework.web.multipart.MultipartFile;
-
-import javax.annotation.Resource;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-import java.util.zip.ZipInputStream;
-
-import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.FILE_UPLOAD_FAILED;
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-
-/**
- * 流程基础管理
- *
- * @author ZJQ
- * @date 2021/9/5 21:04
- */
-@Service
-@Slf4j
-@Deprecated
-public class ProcessServiceImpl implements ProcessService {
-
-    private static final String BPMN20_XML = "bpmn20.xml";
-
-    @Resource
-    private RepositoryService repositoryService;
-
-    /**
-     * 上传流程文件,进行流程部署
-     * @param multipartFile 上传文件
-     */
-    @Override
-    public void deployProcess(MultipartFile multipartFile) {
-        String fileName = multipartFile.getOriginalFilename();
-        try (InputStream inputStream = multipartFile.getInputStream()){
-            Deployment deployment = getDeplymentByType(inputStream,fileName);
-            //获取部署成功的流程模型
-            List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
-            processDefinitions.forEach((processDefinition)->{
-                //设置线上部署流程模型名字
-                String proDefId = processDefinition.getId();
-                repositoryService.setProcessDefinitionCategory(proDefId,fileName);
-                log.info("流程文件部署成功,流程ID="+proDefId);
-            });
-        } catch (IOException e) {
-           log.error("流程部署出现异常"+e);
-        }
-    }
-
-    /**
-     * 根据上传文件类型对应实现不同方式的流程部署
-     * @param inputStream 文件输入流
-     * @param fileName 文件名
-     * @return 文件部署流程
-     */
-    public Deployment getDeplymentByType(InputStream inputStream,String fileName){
-        Deployment deployment;
-        String type = FilenameUtils.getExtension(fileName);
-        switch (type){
-            case "bpmn":
-                String baseName = FilenameUtils.getBaseName(fileName);
-                deployment = repositoryService.createDeployment().addInputStream(baseName+"."+BPMN20_XML,inputStream).deploy();
-                break;
-            case "png":
-                deployment = repositoryService.createDeployment().addInputStream(fileName,inputStream).deploy();
-                break;
-            case "zip":
-            case "bar":
-                ZipInputStream zipInputStream = new ZipInputStream(inputStream);
-                deployment = repositoryService.createDeployment().addZipInputStream(zipInputStream).deploy();
-                break;
-            default:
-                throw exception(FILE_UPLOAD_FAILED);
-        }
-        return deployment;
-    }
-}

+ 6 - 6
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/model/impl/BpmModelServiceImpl.java

@@ -2,7 +2,7 @@ package cn.iocoder.yudao.adminserver.modules.bpm.service.model.impl;
 
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.model.vo.*;
-import cn.iocoder.yudao.adminserver.modules.bpm.convert.model.ModelConvert;
+import cn.iocoder.yudao.adminserver.modules.bpm.convert.model.BpmModelConvert;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmDefinitionService;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmDefinitionCreateReqDTO;
@@ -87,7 +87,7 @@ public class BpmModelServiceImpl implements BpmModelService {
 
         // 拼接结果
         long modelCount = modelQuery.count();
-        return new PageResult<>(ModelConvert.INSTANCE.convertList(models, formMap, deploymentMap, processDefinitionMap), modelCount);
+        return new PageResult<>(BpmModelConvert.INSTANCE.convertList(models, formMap, deploymentMap, processDefinitionMap), modelCount);
     }
 
     @Override
@@ -96,7 +96,7 @@ public class BpmModelServiceImpl implements BpmModelService {
         if (model == null) {
             return null;
         }
-        BpmModelRespVO modelRespVO = ModelConvert.INSTANCE.convert(model);
+        BpmModelRespVO modelRespVO = BpmModelConvert.INSTANCE.convert(model);
         // 拼接 bpmn XML
         byte[] bpmnBytes = repositoryService.getModelEditorSource(id);
         modelRespVO.setBpmnXml(StrUtil.utf8Str(bpmnBytes));
@@ -115,7 +115,7 @@ public class BpmModelServiceImpl implements BpmModelService {
 
         // 创建流程定义
         Model model = repositoryService.newModel();
-        ModelConvert.INSTANCE.copy(model, createReqVO);
+        BpmModelConvert.INSTANCE.copy(model, createReqVO);
         // 保存流程定义
         repositoryService.saveModel(model);
         // 添加 BPMN XML
@@ -134,7 +134,7 @@ public class BpmModelServiceImpl implements BpmModelService {
         }
 
         // 修改流程定义
-        ModelConvert.INSTANCE.copy(model, updateReqVO);
+        BpmModelConvert.INSTANCE.copy(model, updateReqVO);
         // 更新模型
         repositoryService.saveModel(model);
         // 更新 BPMN XML
@@ -155,7 +155,7 @@ public class BpmModelServiceImpl implements BpmModelService {
         }
 
         // 创建流程定义
-        BpmDefinitionCreateReqDTO definitionCreateReqDTO = ModelConvert.INSTANCE.convert2(model)
+        BpmDefinitionCreateReqDTO definitionCreateReqDTO = BpmModelConvert.INSTANCE.convert2(model)
                 .setBpmnXml(StrUtil.utf8Str(bpmnBytes));
         String definitionId = bpmDefinitionService.createDefinition(definitionCreateReqDTO);
 

+ 7 - 0
yudao-admin-ui/src/utils/request.js

@@ -121,4 +121,11 @@ service.interceptors.response.use(res => {
   }
 )
 
+export function getBaseHeader() {
+  return {
+    'Authorization': "Bearer " + getToken(),
+    'tenant-id': Cookies.get('tenantId'),
+  }
+}
+
 export default service

+ 130 - 1
yudao-admin-ui/src/views/bpm/model/index.vue

@@ -28,6 +28,10 @@
         <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd"
                    v-hasPermi="['infra:config:create']">新建流程模型</el-button>
       </el-col>
+      <el-col :span="1.5">
+        <el-button type="info" icon="el-icon-upload2" size="mini" @click="handleImport"
+                   v-hasPermi="['system:user:import']">导入流程模型</el-button>
+      </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
@@ -100,15 +104,75 @@
     <el-dialog title="流程图" :visible.sync="showBpmnOpen" width="80%" append-to-body>
       <my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" />
     </el-dialog>
+
+    <!-- 用户导入对话框 -->
+    <el-dialog title="导入流程模型" :visible.sync="upload.open" width="400px" append-to-body>
+      <el-upload
+        ref="upload"
+        :limit="1"
+        accept=".bpmn, .xml"
+        :headers="upload.headers"
+        :action="upload.url"
+        :disabled="upload.isUploading"
+        :on-progress="handleFileUploadProgress"
+        :on-success="handleFileSuccess"
+        :auto-upload="false"
+        name="bpmnFile"
+        :data="upload.form"
+        drag
+      >
+        <i class="el-icon-upload"></i>
+        <div class="el-upload__text">
+          将文件拖到此处,或
+          <em>点击上传</em>
+        </div>
+        <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“bpm”或“xml”格式文件!</div>
+
+        <!--        <div class="el-upload__tip" slot="tip">-->
+<!--          流程名称:<el-input v-model="upload.name"/>-->
+<!--          流程分类:<el-input v-model="upload.category"/>-->
+<!--        </div>-->
+        <div class="el-upload__tip" slot="tip">
+          <el-form ref="uploadForm" size="mini" label-width="90px" :model="upload.form" :rules="upload.rules" @submit.native.prevent>
+            <el-form-item label="流程标识" prop="key">
+              <el-input v-model="upload.form.key" placeholder="请输入流标标识" />
+            </el-form-item>
+            <el-form-item label="流程名称" prop="name">
+              <el-input v-model="upload.form.name" placeholder="请输入流程名称" clearable />
+            </el-form-item>
+            <el-form-item label="流程分类" prop="category">
+              <el-select v-model="upload.form.category" placeholder="请选择流程分类" clearable style="width: 100%">
+                <el-option v-for="dict in categoryDictDatas" :key="dict.value" :label="dict.label" :value="dict.value"/>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="流程表单" prop="formId">
+              <el-select v-model="upload.form.formId" placeholder="请选择流程表单,非必选哟!" clearable style="width: 100%">
+                <el-option v-for="form in forms" :key="form.id" :label="form.name" :value="form.id"/>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="流程描述" prop="description">
+              <el-input type="textarea" v-model="upload.form.description" clearable />
+            </el-form-item>
+          </el-form>
+        </div>
+      </el-upload>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitFileForm">确 定</el-button>
+        <el-button @click="uploadClose">取 消</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import {deleteModel, deployModel, getModelPage, getModel, updateModelState} from "@/api/bpm/model";
 import {DICT_TYPE, getDictDatas} from "@/utils/dict";
-import {getForm} from "@/api/bpm/form";
+import {getForm, getSimpleForms} from "@/api/bpm/form";
 import {decodeFields} from "@/utils/formGenerator";
 import Parser from '@/components/parser/Parser'
+import {getToken} from "@/utils/auth";
+import {getBaseHeader} from "@/utils/request";
+import {addUser, updateUser} from "@/api/system/user";
 
 export default {
   name: "model",
@@ -144,12 +208,38 @@ export default {
         fields: []
       },
 
+      // 流程导入参数
+      upload: {
+        // 是否显示弹出层(用户导入)
+        open: false,
+        // 是否禁用上传
+        isUploading: false,
+        // 设置上传的请求头部
+        headers: getBaseHeader(),
+        // 上传的地址
+        url: process.env.VUE_APP_BASE_API + '/api/' + "/bpm/model/import",
+        // 表单
+        form: {},
+        // 校验规则
+        rules: {
+          key: [{ required: true, message: "流程标识不能为空", trigger: "blur" }],
+          name: [{ required: true, message: "流程名称不能为空", trigger: "blur" }],
+          category: [{ required: true, message: "流程分类不能为空", trigger: "blur" }],
+        },
+      },
+      // 流程表单的下拉框的数据
+      forms: [],
+
       // 数据字典
       categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
     };
   },
   created() {
     this.getList();
+    // 获得流程表单的下拉框的数据
+    getSimpleForms().then(response => {
+      this.forms = response.data
+    })
   },
   methods: {
     /** 查询流程模型列表 */
@@ -267,6 +357,45 @@ export default {
         this.msgSuccess(statusState + "成功");
       })
     },
+    /** 导入按钮操作 */
+    handleImport() {
+      this.upload.open = true;
+    },
+    // 文件上传中处理
+    handleFileUploadProgress(event, file, fileList) {
+      this.upload.isUploading = true;
+    },
+    // 文件上传成功处理
+    handleFileSuccess(response, file, fileList) {
+      if (response.code !== 0) {
+        this.msgError(response.msg)
+        return;
+      }
+      // 重置表单
+      this.uploadClose();
+      // 提示,并刷新
+      this.msgSuccess("上传成功!请点击【设计流程】按钮,进行编辑保存后,才可以进行【发布流程】");
+      this.getList();
+    },
+    uploadClose() {
+      // 关闭弹窗
+      this.upload.open = false;
+      // 重置上传状态和文件
+      this.upload.isUploading = false;
+      this.$refs.upload.clearFiles();
+      // 重置表单
+      this.upload.form = {};
+      this.resetForm("uploadForm");
+    },
+    /** 提交上传文件 */
+    submitFileForm() {
+      this.$refs["uploadForm"].validate(valid => {
+        if (!valid) {
+          return;
+        }
+        this.$refs.upload.submit();
+      })
+    },
   }
 };
 </script>

+ 1 - 0
yudao-admin-ui/src/views/bpm/model/modelEditor.vue

@@ -61,6 +61,7 @@ export default {
           ...response.data,
           bpmnXml: undefined, // 清空 bpmnXml 属性
         }
+        // this.controlForm.processId = response.data.key
       })
     }
   },

+ 29 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/IoUtils.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.framework.common.util.io;
+
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.io.InputStream;
+
+/**
+ * IO 工具类,用于 {@link cn.hutool.core.io.IoUtil} 缺失的方法
+ *
+ * @author 芋道源码
+ */
+public class IoUtils {
+
+    /**
+     * 从流中读取 UTF8 编码的内容
+     *
+     * @param in 输入流
+     * @param isClose 是否关闭
+     * @return 内容
+     * @throws IORuntimeException IO 异常
+     */
+    public static String readUtf8(InputStream in, boolean isClose) throws IORuntimeException {
+        return StrUtil.utf8Str(IoUtil.read(in, isClose));
+    }
+
+}