☰
RuoYi-Vue-Plus企业级后台开发实战:权限、多租户与代码生成落地
2026/11/2 7:53:30 网站建设 项目流程

后台管理系统这行干久了,会发现一个规律:接十个项目,有八个的前半段长得几乎一模一样。登录、菜单、用户、角色、部门、字典、日志、文件上传、代码生成,这套东西翻来覆去地写,写到最后闭着眼睛都能敲出来。真正拉开差距的是后半段那些业务逻辑,可偏偏前半段要吃掉项目三分之一甚至一半的工期。RuoYi-Vue-Plus 这类企业级快速开发平台存在的意义就在这儿——把前面那套通用底座一次性做扎实,让团队把时间花在真正值钱的地方。它不是那种装完就万事大吉的万能药,用得好能省下一个月的活,用不好会变成一堆看不明白的代码堆在那里,出了问题连从哪儿查都不知道。这篇东西我按自己带项目落地的顺序来写,从环境怎么对齐、模块怎么拆、认证链路怎么走、代码生成器怎么用、缓存锁怎么配,一直写到打包上线和那些文档里不会写的坑。适合准备拿它起新项目的后端、需要接手别人留下的这套代码的维护者,以及想搞清楚"企业级脚手架"到底企业级在哪儿的技术负责人。

1. 选型判断:这套平台到底替你干了哪些活

1.1 通用后台的重复劳动清单

先把账算清楚,才知道上这套框架值不值。一个标准的中后台系统,在业务代码动笔之前,起码要完成这些事:数据库连接池配置与多数据源支持、统一响应体封装与全局异常处理、参数校验、登录认证与令牌续期、接口级别的权限校验、数据行级别的权限过滤、菜单与按钮的动态渲染、操作日志与登录日志的异步落库、字典与参数的可视化维护、文件上传到本地或对象存储、Excel 导入导出、定时任务的可视化配置、接口文档自动生成、跨域与请求加密、前端路由与状态管理、按钮级权限指令。粗算下来,二十多项,每一项单独做都不难,难的是它们要互相咬合——比如权限注解要能读到当前登录人,日志切面要能拿到请求参数又不能把密码记进去,数据权限要在 SQL 层织入而不能靠业务代码自己拼条件。

这些咬合关系如果每个项目重新理一遍,出错概率极高。我见过不止一个项目,日志切面把用户密码明文写进了数据库,或者数据权限在分页查询时只过滤了列表没过滤导出,导致越权导出全表。这类问题不是能力问题,是重复劳动带来的注意力衰减。

1.2 技术栈的组合逻辑与取舍

这套平台的技术选型有一条清晰的主线:能用成熟轮子就不自己造,但每个轮子都要能拆得掉。后端以 Spring Boot 3 为基座,要求 JDK 17 起步,这不是赶时髦,是因为 Spring Boot 3 全面转向了 Jakarta EE 命名空间,同时把 GraalVM 原生镜像、可观测性这些东西做进了内核。ORM 层用 MyBatis-Plus,而不是 JPA,理由很实际:国内中后台的查询条件千变万化,动态 SQL 用 JPA 的 Criteria 写出来又长又难读,MyBatis-Plus 的 LambdaQueryWrapper 和 XML 混写模式更贴合实际。权限框架选了 Sa-Token 而不是 Spring Security,这是这套框架最有争议也最有价值的一个决定,后面单独说。

缓存和分布式协调交给 Redis + Redisson,锁用 Lock4j 这个轻量封装,定时任务提供 Quartz 和 XXL-JOB 两套方案,工作流模块早期用 Flowable,新版分支里也支持更轻量的流程引擎。前端是 Vue 3 + TypeScript + Vite + Element Plus + Pinia,TypeScript 不是可选项,生成器产出的前端代码自带完整类型定义,你要是关掉 TS,等于把一半的收益扔了。

注意:不同大版本之间的技术栈差异很大。4.x 时代还在 JDK 8 + Spring Boot 2 + Vue 2,5.x 才全面切到 JDK 17 + Spring Boot 3 + Vue 3。网上搜到的教程有一大半是 4.x 的,配置项名字、依赖坐标、甚至注解包名都不一样,照着抄十有八九报错。拿到一份资料先看它的版本号,对不上就当参考思路看,别当操作手册用。

1.3 什么样的团队该用,什么样的不该

