目录

  • 一、后端
    • 1、新建子工程 pom.xml引入
    • 2、配置类
      • 2.1 数据库连接
      • 2.2 权限配置
      • 2.3 这个主要是用来覆盖flowable获取当前用户的逻辑
    • 3、主工程
      • 3.1 重写类 SecurityUtils
      • 3.2 新增发布流程的逻辑
      • 3.2 配置文件
  • 二、前端
    • 1、去官网下载包,然后把flowable-modeler的static复制到前端工程public目录下
    • 2、修改配置
      • 2.1 修改 app-cfg.js
      • 2.2 修改public/flowable/scripts/common/providers-config.js angular请求拦截器,放入jeecg的token
    • 3、vue组件引入

一、后端

1、新建子工程 pom.xml引入


<properties><flowable.version>6.5.0</flowable.version>
</properties><dependencies><dependency><groupId>org.jeecgframework.boot</groupId><artifactId>jeecg-boot-base-common</artifactId></dependency><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>${flowable.version}</version></dependency><dependency><groupId>org.flowable</groupId><artifactId>flowable-ui-modeler-rest</artifactId><version>${flowable.version}</version><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.flowable</groupId><artifactId>flowable-ui-modeler-conf</artifactId><version>${flowable.version}</version></dependency><dependency><groupId>org.flowable</groupId><artifactId>flowable-rest</artifactId><version>${flowable.version}</version></dependency><dependency><groupId>org.flowable</groupId><artifactId>flowable-ui-task-conf</artifactId><version>${flowable.version}</version></dependency><dependency><groupId>org.flowable</groupId><artifactId>flowable-ui-admin-conf</artifactId><version>${flowable.version}</version></dependency><dependency><groupId>org.flowable</groupId><artifactId>flowable-ui-idm-conf</artifactId><version>${flowable.version}</version></dependency></dependencies>

2、配置类

2.1 数据库连接

package org.jeecg.modules.oa.flowable;import javax.sql.DataSource;
import org.flowable.rest.service.api.RestResponseFactory;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.flowable.spring.boot.FlowableSecurityAutoConfiguration;
import org.flowable.ui.common.service.exception.InternalServerErrorException;
import org.flowable.ui.common.service.idm.RemoteIdmService;
import org.flowable.ui.modeler.properties.FlowableModelerAppProperties;
import org.flowable.ui.modeler.rest.app.EditorGroupsResource;
import org.flowable.ui.modeler.rest.app.EditorUsersResource;
import org.flowable.ui.modeler.rest.app.ModelResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;import com.fasterxml.jackson.databind.ObjectMapper;import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.DatabaseException;
import liquibase.resource.ClassLoaderResourceAccessor;@Configuration
@EnableConfigurationProperties({ FlowableModelerAppProperties.class })
@ComponentScan(basePackages = { "org.flowable.ui.modeler.repository", "org.flowable.ui.modeler.service","org.flowable.ui.common.service", "org.flowable.ui.common.repository", "org.flowable.ui.common.tenant","org.flowable.ui.modeler.rest.app", "org.flowable.rest.service.api","org.flowable.ui.task.service.debugger" }, excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = RemoteIdmService.class),@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ModelResource.class),@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = EditorUsersResource.class),@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = EditorGroupsResource.class),@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = FlowableSecurityAutoConfiguration.class) })
public class FlowableConfiguration implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {public static final Logger log = LoggerFactory.getLogger(FlowableConfiguration.class);@Value("${spring.datasource.dynamic.datasource.master.url}")private String jdbcUrl;@Value("${spring.datasource.dynamic.datasource.master.driver-class-name}")private String jdbcDriverClassName;@Value("${spring.datasource.dynamic.datasource.master.username}")private String username;@Value("${spring.datasource.dynamic.datasource.master.password}")private String password;@Overridepublic void configure(SpringProcessEngineConfiguration engineConfiguration) {engineConfiguration.setJdbcUrl(jdbcUrl);engineConfiguration.setJdbcDriver(jdbcDriverClassName);engineConfiguration.setJdbcUsername(username);engineConfiguration.setJdbcPassword(password);engineConfiguration.setActivityFontName("\u5B8B\u4F53");engineConfiguration.setLabelFontName("\u5B8B\u4F53");}@Autowiredprotected ObjectMapper objectMapper;@ConditionalOnMissingBean@Beanpublic RestResponseFactory restResponseFactory() {return new RestResponseFactory(objectMapper);}@Beanpublic Liquibase liquibase(DataSource dataSource) {log.info("Configuring Liquibase");Liquibase liquibase = null;try {DatabaseConnection connection = new JdbcConnection(dataSource.getConnection());Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection);database.setDatabaseChangeLogTableName("ACT_DE_" + database.getDatabaseChangeLogTableName());database.setDatabaseChangeLogLockTableName("ACT_DE_" + database.getDatabaseChangeLogLockTableName());liquibase = new Liquibase("META-INF/liquibase/flowable-modeler-app-db-changelog.xml",new ClassLoaderResourceAccessor(), database);liquibase.update("flowable");return liquibase;} catch (Exception e) {throw new InternalServerErrorException("Error creating liquibase database", e);} finally {closeDatabase(liquibase);}}private void closeDatabase(Liquibase liquibase) {if (liquibase != null) {Database database = liquibase.getDatabase();if (database != null) {try {database.close();} catch (DatabaseException e) {log.warn("Error closing database", e);}}}}}

