MD 状态:🌱 更新:2026/5/31

Skyinfi ruoyi-vue-pro 架构学习笔记

一句话定位:这是一个以 Spring Boot + Maven 多模块组织的大型后台管理脚手架,核心目标不是“微服务拆分”,而是在单体部署形态下用模块边界、starter 化横切能力和业务 API 契约承载多业务域扩展。

架构假设

这是一个模块化单体 + 分层架构 + Spring Boot starter 横切能力的系统。核心数据流是:

HTTP 请求 → Security/Web 过滤链 → Controller → Service → Mapper/MyBatis Plus → 数据库/Redis/MQ

最关键的设计决策集中在:

  • pom.xml:定义 Maven 聚合模块与默认启用模块。
  • yudao-server/pom.xml:把 server 设计成“空壳容器”,通过依赖决定装载哪些业务模块。
  • yudao-framework/pom.xml:把安全、Web、MyBatis、Redis、MQ、多租户等能力拆成 starter。
  • YudaoServerApplication.java:用统一 base package 扫描 server 与 module。
  • yudao-module-system:提供认证、权限、用户、字典、日志等系统基础域。

核心问题与约束

这个项目面对的是典型企业后台系统的约束:业务域多、功能横切点多、部署成本要低、二次开发要快。代码规模上,当前仓库顶层存在 14 个 yudao-module-* 业务模块目录、14 个 yudao-spring-boot-starter-* 框架 starter、约 5170 个 Java 文件、9 套 SQL 方言目录。这个规模已经不适合简单按包堆叠,但也未必需要上来就拆成微服务。

它的核心取舍是:用 Maven 模块和 Spring 自动配置建立边界,用一个 Spring Boot 进程承载运行时。 证据是根 pom.xml 只默认启用 systeminfraserver,其他会员、BPM、商城、CRM、ERP、AI、IoT、MES、WMS 模块都以注释方式保留扩展入口(pom.xml:10-31)。yudao-server/pom.xml 也明确把 server 描述成“空壳(容器)”,通过引入 yudao-module-xxx 对外提供 REST API(yudao-server/pom.xml:15-20)。

架构全景

graph TD
    User["Admin / App / API Client"] --> HTTP["HTTP API"]
    HTTP --> Server["yudao-server\nSpring Boot 容器"]
    Server --> Security["framework-security\nToken / 权限 / 操作日志"]
    Server --> Web["framework-web\n异常 / Swagger / XSS / API Log"]
    Server --> MyBatis["framework-mybatis\nMapperScan / 分页 / BaseMapperX"]
    Server --> Modules["yudao-module-* 业务模块"]
    Modules --> System["system\n认证/用户/权限/字典/日志"]
    Modules --> Infra["infra\n基础设施能力"]
    Modules --> Optional["member/bpm/mall/crm/erp/ai/iot...\n按需引入"]
    System --> DAL["Mapper + DO"]
    DAL --> DB["SQL 数据库"]
    Security --> Redis["Redis / Token / Cache"]
    Modules --> MQ["RedisMQ / RabbitMQ / Kafka / RocketMQ"]

    %% 架构师注释:运行时只有一个 Spring Boot 容器,但编译期通过 Maven 依赖选择装配哪些业务模块;横切能力通过 framework starter 自动配置进入容器。

架构风格:模块化单体 + starter 化横切框架

它不是传统“一个 web 包 + 一个 service 包 + 一个 dao 包”的小型三层单体,而是更接近“可裁剪业务域的模块化单体”。yudao-server 不承载业务逻辑,只依赖 yudao-module-systemyudao-module-infra 等模块(yudao-server/pom.xml:23-33),其他业务通过取消注释依赖来装配(yudao-server/pom.xml:35-130)。

横切能力被拆进 yudao-framework。该模块描述中明确说每个技术组件包括 coreconfig 两部分,并区分框架组件与业务组件(yudao-framework/pom.xml:33-43)。例如 Security starter 的自动配置声明了 YudaoSecurityAutoConfigurationYudaoWebSecurityConfigurerAdapterYudaoOperateLogConfiguration,MyBatis starter 声明了数据源、MyBatis、翻译配置。这说明系统依赖 Spring Boot auto-configuration 来减少业务模块重复配置。