适合的场景很明确:三到十人的团队,要做交付型的中后台项目,需求里包含权限体系、审批流、报表导出这些标准件,工期在两个月以上。这种情况下底座的投入能摊薄,越往后越省。已经有自己一套成熟底座的公司没必要换,迁移成本比省下的时间高得多。

不太适合的是两类。一类是极简单的项目,比如就做一个对外的小程序接口服务,用户体系三张表搞定,那引入这套东西属于用航母运快递,光是理解模块依赖就要花两天。另一类是团队里没人愿意读源码的,这套框架封装程度比较高,遇到问题必须能往下钻,如果所有人都只会改 Controller,一旦踩到框架层的坑就会卡死。

2. 环境对齐与工程结构:先把地基看明白再动土

2.1 版本矩阵:错一个版本能耗掉一整天

我踩过最冤的一次坑,是本地 JDK 用的 11,项目死活起不来,报的错是Unsupported class file major version,看着像 Maven 的问题,折腾了半天才想起看 JDK。所以第一步不是急着 clone 代码,是把版本矩阵列出来逐个核对。

组件建议版本说明
JDK17(LTS)Spring Boot 3 硬性要求,11 及以下直接编译失败
Maven3.8.6 以上低版本对 Boot 3 的插件解析有兼容问题
MySQL8.0.30 以上用到 JSON 字段和窗口函数,5.7 会有语法报错
Redis6.2 以上部分命令如GETDEL需要较新版本
Node.js18 或 20 LTS16 在 Vite 5 下会有 crypto 模块报错
包管理器pnpm 8+用 npm 也能跑,但依赖体积差一倍

核对方式很简单,命令行敲java -version、mvn -v、node -v、pnpm -v、redis-cli --version,五个输出贴在一起对照。JDK 尤其要注意,机器上装多个版本时JAVA_HOME指向哪个才生效,IDE 里的 Project SDK 和 Maven 的 Runner JDK 是三处独立配置,任一处不对都会出问题。

2.2 模块划分:依赖方向决定了你能改哪里

看懂模块划分是读懂这套代码的前提。它的目录结构是典型的 Maven 多模块,依赖方向严格单向,从下往上依次是:

  • ruoyi-common:公共能力层,内部又按功能切成十几个子模块,core 放基础工具和常量,web 放全局异常、响应封装、拦截器,mybatis 放分页、多租户、数据权限的插件,satoken 放认证配置,redis 放缓存工具,oss / sms / mail 是各种外部服务的适配层,excel 封装导入导出,log 处理日志切面,doc 集成接口文档。
  • ruoyi-api:对外暴露的接口定义和 DTO,供其他服务或模块引用,避免模块之间直接依赖实现。
  • ruoyi-modules:业务模块,system是核心的权限体系,generator是代码生成,job是定时任务,workflow是流程引擎,demo是各种用法的示例。
  • ruoyi-admin:启动模块,只放启动类和主配置文件,依赖上面所有模块。
  • ruoyi-extend:扩展模块,比如独立的监控服务端。

这个依赖方向的意思是:common不能反过来依赖modules,所以你往common里写业务代码,一定会在某个时刻发现引不到实体类。我见过有人为了图方便,把业务 Service 塞进 common,最后整个工程编译出一堆循环依赖。规矩就是规矩,业务代码一律放modules。

2.3 从零到能登录的完整启动顺序

按下面这个顺序来,基本能一次点亮:

  1. 建库导数据。新建一个ry-vue-plus之类的库,字符集utf8mb4,排序规则utf8mb4_general_ci,然后执行 SQL 脚本目录里的初始化脚本。脚本通常分结构脚本和数据脚本两个,先执行带schema的,再执行带data的,顺序反了会因为外键或字面量报错。
  2. 起 Redis。本地直接redis-server或者用容器起一个,注意如果有密码要记下来,配置里要同步。
  3. 改配置文件。后端配置一般是application.yml加application-dev.yml的分层结构,application.yml里放公共项和 profile 激活,application-dev.yml里放数据源、Redis、文件存储这些环境相关的。数据源部分要改 URL、用户名、密码,URL 后面记得带上时区和编码参数,形如?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true,少了allowPublicKeyRetrieval在某些 MySQL 8 配置下会连不上。
  4. 启动后端。主类在ruoyi-admin模块下,直接 run。控制台看到 banner 和端口号说明起来了,如果卡在数据源初始化,八成是配置没读到,检查 profile 是不是 dev。
  5. 装前端依赖并启动。进前端目录,pnpm install,然后pnpm dev。首次启动会做依赖预构建,慢是正常的。
  6. 登录验证。默认账号密码在初始化数据里,登录进去看菜单能不能正常加载。菜单是后端下发、前端动态注册路由的,如果登录成功但页面空白,多半是路由或接口返回结构对不上。