2.2 权限配置

package org.jeecg.modules.oa.flowable;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.firewall.DefaultHttpFirewall;
import org.springframework.security.web.firewall.HttpFirewall;@Configuration
@EnableWebSecurity
public class FlowbleSecurityConfiguration extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/**").permitAll().anyRequest().authenticated().and().httpBasic();}@Beanpublic HttpFirewall allowUrlEncodedSlashHttpFirewall() {return new DefaultHttpFirewall();}}

2.3 这个主要是用来覆盖flowable获取当前用户的逻辑

这个设置比较重要,不设置的话,发起流程的start_user_id将会是anonymousUser。

package org.jeecg.modules.oa.flowable;import java.security.Principal;import org.flowable.common.engine.api.identity.AuthenticationContext;
import org.flowable.ui.common.security.SecurityUtils;/*** Default implementation of the {@link AuthenticationContext} that uses a {@link ThreadLocal} that stores the {@link Principal}** @author Filip Hrisafov*/
public class MyAuthenticationContext implements AuthenticationContext {@Overridepublic String getAuthenticatedUserId() {return SecurityUtils.getCurrentUserId();}@Overridepublic Principal getPrincipal() {return null;}@Overridepublic void setPrincipal(Principal principal) {}
}
package org.jeecg.modules.oa.flowable;import org.flowable.common.engine.impl.identity.Authentication;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;/*** 启动成功后的处理* @author 76920**/
@Component
public class FlowableStartedListener implements ApplicationListener<ContextRefreshedEvent>{@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {Authentication.setAuthenticationContext(new MyAuthenticationContext());}}

3、主工程

3.1 重写类 SecurityUtils

这个是放在主工程里面的


package org.flowable.ui.common.security;import java.util.ArrayList;import org.flowable.idm.api.User;
import org.flowable.ui.common.model.RemoteUser;
import org.jeecg.common.system.vo.LoginUser;/*** Utility class for Spring Security.*/
public class SecurityUtils {private static User assumeUser;private SecurityUtils() {}/*** Get the login of the current user.*/public static String getCurrentUserId() {LoginUser sysUser = (LoginUser)org.apache.shiro.SecurityUtils.getSubject().getPrincipal();if (sysUser != null) {return sysUser.getId();}return null;}/*** @return the {@link User} object associated with the current logged in user.*/public static User getCurrentUserObject() {if (assumeUser != null) {return assumeUser;}User user = new RemoteUser();LoginUser sysUser = (LoginUser)org.apache.shiro.SecurityUtils.getSubject().getPrincipal();if (sysUser != null) {user.setId(sysUser.getId());user.setDisplayName(sysUser.getUsername());user.setPassword(sysUser.getPassword());}return user;}public static FlowableAppUser getCurrentFlowableAppUser() {User s = getCurrentUserObject();FlowableAppUser user = new FlowableAppUser(s, s.getId(), new ArrayList<>());return user;}public static boolean currentUserHasCapability(String capability) {return true;}public static void assumeUser(User user) {assumeUser = user;}public static void clearAssumeUser() {assumeUser = null;}}

