# RuoYi-Vue-Plus项目分析报告 ## 1. 项目概述 RuoYi-Vue-Plus是在RuoYi-Vue基础上进行重构升级的开源项目,它专为分布式集群与多租户场景设计,采用插件化和扩展包的结构形式,极大提高了系统的解耦程度和扩展性。项目使用Spring Boot 3.4、JDK 17/21,基于Vue3+TS+ElementPlus重写前端,并使用Undertow替代Tomcat作为Web容器。 ### 1.1 项目特点 - 采用插件化+扩展包形式,结构解耦,易于扩展 - 严格遵守Alibaba规范,代码格式统一 - 支持多种数据库(MySQL、Oracle、PostgreSQL、SQLServer等) - 支持多租户架构 - 前端使用Vue3+TS+ElementPlus - 底层基于Redisson的分布式能力(分布式锁、限流等) - 使用Sa-Token+JWT实现权限认证 - 使用Mybatis-Plus作为ORM框架 - 内置丰富的功能模块和实用工具 ## 2. 项目架构 ### 2.1 整体架构 RuoYi-Vue-Plus采用前后端分离的架构模式: - 后端:Spring Boot 3.4 + MyBatis-Plus + Sa-Token - 前端:Vue3 + TypeScript + ElementPlus 项目部署架构图: ![Plus部署架构图](https://foruda.gitee.com/images/1678981882624240692/ae2a3f3e_1766278.png) ### 2.2 目录结构 项目主要分为以下几个部分: #### 2.2.1 后端结构 ``` ruoyi-vue-plus ├── ruoyi-admin -- 启动模块,项目入口 ├── ruoyi-common -- 通用模块,各种功能组件 │ ├── ruoyi-common-bom -- 依赖版本管理 │ ├── ruoyi-common-core -- 核心功能 │ ├── ruoyi-common-doc -- 接口文档 │ ├── ruoyi-common-encrypt -- 数据加解密 │ ├── ruoyi-common-excel -- Excel处理 │ ├── ruoyi-common-idempotent -- 幂等处理 │ ├── ruoyi-common-job -- 定时任务 │ ├── ruoyi-common-json -- JSON处理 │ ├── ruoyi-common-log -- 日志处理 │ ├── ruoyi-common-mail -- 邮件处理 │ ├── ruoyi-common-mybatis -- ORM配置 │ ├── ruoyi-common-oss -- 对象存储 │ ├── ruoyi-common-ratelimiter -- 限流处理 │ ├── ruoyi-common-redis -- Redis配置 │ ├── ruoyi-common-satoken -- 认证鉴权 │ ├── ruoyi-common-security -- 安全配置 │ ├── ruoyi-common-sensitive -- 数据脱敏 │ ├── ruoyi-common-sms -- 短信服务 │ ├── ruoyi-common-social -- 社交登录 │ ├── ruoyi-common-sse -- SSE推送 │ ├── ruoyi-common-tenant -- 多租户 │ ├── ruoyi-common-translation -- 数据翻译 │ ├── ruoyi-common-web -- Web功能 │ └── ruoyi-common-websocket -- WebSocket ├── ruoyi-extend -- 扩展模块 │ ├── ruoyi-monitor-admin -- 监控管理 │ └── ruoyi-snailjob-server -- 任务调度 ├── ruoyi-modules -- 业务模块 │ ├── ruoyi-demo -- 示例模块 │ ├── ruoyi-generator -- 代码生成 │ ├── ruoyi-job -- 定时任务 │ ├── ruoyi-system -- 系统管理 │ └── ruoyi-workflow -- 工作流 └── ruoyi-plus-soybean -- 前端项目 ``` #### 2.2.2 前端结构 ``` ruoyi-plus-soybean ├── docs -- 文档 ├── packages -- 功能包 ├── public -- 静态资源 ├── src -- 源代码目录 │ ├── assets -- 静态资源 │ ├── components -- 组件 │ ├── constants -- 常量 │ ├── enum -- 枚举 │ ├── hooks -- 钩子函数 │ ├── layouts -- 布局 │ ├── locales -- 国际化 │ ├── plugins -- 插件 │ ├── router -- 路由 │ ├── service -- 服务调用 │ ├── store -- 状态管理 │ ├── styles -- 样式 │ ├── theme -- 主题 │ ├── typings -- 类型定义 │ ├── utils -- 工具类 │ ├── views -- 视图 │ ├── App.vue -- 主组件 │ └── main.ts -- 入口文件 ``` ## 3. 功能模块 ### 3.1 核心功能 #### 基础功能 - **多租户管理**:支持租户套餐、过期时间、用户数量等管理 - **用户权限**:用户、角色、部门、菜单权限管理 - **系统监控**:在线用户、操作日志、登录日志 - **系统管理**:参数设置、字典管理、附件管理 - **系统工具**:代码生成、表单设计、接口文档 #### 扩展功能 - **工作流**:支持复杂审批流程 - **在线构建器**:拖拽式表单生成 - **定时任务**:任务调度管理 - **系统接口**:API文档自动生成 - **服务监控**:监控系统资源和性能 - **缓存监控**:Redis监控 ### 3.2 技术特性 - **数据权限**:基于Mybatis-Plus插件的无感式数据权限过滤 - **数据脱敏**:支持注解+Jackson方式的数据脱敏 - **数据加解密**:支持数据库字段级加解密 - **接口加密**:动态AES+RSA加密请求体 - **数据翻译**:注解+序列化期间自动翻译 - **多数据源**:支持动态数据源配置和切换 - **分布式锁**:基于Redisson的分布式锁 - **分布式任务调度**:基于SnailJob的分布式任务调度 - **文件存储**:支持MinIO和S3协议的对象存储 ## 4. 代码规范 RuoYi-Vue-Plus项目严格遵循以下规范: ### 4.1 项目规范 - 严格遵守Alibaba编码规范 - 使用统一的代码格式化配置 - 采用插件化结构,功能模块独立封装 - 按职责分离不同类型的代码(控制器、服务、实体等) ### 4.2 编码规范 #### 后端规范 1. **命名规范**: - 类名:大驼峰命名(如:UserController) - 方法名/变量名:小驼峰命名(如:getUserInfo) - 常量:全大写下划线分隔(如:MAX_COUNT) 2. **包结构**: - controller:控制器 - service:服务层 - mapper:数据访问层 - domain:实体类(entity、vo、bo、dto等) - util:工具类 3. **注释要求**: - 类注释:说明类的用途 - 方法注释:说明方法功能、参数和返回值 - 关键代码注释:解释复杂逻辑 #### 前端规范 1. **命名规范**: - 组件名:大驼峰命名(如:UserForm) - 文件名:kebab-case(如:user-form.vue) - 变量/方法:小驼峰命名(如:getUserInfo) 2. **目录结构**: - components:组件 - views:页面 - api:接口定义 - utils:工具函数 - store:状态管理 3. **编码风格**: - 使用TypeScript进行类型检查 - 组件使用组合式API (Composition API) - 样式使用SCSS并遵循BEM规范 ### 4.3 常见代码示例 #### 4.3.1 控制器规范示例 ```java /** * 用户信息控制器 * * @author ruoyi-vue-plus */ @RestController @RequestMapping("/system/user") public class SysUserController extends BaseController { @Autowired private ISysUserService userService; /** * 获取用户列表 * * @param user 查询参数 * @return 用户列表 */ @SaCheckPermission("system:user:list") @GetMapping("/list") public TableDataInfo list(SysUserBo user) { startPage(); List list = userService.selectUserList(user); return getDataTable(list); } /** * 新增用户 * * @param user 用户信息 * @return 结果 */ @SaCheckPermission("system:user:add") @Log(title = "用户管理", businessType = BusinessType.INSERT) @PostMapping public R add(@Validated @RequestBody SysUserBo user) { return toAjax(userService.insertUser(user)); } } ``` #### 4.3.2 服务接口和实现规范示例 ```java /** * 用户服务接口 * * @author ruoyi-vue-plus */ public interface ISysUserService { /** * 查询用户列表 * * @param user 查询参数 * @return 用户列表 */ List selectUserList(SysUserBo user); /** * 新增用户 * * @param user 用户信息 * @return 结果 */ int insertUser(SysUserBo user); } /** * 用户服务接口实现 * * @author ruoyi-vue-plus */ @Service public class SysUserServiceImpl implements ISysUserService { @Autowired private SysUserMapper userMapper; /** * 查询用户列表 * * @param user 查询参数 * @return 用户列表 */ @Override public List selectUserList(SysUserBo user) { return userMapper.selectUserList(user); } /** * 新增用户 * * @param user 用户信息 * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int insertUser(SysUserBo user) { // 业务逻辑实现 return userMapper.insert(user.toEntity()); } } ``` #### 4.3.3 实体类规范示例 ```java /** * 用户实体类 * * @author ruoyi-vue-plus */ @Data @TableName("sys_user") @KeySequence("sys_user_seq") public class SysUser extends TenantEntity { /** * 用户ID */ @TableId(value = "user_id", type = IdType.ASSIGN_ID) private Long userId; /** * 用户账号 */ private String userName; /** * 用户昵称 */ private String nickName; /** * 用户类型(sys_user系统用户) */ private String userType; /** * 手机号码 */ @Sensitive(strategy = SensitiveStrategy.PHONE) private String phonenumber; /** * 帐号状态(0正常 1停用) */ private String status; } ``` #### 4.3.4 前端页面示例 (Vue3 + TS) ```typescript ``` ## 5. 二次开发指南 ### 5.1 开发环境准备 1. **基础环境**: - JDK 17/21 - Maven 3.8+ - Node.js 16+ - MySQL 8.0+/Oracle/PostgreSQL/SQLServer - Redis 5.0+ 2. **IDE推荐**: - IntelliJ IDEA(后端) - VSCode(前端) ### 5.2 开发规范与原则 1. **遵循现有架构**: - 保持与现有代码风格一致 - 保持模块的独立性和可插拔性 - 不随意修改核心模块代码 2. **扩展而非修改**: - 通过扩展现有组件实现功能 - 避免直接修改框架核心代码 3. **注重代码质量**: - 编写单元测试 - 遵循代码规范 - 注释完善 ### 5.3 增加PMS模块实施步骤 以下是添加一个产品管理系统(PMS)模块的具体步骤: #### 5.3.1 后端开发 1. **创建模块结构**: ``` ruoyi-modules └── ruoyi-pms -- PMS模块 ├── src/main/java/org/dromara/pms │ ├── controller -- 控制器 │ ├── domain -- 实体类 │ │ ├── bo -- 业务对象 │ │ ├── entity -- 数据库实体 │ │ └── vo -- 视图对象 │ ├── mapper -- MyBatis接口 │ └── service -- 服务实现 └── src/main/resources ├── mapper -- MyBatis XML └── i18n -- 国际化资源 ``` 2. **配置模块POM**: - 创建pom.xml,添加必要依赖 - 在父模块中添加新模块引用 3. **创建数据库表**: - 设计表结构 - 编写SQL脚本 4. **开发核心功能**: - 使用代码生成器生成基础CRUD代码 - 扩展实现具体业务逻辑 - 添加权限控制 - 实现多租户和数据权限 - 添加接口文档注释 #### 5.3.2 前端开发 1. **创建模块目录**: ``` ruoyi-plus-soybean/src/views/pms ├── product -- 产品管理 ├── category -- 分类管理 └── inventory -- 库存管理 ``` 2. **API接口定义**: ``` ruoyi-plus-soybean/src/service/api/pms ├── product.ts -- 产品API ├── category.ts -- 分类API └── inventory.ts -- 库存API ``` 3. **配置路由**: - 在router/routes目录下创建pms.ts - 在router/index.ts中导入并注册路由 4. **开发页面组件**: - 列表页面 - 表单页面 - 详情页面 5. **添加权限控制**: - 配置菜单与按钮权限 - 实现页面级与按钮级权限控制 #### 5.3.3 集成与测试 1. **数据库脚本集成**: - 将建表SQL添加到初始化脚本中 2. **菜单配置**: - 通过系统管理-菜单管理添加PMS模块菜单 - 配置菜单权限 3. **角色授权**: - 为相关角色分配PMS模块权限 4. **单元测试**: - 编写API测试 - 编写服务层测试 5. **集成测试**: - 测试完整业务流程 - 测试与其他模块的交互 ### 5.4 使用框架核心功能的最佳实践 #### 5.4.1 使用多租户 1. **实体类继承TenantEntity**: ```java @Data @TableName("pms_product") public class PmsProduct extends TenantEntity { // 实体字段定义 } ``` 2. **多租户过滤配置**: ```java @Configuration public class TenantConfig { /** * 配置需要进行多租户过滤的表 */ @Bean public TenantLineHandler tenantLineHandler() { return new PlusTenantLineHandler() { @Override public String getTenantIdColumn() { return "tenant_id"; } @Override public boolean ignoreTable(String tableName) { // 配置不需要过滤的表 return ArrayUtil.contains(IGNORE_TENANT_TABLES, tableName); } }; } } ``` #### 5.4.2 使用数据权限 1. **在Mapper接口上使用注解**: ```java @DataPermission({ @DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "userName", value = "u.user_id") }) public interface SysUserMapper extends BaseMapperPlus { // 方法定义 } ``` 2. **在查询方法上使用注解**: ```java @Override @DataScope(userAlias = "u", deptAlias = "d") public List selectUserList(SysUserBo user) { return baseMapper.selectUserList(user); } ``` #### 5.4.3 使用数据脱敏 ```java public class UserVo { // 手机号码脱敏 @Sensitive(strategy = SensitiveStrategy.PHONE) private String phonenumber; // 邮箱脱敏 @Sensitive(strategy = SensitiveStrategy.EMAIL) private String email; // 身份证脱敏 @Sensitive(strategy = SensitiveStrategy.ID_CARD) private String idCard; } ``` #### 5.4.4 使用Excel导入导出 ```java @Data @ExcelIgnoreUnannotated public class UserImportVo { @ExcelProperty(value = "用户编号") private Long userId; @ExcelProperty(value = "用户名称") @ExcelRequired private String userName; @ExcelProperty(value = "用户昵称") @ExcelRequired private String nickName; @ExcelProperty(value = "手机号码") @ExcelRequired private String phonenumber; @ExcelProperty(value = "邮箱") private String email; } @RestController public class UserController { /** * 导出用户 */ @SaCheckPermission("system:user:export") @Log(title = "用户管理", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(HttpServletResponse response, SysUserBo user) { List list = userService.selectUserList(user); ExcelUtil.exportExcel(list, "用户数据", SysUserVo.class, response); } /** * 导入用户 */ @SaCheckPermission("system:user:import") @Log(title = "用户管理", businessType = BusinessType.IMPORT) @PostMapping("/importData") public R importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception { ExcelResult result = ExcelUtil.importExcel(file, UserImportVo.class); userService.importUser(result.getList(), updateSupport); return R.ok(); } } ``` #### 5.4.5 使用接口幂等性控制 ```java @RestController @RequestMapping("/system/user") public class SysUserController { /** * 新增用户 */ @SaCheckPermission("system:user:add") @Log(title = "用户管理", businessType = BusinessType.INSERT) @RepeatSubmit(interval = 5000) // 5秒内不允许重复提交 @PostMapping public R add(@Validated @RequestBody SysUserBo user) { return toAjax(userService.insertUser(user)); } } ``` #### 5.4.6 使用Redis缓存 ```java @Service public class SysConfigServiceImpl implements ISysConfigService { /** * 查询参数配置信息 * * @param configId 参数配置ID * @return 参数配置信息 */ @Override @Cacheable(cacheNames = CacheNames.SYS_CONFIG, key = "#configId") public SysConfig selectConfigById(Long configId) { return baseMapper.selectById(configId); } /** * 新增参数配置 * * @param config 参数配置信息 * @return 结果 */ @Override @CachePut(cacheNames = CacheNames.SYS_CONFIG, key = "#config.configId") public int insertConfig(SysConfig config) { return baseMapper.insert(config); } /** * 删除参数配置 * * @param configId 参数ID * @return 结果 */ @Override @CacheEvict(cacheNames = CacheNames.SYS_CONFIG, key = "#configId") public int deleteConfigById(Long configId) { return baseMapper.deleteById(configId); } } ``` ## 6. 总结 RuoYi-Vue-Plus是一个功能完善、架构清晰的企业级应用开发框架,其插件化设计和模块化结构使其非常适合二次开发。在进行二次开发时,应当遵循项目的架构设计和编码规范,通过扩展而非修改的方式实现业务需求,确保系统的可维护性和可扩展性。 通过合理利用项目提供的代码生成、多租户、权限控制等特性,可以大幅提高开发效率,专注于业务逻辑的实现,而非底层架构的搭建。 ## 7. 常见问题与解决方案 ### 7.1 多租户问题 **问题**:如何对特定表或操作排除多租户过滤? **解决方案**: - 全局排除:在TenantConfig中的ignoreTable方法中添加表名 - 局部排除:使用@TenantIgnore注解标注在方法或类上 ```java @TenantIgnore public List selectTenantList(SysTenant tenant) { return baseMapper.selectList(buildQueryWrapper(tenant)); } ``` ### 7.2 权限问题 **问题**:如何实现细粒度的数据权限控制? **解决方案**: - 使用@DataScope注解并配置用户、部门别名 - 在Mapper层使用@DataPermission定义权限字段 - 在XML中引用权限过滤片段 ### 7.3 分布式事务问题 **问题**:如何处理跨服务的分布式事务? **解决方案**: - 对于强一致性需求,使用Seata进行分布式事务管理 - 对于最终一致性需求,考虑使用本地消息表+定时任务或消息队列实现 - 避免长事务,将业务拆分成多个小事务 ### 7.4 性能优化问题 **问题**:系统运行缓慢,如何优化? **解决方案**: - 使用Redis缓存热点数据,减少数据库访问 - 优化SQL查询,避免全表扫描 - 使用分页查询代替全量查询 - 合理使用索引 - 利用多级缓存(本地缓存+Redis缓存) - 考虑使用读写分离或分库分表