静态缓存页面 · 查看动态版本 · 登录
智柴论坛 登录 | 注册
← 返回列表

RuoYi-Vue-Plus 项目详解:架构、原理与设计思想

✨步子哥 @steper · 2025-10-13 04:41 · 53浏览

RuoYi-Vue-Plus 项目详解:架构、原理与设计思想

RuoYi-Vue-Plus 项目详解:架构、原理与设计思想

项目概述

RuoYi-Vue-Plus 是基于 Vue3 和 SpringBoot 的一款现代化后台管理系统,它对原始的 RuoYi-Vue 进行了全面重构,专为分布式集群和多租户场景设计。项目不仅代码和文档开放且免费,还提供了高度的可定制性和扩展性,适用于商业应用。

infoRuoYi-Vue-Plus 是 dromara 组织下的开源项目,完全遵循 MIT 开源协议,可以免费用于个人及商业用途。

项目定位

RuoYi-Vue-Plus 定位为企业级多租户后台管理系统,旨在提供一个开箱即用、高度可扩展的开发平台,使开发者能够快速构建企业级应用,减少重复开发工作,提高开发效率。

主要特点

    • 前后端分离:采用 Vue3 + SpringBoot 的前后端分离架构,便于团队协作和独立部署
    • 多租户支持:原生支持多租户架构,实现数据隔离和权限控制
    • 插件化设计:核心功能模块化,支持插件式扩展,降低系统耦合度
    • 权限精细控制:基于 Sa-Token 的权限认证和授权,支持数据权限和菜单权限
    • 工作流引擎:集成 Warm-Flow 工作流引擎,支持复杂业务流程设计
    • 代码生成:提供强大的代码生成器,一键生成前后端代码

技术架构

前端技术栈

    • Vue 3:采用 Vue 3 作为前端框架,利用 Composition API 提高代码复用性和可维护性
    • TypeScript:全面使用 TypeScript 增强代码类型安全性和开发体验
    • Element Plus:基于 Element Plus 组件库构建 UI 界面,提供丰富的组件和主题定制能力
    • Vite:使用 Vite 作为构建工具,提供快速的开发服务器和优化的生产构建
    • Pinia:采用 Pinia 作为状态管理库,替代 Vuex,提供更简洁的 API 和更好的 TypeScript 支持

后端技术栈

    • Spring Boot:基于 Spring Boot 3.x 框架,简化应用开发和部署
    • Spring Security:集成 Spring Security 提供安全认证和授权功能
    • MyBatis-Plus:使用 MyBatis-Plus 作为 ORM 框架,简化数据库操作
    • Sa-Token:采用 Sa-Token 替代传统 Shiro,提供更轻量、更强大的权限控制
    • Hutool:集成 Hutool 工具库,提供丰富的 Java 工具方法
    • Warm-Flow:集成 Warm-Flow 工作流引擎,支持复杂业务流程设计

系统架构设计

前端层 (Vue3 + Element Plus)
后端层 (Spring Boot + Sa-Token)
数据层 (MySQL + Redis)

系统采用分层架构设计,清晰划分各层职责,实现高内聚低耦合。前端层负责用户界面展示和交互,后端层负责业务逻辑处理和数据访问,数据层负责数据存储和缓存。

模块划分

项目采用模块化设计,主要包含以下核心模块:

    • ruoyi-admin:系统启动模块,包含系统配置和启动类
    • ruoyi-common:公共模块,包含工具类、通用注解和异常处理等
    • ruoyi-framework:框架核心模块,包含安全、权限、缓存等核心功能
    • ruoyi-system:系统管理模块,包含用户、角色、菜单等系统管理功能
    • ruoyi-job:定时任务模块,提供定时任务调度功能
    • ruoyi-generator:代码生成模块,提供代码生成功能

核心特性

apartment多租户支持

原生支持多租户架构,通过租户 ID 实现数据隔离,每个租户拥有独立的数据空间,确保数据安全。支持动态数据源切换,可配置不同租户使用不同数据库。

security权限管理

基于 Sa-Token 的权限认证和授权,支持 JWT 认证。提供细粒度的权限控制,包括菜单权限、按钮权限和数据权限,满足企业级应用的复杂权限需求。

account_tree工作流引擎

集成 Warm-Flow 工作流引擎,支持可视化流程设计、动态流程调整和复杂分支逻辑。与系统权限体系无缝集成,实现精细化的流程权限控制。

code代码生成

提供强大的代码生成器,支持一键生成前后端代码。可根据数据库表结构生成实体类、Mapper、Service、Controller 以及前端页面,大幅提高开发效率。

数据权限实现