这种风格适合后台管理系统:部署简单、二次开发成本低、模块边界比普通单体清晰。代价是运行时边界仍在同一进程内,模块之间的依赖纪律主要靠 Maven、包结构和接口约定维护。

核心模块解析

模块单一职责与其他模块的耦合点
yudao-server应用启动与模块装配容器通过 Maven 依赖决定装载哪些 yudao-module-*;启动类扫描 ${yudao.info.base-package}.server.moduleYudaoServerApplication.java:15-24
yudao-framework横切技术能力集合拆成 14 个 starter,例如 web/security/mybatis/redis/mq/job/tenant/data-permission/protection(yudao-framework/pom.xml:12-31
yudao-common通用 DTO、工具、跨模块 API 契约Security 依赖 OAuth2TokenCommonApi,具体实现由 system 模块提供(OAuth2TokenCommonApi.java:14-48
yudao-module-system系统基础域:认证、用户、权限、角色、菜单、字典、日志等对框架提供 OAuth2 token 校验实现;对业务模块提供用户、权限、短信、日志等 API
yudao-module-infra基础设施/平台能力默认随 server 启用,和 system 一起构成后台管理基础盘(yudao-server/pom.xml:23-33
可选业务模块member、bpm、mall、crm、erp、ai、iot、mes、wms 等领域默认注释以保证编译速度,需要时通过 Maven 依赖启用(pom.xml:18-30yudao-server/pom.xml:35-130
sql/*多数据库初始化脚本提供 MySQL、PostgreSQL、Oracle、SQL Server、Kingbase、DM、DB2、openGauss 等方言目录,支撑国产化/多数据库部署

数据流分析

flowchart LR
    Request["HTTP 请求"] --> Filter["TokenAuthenticationFilter"]
    Filter --> Context["LoginUser 写入 SecurityContext / request"]
    Context --> Controller["Controller\nVO 校验/返回 CommonResult"]
    Controller --> Service["Service\n业务规则/事务/日志"]
    Service --> Mapper["Mapper extends BaseMapperX"]
    Mapper --> MP["MyBatis Plus / MP Join"]
    MP --> DB["数据库"]
    Service --> Redis["Redis / Cache / Token"]
    Service --> MQ["MQ / Job / Async 扩展"]

    %% 架构师注释:控制流仍是经典三层,但框架层在 Controller 之前注入认证、异常、日志、权限、数据权限等横切逻辑;数据访问层通过 BaseMapperX 把分页、Join、批处理抽象成统一习惯。

TokenAuthenticationFilter 从请求中取 token,调用 OAuth2TokenCommonApi.checkAccessToken 校验,然后构造 LoginUser 放入安全上下文(TokenAuthenticationFilter.java:44-59TokenAuthenticationFilter.java:71-88)。业务入口以 AuthController 为例,/system/auth/login 标记 @PermitAll,只做请求接收和委托,真正认证由 AdminAuthService.login 完成(AuthController.java:66-71)。

服务层负责组合规则:验证码、用户名密码校验、社交绑定、登录日志、token 创建都在 AdminAuthServiceImpl 中编排(AdminAuthServiceImpl.java:101-117AdminAuthServiceImpl.java:212-219)。数据层则通过 AdminUserMapper extends BaseMapperX<AdminUserDO> 获得基础查询与分页能力(AdminUserMapper.java:13-36)。

关键架构决策

决策 1:server 只做容器,业务按 Maven 依赖装配

  • 背景:项目覆盖后台、会员、商城、CRM、ERP、AI、IoT 等多个领域,如果全部默认启用,会显著增加构建和启动成本。
  • 决策:根 pom.xmlyudao-server/pom.xml 默认只启用 systeminfra,其他业务模块注释保留。
  • 原因:保持单体部署简单,同时允许按业务场景裁剪。
  • 代价:模块启停不是运行时插件,而是编译期依赖选择;不同部署形态需要维护 POM 差异。
  • 风险:如果业务模块之间出现隐式依赖,取消注释组合模块时容易暴露循环依赖或 Bean 冲突。

决策 2:把横切能力做成 Spring Boot starter

  • 背景:后台系统普遍需要认证、权限、日志、数据权限、多租户、缓存、MQ、限流、幂等、锁等横切能力。
  • 决策:在 yudao-framework 下拆出 14 个 starter,由 AutoConfiguration.imports 自动装配。
  • 原因:业务模块只关心领域逻辑,公共能力以 starter 方式复用。
  • 代价:自动配置链变长,问题排查需要理解 starter 顺序、条件装配和 Bean 注入关系。
  • 风险:starter 过多时,业务模块可能不清楚“某个行为从哪里来”,尤其是数据权限、租户、操作日志这类隐式拦截。

决策 3:认证走自定义 token + OAuth2 服务,而不是完全托管给 Spring Security 登录流程

  • 背景:系统要支持后台用户、App 用户、账号密码、短信、社交登录、token 刷新等多种登录方式。
  • 决策:登录接口由 Controller/Service 显式处理,Spring Security 主要负责后续请求过滤和权限判断。代码注释也说明登录暂不使用 Spring Security 拓展点,原因是多用户、多登录方式扩展复杂、学习成本高(YudaoWebSecurityConfigurerAdapter.java:123)。
  • 原因:业务登录流程更可控,适合国内后台系统常见的验证码、短信、社交绑定需求。
  • 代价:认证逻辑散在业务服务与 security filter 之间,需要额外维护一致性。
  • 风险:mock login、permitAll、token 校验策略如果配置不严,容易形成认证绕过风险。

决策 4:数据访问层以 MyBatis Plus 基类约束团队习惯

  • 背景:后台管理 CRUD 多、分页多、条件查询多,重复 Mapper 代码会膨胀。
  • 决策BaseMapperX 扩展 MyBatis Plus 与 MP Join,提供分页、Join、批处理、常见查询方法(BaseMapperX.java:26-32BaseMapperX.java:34-69)。
  • 原因:让业务 Mapper 只保留领域相关查询,如 AdminUserMapper 中的用户名、手机号、分页条件查询(AdminUserMapper.java:16-36)。
  • 代价:Mapper default method 会变成“轻业务逻辑”的聚集地,长期可能模糊 DAL 和 Service 边界。
  • 风险selectFirstOne 为并发重复数据场景兜底,但也可能掩盖唯一约束缺失的问题(BaseMapperX.java:147-160)。

请求链路

sequenceDiagram
    participant C as Client
    participant F as TokenAuthenticationFilter
    participant A as AuthController
    participant S as AdminAuthService
    participant U as AdminUserService
    participant O as OAuth2TokenService
    participant L as LoginLogService
    participant D as Database/Redis

    C->>A: POST /system/auth/login
    A->>S: login(reqVO)
    S->>S: validateCaptcha()
    S->>U: getUserByUsername()
    U->>D: query user
    S->>S: password/status check
    S->>L: createLoginLog()
    S->>O: createAccessToken()
    O->>D: persist/cache token
    S-->>A: AuthLoginRespVO
    A-->>C: CommonResult(token)

    C->>F: 后续请求携带 token
    F->>O: checkAccessToken()
    O->>D: validate token
    F->>F: set LoginUser
    F-->>C: 继续进入 Controller

    %% 架构师注释:登录本身绕开 Spring Security 标准认证管理器,业务服务创建 token;后续请求再由 Security Filter 接管,这是“业务登录 + 框架鉴权”的混合模式。

微信小程序连接链路

这个项目连接微信小程序,不是“小程序直连业务数据库”,而是通过后端 App API 把微信平台能力收敛到 system 模块,再由 member/mall/pay 等业务模块调用。

依赖入口在 yudao-module-system/pom.xml,它引入 wx-java-miniapp-spring-boot-starter 作为微信小程序 SDK(yudao-module-system/pom.xml:103-110)。配置入口在 application-local.yamlapplication-dev.yamlwx.miniapp,包含 appidsecretconfig-storage,其中 config-storage.type: RedisTemplate 用于共享微信 AccessToken(application-local.yaml:215-227application-dev.yaml:161-167)。同时,JustAuth 的 WECHAT_MINI_PROGRAM 配置复用 ${wx.miniapp.appid}${wx.miniapp.secret},并关闭 redirect/state 校验以适配小程序登录场景(application-local.yaml:263-267)。

sequenceDiagram
    participant Mini as 微信小程序
    participant API as AppAuthController
    participant Auth as MemberAuthService
    participant SocialClient as SocialClientApi
    participant WxMa as WxMaService
    participant Member as MemberUserService
    participant SocialUser as SocialUserApi
    participant Token as OAuth2TokenCommonApi

    Mini->>Mini: wx.login() 获取 loginCode
    Mini->>Mini: wx.getPhoneNumber() 获取 phoneCode
    Mini->>API: POST /app-api/member/auth/weixin-mini-app-login
    API->>Auth: weixinMiniAppLogin(reqVO)
    Auth->>SocialClient: getWxMaPhoneNumberInfo(phoneCode)
    SocialClient->>WxMa: getUserService().getPhoneNumber(phoneCode)
    Auth->>Member: createUserIfAbsent(phone, terminal=WECHAT_MINI_PROGRAM)
    Auth->>SocialUser: bindSocialUser(loginCode, state)
    SocialUser->>WxMa: 通过 JustAuth 换取 openid
    Auth->>Token: createAccessToken(userId)
    Token-->>Mini: accessToken + openid

    %% 架构师注释:微信 code 只用于换手机号和 openid,系统最终仍发放自己的 OAuth2 token;微信身份被持久化为 social_user/social_user_bind 关系,业务侧不直接依赖微信会话。

小程序登录入口在 AppAuthController.weixinMiniAppLogin,路径是 /member/auth/weixin-mini-app-loginAppAuthController.java:118-123)。请求对象要求小程序传入 phoneCodeloginCode,分别来自 wx.getPhoneNumberwx.loginAppAuthWeixinMiniAppLoginReqVO.java:18-28)。

业务编排在 MemberAuthServiceImpl.weixinMiniAppLogin:先通过 SocialClientApi.getWxMaPhoneNumberInfo 换取手机号,再用手机号创建或获取会员用户,并用 loginCode + state 绑定微信小程序社交用户,最后通过 OAuth2TokenCommonApi.createAccessToken 生成系统自己的登录 token(MemberAuthServiceImpl.java:136-164)。终端类型会写为 WECHAT_MINI_PROGRAM,其枚举值是 10TerminalEnum.java:18-20)。

真正访问微信平台的是 SocialClientServiceImpl。它通过 WxMaService.getUserService().getPhoneNumber(phoneCode) 获取手机号(SocialClientServiceImpl.java:282-290),并通过 getWxMaService(userType) 决定使用哪个小程序配置:优先查数据库里的 SocialClientDO,如果存在启用配置,就用 clientId + clientSecret 构建并缓存新的 WxMaService;否则退回到 application-*.yaml 注入的默认 WxMaServiceSocialClientServiceImpl.java:434-465)。这让项目同时支持默认配置和后台动态配置。

openid 的绑定在 SocialUserServiceImplbindSocialUser 会先通过 authSocialUser 调微信授权换取三方用户,再清理旧绑定,最后写入新的 social_user_bind 关系并返回 openid(SocialUserServiceImpl.java:61-80)。authSocialUser 会把 code + state、openid、token、昵称、头像、原始用户信息保存到本地社交用户表,避免同一个 code 在后续绑定流程里重复消费失败(SocialUserServiceImpl.java:132-160)。微信小程序在 SocialTypeEnum 中的类型是 WECHAT_MINI_PROGRAM(34)SocialTypeEnum.java:50-55)。

这套 WxMaService 不只服务登录,还承担小程序码、订阅消息和交易发货同步:getWxaQrcode 调微信生成小程序码(SocialClientServiceImpl.java:293-310),sendSubscribeMessage 调订阅消息服务(SocialClientServiceImpl.java:327-357),uploadWxaOrderShippingInfonotifyWxaOrderConfirmReceive 调微信小程序交易发货/确认收货能力(SocialClientServiceImpl.java:360-425)。其中发货上传还设计了 1s、2s、4s 的重试,用来覆盖微信支付回调和订单信息上传之间的时间差(SocialClientServiceImpl.java:106-113SocialClientServiceImpl.java:385-400)。

需要注意的是,当前 fork 的 yudao-server 默认只启用了 systeminfra 模块,member/mall/pay 等模块依赖在 POM 中是注释状态(yudao-server/pom.xml:23-33yudao-server/pom.xml:35-130)。因此完整跑通“小程序会员登录/商城/支付”时,要把对应模块依赖打开。

架构质量评估

质量属性设计手段(引用具体文件/行)评估潜在风险
可扩展性可选业务模块默认注释,server 通过依赖启用模块(yudao-server/pom.xml:35-130★★★★☆编译期扩展,不是运行时插件;模块组合复杂后可能出现 Bean 冲突
可维护性framework starter 拆出 web/security/mybatis/redis/mq 等横切能力(yudao-framework/pom.xml:12-31★★★★☆自动配置隐藏了控制流,新人需要学习框架内部约定
安全性stateless token、@PermitAll 扫描、兜底 authenticated(YudaoWebSecurityConfigurerAdapter.java:109-152★★★☆☆application.yaml 中存在示例/硬编码第三方 AI key 配置区,生产环境必须移出代码库;mock login 必须关闭
数据访问一致性MyBatis Plus 统一分页、逻辑删除、多数据库 id 生成、BaseMapperX(application.yaml:66-89YudaoMybatisAutoConfiguration.java:47-58★★★★☆为兼容多数据库牺牲部分数据库特定优化;Mapper default method 易膨胀
性能Redis cache 默认、Mapper 懒加载可配置、JSQL parser cache、EasyTrans 默认关闭全局翻译(application.yaml:26-30application.yaml:97-100YudaoMybatisAutoConfiguration.java:39-44★★★☆☆大量 starter 与 Bean 装配会增加启动复杂度;部分全局能力打开后可能影响接口响应
可用性支持 Redis、MQ、Quartz、限流、幂等、分布式锁等保障能力(yudao-dependencies/pom.xml 中相关依赖)★★★☆☆单体部署下进程级故障影响所有模块;可用性更多依赖部署架构而非代码结构

架构风险与改进建议

[!danger] 风险点 spring.main.allow-circular-references: true 被显式打开,并注释为“三层架构无法避免”(application.yaml:8-10)。这是一种架构味道:它能降低迁移成本,但也可能掩盖服务之间的循环依赖。越是模块化单体,越应该把循环依赖当作边界失效信号。

[!danger] 风险点 application.yaml 的 AI 配置区出现多家模型供应商的 API key 字段和值示例(application.yaml:167-193)。无论这些 key 是否有效,生产实践都应改为环境变量、配置中心或密钥管理服务,避免训练二开团队形成“配置进仓库”的习惯。

[!tip] 改进建议 建议为模块依赖建立 ArchUnit 或 Maven Enforcer 规则:module-* 可以依赖 framework/common 和模块公开 api,但禁止直接跨模块访问对方 service/dal。这样能把“模块化单体”从目录约定提升为可验证规则。

[!tip] 改进建议 对 starter 自动配置补一张启动装配图或 ADR。当前 starter 设计很好,但隐式装配较多,新人排查“某个过滤器/拦截器从哪里来”会有成本。

可复用架构经验

值得借鉴的模式:

  • 空壳 Server + 业务模块依赖装配(适用条件:企业后台、私有化部署、二次开发场景)— yudao-server/pom.xml:15-20 明确 server 是容器,业务能力由依赖决定。
  • 横切能力 starter 化(适用条件:多个业务模块共享安全、Web、缓存、数据权限、日志)— yudao-framework/pom.xml:33-43 明确每个组件拆 coreconfig
  • Common API 反转模块依赖(适用条件:框架层需要调用业务能力,但不能依赖业务实现)— Security 依赖 OAuth2TokenCommonApi,system 模块提供 OAuth2TokenApiImpl
  • Mapper 基类沉淀团队查询习惯(适用条件:CRUD 密集型后台)— BaseMapperX 统一分页、Join、批处理、常用查询。

值得警惕的反模式:

  • 用开启循环依赖来维持三层架构(出现场景:Service 相互调用频繁)— allow-circular-references 是短期兼容,长期应拆接口或事件。
  • 配置文件承载敏感信息(出现场景:AI、短信、支付、第三方平台接入)— 应迁移到环境变量/配置中心。
  • 模块目录很多但运行时仍无隔离(出现场景:模块化单体膨胀后)— 一旦跨模块直接调用 service/dal,模块边界会快速退化。

关联概念

架构风格模块化单体 · 分层架构 · 插件式架构

技术选型Spring Boot · MyBatis Plus · Redis · Spring Security · Maven 多模块

相关项目RuoYi-架构学习笔记 · FastAPI 架构设计