diff --git a/laws-base/laws-base-core/src/main/java/com/jero/config/shiro/ShiroConfig.java b/laws-base/laws-base-core/src/main/java/com/jero/config/shiro/ShiroConfig.java index a14bc2eb..c4d1c928 100644 --- a/laws-base/laws-base-core/src/main/java/com/jero/config/shiro/ShiroConfig.java +++ b/laws-base/laws-base-core/src/main/java/com/jero/config/shiro/ShiroConfig.java @@ -123,6 +123,10 @@ public class ShiroConfig { filterChainDefinitionMap.put("/test/bigScreen/**", "anon"); //大屏模板例子 //activti filterChainDefinitionMap.put("/modeler/**", "anon"); + filterChainDefinitionMap.put("/activitiModel/**", "anon"); + filterChainDefinitionMap.put("/editor-app/**", "anon"); + filterChainDefinitionMap.put("/service/**", "anon"); + filterChainDefinitionMap.put("/repository/**", "anon"); filterChainDefinitionMap.put("/runtime/**", "anon"); filterChainDefinitionMap.put("/management/**", "anon"); diff --git a/laws-modules/src/main/java/com/jero/modules/activiti/controller/AController.java b/laws-modules/src/main/java/com/jero/modules/activiti/controller/AController.java index bac11c2a..fe68f468 100644 --- a/laws-modules/src/main/java/com/jero/modules/activiti/controller/AController.java +++ b/laws-modules/src/main/java/com/jero/modules/activiti/controller/AController.java @@ -2,6 +2,9 @@ package com.jero.modules.activiti.controller; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.jero.common.api.vo.Result; import com.jero.common.constant.enums.LanguageEnum; import com.jero.common.util.MessageUtils; @@ -12,13 +15,17 @@ import com.jero.modules.activiti.service.LawsProcessNodeService; import com.jero.modules.activiti.service.ProcessService; import com.jero.modules.activiti.vo.AddTaskVO; import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.extern.slf4j.Slf4j; 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.*; import org.activiti.engine.repository.Deployment; +import org.activiti.engine.repository.Model; import org.activiti.engine.repository.ProcessDefinition; import org.activiti.engine.repository.ProcessDefinitionQuery; import org.activiti.engine.runtime.ProcessInstance; @@ -34,8 +41,12 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamReader; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.*; @@ -183,6 +194,17 @@ public class AController { public Result complete(@RequestBody Map map) { //todo 判断当前节点,执行不同的操作 String taskId = map.get("taskId").toString(); + + Task task=taskService.createTaskQuery() // 创建任务查询 + .taskId(taskId) // 根据任务id查询 + .singleResult(); + String taskName = task.getName(); + + if ("填写基本信息,在线编辑,分发其他起草人".equals(taskName)){ + // todo + }else if ("其他起草人线下填写,上传".equals(taskName)){ + // todo + } //Map vars = new HashMap<>(); //List otherDrafterList = new ArrayList<>(); //otherDrafterList.add("zhangsan"); diff --git a/laws-modules/src/main/java/com/jero/modules/activiti/controller/ActivitiModelerController.java b/laws-modules/src/main/java/com/jero/modules/activiti/controller/ActivitiModelerController.java new file mode 100644 index 00000000..d109fbdc --- /dev/null +++ b/laws-modules/src/main/java/com/jero/modules/activiti/controller/ActivitiModelerController.java @@ -0,0 +1,259 @@ +package com.jero.modules.activiti.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.jero.common.api.vo.Result; +import com.jero.modules.activiti.service.LawsProcessNodeOriginService; +import com.jero.modules.activiti.util.CreatedTools; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +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.*; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cglib.beans.BeanMap; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author liJiaRao + * @date 2023-10-07 9:51 + */ +@Slf4j +@RestController +@Api(tags = "工作流模板管理") +@RequestMapping("/activitiModel") +public class ActivitiModelerController { + + @Autowired + private RepositoryService repositoryService; + @Autowired + private ObjectMapper objectMapper; + @Resource + private LawsProcessNodeOriginService processNodeOriginService; + //@Autowired + //private IFormVariableService iFormVariableService; + //@Autowired + //private ICategoryService iCategoryService; + + @ApiOperation(value = "导入model文件") + @ApiImplicitParams({ + @ApiImplicitParam(name = "name", value = "模型名称") + }) + @PostMapping("/importModelFile") + public void importModelFile(@RequestPart(value = "file", required = false) MultipartFile file, + @RequestParam(value = "name", required = false) String name) throws Exception { + InputStream fileInputStream = file.getInputStream(); + XMLInputFactory xif = XMLInputFactory.newInstance(); + InputStreamReader in = new InputStreamReader(fileInputStream, "UTF-8"); + XMLStreamReader xtr = xif.createXMLStreamReader(in); + // 转为bpmnModel + BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr); + Model model = repositoryService.newModel(); + String modelKey = "m_" + CreatedTools.getCreated(); + ObjectNode modelNode = objectMapper.createObjectNode(); + modelNode.put(ModelDataJsonConstants.MODEL_NAME, modelKey); + modelNode.put(ModelDataJsonConstants.MODEL_REVISION, 1); + model.setName(name); + model.setKey(modelKey); + model.setMetaInfo(modelNode.toString()); + repositoryService.saveModel(model); + ObjectNode objectNode = new BpmnJsonConverter().convertToJson(bpmnModel); + repositoryService.addModelEditorSource(model.getId(), objectNode.toString().getBytes("utf-8")); + } + + @ApiOperation(value = "新建模板") + @ApiImplicitParam(name = "category_id", value = "类型id") + @SuppressWarnings("deprecation") + @GetMapping("/create") + public void newModel(HttpServletRequest request, HttpServletResponse response, String category_id) + throws IOException { + // 初始化一个空模型 + Model model = repositoryService.newModel(); + + // 设置一些默认信息 + String name = ""; + String description = ""; + int revision = 1; + String key = "m_" + CreatedTools.getCreated(); + // 设置子系统 + //model.setTenantId("子系统1"); + //model.setCategory(category_id); + + ObjectNode modelNode = objectMapper.createObjectNode(); + modelNode.put(ModelDataJsonConstants.MODEL_NAME, name); + modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description); + modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision); + + model.setName(name); + model.setKey(key); + model.setMetaInfo(modelNode.toString()); + + repositoryService.saveModel(model); + String id = model.getId(); + + // 完善ModelEditorSource + 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); + repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8")); + response.sendRedirect(request.getContextPath() + "/modeler.html?modelId=" + id); + } + + @ApiOperation(value = "模板列表") + @ApiImplicitParams({ @ApiImplicitParam(name = "name", value = "名称"), + @ApiImplicitParam(name = "category_id", value = "类型id") }) + @GetMapping("/modelList") + public Result> modelList(String name, String category_id) { + //String sql = "select distinct RES.* from ACT_RE_MODEL RES WHERE RES.TENANT_ID_ = '子系统1' and RES.NAME_<>'' and RES.CATEGORY_=#{category} order by RES.CREATE_TIME_ desc"; + String sql = "select distinct RES.* from ACT_RE_MODEL RES WHERE RES.NAME_<>'' order by RES.CREATE_TIME_ desc"; + NativeModelQuery nativeModelQuery = repositoryService.createNativeModelQuery(); + //if (StringUtils.isNull(category_id)) { + // sql = sql.replace("and RES.CATEGORY_=#{category}", ""); + //}else { + // nativeModelQuery.parameter("category", category_id); + //} + nativeModelQuery.sql(sql); + List list = nativeModelQuery.list(); + return Result.OK(list); + } + + @ApiOperation(value = "模板列表-分页") + @ApiImplicitParams({ @ApiImplicitParam(name = "name", value = "名称"), + @ApiImplicitParam(name = "current", value = "当前页"), + @ApiImplicitParam(name = "size", value = "每页条数") }) + @GetMapping("/modelListPage") + public Result> modelListPage(String name, int current, int size) { + if (StringUtils.isBlank(name)) { + name = ""; + } else { + current = 1; + } + ModelQuery modelQuery = repositoryService.createModelQuery(); + //if (StringUtils.isNotNull(category_id)) { + // modelQuery.modelCategory(category_id); + //} + //modelQuery.modelNameLike("%" + name + "%").modelTenantId("子系统1"); + long total = modelQuery.count(); + List list = modelQuery.orderByCreateTime().desc().listPage((current - 1) * size, size); + List> resultList = new ArrayList<>(); + Map map = new HashMap<>(); + for(Model model:list){ + Map resultMap = beanToMap(model); + //if(resultMap.get("category")!=null){ + // Category category = iCategoryService.getById(resultMap.get("category").toString()); + // resultMap.put("categoryName",category.getType()); + //} + resultList.add(resultMap); + } + map.put("records", resultList); + map.put("current", current); + map.put("size", size); + map.put("total", total); + return Result.OK(map); + } + + public static Map beanToMap(T bean) { + Map map = new HashMap<>(); + if (bean != null) { + BeanMap beanMap = BeanMap.create(bean); + for (Object key : beanMap.keySet()) { + map.put(key+"", beanMap.get(key)); + } + } + return map; + } + + @ApiOperation(value = "删除模板") + @ApiImplicitParam(name = "modelId", value = "模型id") + @GetMapping("/deleteModel") + public Result deleteModel(String modelId) { + repositoryService.deleteModel(modelId); + return Result.OK("删除成功"); + } + + @ApiOperation(value = "修改流程所属分类") + @ApiImplicitParam(name = "modelId", value = "模型id") + @GetMapping("/updatModel") + public Result updatModel(String modelId,String categoryId) { + Model modelData = repositoryService.getModel(modelId); + modelData.setCategory(categoryId); + repositoryService.saveModel(modelData); + return Result.OK("修改成功"); + } + + @ApiOperation(value = "部署") + @ApiImplicitParam(name = "modelId", value = "模型id") + @GetMapping("/deploy") + public Result> deploy(String modelId) throws Exception { + + // 获取模型 + Model modelData = repositoryService.getModel(modelId); + byte[] bytes = repositoryService.getModelEditorSource(modelData.getId()); + + if (bytes == null) { + return Result.OK("模型数据为空,请先设计流程并成功保存,再进行发布。"); + } + + // 生成/更新 模板变量表 + //iFormVariableService.createAndUpdateFormVariable(bytes, modelId); + + JsonNode modelNode = new ObjectMapper().readTree(bytes); + BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode); + model.setTargetNamespace(modelData.getCategory()); + + if (model.getProcesses().size() == 0) { + return Result.OK("数据模型不符要求,请至少设计一条主线流程。"); + } + byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model); + + // 发布流程 + String processName = modelData.getName() + ".bpmn20.xml"; + Deployment deployment = repositoryService.createDeployment() + .name(modelData.getName()) + .category(modelData.getCategory()) + .addString(processName, new String(bpmnBytes, "UTF-8")) + .deploy(); + modelData.setDeploymentId(deployment.getId()); + repositoryService.saveModel(modelData); + + //创建一个流程定义查询 + ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery(); + processDefinitionQuery.deploymentId(deployment.getId()); + ProcessDefinition processDefinition = processDefinitionQuery.singleResult(); + String processDefinitionId = processDefinition.getId(); + processNodeOriginService.saveNodeList(processDefinitionId); +// 4、输出部署信息 + Map map = new HashMap<>(); + map.put("流程部署id:", deployment.getId()); + map.put("流程部署名称:", deployment.getName()); + map.put("流程部署key:", deployment.getKey()); + map.put("流程定义Id:", processDefinitionId); + return Result.OK(map); + } + +} diff --git a/laws-modules/src/main/java/com/jero/modules/activiti/editor/ModelEditorController.java b/laws-modules/src/main/java/com/jero/modules/activiti/editor/ModelEditorController.java new file mode 100644 index 00000000..daef4608 --- /dev/null +++ b/laws-modules/src/main/java/com/jero/modules/activiti/editor/ModelEditorController.java @@ -0,0 +1,142 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jero.modules.activiti.editor; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.activiti.editor.constants.ModelDataJsonConstants; +import org.activiti.engine.ActivitiException; +import org.activiti.engine.RepositoryService; +import org.activiti.engine.repository.Model; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.PNGTranscoder; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; + +/** + * @author Tijs Rademakers + */ +@Slf4j +@Api("工作流编辑器") +@RestController +@RequestMapping(value = "/service") +public class ModelEditorController implements ModelDataJsonConstants { + + @Autowired + private RepositoryService repositoryService; + + @Autowired + private ObjectMapper objectMapper; + + @ApiOperation(value = "获取模型json数据") + @ApiImplicitParam(name = "modelId", value = "模型id") + @SuppressWarnings("deprecation") + @GetMapping(value = "/model/{modelId}/json", produces = "application/json") + public ObjectNode getEditorJson(@PathVariable String modelId) { + ObjectNode modelNode = null; + + Model model = repositoryService.getModel(modelId); + + if (model != null) { + try { + if (StringUtils.isNotEmpty(model.getMetaInfo())) { + modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo()); + } else { + modelNode = objectMapper.createObjectNode(); + modelNode.put(MODEL_NAME, model.getName()); + } + modelNode.put(MODEL_ID, model.getId()); + byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId()); + String s = new String(modelEditorSource, "utf-8"); + ObjectNode editorJsonNode = (ObjectNode) objectMapper + .readTree(s); + modelNode.put("model", editorJsonNode); + + } catch (Exception e) { + log.error("Error creating model JSON", e); + throw new ActivitiException("Error creating model JSON", e); + } + } + return modelNode; + } + + @ApiOperation(value = "模型保存") + @ApiImplicitParams({ @ApiImplicitParam(name = "modelId", value = "模型id"), + @ApiImplicitParam(name = "name", value = "名称"), + @ApiImplicitParam(name = "json_xml", value = "流程图xml"), + @ApiImplicitParam(name = "svg_xml", value = "流程配置xml"), + @ApiImplicitParam(name = "description", value = "描述") }) + @PutMapping(value = "/model/{modelId}/save") + @ResponseStatus(value = HttpStatus.OK) + public void saveModel(@PathVariable String modelId, @RequestParam("name") String name, + @RequestParam("json_xml") String json_xml, @RequestParam("svg_xml") String svg_xml, + @RequestParam("description") String description) { + try { + + Model model = repositoryService.getModel(modelId); + + ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo()); + + modelJson.put(MODEL_NAME, name); + modelJson.put(MODEL_DESCRIPTION, description); + model.setMetaInfo(modelJson.toString()); + model.setName(name); + + repositoryService.saveModel(model); + + repositoryService.addModelEditorSource(model.getId(), json_xml.getBytes("utf-8")); + + InputStream svgStream = new ByteArrayInputStream(svg_xml.getBytes("utf-8")); + TranscoderInput input = new TranscoderInput(svgStream); + + PNGTranscoder transcoder = new PNGTranscoder(); + // Setup output + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + TranscoderOutput output = new TranscoderOutput(outStream); + + // Do the transformation + transcoder.transcode(input, output); + final byte[] result = outStream.toByteArray(); + repositoryService.addModelEditorSourceExtra(model.getId(), result); + outStream.close(); + + } catch (Exception e) { + log.error("Error saving model", e); + throw new ActivitiException("Error saving model", e); + } + } + + @ApiOperation(value = "获取模型stencilset的数据") + @GetMapping(value = "/editor/stencilset", produces = "application/json;charset=utf-8") + public @ResponseBody String getStencilset() { + InputStream stencilsetStream = this.getClass().getClassLoader().getResourceAsStream("stencilset.json"); + try { + return IOUtils.toString(stencilsetStream, "utf-8"); + } catch (Exception e) { + throw new ActivitiException("Error while loading stencil set", e); + } + } +} diff --git a/laws-modules/src/main/java/com/jero/modules/activiti/util/CreatedTools.java b/laws-modules/src/main/java/com/jero/modules/activiti/util/CreatedTools.java new file mode 100644 index 00000000..f82b286f --- /dev/null +++ b/laws-modules/src/main/java/com/jero/modules/activiti/util/CreatedTools.java @@ -0,0 +1,30 @@ +package com.jero.modules.activiti.util; + +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class CreatedTools { + + private final static DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); + + // 返回long型的创建时间 + public static long getCreated() { + Date d = new Date(); + long l = Long.parseLong(df.format(d)); + return l; + } + + public static long getCreated(int add) { + long t = System.currentTimeMillis() + add; + Timestamp time = new Timestamp(t); + long l = Long.parseLong(df.format(time)); + return l; + } + + public static long t() { + return 0l; + } + +}