3.2 新增发布流程的逻辑

package org.jeecg.modules.oa.flowable.controller;/* 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.*/import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.idm.api.User;
import org.flowable.ui.common.security.SecurityUtils;
import org.flowable.ui.common.service.exception.BadRequestException;
import org.flowable.ui.common.service.exception.ConflictingRequestException;
import org.flowable.ui.common.service.exception.InternalServerErrorException;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.model.ModelKeyRepresentation;
import org.flowable.ui.modeler.model.ModelRepresentation;
import org.flowable.ui.modeler.repository.ModelRepository;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.text.ParseException;
import java.util.Date;@RestController
@RequestMapping("/app")
public class MyModelResources {private static final Logger LOGGER = LoggerFactory.getLogger(MyModelResources.class);private static final String RESOLVE_ACTION_OVERWRITE = "overwrite";private static final String RESOLVE_ACTION_SAVE_AS = "saveAs";private static final String RESOLVE_ACTION_NEW_VERSION = "newVersion";@Autowiredprotected ModelService modelService;@Autowiredprotected ModelRepository modelRepository;@Autowiredprotected ObjectMapper objectMapper;@Autowiredprivate RepositoryService repositoryService;protected BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter();protected BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();@PostMapping("publish/{modelId}")public JSONObject publish(@PathVariable("modelId") String modelId) {Model model = modelService.getModel(modelId);String name = model.getName().replaceAll(" ", "_") + ".bpmn20.xml";BpmnModel bpmnModel = modelService.getBpmnModel(model);byte[] xmlBytes = modelService.getBpmnXML(bpmnModel);BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(xmlBytes));DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();deploymentBuilder.addInputStream(name, in);deploymentBuilder.name(model.getName());deploymentBuilder.key(model.getKey());deploymentBuilder.tenantId(model.getTenantId());Deployment deployment = deploymentBuilder.deploy();JSONObject rs = new JSONObject();rs.put("success", true);rs.put("data", deployment.getId());return rs;}/*** GET /rest/models/{modelId} -> Get process model*/@GetMapping(value = "/rest/models/{modelId}", produces = "application/json")public ModelRepresentation getModel(@PathVariable String modelId) {return modelService.getModelRepresentation(modelId);}/*** GET /rest/models/{modelId}/thumbnail -> Get process model thumbnail*/@GetMapping(value = "/rest/models/{modelId}/thumbnail", produces = MediaType.IMAGE_PNG_VALUE)public byte[] getModelThumbnail(@PathVariable String modelId) {Model model = modelService.getModel(modelId);return model.getThumbnail();}/*** PUT /rest/models/{modelId} -> update process model properties*/@PutMapping(value = "/rest/models/{modelId}")public ModelRepresentation updateModel(@PathVariable String modelId, @RequestBody ModelRepresentation updatedModel) {// Get model, write-permission required if not a favorite-updateModel model = modelService.getModel(modelId);ModelKeyRepresentation modelKeyInfo = modelService.validateModelKey(model, model.getModelType(), updatedModel.getKey());if (modelKeyInfo.isKeyAlreadyExists()) {throw new BadRequestException("Model with provided key already exists " + updatedModel.getKey());}try {updatedModel.updateModel(model);if (model.getModelType() != null) {ObjectNode modelNode = (ObjectNode) objectMapper.readTree(model.getModelEditorJson());modelNode.put("name", model.getName());modelNode.put("key", model.getKey());if (Model.MODEL_TYPE_BPMN == model.getModelType()) {ObjectNode propertiesNode = (ObjectNode) modelNode.get("properties");propertiesNode.put("process_id", model.getKey());propertiesNode.put("name", model.getName());if (StringUtils.isNotEmpty(model.getDescription())) {propertiesNode.put("documentation", model.getDescription());}modelNode.set("properties", propertiesNode);}model.setModelEditorJson(modelNode.toString());}modelRepository.save(model);ModelRepresentation result = new ModelRepresentation(model);return result;} catch (Exception e) {throw new BadRequestException("Model cannot be updated: " + modelId);}}/*** DELETE /rest/models/{modelId} -> delete process model or, as a non-owner, remove the share info link for that user specifically*/@ResponseStatus(value = HttpStatus.OK)@DeleteMapping(value = "/rest/models/{modelId}")public void deleteModel(@PathVariable String modelId) {// Get model to check if it exists, read-permission required for deleteModel model = modelService.getModel(modelId);try {modelService.deleteModel(model.getId());} catch (Exception e) {LOGGER.error("Error while deleting: ", e);throw new BadRequestException("Model cannot be deleted: " + modelId);}}/*** GET /rest/models/{modelId}/editor/json -> get the JSON model*/@GetMapping(value = "/rest/models/{modelId}/editor/json", produces = "application/json")public ObjectNode getModelJSON(@PathVariable String modelId) {Model model = modelService.getModel(modelId);ObjectNode modelNode = objectMapper.createObjectNode();modelNode.put("modelId", model.getId());modelNode.put("name", model.getName());modelNode.put("key", model.getKey());modelNode.put("description", model.getDescription());modelNode.putPOJO("lastUpdated", model.getLastUpdated());modelNode.put("lastUpdatedBy", model.getLastUpdatedBy());if (StringUtils.isNotEmpty(model.getModelEditorJson())) {try {ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(model.getModelEditorJson());editorJsonNode.put("modelType", "model");modelNode.set("model", editorJsonNode);} catch (Exception e) {LOGGER.error("Error reading editor json {}", modelId, e);throw new InternalServerErrorException("Error reading editor json " + modelId);}} else {ObjectNode editorJsonNode = objectMapper.createObjectNode();editorJsonNode.put("id", "canvas");editorJsonNode.put("resourceId", "canvas");ObjectNode stencilSetNode = objectMapper.createObjectNode();stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");editorJsonNode.put("modelType", "model");modelNode.set("model", editorJsonNode);}return modelNode;}/*** POST /rest/models/{modelId}/editor/json -> save the JSON model*/@PostMapping(value = "/rest/models/{modelId}/editor/json")public ModelRepresentation saveModel(@PathVariable String modelId, @RequestBody MultiValueMap<String, String> values) {// Validation: see if there was another update in the meantimelong lastUpdated = -1L;String lastUpdatedString = values.getFirst("lastUpdated");if (lastUpdatedString == null) {throw new BadRequestException("Missing lastUpdated date");}try {Date readValue = objectMapper.getDeserializationConfig().getDateFormat().parse(lastUpdatedString);lastUpdated = readValue.getTime();} catch (ParseException e) {throw new BadRequestException("Invalid lastUpdated date: '" + lastUpdatedString + "'");}Model model = modelService.getModel(modelId);User currentUser = SecurityUtils.getCurrentUserObject();boolean currentUserIsOwner = model.getLastUpdatedBy().equals(currentUser.getId());String resolveAction = values.getFirst("conflictResolveAction");// If timestamps differ, there is a conflict or a conflict has been resolved by the userif (model.getLastUpdated().getTime() != lastUpdated) {if (RESOLVE_ACTION_SAVE_AS.equals(resolveAction)) {String saveAs = values.getFirst("saveAs");String json = values.getFirst("json_xml");return createNewModel(saveAs, model.getDescription(), model.getModelType(), json);} else if (RESOLVE_ACTION_OVERWRITE.equals(resolveAction)) {return updateModel(model, values, false);} else if (RESOLVE_ACTION_NEW_VERSION.equals(resolveAction)) {return updateModel(model, values, true);} else {// Exception case: the user is the owner and selected to create a new versionString isNewVersionString = values.getFirst("newversion");return updateModel(model, values, true);}} else {// Actual, regular, updatereturn updateModel(model, values, false);}}/*** POST /rest/models/{modelId}/newversion -> create a new model version*/@PostMapping(value = "/rest/models/{modelId}/newversion")public ModelRepresentation importNewVersion(@PathVariable String modelId, @RequestParam("file") MultipartFile file) {InputStream modelStream = null;try {modelStream = file.getInputStream();} catch (Exception e) {throw new BadRequestException("Error reading file inputstream", e);}return modelService.importNewVersion(modelId, file.getOriginalFilename(), modelStream);}protected ModelRepresentation updateModel(Model model, MultiValueMap<String, String> values, boolean forceNewVersion) {String name = values.getFirst("name");String key = values.getFirst("key").replaceAll(" ", "");String description = values.getFirst("description");String isNewVersionString = values.getFirst("newversion");String newVersionComment = null;ModelKeyRepresentation modelKeyInfo = modelService.validateModelKey(model, model.getModelType(), key);if (modelKeyInfo.isKeyAlreadyExists()) {throw new BadRequestException("Model with provided key already exists " + key);}boolean newVersion = false;if (forceNewVersion) {newVersion = true;newVersionComment = values.getFirst("comment");} else {if (isNewVersionString != null) {newVersion = "true".equals(isNewVersionString);newVersionComment = values.getFirst("comment");}}String json = values.getFirst("json_xml");try {ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(json);ObjectNode propertiesNode = (ObjectNode) editorJsonNode.get("properties");String processId = key;propertiesNode.put("process_id", processId);propertiesNode.put("name", name);if (StringUtils.isNotEmpty(description)) {propertiesNode.put("documentation", description);}editorJsonNode.set("properties", propertiesNode);model = modelService.saveModel(model.getId(), name, key, description, editorJsonNode.toString(), newVersion,newVersionComment, SecurityUtils.getCurrentUserObject());return new ModelRepresentation(model);} catch (Exception e) {LOGGER.error("Error saving model {}", model.getId(), e);throw new BadRequestException("Process model could not be saved " + model.getId());}}protected ModelRepresentation createNewModel(String name, String description, Integer modelType, String editorJson) {ModelRepresentation model = new ModelRepresentation();model.setName(name);model.setDescription(description);model.setModelType(modelType);Model newModel = modelService.createModel(model, editorJson, SecurityUtils.getCurrentUserObject());return new ModelRepresentation(newModel);}
}

