后端集成

willian fu 2022 ~

后端集成

后端集成至少需要满足如下技术栈要求:jdk8springboot2.xmybatis / mybatis-plusmysql5+maven

1、后端代码合并

代码合并分3种情况:微服务项目单体单模块项目单体多模块项目

微服务

新建微服务模块wflow ,命名根据系统风格要求来加上 -wflow 后缀即可,将wflow相关配置及代码配置到该模块内即可

单体单模块项目

在主类同级中新建包 wflow ,将wflow代码全部复制过去即可

单体多模块项目

根据目标要求新建新maven模块,如上和微服务差不多,注意pom依赖项

2、合并pom依赖

这个没啥说的了,把 wflowpom 配置依赖都合并过去,注意和目标项目的依赖冲突,主要是mybatis依赖冲突,在mybatis-plus 中已经包含了 mybatis 的依赖,无需额外引入,flowable-spring-stater 包也包含了 mybatis 的依赖,注意需要排除

 <dependency>
     <groupId>org.flowable</groupId>
     <artifactId>flowable-spring-boot-starter-actuator</artifactId>
     <version>${flowable.version}</version>
     <exclusions>
         <exclusion>
             <groupId>org.mybatis</groupId>
             <artifactId>mybatis</artifactId>
         </exclusion>
     </exclusions>
 </dependency>

如果你的项目还存在例如 pagehelper 之类的包,注意它可能也包含了mybatis 依赖,也需要排除掉,否则启动项目会出现找不到类的错误

3、合并application配置

以下几个配置项需要添加进去

spring:
#邮件发送配置,如果不需要发邮件功能这里可以不加,同时代码里面也要去除邮件功能
  mail:
    host: smtp.qq.com
    username: smartiots@qq.com
    password: fnrruelrccqaeaefx0
    protocol: smtps
    default-encoding: UTF-8
    properties:
      default-encoding: utf-8
      mail:
        smtp:
          port: 465
          auth: true
          starttls:
            enable: true
            required: true
            
management:
  health:
  	# 关闭启动时邮件配置检查
    mail:
      enabled: false 
      
flowable:
  async-executor-activate: true
  #关闭一些不需要的功能服务
  rest-api-enabled: false
  idm:
    enabled: false
  cmmn:
    enabled: false
  dmn:
    enabled: false
  form:
    enabled: false
  app:
    enabled: false

# wflow自带的文件上传控制配置
wflow:
  file:
    max-size: 20 #最大文件上传大小,MB

4、启动项目,逐步排查错误

注意

我们按上述步骤合并好代码后可能出现以下错误,需要逐步去排除

包/类路径报错

由于代码是直接复制过来的,和当前项目包和类路径不一致,需要手动批量去引入,根据开发工具的错误提示去挨个修改即可,可以使用批量替换

类冲突

wflow存在一些 config 配置类,例如 MyBatisPlusConfigAsyncTaskTheadPoolConfig等,去除不需要的配置类,这个 AsyncTaskTheadPoolConfig 是用来提供线程处理异步任务的,如果大家各自项目内已经有了线程池配置,那么就不需要这个,但是要在目标项目的线程池配置里面加两个静态属性,如下:

@Configuration
public class ThreadPoolConfig {
    
    public static ThreadPoolTaskExecutor executor;

    public static ThreadPoolTaskScheduler taskScheduler;
}

在spring初始化这个类进行自动配置时,为上述两个属性赋值,taskScheduler 这个如果本身项目没有的话可以这样配置

@Bean
@Qualifier(value="applicationTaskExecutor") //这个注解下,防止线程池配置冲突
public ThreadPoolTaskScheduler threadPoolTaskScheduler(ThreadPoolTaskExecutor executor){
    ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
    taskScheduler = threadPoolTaskScheduler;
    threadPoolTaskScheduler.setThreadFactory(executor);
    return threadPoolTaskScheduler;
}

5、集成组织架构及用户角色

wflow 自带了组织架构表,参见 数据库说明文档

那几张组织架构表如果是集成的话,一般目标系统都自带了,就不需要了,可以去掉它们,我们接下来就是要把查询这块全部换成目标系统的

设计方式

集成组织架构及用户角色等信息只需要实现 wflow 定义好的一个接口 com.wflow.service.OrgRepositoryService ,wflow把需要读取组织架构相关信息的地方都封装到了这个接口中,用户就可以根据自己的系统去实现对应的查询即可,返回wflow指定的实体类,这样就能适应各类系统了,甚至是从接口查组织架构数据。

事实上,wflow自带的组织架构相关表和绝大多数 RBAC 系统设计的结构大致一样,因此可以直接根据 wflow 默认实现 DefaultOrgRepositoryServiceImpl 稍作修改即可兼容到现有系统