提示:第一次跑起来之后,立刻做一次git init或者打个 tag,把"能跑"这个状态固定住。后面不管怎么改配置、怎么删代码,都能一键回到可用状态。这个习惯救过我很多次。

3. 认证与权限链路:这套框架最该吃透的部分

3.1 为什么用 Sa-Token 而不是 Spring Security

这是新人问得最多的一个问题。Spring Security 当然更强,生态更全,但它的问题是"重"——一个简单的注解鉴权,背后是 FilterChain、SecurityContext、AuthenticationProvider 一整套概念,改一个登录逻辑要顺着链条找半天。而中后台项目的权限需求其实非常标准化:登录、令牌、权限点、角色、数据范围,没有 OAuth2 那种复杂的授权场景。

Sa-Token 把这件事压缩到了极简:StpUtil.login(userId)登录,StpUtil.getTokenValue()拿令牌,@SaCheckPermission("system:user:list")注解鉴权,StpUtil.hasPermission("xxx")代码里判断。心智负担低,出错概率就低。这套框架还基于 Sa-Token 做了一层多端适配,请求头里要求带一个clientid,用来区分 PC 端、小程序端、APP 端,不同端的令牌互不干扰,同一账号可以同时在线多端。

认证链路走一遍:前端提交账号密码到登录接口,后端校验后调StpUtil.login,同时把用户信息、权限集合、角色集合写进 Sa-Token 的 Session(底层落在 Redis),返回令牌。之后每个请求带Authorization: Bearer xxx和clientid,拦截器解析后把用户上下文放进LoginHelper,业务代码里随时LoginHelper.getUserId()拿当前用户。

这里有个容易忽略的点:权限集合不要每次请求都查库。框架默认是把权限列表缓存在 Sa-Token Session 里的,改角色权限后必须手动让对应会话失效,否则用户要等令牌过期才生效。框架一般提供了踢人下线的接口,权限变更后调一下就行。

3.2 数据权限:注解一行,SQL 自动加条件

功能权限控制"能不能点这个按钮",数据权限控制"能看到哪些行",后者才是真正容易出安全事故的地方。这套框架的做法是在 MyBatis 层挂一个拦截器,通过注解声明哪些字段需要按数据范围过滤:

@DataPermission({ @DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "userName", value = "create_by") }) public Page<UserVo> selectPageUserList(UserBo user, PageQuery pageQuery) { return baseMapper.selectPageUserList(pageQuery.build(), this.buildQueryWrapper(user)); }

拦截器解析注解,拿到dept_id这个字段名,再结合当前登录人的角色所配置的数据范围(全部、本部门、本部门及以下、仅本人、自定义),在最终 SQL 外面套一层AND dept_id IN (...)。整个过程业务代码零感知。

实操上有几个必须注意的地方。第一,字段名要和实际 SQL 里的列名对得上,如果查询用了别名或者多表 JOIN 有歧义,拦截器拼出来的条件会报Unknown column。第二,导出接口一定要单独加注解,很多人只在列表查询上加,结果导出走了另一条 mapper 方法,等于开了个越权后门。第三,超级管理员默认跳过过滤,这个逻辑在拦截器里,如果你们的业务需要超管也受限,得自己改。

3.3 多租户隔离:三种模式与选型

多租户这块,框架提供的是共享数据库、共享表、按租户字段隔离的方案,靠 MyBatis-Plus 的租户插件自动给 SQL 加tenant_id = ?条件。开启方式是在配置里把开关打开,并配置好租户字段名和需要忽略的表。

选型上其实有三种做法,各有适用面:

方案隔离级别改造成本适用场景
字段隔离(框架默认)逻辑隔离低,插件自动处理SaaS 中小客户,数据量可控
独立库物理隔离中,需动态数据源大客户、有合规要求
独立表半物理隔离中高,需动态表名折中方案,少见

字段隔离最省事,但有两个必须处理的细节。一是全局表要加进忽略名单,比如字典表、菜单表、租户表本身,这些是所有租户共用的,被织入tenant_id条件会导致新租户进来菜单全空。二是定时任务和异步线程里拿不到租户上下文,因为租户 ID 是从请求头或登录信息里取的,异步线程没有请求上下文,这时候需要在提交任务前手动把租户 ID 传进去,用框架提供的租户工具类在子线程里 set 一下。