3.2 配置文件

加了标记部分,其余不变。

mybatis-plus:mapper-locations: classpath*:org/jeecg/modules/**/xml/*Mapper.xml,classpath:/META-INF/modeler-mybatis-mappings/*.xmlconfiguration-properties:blobType: BLOBboolValue: trueprefix: ''

还有excludeUrls放开了一部分url

可以根据自己需要来

/app/rest/process-instances/history/*/model-json,/app/rest/process-instances/*/model-json,/app/rest/process-definitions/*/model-json,/repository/process-definitions/*/image,/app/rest/models/*/*

数据库连接注意加上设置:nullCatalogMeansCurrent=true

url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true

因为flowable启动扫描表用了这个getTables方法

二、前端

1、去官网下载包,然后把flowable-modeler的static复制到前端工程public目录下


2、修改配置

2.1 修改 app-cfg.js


此处/jeecg-boot 与 vue.config.js 的配置对应

2.2 修改public/flowable/scripts/common/providers-config.js angular请求拦截器,放入jeecg的token

request: function(config) {config.headers = config.headers || {}if (localStorage.getItem('pro__Access-Token')) {config.headers['X-Access-Token'] = JSON.parse(localStorage.getItem('pro__Access-Token')).value;};return config || $q.when(config)},

3、vue组件引入