示例

下面展示一个用户查询的集成,对应Mapper为 WflowUserMapper

public interface WflowUsersMapper extends BaseMapper<WflowUsers> {

    /**
     * 查询该部门下的所有用户
     * @param deptId 部门ID
     * @return 用户列表 type为固定值user
     */
    @Select("SELECT ou.user_id id, ou.user_name `name`, 'user' AS 'type', ou.avatar FROM wflow_user_departments oud LEFT JOIN wflow_users ou ON ou.user_id = oud.user_id WHERE oud.dept_id = #{deptId}")
    List<OrgTreeVo> selectUsersByDept(@Param("deptId") String deptId);

 }

以集成到 ruoyi-vue 为例,那么只需要吧 BaseMapper<WflowUsers>换成 BaseMapper<SysUser>,然后由于若依的用户只能在一个部门下面,那么改造如下:

public interface WflowUsersMapper extends BaseMapper<SysUser> {

    /**
     * 查询该部门下的所有用户
     * @param deptId 部门ID
     * @return 用户列表 type为固定值user
     */
     @Select("SELECT user_id id, nick_name `name`, 'user' AS 'type', avatar " +
            "FROM sys_user WHERE del_flag = 0 AND dept_id = #{deptId}")
    List<OrgTreeVo> selectUsersByDept(@Param("deptId") String deptId);
 }

然后对应的 WflowUsers 都需要换成 SysUser ,其他需要修改的项为,部门角色,注意有的表字段名称可能也不一样,需要修改

ID类型注意

wflow 默认的用户、部门、角色等 id类型是String,那么如果和目标系统是数值类型,那么需要在DefaultOrgRepositoryServiceImpl 内和其他地方把这个id toString() 或者 String.valueOf() 转换一下,注意有的地方id可能会是null ,用 toString() 的话需要注意空指针

还有一处查询涉及到了部门和用户关系,没有列入本接口,需要大家修改下这块

public interface WflowModelPermsMapper extends BaseMapper<WflowModelPerms> {
    @Select("这段sql需要修改下部门表名称为目标系统的表名,这里默认是wflow的部门表")
    List<ModelGroupVo.Form> selectByPerms(@Param("userId") String userId);
}

6、集成用户ID获取

wflow 的流程和用户身份信息相关,因此系统需要去读取 当前操作流程的人是谁,集成的话需要实现一个方法 com.wflow.utils.UserUtil.getLoginUserId(),

public class UserUtil {

    /**
     * 获取当前登录用户的id
     * @return 用户ID
     */
    public static String getLoginUserId(){
        //这里默认是用satoken实现的,需要换成各位自己的逻辑
        return StpUtil.getLoginIdAsString();
    }
}

例如:在 ruoyi 里面是 String.valueOf(SecurityUtils.getUserId())

8、组织架构缓存

组织架构存在深层嵌套关系,但是wflow里面有很多地方用到组织架构用户归属关系判断,例如判断张三是不是属于某个部门的人员,判断E部门是不是A部门的子部门等

我们不知道部门会有多少层级,在 wflow 中支持递归层级的,这种层级递归需要查询很频繁,那么在wflow中,系统启动的时候会直接把所有部门与用户的归属关系加载到内存中,维护2个Map,如下:

@Slf4j
@Service
public class MemoryOrgOwnershipServiceImpl implements OrgOwnershipService {

    //用户ID与其所有层级所属部门ID级联映射
    private static final Map<String, Set<String>> userDeptMap = new ConcurrentHashMap<>();

    //部门ID与其所有父级部门ID级联关系映射
    private static final Map<String, Set<String>> deptAndDeptMap = new ConcurrentHashMap<>();
}

通过这种方式,系统启动后即可通过缓存关系迅速判断这个归属关系逻辑,无需每次都递归查询数据库

通常情况下,系统的组织架构及人员数据库会变更,那么这时候缓存就要重新加载了,OrgOwnershipService 接口提供2个方法用来重载组织架构及用户归属关系缓存,直接调用即可

/**
 * 重载用户与部门关系
 * @param userId 用户ID
 * @param isRemove 是移除还是加入
 */
void reloadUserDept(String userId, boolean isRemove);

/**
 * 重载部门与部门关系
 */
void reloadDeptAndDept();

9、集成部门负责人

在wflow中存在 部门负责人 的概念,每个部门可以配置一个部门负责人,这个字段默认为leader,如果大家要集成的系统中不存在该字段,那么可以在部门表中新增这个字段,存储作为该部门负责人的ID即可。

10、相关问题及报错

集成过程可能出现一些问题,上述过程有些章节已经有说明,当然可能补充的也不够完善,考虑不到所有的情况,欢迎大家上报给我相关问题及当时的解决方案以做补充💕