我在一个项目里被这个坑住过:定时任务批量生成对账单,跑出来所有数据都归到了默认租户。排查了半天才发现是线程池里的任务丢失了租户上下文,加上手动设置后立刻正常。

4. 代码生成器:效率的分水岭,也是最容易埋雷的地方

4.1 建表规范决定了生成质量

代码生成器的输入是数据库表,表设计得不规范,生成出来的代码就得大改。我总结了几个建表时必须遵守的约定:

  • 每张表必备五个审计字段:create_dept、create_by、create_time、update_by、update_time,加上逻辑删除的del_flag。这几个字段在基类里已经有定义,表里没有的话实体继承会报错。
  • 主键统一用bigint自增或者雪花 ID,字段名统一id。
  • 字段注释必须写,生成器会把注释直接变成前端表单的 label 和接口文档的说明。不写注释,前端就是一堆field1、field2。
  • 布尔语义用char(1)存0/1,而不是tinyint,因为框架的字典机制和前端开关组件默认按字符处理。
  • 金额一律decimal(18,2),别用double,浮点误差在对账场景是致命的。

4.2 生成的完整操作路径

登录后进"系统工具 → 代码生成",流程是:选择数据源 → 导入表 → 编辑生成配置 → 预览 → 生成。

编辑配置这一步有几个字段要填对:包名决定代码落在哪个包下,一般填com.xxx.模块名;模块名是 URL 前缀,比如填order,接口路径就是/order/xxx;业务名是类名的核心部分,填OrderItem,生成的类就是OrderItemController、OrderItemServiceImpl;作者随便填,会出现在类注释里;菜单上级决定生成的菜单挂在哪。

生成方式建议先用"下载"而不是"生成到项目",下载下来自己看一遍再往项目里拷。原因很实际:直接生成到项目会覆盖同名文件,如果你之前手改过,改动就没了。

4.3 生成之后必须手工改的五处

生成器给的是骨架,直接拿去用会出事。下面这五处是我每次都会动的:

  1. 补参数校验注解。生成的 BO 类里字段基本是裸的,@NotBlank、@NotNull、@Size、@Pattern要按业务补上,否则接口能接收任何脏数据。
  2. 改权限标识。生成的权限字符串是按模板拼的,比如order:orderItem:list,如果和你们团队的命名规范不一致,趁早改,晚了改起来涉及菜单表、角色关联表一起动。
  3. 检查数据权限注解。默认是不加的,业务上需要行级隔离的接口要手工补@DataPermission。
  4. 补业务校验。生成的insertByBo里只有基础的唯一性检查(而且还是要你自己确认字段),像"同一订单下商品不能重复"这种业务约束,生成器管不了。
  5. 前端路由与字典。菜单 SQL 要手工执行,字典类型和字典数据要自己建,前端页面里用到的下拉选项才能正常显示。

心得:我一般会把生成器的模板改一改,让它在生成时就带上统一的日志注解、统一的权限前缀、统一的校验分组。改模板一次,后面每张表都省事。模板文件在generator模块的 resources 目录下,是 freemarker 格式,改之前记得备份。

5. 缓存、分布式锁与外部服务适配

5.1 缓存注解的 key 设计与失效策略

框架对 Spring Cache 做了一层封装,用注解就能把方法结果写进 Redis:

@Cacheable(cacheNames = "sys_dict", key = "#dictType") public List<SysDictDataVo> selectDictDataByType(String dictType) { return baseMapper.selectDictDataByType(dictType); }

cacheNames是缓存的"命名空间",在 Redis 里会体现为 key 前缀。key 的设计原则是能唯一定位、能批量清理。比如字典用dictType,租户相关的必须把租户 ID 拼进去,否则 A 租户的缓存会被 B 租户读到,这是多租户项目里最常见的数据串门事故。

清理的时机同样关键。字典数据改动后必须@CacheEvict,或者用框架的缓存管理页面手动清。我见过一个项目,字典改了但前端死活不刷新,查了两小时发现是缓存没清——后来他们在字典的增删改方法上都加了@CacheEvict(cacheNames = "sys_dict", allEntries = true),问题绝迹。

