diff --git a/pom.xml b/pom.xml index 25efc0b0..ffddf3a2 100644 --- a/pom.xml +++ b/pom.xml @@ -817,6 +817,9 @@ **/*.xml **/*.properties + **/*.json + **/*.bpmn + **/*.png false diff --git a/src/main/java/com/sipai/controller/activiti/ModelController.java b/src/main/java/com/sipai/controller/activiti/ModelController.java index c3e6320d..1f675cc6 100644 --- a/src/main/java/com/sipai/controller/activiti/ModelController.java +++ b/src/main/java/com/sipai/controller/activiti/ModelController.java @@ -10,6 +10,7 @@ import com.sipai.entity.activiti.ProcessType; import com.sipai.entity.activiti.TaskModel; import com.sipai.entity.user.Company; import com.sipai.entity.user.User; +import com.sipai.service.activiti.BpmnImportService; import com.sipai.service.activiti.ProcessModelService; import com.sipai.service.business.BusinessUnitService; import com.sipai.service.user.JobService; @@ -79,6 +80,8 @@ public class ModelController { @Resource private ProcessModelService processModelService; @Resource + private BpmnImportService bpmnImportService; + @Resource private BusinessUnitService businessUnitService; @Resource private UnitService unitService; @@ -729,23 +732,23 @@ public class ModelController { json.put("businessunit", businessUnitService.selectById(documentation)); String resourceId = json.get("resourceId").toString(); List list = this.jobService.selectModelNodeJobListByWhere(" where resource_id='"+resourceId+"' and model_id ='"+modelData.getId()+"' "); - String jobNames=""; - String jobIds = ""; - if(list != null && list.size()>0){ + StringBuilder jobNames= new StringBuilder(); + StringBuilder jobIds = new StringBuilder(); + if(list != null && !list.isEmpty()){ for(int j=0;j> bpmnFiles = bpmnImportService.listBpmnFiles(); + JSONArray json = JSONArray.fromObject(bpmnFiles); + String result = "{\"total\":" + bpmnFiles.size() + ",\"rows\":" + json + "}"; + model.addAttribute("result", result); + return new ModelAndView("result"); + } + + /** + * 导入BPMN文件到模型编辑器 + */ + @RequestMapping(value = "/importBpmnFile.do", method = RequestMethod.POST) + public String importBpmnFile(HttpServletRequest request, Model model, + @RequestParam(value = "bpmnFilePath") String bpmnFilePath, + @RequestParam(value = "modelName", required = false) String modelName, + @RequestParam(value = "description", required = false) String description, + @RequestParam(value = "companyId", required = false) String companyId) { + + // 构建模型Key + String modelKey = null; + User cu = (User)request.getSession().getAttribute("cu"); + if (companyId != null && !companyId.isEmpty()) { + modelKey = "imported-" + companyId; + } else if (cu != null && !cu.getId().equals(CommString.ID_Admin)) { + List companies = unitService.getCompaniesByUserId(cu.getId()); + if (companies != null && companies.size() > 0) { + modelKey = "imported-" + companies.get(0).getId(); + } + } + + String modelId = bpmnImportService.importBpmnFile(bpmnFilePath, modelName, description, modelKey); + int result = modelId != null ? 1 : 0; + + JSONObject resObj = new JSONObject(); + resObj.put("res", result); + resObj.put("modelId", modelId); + + model.addAttribute("result", resObj.toString()); + return "result"; + } + + /** + * 直接部署BPMN文件 + */ + @RequestMapping(value = "/deployBpmnFile.do", method = RequestMethod.POST) + public String deployBpmnFile(HttpServletRequest request, Model model, + @RequestParam(value = "bpmnFilePath") String bpmnFilePath, + @RequestParam(value = "deploymentName", required = false) String deploymentName) { + + String deploymentId = bpmnImportService.deployBpmnFile(bpmnFilePath, deploymentName); + int result = deploymentId != null ? 1 : 0; + + JSONObject resObj = new JSONObject(); + resObj.put("res", result); + resObj.put("deploymentId", deploymentId); + + model.addAttribute("result", resObj.toString()); + return "result"; + } + + /** + * 上传BPMN文件并导入 + */ + @RequestMapping(value = "/uploadBpmnFile.do", method = RequestMethod.POST) + public String uploadBpmnFile(@RequestParam MultipartFile[] file, HttpServletRequest request, + Model model) { + int result = 0; + String modelId = null; + + try { + // 获取当前用户和公司信息 + User cu = (User)request.getSession().getAttribute("cu"); + String processTypeId = request.getParameter("processTypeId"); + String companyId = request.getParameter("companyId"); + String maintenanceType = request.getParameter("maintenanceType"); + + for (MultipartFile myfile : file) { + if (!myfile.isEmpty()) { + String originalFilename = myfile.getOriginalFilename(); + String modelName = originalFilename; + if (originalFilename != null && originalFilename.contains(".")) { + modelName = originalFilename.substring(0, originalFilename.lastIndexOf(".")); + } + + // 构建模型Key(格式:processTypeId-companyId-maintenanceType) + String modelKey = null; + if (companyId != null && !companyId.isEmpty()) { + StringBuilder keyBuilder = new StringBuilder(); + if (processTypeId != null && !processTypeId.isEmpty()) { + keyBuilder.append(processTypeId); + } else { + keyBuilder.append("imported"); + } + keyBuilder.append("-").append(companyId); + if (maintenanceType != null && !maintenanceType.isEmpty()) { + keyBuilder.append("-").append(maintenanceType); + } + modelKey = keyBuilder.toString(); + } else if (cu != null && !cu.getId().equals(CommString.ID_Admin)) { + // 非管理员用户,自动关联其所属公司 + List companies = unitService.getCompaniesByUserId(cu.getId()); + if (companies != null && companies.size() > 0) { + modelKey = "imported-" + companies.get(0).getId(); + } + } + + modelId = bpmnImportService.importBpmnFromInputStream( + myfile.getInputStream(), modelName, "Uploaded from " + originalFilename, modelKey); + + if (modelId != null) { + result = 1; + } + } + } + } catch (Exception e) { + logger.error("Error uploading BPMN file", e); + } + + JSONObject resObj = new JSONObject(); + resObj.put("res", result); + resObj.put("modelId", modelId); + + model.addAttribute("result", resObj.toString()); + return "result"; + } + + /** + * 获取BPMN文件内容 + */ + @RequestMapping("/getBpmnFileContent.do") + public void getBpmnFileContent(HttpServletRequest request, HttpServletResponse response, + @RequestParam(value = "bpmnFilePath") String bpmnFilePath) { + try { + String content = bpmnImportService.getBpmnFileContent(bpmnFilePath); + if (content != null) { + response.setContentType("application/xml"); + response.setCharacterEncoding("UTF-8"); + response.getWriter().write(content); + } + } catch (Exception e) { + logger.error("Error getting BPMN file content", e); + } + } + + /** + * 批量导入所有BPMN文件到模型 + */ + @RequestMapping(value = "/importAllBpmnFiles.do", method = RequestMethod.POST) + public String importAllBpmnFiles(HttpServletRequest request, Model model) { + List> bpmnFiles = bpmnImportService.listBpmnFiles(); + int successCount = 0; + int failCount = 0; + List importedModels = new ArrayList<>(); + + for (Map fileInfo : bpmnFiles) { + String path = (String) fileInfo.get("path"); + String processName = (String) fileInfo.get("processName"); + String processId = (String) fileInfo.get("processId"); + + // 提取相对路径 + String relativePath = path; + if (path != null && path.contains("diagrams/")) { + relativePath = path.substring(path.indexOf("diagrams/") + 9); + } + + String modelName = processName != null ? processName : fileInfo.get("filename").toString(); + String modelId = bpmnImportService.importBpmnFile(relativePath, modelName, ""); + + if (modelId != null) { + successCount++; + importedModels.add(modelName + ":" + modelId); + } else { + failCount++; + } + } + + JSONObject resObj = new JSONObject(); + resObj.put("res", 1); + resObj.put("successCount", successCount); + resObj.put("failCount", failCount); + resObj.put("importedModels", importedModels); + + model.addAttribute("result", resObj.toString()); + return "result"; + } + + /** + * 显示流程编辑器页面(整合的编辑页面) + */ + @RequestMapping("/showFlowEditor.do") + public String showFlowEditor(HttpServletRequest request, Model model) { + String modelId = request.getParameter("modelId"); + if (modelId != null && !modelId.isEmpty()) { + org.activiti.engine.repository.Model modelData = repositoryService.getModel(modelId); + model.addAttribute("modelData", modelData); + } + return "/activiti/flowEditor"; + } } diff --git a/src/main/java/com/sipai/service/activiti/BpmnImportService.java b/src/main/java/com/sipai/service/activiti/BpmnImportService.java new file mode 100644 index 00000000..ac91eef2 --- /dev/null +++ b/src/main/java/com/sipai/service/activiti/BpmnImportService.java @@ -0,0 +1,367 @@ +package com.sipai.service.activiti; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.activiti.bpmn.converter.BpmnXMLConverter; +import org.activiti.bpmn.model.BpmnModel; +import org.activiti.editor.constants.ModelDataJsonConstants; +import org.activiti.editor.language.json.converter.BpmnJsonConverter; +import org.activiti.engine.RepositoryService; +import org.activiti.engine.repository.Deployment; +import org.activiti.engine.repository.Model; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.stereotype.Service; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * BPMN文件导入服务 + * 用于导入现有的BPMN文件到Activiti模型编辑器 + * + * @author system + */ +@Service +public class BpmnImportService { + + protected Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + protected RepositoryService repositoryService; + + // BPMN文件存储的基础路径(文件系统路径) + private static final String DIAGRAMS_BASE_PATH = "src/main/resources/diagrams"; + + /** + * 获取所有BPMN文件列表 + */ + public List> listBpmnFiles() { + List> result = new ArrayList<>(); + + // 首先尝试从文件系统读取 + File diagramsDir = new File(DIAGRAMS_BASE_PATH); + if (diagramsDir.exists() && diagramsDir.isDirectory()) { + listBpmnFilesFromFileSystem(diagramsDir, "", result); + if (!result.isEmpty()) { + return result; + } + } + + // 如果文件系统路径不存在,尝试从classpath读取 + try { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource[] resources = resolver.getResources("classpath:diagrams/**/*.bpmn"); + + for (Resource resource : resources) { + Map fileInfo = new HashMap<>(); + String filename = resource.getFilename(); + String path = resource.getURL().getPath(); + + fileInfo.put("filename", filename); + fileInfo.put("path", path); + fileInfo.put("size", resource.contentLength()); + fileInfo.put("lastModified", System.currentTimeMillis()); + + // 尝试解析BPMN获取流程名称 + try (InputStream is = resource.getInputStream()) { + BpmnModel model = parseBpmnFromInputStream(is); + if (model != null && model.getMainProcess() != null) { + fileInfo.put("processId", model.getMainProcess().getId()); + fileInfo.put("processName", model.getMainProcess().getName()); + } + } catch (Exception e) { + logger.warn("Failed to parse BPMN file: {}", filename); + } + + result.add(fileInfo); + } + } catch (Exception e) { + logger.warn("Could not load BPMN files from classpath, trying file system: {}", e.getMessage()); + } + + return result; + } + + /** + * 从文件系统递归获取BPMN文件列表 + */ + private void listBpmnFilesFromFileSystem(File dir, String relativePath, List> result) { + File[] files = dir.listFiles(); + if (files == null) return; + + for (File file : files) { + if (file.isDirectory()) { + listBpmnFilesFromFileSystem(file, relativePath + file.getName() + "/", result); + } else if (file.getName().endsWith(".bpmn")) { + Map fileInfo = new HashMap<>(); + fileInfo.put("filename", file.getName()); + fileInfo.put("path", relativePath + file.getName()); + fileInfo.put("size", file.length()); + fileInfo.put("lastModified", file.lastModified()); + + // 尝试解析BPMN获取流程名称 + try (InputStream is = new FileInputStream(file)) { + BpmnModel model = parseBpmnFromInputStream(is); + if (model != null && model.getMainProcess() != null) { + fileInfo.put("processId", model.getMainProcess().getId()); + fileInfo.put("processName", model.getMainProcess().getName()); + } + } catch (Exception e) { + logger.warn("Failed to parse BPMN file: {}", file.getName()); + } + + result.add(fileInfo); + } + } + } + + /** + * 从classpath路径导入BPMN文件创建模型 + * + * @param bpmnFilePath BPMN文件路径(相对于diagrams目录) + * @param modelName 模型名称 + * @param description 模型描述 + * @return 模型ID + */ + public String importBpmnFile(String bpmnFilePath, String modelName, String description) { + return importBpmnFile(bpmnFilePath, modelName, description, null); + } + + /** + * 从classpath路径导入BPMN文件创建模型(带自定义Key) + * + * @param bpmnFilePath BPMN文件路径(相对于diagrams目录) + * @param modelName 模型名称 + * @param description 模型描述 + * @param modelKey 模型Key(格式:processTypeId-companyId-maintenanceType) + * @return 模型ID + */ + public String importBpmnFile(String bpmnFilePath, String modelName, String description, String modelKey) { + InputStream is = null; + try { + // 首先尝试从文件系统读取 + File file = new File(DIAGRAMS_BASE_PATH + "/" + bpmnFilePath); + if (file.exists()) { + is = new FileInputStream(file); + } else { + // 如果文件系统不存在,尝试从classpath读取 + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource resource = resolver.getResource("classpath:diagrams/" + bpmnFilePath); + if (resource.exists()) { + is = resource.getInputStream(); + } + } + + if (is == null) { + logger.error("BPMN file not found: {}", bpmnFilePath); + return null; + } + + return importBpmnFromInputStream(is, modelName, description, modelKey); + } catch (Exception e) { + logger.error("Error importing BPMN file: {}", bpmnFilePath, e); + return null; + } finally { + if (is != null) { + try { is.close(); } catch (Exception e) { } + } + } + } + + /** + * 从输入流导入BPMN创建模型 + * + * @param inputStream BPMN文件输入流 + * @param modelName 模型名称 + * @param description 模型描述 + * @return 模型ID + */ + public String importBpmnFromInputStream(InputStream inputStream, String modelName, String description) { + return importBpmnFromInputStream(inputStream, modelName, description, null); + } + + /** + * 从输入流导入BPMN创建模型(带自定义Key) + * + * @param inputStream BPMN文件输入流 + * @param modelName 模型名称 + * @param description 模型描述 + * @param modelKey 模型Key(格式:processTypeId-companyId-maintenanceType) + * @return 模型ID + */ + public String importBpmnFromInputStream(InputStream inputStream, String modelName, String description, String modelKey) { + try { + // 解析BPMN XML + BpmnModel bpmnModel = parseBpmnFromInputStream(inputStream); + if (bpmnModel == null) { + logger.error("Failed to parse BPMN model"); + return null; + } + + // 获取流程信息 + String processId = bpmnModel.getMainProcess().getId(); + String processName = bpmnModel.getMainProcess().getName(); + if (modelName == null || modelName.isEmpty()) { + modelName = processName != null ? processName : processId; + } + + // 转换为JSON格式 + BpmnJsonConverter jsonConverter = new BpmnJsonConverter(); + ObjectNode modelNode = jsonConverter.convertToJson(bpmnModel); + + // 创建模型 + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode editorNode = objectMapper.createObjectNode(); + editorNode.put("id", "canvas"); + editorNode.put("resourceId", "canvas"); + + ObjectNode stencilSetNode = objectMapper.createObjectNode(); + stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#"); + editorNode.put("stencilset", stencilSetNode); + + // 合并转换后的模型数据 + editorNode.setAll(modelNode); + + // 创建模型元数据 + Model modelData = repositoryService.newModel(); + ObjectNode modelObjectNode = objectMapper.createObjectNode(); + modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, modelName); + modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1); + modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, + description != null ? description : ""); + modelData.setMetaInfo(modelObjectNode.toString()); + modelData.setName(modelName); + // 如果提供了自定义Key则使用,否则使用processId + modelData.setKey(modelKey != null && !modelKey.isEmpty() ? modelKey : processId); + + // 保存模型 + repositoryService.saveModel(modelData); + repositoryService.addModelEditorSource(modelData.getId(), + editorNode.toString().getBytes("UTF-8")); + + logger.info("Successfully imported BPMN model: {} with ID: {} and Key: {}", modelName, modelData.getId(), modelData.getKey()); + return modelData.getId(); + + } catch (Exception e) { + logger.error("Error importing BPMN from input stream", e); + return null; + } + } + + /** + * 直接部署BPMN文件 + * + * @param bpmnFilePath BPMN文件路径 + * @param deploymentName 部署名称 + * @return 部署ID + */ + public String deployBpmnFile(String bpmnFilePath, String deploymentName) { + try { + byte[] bpmnBytes = null; + String filename = bpmnFilePath; + + // 首先尝试从文件系统读取 + File file = new File(DIAGRAMS_BASE_PATH + "/" + bpmnFilePath); + if (file.exists()) { + try (InputStream is = new FileInputStream(file)) { + bpmnBytes = IOUtils.toByteArray(is); + filename = file.getName(); + } + } else { + // 如果文件系统不存在,尝试从classpath读取 + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource resource = resolver.getResource("classpath:diagrams/" + bpmnFilePath); + if (resource.exists()) { + try (InputStream is = resource.getInputStream()) { + bpmnBytes = IOUtils.toByteArray(is); + filename = resource.getFilename(); + } + } + } + + if (bpmnBytes == null) { + logger.error("BPMN file not found: {}", bpmnFilePath); + return null; + } + + if (deploymentName == null || deploymentName.isEmpty()) { + deploymentName = filename; + if (deploymentName.endsWith(".bpmn")) { + deploymentName = deploymentName.substring(0, deploymentName.length() - 5); + } + } + + String processName = deploymentName + ".bpmn20.xml"; + Deployment deployment = repositoryService.createDeployment() + .name(deploymentName) + .addString(processName, new String(bpmnBytes, "UTF-8")) + .deploy(); + + logger.info("Successfully deployed BPMN: {} with deployment ID: {}", + deploymentName, deployment.getId()); + return deployment.getId(); + } catch (Exception e) { + logger.error("Error deploying BPMN file: {}", bpmnFilePath, e); + return null; + } + } + + /** + * 解析BPMN XML文件 + */ + private BpmnModel parseBpmnFromInputStream(InputStream inputStream) { + try { + XMLInputFactory xif = XMLInputFactory.newInstance(); + InputStreamReader in = new InputStreamReader(inputStream, "UTF-8"); + XMLStreamReader xtr = xif.createXMLStreamReader(in); + + BpmnXMLConverter converter = new BpmnXMLConverter(); + return converter.convertToBpmnModel(xtr); + } catch (Exception e) { + logger.error("Error parsing BPMN", e); + return null; + } + } + + /** + * 获取BPMN文件内容 + */ + public String getBpmnFileContent(String bpmnFilePath) { + try { + // 首先尝试从文件系统读取 + File file = new File(DIAGRAMS_BASE_PATH + "/" + bpmnFilePath); + if (file.exists()) { + try (InputStream is = new FileInputStream(file)) { + return IOUtils.toString(is, "UTF-8"); + } + } + + // 如果文件系统不存在,尝试从classpath读取 + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource resource = resolver.getResource("classpath:diagrams/" + bpmnFilePath); + if (resource.exists()) { + try (InputStream is = resource.getInputStream()) { + return IOUtils.toString(is, "UTF-8"); + } + } + + return null; + } catch (Exception e) { + logger.error("Error reading BPMN file: {}", bpmnFilePath, e); + return null; + } + } +} diff --git a/src/main/webapp/jsp/activiti/bpmnFileList.jsp b/src/main/webapp/jsp/activiti/bpmnFileList.jsp new file mode 100644 index 00000000..353d7c49 --- /dev/null +++ b/src/main/webapp/jsp/activiti/bpmnFileList.jsp @@ -0,0 +1,311 @@ +<%@ page language="java" pageEncoding="UTF-8"%> +<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<%@ page import="com.sipai.entity.base.ServerObject"%> +<%@ page import="com.sipai.entity.user.User"%> +<%@ page import="com.sipai.entity.user.Company"%> +<%@ page import="com.sipai.service.user.UnitService"%> +<%@ page import="org.springframework.web.context.WebApplicationContext"%> +<%@ page import="org.springframework.web.context.support.WebApplicationContextUtils"%> +<%@ page import="java.util.List" %> +<%@ taglib uri="http://www.springsecurity.org/jsp" prefix="security"%> +<% +String contextPath = request.getContextPath(); +User cu = (User)request.getSession().getAttribute("cu"); +WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext()); +UnitService unitService = (UnitService)ctx.getBean("unitService"); +List companies = null; +if (cu != null) { + companies = unitService.getCompaniesByUserId(cu.getId()); +} +%> + + + +<%= ServerObject.atttable.get("TOPTITLE")%> - BPMN流程文件管理 + + + + +
+
+
+
+ + + +
+
+ + + + + 模型列表 + +
+
+ BPMN文件目录: src/main/resources/diagrams/ +
+
+ + + + +
+
+
+
+ + diff --git a/src/main/webapp/jsp/activiti/modelList.jsp b/src/main/webapp/jsp/activiti/modelList.jsp index c22daacf..bee235fe 100644 --- a/src/main/webapp/jsp/activiti/modelList.jsp +++ b/src/main/webapp/jsp/activiti/modelList.jsp @@ -72,16 +72,19 @@ var companyId = "${param.unitId}"; alert(); // 执行一些动作... }); */ - /* var editFlowFun = function(id) { - var w = window.screen.width*0.7; - var h = window.screen.height*0.7; - var l = window.screen.width*(1-0.7)/2; - window.open(ext.contextPath + '/modeler.html?modelId=' + id,'_black', + /* 编辑流程图 - 打开Activiti Modeler */ + var editFlowFun = function(id) { + var w = window.screen.width*0.8; + var h = window.screen.height*0.8; + var l = window.screen.width*(1-0.8)/2; + var t = window.screen.height*(1-0.8)/2; + window.open(ext.contextPath + '/modeler.html?modelId=' + id,'_blank', "left="+ l - + ",top=50,scrollbars=yes,location=no,status=no,toolbar=no,resizable=yes" + + ",top="+ t + + ",scrollbars=yes,location=no,status=no,toolbar=no,resizable=yes" + ",width="+w + ",height="+h); - }; */ + }; var copyFun = function(id) { swal({ text: "您确定要为该模型生成副本?", @@ -287,11 +290,12 @@ var companyId = "${param.unitId}"; title: "操作", align: 'center', valign: 'middle', - width: 240, // 定义列的宽度,单位为像素px + width: 320, // 定义列的宽度,单位为像素px formatter: function (value, row, index) { var str = ''; str += ''; str += ''; + str += ''; str += ''; str += ''; str += ''; @@ -413,10 +417,13 @@ var companyId = "${param.unitId}"; -->
-
+