RuoYi-Vue-Plus 5.3.0 版本对数据权限系统进行了彻底重构,采用基于 Mybatis-Plus 插件的数据权限解决方案,实现了权限控制与业务逻辑的解耦。

Java
/**
  • 数据权限注解
*/ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataScope { /**
  • 表别名
*/ String tableAlias() default ""; /**
  • 部门ID字段名
*/ String deptId() default ""; /**
  • 用户ID字段名
*/ String userId() default ""; }

通过 @DataScope 注解,可以在 Mapper 接口上声明数据权限规则,Mybatis-Plus 插件会自动根据当前用户的角色数据权限,动态生成对应的 WHERE 条件,实现数据权限的无感过滤。

多数据源支持

系统支持多数据源配置,可以同时连接多种类型的数据库,实现读写分离和分库分表。通过动态数据源路由,可以根据业务需求自动切换数据源。

YAML
spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/ry-vue
          username: root
          password: password
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave:
          url: jdbc:mysql://localhost:3307/ry-vue
          username: root
          password: password
          driver-class-name: com.mysql.cj.jdbc.Driver

设计思想

架构思想

RuoYi-Vue-Plus 遵循以下架构思想:

    • 分层架构:采用经典的分层架构,清晰划分各层职责,实现高内聚低耦合
    • 模块化设计:将系统功能划分为多个独立模块,每个模块负责特定功能,便于维护和扩展
    • 插件化扩展:核心功能采用插件化设计,支持动态加载和卸载,提高系统灵活性
    • 前后端分离:采用前后端分离架构,前端负责界面展示和用户交互,后端负责业务逻辑和数据处理

开发模式

项目采用以下开发模式:

    • 约定优于配置:遵循约定优于配置的原则,减少配置工作,提高开发效率
    • 面向接口编程:采用面向接口编程的方式,定义清晰的接口规范,降低模块间耦合
    • 领域驱动设计:在核心业务模块采用领域驱动设计思想,以业务领域为中心组织代码
    • 测试驱动开发:鼓励编写单元测试和集成测试,确保代码质量和系统稳定性

设计原则

项目遵循以下设计原则:

    • 单一职责原则:每个类和模块只负责一项功能,保持职责单一
    • 开闭原则:对扩展开放,对修改关闭,通过扩展而非修改来增加新功能
    • 里氏替换原则:子类必须能够替换其基类,保持系统的稳定性
    • 接口隔离原则:使用多个专门的接口,而不是使用单一的总接口
    • 依赖倒置原则:依赖于抽象而非具体实现,降低模块间耦合

原理分析

权限控制原理

RuoYi-Vue-Plus 采用 Sa-Token 作为权限控制框架,其核心原理如下:

Java
/**
  • 权限服务接口
*/ public interface PermissionService { /**
  • 获取角色数据权限
  • * @param roleId 角色ID
  • @return 数据权限范围
*/ Set<Long> getRoleDataScope(Long roleId); /**
  • 获取菜单数据权限
  • * @param userId 用户ID
  • @return 菜单权限标识集合
*/ Set<String> getMenuPermission(Long userId); }

权限控制的核心是通过 PermissionService 接口实现的,该接口定义了获取角色数据权限和菜单权限的方法。系统在用户登录时,会加载用户的权限信息并缓存,后续请求通过拦截器进行权限校验。

多租户实现原理

多租户是 RuoYi-Vue-Plus 的核心特性之一,其实现原理如下:

Java
/**
  • 多租户拦截器
*/ public class TenantInterceptor implements InnerInterceptor { @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { // 获取租户ID Long tenantId = TenantHelper.getTenantId(); if (tenantId == null) { return; } // 修改SQL,添加租户条件 String originalSql = boundSql.getSql(); String newSql = originalSql + " AND tenant_id = " + tenantId; ReflectUtil.setFieldValue(boundSql, "sql", newSql); } }

多租户实现的核心是通过 Mybatis-Plus 的拦截器机制,在 SQL 执行前动态添加租户条件,实现数据隔离。系统通过 TenantHelper 工具类获取当前租户 ID,并在 SQL 中自动添加租户条件,确保每个租户只能访问自己的数据。

工作流引擎原理

RuoYi-Vue-Plus 集成了 Warm-Flow 工作流引擎,其核心原理如下:

Java
/**
  • 工作流服务接口
*/ public interface IFlwCommonService { /**
  • 构建工作流用户
  • * @param userId 用户ID
  • @return 工作流用户对象
*/ User buildWorkFlowUser(Long userId); /**
  • 启动流程
  • * @param processKey 流程定义键
  • @param businessKey 业务键
  • @param variables 变量
  • @return 流程实例ID
*/ String startProcess(String processKey, String businessKey, Map<String, Object> variables); }

工作流引擎的核心是通过流程定义、流程实例和任务三个核心概念实现的。流程定义定义了业务流程的结构和规则,流程实例是流程定义的一次执行,任务是流程实例中的具体工作项。系统通过工作流服务接口提供流程启动、任务处理、流程查询等功能。

插件化设计原理

RuoYi-Vue-Plus 采用插件化设计,其核心原理如下:

Java
/**
  • 插件接口
*/ public interface Plugin { /**
  • 插件名称
*/ String getName(); /**
  • 插件版本
*/ String getVersion(); /**
  • 插件描述
*/ String getDescription(); /**
  • 初始化插件
*/ void initialize(); /**
  • 启动插件
*/ void start(); /**
  • 停止插件
*/ void stop(); }

插件化设计的核心是通过定义统一的插件接口,实现功能的动态加载和卸载。系统通过插件管理器负责插件的加载、初始化、启动和停止,实现功能的动态扩展。插件可以独立开发和部署,不影响系统的稳定性。

代码示例

配置示例

YAML
# 项目配置
ruoyi:
  # 名称
  name: RuoYi-Vue-Plus
  # 版本
  version: 5.3.0
  # 版权年份
  copyrightYear: 2023
  # 实例演示开关
  demoEnabled: true
  # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: D:/ruoyi/uploadPath
  # 获取ip地址开关
  addressEnabled: false
  # 验证码类型 math 数组计算 char 字符验证
  captchaType: math

开发环境配置

server: # 服务器的HTTP端口,默认为8080 port: 8080 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # tomcat最大线程数,默认为200 max-threads: 800 # Tomcat启动初始化的线程数,默认值25 min-spare-threads: 30

核心类示例

Java
/**
  • 登录服务实现
*/ @Service public class SysLoginService { @Autowired private SysUserMapper userMapper; @Autowired private SysPermissionService permissionService; /**
  • 登录验证
  • * @param username 用户名
  • @param password 密码
  • @param code 验证码
  • @param uuid 唯一标识
  • @return 结果
*/ public String login(String username, String password, String code, String uuid) { // 验证码校验 validateCaptcha(username, code, uuid); // 登录前置校验 loginPreCheck(username, password); // 用户验证 SysUser user = loadUserByUsername(username); if (!SecurityUtils.matchesPassword(password, user.getPassword())) { throw new UserPasswordNotMatchException(); } if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { throw new UserBlockedException(); } // 生成token return createToken(user); } /**
  • 创建令牌
*/ public String createToken(SysUser user) { // 生成token String token = IdUtil.fastSimpleUUID(); // 缓存用户信息 LoginUser loginUser = new LoginUser(); loginUser.setUserId(user.getUserId()); loginUser.setUsername(user.getUserName()); loginUser.setPermission(permissionService.getMenuPermission(user.getUserId())); // 缓存登录用户到Redis RedisUtils.setCacheObject(CacheConstants.LOGIN_TOKEN_KEY + token, loginUser, CacheConstants.LOGIN_TOKEN_TTL, TimeUnit.MINUTES); return token; } }

接口设计示例

Java
/**
  • 用户管理接口
*/ @RestController @RequestMapping("/system/user") public class SysUserController extends BaseController { @Autowired private ISysUserService userService; /**
  • 获取用户列表
*/ @SaCheckPermission("system:user:list") @GetMapping("/list") public TableDataInfo<SysUserVo> list(SysUserBo user, PageQuery pageQuery) { return userService.selectPageUserList(user, pageQuery); } /**
  • 根据用户编号获取详细信息
*/ @SaCheckPermission("system:user:query") @GetMapping("/{userId}") public R<SysUserVo> getInfo(@PathVariable Long userId) { userService.checkUserDataScope(userId); return R.ok(userService.selectUserById(userId)); } /**
  • 新增用户
*/ @SaCheckPermission("system:user:add") @Log(title = "用户管理", businessType = BusinessType.INSERT) @PostMapping public R<Void> add(@Validated @RequestBody SysUserBo user) { if (!userService.checkUserNameUnique(user.getUserName())) { return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); } return toAjax(userService.insertUser(user)); } /**
  • 修改用户
*/ @SaCheckPermission("system:user:edit") @Log(title = "用户管理", businessType = BusinessType.UPDATE) @PutMapping public R<Void> edit(@Validated @RequestBody SysUserBo user) { userService.checkUserAllowed(user); userService.checkUserDataScope(user.getUserId()); if (!userService.checkUserNameUnique(user.getUserName())) { return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); } return toAjax(userService.updateUser(user)); } }

讨论回复 (0)