粒度上有个经验:列表查询慎用@Cacheable。列表通常带分页和一堆筛选条件,key 组合爆炸,命中率低还占内存。真正适合缓存的是"变更频率低、查询频率高、结果集小"的数据,字典、配置参数、地区树、权限菜单这几类是典型。

5.2 分布式锁的加锁粒度与超时设置

单机环境用synchronized就够了,但项目一上集群就会出问题。框架集成了 Lock4j,注解式加锁:

@Lock4j(keys = "#bo.id", expire = 10000, acquireTimeout = 3000) public boolean updateStock(StockBo bo) { // 扣减库存逻辑 }

三个参数的含义要搞清楚。keys是锁的粒度,用 SpEL 表达式取业务 ID,保证同一个资源才互斥,不同资源并行;expire是锁的自动释放时间,防止持锁进程崩溃后死锁;acquireTimeout是获取锁的最长等待时间,超时抛异常,避免请求全部堆积。

expire设多少合适?我的经验值是业务执行耗时的三到五倍。设太短,业务没跑完锁就释放了,等于没锁;设太长,一旦进程挂掉,这段时间内所有请求都进不来。所以更稳妥的做法是配合看门狗续期,Lock4j 支持配置自动续期,业务没结束就自动延长锁的过期时间。

另一个常见误区是锁的范围。把@Lock4j加在 Controller 上没有任何意义,因为 Controller 里通常没有事务边界,锁释放了事务可能还没提交,其他线程读到旧数据。锁必须加在带@Transactional的 Service 方法内部,且锁的范围要包住整个事务。

5.3 对象存储、短信、邮件的适配层怎么用

这三块框架都做了抽象,核心是一个配置开关加一组实现类。以对象存储为例,配置里选择minio、s3、oss之类,填好 endpoint、bucket、密钥,业务代码统一注入OssTemplate调用,换供应商的时候只改配置不动代码。这个设计很实用,尤其是项目交付时甲方指定存储厂商的情况。

短信和邮件同理,都在common下对应模块里,配置好模板和签名就能用。要注意的是发送类操作一律异步化,用@Async或者丢进队列,别在主流程里同步等第三方接口,一个超时就能把整个请求拖垮。异步线程池的拒绝策略也要配,默认的AbortPolicy在高峰期会直接抛异常,改成CallerRunsPolicy或者自定义队列长度更稳。

6. 任务调度与工作流的工程化落地

6.1 定时任务的两套方案怎么选

框架同时支持 Quartz 和 XXL-JOB。判断标准很直接:单机或小规模集群用 Quartz 就够了,任务多、需要独立调度中心、需要失败重试和日志追踪的用 XXL-JOB。

Quartz 的坑主要在集群模式。多实例部署时如果不配置数据库锁,同一个任务会在每个节点各跑一遍,导致重复扣款、重复发消息这类事故。启用集群需要在配置里打开org.quartz.jobStore.isClustered=true,并配好clusterCheckinInterval,框架的配置模板里都有,照着填就行。

更隐蔽的问题是任务幂等。调度器保证"至少执行一次",不保证"只执行一次",网络抖动重试就会重复跑。所以任何任务方法内部都要自己做幂等:加分布式锁、或者用状态位判断、或者在数据库层面加唯一约束。这一点无论用哪套调度器都一样。

6.2 工作流模块的适用边界

流程引擎这块要泼点冷水。如果需求就是"提交-审批-结束"这种两级直线流程,用工作流引擎反而是杀鸡用牛刀,一张biz_approval表加状态机实现起来更快更好维护。真正需要引擎的场景是:流程节点动态可变、审批人由规则计算、需要会签/或签/退回/加签、需要流程图可视化。这些需求自己实现成本极高,用引擎才划算。

接入时要注意表结构。流程引擎会往库里建几十张ACT_或类似前缀的表,务必和业务表分开放,最好单独建库或者至少加前缀区分,不然后期维护看到一堆看不懂的表会崩溃。另外流程定义文件是 XML 或 BPMN 模型,建议纳入版本管理,每次发布变更留痕。

7. 打包部署:从本地能跑到线上能扛

7.1 后端打包与环境隔离

后端用 Maven 打包,命令是mvn clean package -DskipTests -P prod,-P指定 profile,不同 profile 对应的配置文件会被打进 jar。这里有个细节:不要在打包时才改配置,应该把环境差异全部放进配置中心或者外部配置文件,jar 包本身保持环境无关。

启动命令建议加上 JVM 参数:

java -Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \ -Dspring.profiles.active=prod \ -jar ruoyi-admin.jar

-Xms和-Xmx设成一样大,避免运行时堆伸缩带来的抖动;G1 在 2G 到 8G 这个区间表现稳定;MaxGCPauseMillis是目标值不是硬保证,设 200 比较通用。容器部署时要注意,JDK 17 的容器感知默认开启,堆大小会按容器 limit 自动算,这时候显式设-Xmx反而可能导致 OOM Kill,要么把 limit 设得比-Xmx大 25%,要么干脆不设交给 JVM 自己算。

7.2 前端打包与 Nginx 配置要点

前端pnpm build之后产出静态文件,扔给 Nginx。配置里最容易出错的是两处:history 路由的 fallback和接口代理。

location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://backend:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 120s; }

try_files ... /index.html这行不能少,否则用户刷新非首页路径会 404。proxy_pass结尾的斜杠也有讲究,带斜杠会截掉/api前缀再转发,不带则原样转发,配错了就是一片 404。proxy_read_timeout建议调大,报表导出这类接口很容易超过默认的 60 秒。

静态资源要开 gzip 和缓存。JS、CSS 这类带 hash 的文件可以设一年强缓存,index.html必须设不缓存,否则用户永远拿到旧版本。

8. 排查速查:那些文档里不会写的问题

8.1 启动阶段高频问题

现象大概率原因处理方式
启动报找不到数据源profile 未激活或配置项缩进错误检查spring.profiles.active,YAML 缩进必须用空格
编译报找不到符号模块依赖没加或 JDK 版本不对检查 pom 依赖树,确认 JDK 17
连不上 MySQL缺allowPublicKeyRetrieval或时区未指定URL 补全参数,serverTimezone必填
Redis 连接超时密码错或 bind 配置限制先用 redis-cli 手工连一次验证
端口占用上次进程没退干净lsof -i:8080找到后 kill
前端安装依赖失败Node 版本或镜像源问题切 LTS 版本,检查 registry 配置

8.2 运行阶段的高频问题

登录成功但菜单空白。这条占了我处理过的咨询量的一半。原因通常是后端返回的菜单树结构和前端期望的不一致,或者动态路由注册失败。排查顺序:先看浏览器 Network 里菜单接口的返回,确认data不为空;再看前端路由注册的日志,Vue Router 注册失败会有警告;最后检查菜单表里的path、component字段,component必须和前端views目录下的相对路径严格对应,多一层少一层都渲染不出来。

接口 401 但令牌明明没过期。八成是clientid没带或者带了不一致的值。多端登录的令牌是分开存的,PC 端登录拿的令牌拿去调小程序的接口,必然 401。还有人把令牌放在了自定义 header 里,前端拦截器配置和网关读取的 header 名对不上,也会这样。

分页查询总数不对。多表 JOIN 时分页插件生成 count 语句可能把一对多的关联算重了。解决方式是在 mapper 的查询方法上关掉 count 自动优化,或者手写 count 语句。

导出 Excel 中文乱码。前端下载时没设responseType: 'blob',或者后端响应头里的文件名编码方式不对。文件名要 URL 编码,Content-Disposition里用filename*=UTF-8''的写法。

时间字段差八小时。三个地方要同时对齐:数据库连接的serverTimezone、JVM 的默认时区(启动参数加-Duser.timezone=Asia/Shanghai)、以及序列化配置里是否指定了时区。三处有一个不对就会偏。

改了代码不生效。开发环境开了热部署但没生效,检查 IDE 的自动编译是否开启、spring-boot-devtools是否引入、以及 IDEA 里Build project automatically的勾选状态。生产环境则是缓存问题,先确认是不是 Nginx 缓存了旧的静态资源。

我个人在实际操作中的体会是,这套框架真正的价值不在于省了多少行代码,而在于它把一堆"正确做法"固化成了默认行为——默认加了日志切面、默认做了参数校验、默认用雪花 ID 而不是自增、默认把敏感字段加密存储。新手照着它写出来的代码,比自己从零搭出来的要规范得多。但也正因为默认行为太多,出问题时容易找不到是哪一层在起作用。我的建议是,接手或者新起一个项目时,花两天时间专门读common模块的源码,把全局异常处理、日志切面、权限拦截这三条链路画一遍,后面遇到任何诡异现象,你都能快速判断是框架层的锅还是业务代码的锅。这两天不亏,属于投入产出比最高的时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询