# spring-rui 代码分析报告 > 分析日期:2026-05-26 > 目标:分析 `~/rhkj/spring-rui`(排除 `app/` 目录),对比 {root},识别可复用模式和缺失能力。 --- ## 一、项目架构对比 | 维度 | spring-rui | {root}(本项目) | |------|-----------|-------------------| | 包名 | `org.rui` | `com.rui` | | 模块组织 | 单体 `rui-common` 内含 15 子模块 | 平铺 4 子模块 `core/mybatis/security/web` | | ORM | MyBatis Plus + 自定义类型处理器 | MyBatis Plus | | 响应模型 | `Result` extends `JSONObject` | `R` POJO | | JSON 框架 | **fastjson2**(核心依赖) | Jackson | | 多租户 | 配置驱动(TABLE/IGNORE/NONE 三种模式) | 硬编码忽略表列表 | | 缓存 | Redis Manager(发布/订阅) | 无 | --- ## 二、spring-rui 各模块分析 ### 2.1 rui-common-core | 文件 | 说明 | {root} 状态 | |------|------|---------------| | `Result.java` | 响应模型,extends `JSONObject`,含 `error`/`message`/`code`/`data`/`rows`/`results`,支持链式调用 | 已有 `R`,功能较弱 | | `ResultResponse.java` | 简单响应 holder | 已有 `ResultCode` | | `ResponseStatus.java` | 枚举:SUCCESSFUL/UNAUTHORIZED/FORBIDDEN/NOT_FOUND/VALIDATE_FAILED 等 | 可参考补全 | | `TenantContextHolder.java` | 租户上下文 ThreadLocal | 已有 `TenantContextHolder` | | `TenantBroker.java` | 支持在指定租户下执行代码 | 无,可选 | | `IdWorker.java` | ID 生成器 | 无 | | `JacksonConfig.java` | Jackson 全局配置 | 可参考 | | `KeyStrResolver.java` / `TenantKeyStrResolver.java` | 缓存 Key 解析(含租户隔离) | 无,可复用 | | `SpringUtil.java` | Spring 上下文工具 | 无 | | `ServletUtil.java` | Servlet 请求工具(提取所有参数为 Map) | 无 | | `JsonUtil.java` | JSON 合并/赋值工具(`JsonUtil.assign()`) | 无 | | `AmountUtil.java` / `NumberUtil.java` / `RandomUtil.java` | 金额/数字/随机工具 | 无 | | `IPSeekerUtil.java` / `IpUtil.java` | IP 归属地查询 | 无 | | `StringUtil.java` | 字符串增强工具 | 已有 hutool | | `LocalDateTimeUtil.java` | 时间工具 | 无 | | `Validate.java` / `BaseRegex.java` / `NumericUtil.java` | 正则/校验工具 | 无 | | `HttpClient.java` | HTTP 客户端封装 | 无 | | `CertHelper.java` / `CertUtil.java` / `RsaUtil.java` / `AesUtil.java` / `MD5Util.java` / `ShaUtil.java` / `SignatureUtil.java` | 加密/证书工具链 | 无 | | `DelayQueueManager.java` / `QueueTask.java` | 延迟队列 | 无 | | `Scheduled.java` / `Task.java` / `TaskConfiguration.java` / `TaskFactory.java` | 定时任务调度 | 无 | | `ProxyProperties.java` / `ProxyInterceptor.java` / `ProxyProperty.java` | 代理链支持 | 无(我们有 proxy chain ThreadLocal) | | `LocaleContextHolder.java` / `LocaleUtil.java` | 国际化支持 | 无 | | `FileUtil.java` / `MimeUtil.java` / `ZipUtil.java` / `GzipUtil.java` | 文件/压缩工具 | 无 | | `AntPathMatcher.java` / `PathMatcher.java` | 路径匹配 | 无 | | `AccountProperty.java` / `AppProperty.java` / `MqttProperty.java` / `WsProperty.java` | 配置属性类 | 无 | ### 2.2 rui-common-data | 文件 | 说明 | {root} 状态 | |------|------|---------------| | `MybatisPlusConfiguration.java` | MP 插件配置(分页 + 多租户 + 防全表更新) | 已有,缺 `BlockAttackInnerInterceptor` | | `MybatisProperties.java` | MP 配置属性(表前缀、租户模式、忽略表列表) | 无 | | **`PageService.java`** | **通用查询构建器**(类型字段 + 排序 + 分页 + 返回 Result) | **2026-05-26 已移植** | | `PageOptions.java` | 查询选项(下拉框过滤) | 无,可选 | | `TenantHandler.java` | 配置驱动多租户(TABLE/IGNORE/NONE 三种模式) | 可参考优化 | | `BaseMultiTableInnerInterceptor.java` | 多表拦截器 | 无 | | `TableInterceptor.java` | 表前缀替换(`#prefix#` 占位符) | 无 | | `AbstractObjectTypeHandler.java` / `JsonObjectTypeHandler.java` / `JsonArrayTypeHandler.java` / `StringArrayTypeHandler.java` / `LongTypeHandler.java` / `IntegerTypeHandler.java` / `BigDecimalArrayTypeHandler.java` / `ArrayTypeHandler.java` | 类型处理器 | 无 | ### 2.3 rui-common-security | 文件 | 说明 | {root} 状态 | |------|------|---------------| | `ResourceServerAutoConfiguration.java` / `ResourceServerConfiguration.java` | OAuth2 资源服务器配置 | 无(不同安全架构) | | `AuthUtil.java` / `UserUtil.java` | 认证用户工具 | 部分(`SecurityUtils`) | | `OAuthBearerTokenResolver.java` | Token 解析器 | 无 | | `RedisOAuth2AuthorizationService.java` | Redis 授权存储 | 无 | | `CustomOAuth2FeignRequestInterceptor.java` | Feign Token 传递 | 无 | | `OAuthUserDetailsService.java` | 用户详情服务接口 | 无 | | `DefaultOAuthUserDetailsServiceImpl.java` | 默认实现 | 无 | | `WeixinOAuthUserDetailsServiceImpl.java` | 微信登录实现 | 无 | | `RestTemplateUserDetailsServiceImpl.java` | RestTemplate 远程调用实现 | 无 | | `PermissionService.java` | 权限校验 Bean(`@perm.isSystemTenant()`) | 无 | | `AuthIgnore.java` / `Inner.java` | 注解 | 无 | | `SecurityInnerAspect.java` | 内部调用切面 | 无 | | `FeignClientConfiguration.java` / `FeignConfiguration.java` | Feign 配置 | 无 | | `RemoteUserService.java` / `RemoteClientService.java` | Feign 远程调用接口 | 无 | | `GlobalExceptionHandler.java` | 全局异常处理 | 已有 | ### 2.4 rui-service-system(参考 CRUD 模式) **重点分析 — CRUD 标准模式:** ``` Controller ├── GET /{module}/list → PageService 分页列表 ├── GET /{module}/{id} → doGet: service.getById(id) ├── POST /{module} → doAdd: service.doAdd(bean) ├── PUT /{module}/{id} → doUpdate: service.doUpdate(bean) ├── DELETE /{module}/remove/{id} → doRemove: 物理删除 └── DELETE /{module}/{id} → doDelete: 软删除 Service (extends ServiceImpl) ├── doAdd(bean) → bean.setDefaultValue(); this.save(bean) ├── doUpdate(bean) → bean.setUpdatedAt(); this.updateById(bean) ├── doRemove(id) → this.removeById(id) └── doDelete(id) → baseMapper.doDelete(id) // SQL: UPDATE SET deleted=!deleted Mapper (extends BaseMapper) └── @Update("UPDATE #prefix#table SET deleted=!deleted WHERE id=#{id}") Integer doDelete(Serializable id); ``` **模式要点:** - `doRemove` = 物理删除,`doDelete` = 软删除(toggle deleted 标记) - 所有 Service 方法返回 `Result` 而非实体 - Mapper 中使用 `#prefix#` 占位符,由 `TableInterceptor` 自动替换为实际表前缀 - Entity 实现 `setDefaultValue()` 方法初始化字段 ### 2.5 rui-service-users(同上,CRUD 模式验证) 与 rui-service-system 完全一致的 CRUD 模式。每个模块有: - `model/` — 实体类(`implements Serializable`) - `mapper/` — MyBatis Plus Mapper - `service/` — 接口 - `service/impl/` — 实现(`extends ServiceImpl` + `doAdd/doUpdate/doRemove/doDelete`) - `controller/` — 接口(`extends BaseController` + CRUD 端点) ### 2.6 rui-gateway | 文件 | 说明 | {root} 状态 | |------|------|---------------| | `GrayLoadBalancer.java` | 灰度负载均衡 | 无 | | `GrayReactiveLoadBalancerClientFilter.java` | 灰度过滤器 | 无 | | `GlobalExceptionHandler.java` | 网关异常处理 | 无 | ### 2.7 rui-oauth2 | 文件 | 说明 | {root} 状态 | |------|------|---------------| | `OauthApplication.java` | 启动类(无额外配置) | 已有 AuthApplication | --- ## 三、已移植到 {root} 的内容 ### 2026-05-26 批量移植 | 文件 | 位置 | 对应 spring-rui | |------|------|----------------| | `PageResult.java` | `rui-common-core` | 新增(基于 Result 分页字段) | | `PageService.java` | `rui-common-mybatis` | `rui-common-data/PageService.java` | | `BaseController.java` | `rui-common-web` | `rui-service-system/controller/BaseController.java` | | 防全表更新 | `MyBatisPlusConfig.java` | `MybatisPlusConfiguration.java` 中的 `BlockAttackInnerInterceptor` | --- ## 四、后续可选移植项 ### 优先级高(核心能力) | 功能 | 来源模块 | 说明 | |------|---------|------| | `SpringUtil.java` | core | Spring 上下文静态获取 | | `ServletUtil.java` | core | 请求参数提取工具(PageService 用) | | `JsonUtil.java` | core | JSON 合并/赋值(`assign` 方法在更新场景很有用) | | `MybatisProperties.java` | data | 配置化表前缀/租户模式,替代硬编码 | ### 优先级中(开发提效) | 功能 | 来源模块 | 说明 | |------|---------|------| | `StringUtil.java` / `AmountUtil.java` / `NumberUtil.java` | core | 常用工具 | | `LocalDateTimeUtil.java` | core | 时间工具 | | `IdWorker.java` | core | ID 生成器 | | `IPSeekerUtil.java` / `IpUtil.java` | core | IP 工具 | | `ArrayTypeHandler.java` 系列 | data | MP 类型处理器(JSON/数组) | | `KeyStrResolver.java` 系列 | core | 缓存 Key 租户隔离 | ### 优先级低(按需移植) | 功能 | 来源模块 | 说明 | |------|---------|------| | 加密工具链(RSA/AES/MD5/SHA/证书) | core | spring-rui 安全相关 | | `HttpClient.java` | core | HTTP 客户端 | | `DelayQueueManager.java` | core | 延迟队列 | | 定时任务调度 | core | 自定义任务框架 | | 国际化 | core | Locale 工具 | | 灰度负载均衡 | gateway | 与部署环境强相关 | | Feign 远程调用 | security | 与认证架构耦合 | --- ## 五、CRUD 编程规范(推荐) 基于 spring-rui 模式,推荐 {root} 业务模块遵循以下规范: ### Controller 规范 ```java @RestController @RequiredArgsConstructor @RequestMapping("system/tenant") public class SysTenantController extends BaseController { private final SysTenantService service; @GetMapping("/list") public R> list(HttpServletRequest request) { PageService ps = new PageService<>(request); ps.setDefaultOrderColumn("updatedAt"); ps.putBoolean("deleted").putQuery("deleted", false); ps.putLike("name"); return ps.getResults(service); } @GetMapping("{id}") public R get(@PathVariable Long id) { return R.ok(service.getById(id)); } @PostMapping public R add(@RequestBody SysTenant bean) { return service.doAdd(bean); } @PutMapping("{id}") public R update(@RequestBody SysTenant bean) { return service.doUpdate(bean); } @DeleteMapping("{id}") public R delete(@PathVariable Long id) { return service.doDelete(id); } } ``` ### Service 规范 ```java public interface SysTenantService extends IService { R doAdd(SysTenant bean); R doUpdate(SysTenant bean); R doDelete(Long id); } public class SysTenantServiceImpl extends ServiceImpl implements SysTenantService { public R doAdd(SysTenant bean) { bean.setDefaultValue(); if (this.save(bean)) return R.ok("添加成功"); return R.fail("添加失败"); } public R doUpdate(SysTenant bean) { bean.setUpdatedAt(LocalDateTime.now()); if (this.updateById(bean)) return R.ok("更新成功"); return R.fail("更新失败"); } public R doDelete(Long id) { if (baseMapper.doDelete(id) == 1) return R.ok("删除成功"); return R.fail("删除失败"); } } ``` ### Mapper 规范 ```java @Mapper public interface SysTenantMapper extends BaseMapper { @Update("UPDATE rui_system_tenant SET deleted = !deleted WHERE id = #{id}") int doDelete(@Param("id") Long id); } ``` > 注:spring-rui 使用 `#prefix#` 占位符 + TableInterceptor 自动替换表前缀。本项目暂不引入该机制,直接在 SQL 中写完整表名。