<template><div class="flowable-main"><iframe src="/flowable/index.html" width="100%" height="100%" frameborder="0"></iframe></div>
</template><script>
export default {name: 'Main'
}
</script><style scoped>
.flowable-main {height: 650px
}
</style>

然后菜单配置下就ok了。

JEECG集成flowable相关推荐

  1. jeecg集成实现websocket

    jeecg集成实现websocket在线聊天功能,使用layim作为在线聊天ui框架,java的websocket作为后台服务. 在jeecg各风格的首页引入layui.jsp这个文件为当前风格引入在 ...

  2. springboot集成flowable创建请假流程实例

    springboot如何集成flowable,如何部署flowable在线编辑器画bpm图以及bpm图的画法,我在上一篇博客中写了,这里直接上代码(源码地址:晚安/flowable_holiday ( ...

  3. 【Odoo】集成flowable,解决复杂的审批流

    目录 一.背景 二.本地部署flowable 三.设计流程demo 四.Odoo对接flowable 1.同步流程配置 2.绑定业务模型 五.demo实操 一.背景 Odoo的审批流实际是状态机,通过 ...

  4. 项目集成Flowable工作流

    企业级项目集成Flowable工作流,基于(师父)的开源项目的集成,此开源项目是师傅十几年的经验和实战总结而成,直接可以用于企业开发,旨为帮助更多需要帮助的人! 说明 链接 项目开原地址 https: ...

  5. JEECG 集成KiSSO单点登录实现统一身份认证

    JEECG 集成KiSSO单点登录实现统一身份认证 JEECG 如何为其他第三方系统实现统一身份认证服务,实现单点登录? 第三方系统如何对接呢? 今天为大家揭开这层面纱,让大家了解实质,使用它更快速的 ...

  6. SpringBoot 集成Flowable设计器(Flowable-ui)

    一.项目场景: 提示:使用版本6.7.0 公司使用前后端项目分离,前端使用bpmn插件生成bpmn xml文件,后端解析处理数据.今天主要介绍后端集成flowable设计器的过程中遇到的问题. 如需了 ...

  7. java单点登录统一认证,JEECG 集成KiSSO单点登录实现统一身份认证

    JEECG 集成KiSSO单点登录实现统一身份认证 JEECG 如何为其他第三方系统实现统一身份认证服务,实现单点登录? 第三方系统如何对接呢? 今天为大家揭开这层面纱,让大家了解实质,使用它更快速的 ...

  8. Flowable6.5 之 springboot集成flowable modeler设计器

    源码 githup上下载老版本源码https://github.com/flowable/flowable-engine/releases gitHub:https://github.com/flow ...

  9. SprinBoot 集成 Flowable/Activiti工作流引擎

    文章目录 一. Flowable工作流引擎 1. flow 2. flowable 3. cims 4. RuoYi-flowable 5. springboot-flowable-modeler 6 ...

  10. 2分钟 Docker 部署 SprinBoot 集成 Flowable 工作流引擎

    文章目录 一. 简介 二. docker流程引擎部署 2.1. jdk安装配置 2.2. 下载打包好的tar包 2.3. 上传tar包 2.4. 在线安装Docker 2.5. 检验安装是否成功 三. ...

最新文章

  1. 如何获取cURL以不显示进度栏?
  2. 【CCNA考试】2010-06-29-北京-987(PASS)
  3. Java数据库连接池实现原理
  4. 交叉编译及树莓派(或其他平台)交叉编译工具链的安装
  5. J2ME开发环境配置(MyEclipse插件+WTK+jdk)
  6. 【亲测有效】Kali Linux无法安装网易云音乐的解决方案
  7. 【hue】 There are currently no roles defined
  8. 网页登陆注册(jsp实现)验证码
  9. linux mq脚本,Linux自动化命令工具expect
  10. 字符串转换的UnicodeDecodeError—— ‘\xa0’问题
  11. Python三大神器之pip用法详解
  12. fast无线路由器设置服务器,迅捷(FAST)FW300R无线路由器怎么设置
  13. #模块:如何查模块的户口,看里面都由什么组成
  14. python123:大小写转换
  15. Kali新安装时软件安装及配置[自用 欢迎补充]
  16. 大数据技术之 Linux 基础
  17. 2018年科研大事件——科学和伦理之间的较量
  18. 中国AI方法影响越来越大,天大等从大量文献中挖掘AI发展规律
  19. 八字易经算法之用JAVA实现二十八星宿算命
  20. MSRA的2022秋招各大厂SSP+ offer

热门文章

  1. System32与SysWow64的区别
  2. C#数据库教程5-ADO.NET登录页面设计
  3. CSS溢出文字使用省略号表示
  4. Dubbo之Adaptive注解用法
  5. NUC140之I2C和AT24C32
  6. java 软键盘_【学习笔记】【java appium】软键盘搜索、回车按钮
  7. 幼儿园调查过程怎么写_幼儿园的调查报告范文
  8. 网络通信,IP地址, 端口,socket
  9. 你知道什么是真正的勇敢?
  10. 浏览器攻击框架BeEF简介