Spring Boot 通用后台管理系统:解压、跑通与二次开发实战指南
简介这是一份基于JavaSpringBoot的通用后台管理系统源码包面向需要快速搭建管理后台的Java开发者与初学者。项目整合了自动配置、数据持久化、权限控制、页面模板渲染等SpringBoot典型场景代码结构清晰适合用于学习企业级项目的分层与模块组织。压缩包共包含152个文件以129个Java类为核心覆盖服务层、工具类与控制器另有少量XML配置、properties属性文件、Gradle构建脚本、SQL数据库脚本及说明文档整体仅623KB。资源中包含用户服务、文件处理等实际功能模块并附有设计文档与数据库脚本通过阅读源码可以熟悉常用注解、事务处理与接口规范能够帮助读者理解从表结构设计到接口实现的完整链路。已有109人学习下载适合结合SpringBoot官方文档逐步阅读也可作为毕业设计或通用后台管理系统的开发参考。1. springboot 通用后台管理系统.zip一套能直接起步的脚手架但别急着启动到手一个 springboot 通用后台管理系统.zip最常见的场景是业务系统还没影运营后台得先立起来。用户登录、角色分配、菜单权限、操作日志、字典管理这些每套后台都绕不开的功能从零写要耗掉一周多而这个压缩包里这套底子已经铺好。这类系统形态高度统一Spring Boot 做底座MyBatis-Plus 管数据库Redis 扛缓存与登录态权限走 RBAC用户-角色-菜单再配一个一键生成增删改查代码的生成器。它解决的不是具体业务而是后台管理系统这个重复度极高的通用需求。如果你是给新项目搭管理端或者被要求一周内把后台跑起来这套骨架能帮你省下起步时间。但记住它只是骨架业务得自己长。拿到包先别急着启动花十分钟看明白结构、想清楚要改哪里后面才少踩坑。2. 拆解通用后台管理系统的核心权限模型、技术栈与目录结构这类系统虽然叫通用但每套的模块切分、权限实现、配置约定都不一样。我个人的习惯是解压后先别执行任何启动命令花半小时看三件事权限怎么建模、技术栈选了哪套、代码往哪个包放。这三件事决定了你后面加业务模块时SQL 写在哪、注解加在哪、前端页面挂在哪。2.1 RBAC 权限模型用户、角色、菜单五张表怎么串起来几乎所有 springboot 后台系统的权限模型都是 RBACRole-Based Access Control区别只是表和字段命名。核心是五张表用户表、角色表、菜单表加上用户-角色、角色-菜单两张关联表。菜单表不只是导航菜单它同时承载了按钮权限——页面上某个按钮能不能点、某个接口能不能调都靠这张表控制。-- RBAC 核心五张表字段按常见后台系统的约定精简 CREATE TABLE sys_user ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, status TINYINT DEFAULT 1, -- 1启用 0禁用 create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_username (username) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4; CREATE TABLE sys_role ( id BIGINT AUTO_INCREMENT PRIMARY KEY, role_key VARCHAR(50) NOT NULL, -- 角色标识如 admin、common role_name VARCHAR(50) NOT NULL, status TINYINT DEFAULT 1 ) ENGINE InnoDB DEFAULT CHARSET utf8mb4; CREATE TABLE sys_menu ( id BIGINT AUTO_INCREMENT PRIMARY KEY, parent_id BIGINT DEFAULT 0, -- 父节点 id0 表示顶级 menu_name VARCHAR(50) NOT NULL, path VARCHAR(200), -- 前端路由路径按钮可为空 perms VARCHAR(100), -- 权限标识如 system:user:add menu_type TINYINT DEFAULT 1, -- 1目录 2菜单 3按钮 sort INT DEFAULT 0 ) ENGINE InnoDB DEFAULT CHARSET utf8mb4; -- 用户和角色多对多一张关联表解决 CREATE TABLE sys_user_role ( user_id BIGINT NOT NULL, role_id BIGINT NOT NULL, PRIMARY KEY (user_id, role_id) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4; -- 角色和菜单多对多权限标识从菜单表带出来 CREATE TABLE sys_role_menu ( role_id BIGINT NOT NULL, menu_id BIGINT NOT NULL, PRIMARY KEY (role_id, menu_id) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4;这五张表的运行链路是用户登录后系统先从 sys_user_role 查出该用户的角色集合再从 sys_role_menu 查出这些角色关联的菜单把菜单里的 perms 字段汇总成一个权限字符串集合缓存到 Redis。之后每次请求进 Controller 前鉴权拦截器校验方法上的权限注解比如 RequiresPermissions(system:user:add)是否在这个集合里。这条链路决定了你改权限时动哪张表给用户加角色就是插 sys_user_role给角色加功能就是插 sys_role_menu。sys_menu 的 menu_type 三个值要分清目录1只用于左侧菜单分组不挂具体页面菜单2对应一个路由页面点击跳转按钮3不参与路由渲染只参与权限校验。很多新手把按钮权限理解成前端写死的开关其实按钮显隐是前端指令判断当前用户 perms 集合里有没有对应值数据库里删掉这条记录按钮立刻消失。2.2 技术选型Spring Boot MyBatis-Plus Redis 为什么是标配这套系统的技术栈几乎是固定的换汤不换药。我把最常见的选型和各自干的活整理成一张表组件常见选型主要作用基础框架Spring Boot 2.x自动装配、内嵌容器、起步依赖持久层MyBatis-Plus单表 CRUD 免写 SQL、分页插件、代码生成器缓存Redis登录态、验证码、在线用户、字典缓存认证授权Shiro 或 Spring Security JWT登录认证、接口鉴权接口文档Swaggerdoc.html 或 swagger-ui前后端联调看接口定义数据库MySQL 5.7 / 8.0业务数据持久化Spring Boot 能成为底座靠的是自动配置和 starter 生态。引入一个依赖就自动配好数据源、事务、Web 容器不需要写一堆 XML。MyBatis-Plus 的价值在单表操作上继承 BaseMapper 后insert、update、selectById 这些常用方法直接有实现分页有插件实体类还能配合 Lombok 省掉 getter/setter。和 JPA 相比复杂查询的 SQL 完全可控和原生 MyBatis 相比日常开发效率高不少。Redis 在这套系统里不是可选项是必选项。登录后的 token 或会话信息放 Redis验证码放 Redis在线用户列表从 Redis 拉字典数据缓存也常用 Redis。本地没有装 Redis 的话项目大概率起不来或者登录后报错。认证授权选型上我见到的更多是 Shiro JWTShiro 的过滤器链概念直观JWT 无状态化适合前后端分离整体配置量小Spring Security 功能更强但配置复杂。你不需要纠结用哪个跟着项目走重点搞清楚它的过滤器链顺序和 token 刷新机制就够了。2.3 目录结构解压后先看哪几个包十分钟摸清家底典型工程结构长这样不管模块怎么拆大方向不变admin-system/ ├── pom.xml -- 父 POM锁定依赖版本 ├── sql/ -- 建库建表、初始化数据脚本 ├── src/main/java/com/xxx/admin/ │ ├── AdminApplication.java -- 启动类 │ ├── common/ -- 通用包返回值、异常、常量 │ ├── config/ -- 配置类MyBatis-Plus、Swagger、跨域 │ ├── security/ -- 认证授权过滤器链、JWT 工具 │ ├── modules/ │ │ ├── system/ -- 系统模块用户、角色、菜单、字典 │ │ │ ├── controller/ -- 接口层 │ │ │ ├── service/ -- 业务层 │ │ │ ├── mapper/ -- 数据访问层 │ │ │ └── entity/ -- 实体类 │ │ └── monitor/ -- 监控模块操作日志、在线用户 │ └── utils/ -- 工具类 ├── src/main/resources/ │ ├── application.yml -- 主配置 │ ├── application-dev.yml -- 开发环境配置 │ └── mapper/ -- 手写 XML复杂查询才用 └── web/ -- 前端 Vue 工程看包顺序有讲究。先看 pom.xml确认 JDK 版本和 Spring Boot 基线再看 sql 目录知道要建什么库、默认账号在哪然后看 application.yml 和 application-dev.yml确定数据源和 Redis 地址接着进 modules/system 挑一个模块从上往下读一遍掌握这套代码的写法风格最后看 security 包搞明白登录认证的过滤链。这五个位置看完项目在你手里就不是黑匣子了。提示不要试图逐文件读源码。最高效的方式是跟着一条请求链路走登录接口 - 过滤器 - Controller - Service - Mapper - SQL一条线读完整个项目的代码风格就清楚了。3. 本地跑通从建库到启动的完整流程本地跑通一套 springboot 通用后台管理系统步骤就四步准备环境、初始化数据库、改配置、启动验证。大部分人在第一步图省事结果启动时报一堆版本错误。按顺序来每一步都验证完再走下一步能省掉后面大量排查时间。3.1 环境准备JDK、MySQL、Redis、Maven 版本怎么匹配先确认本机环境再解压折腾。我一般先把下面四个命令跑一遍把版本基线拍下来再动手。# 版本确认四连任何一个缺失或版本不对都先补上 java -version # 需要 JDK 1.8 或 11具体以 pom.xml 里的 java.version 为准 mvn -v # Maven 3.6低于 3.5 可能解析不了部分插件 mysql --version # 5.7 或 8.0 都行驱动配置不一样 redis-cli ping # 返回 PONG 表示 Redis 正常JDK 版本不匹配时编译阶段就会报错错误信息往往指向某个语法不兼容或依赖无法解析。MySQL 版本的坑集中在驱动类上8.0 用 com.mysql.cj.jdbc.Driver5.7 用 com.mysql.jdbc.Driver配置写错直接连接失败。Redis 5.x 以上对这套系统基本兼容本地开发单机版就够了不需要搭集群。Maven 依赖下载慢是最耗耐心的环节。检查一下本地 Maven 是否配置了国内镜像没有的话配一个否则拉一个几百 MB 的依赖能等半小时。这里不要用中文路径存放项目Maven 对中文路径的处理经常翻车编译到一半报路径错误找半天找不到原因。3.2 初始化数据库SQL 脚本执行顺序与字符集坑打开 sql 目录先看脚本再决定怎么执行。常见套路有两种一个 init.sql 同时包含建库、建表、初始化数据或者拆成 schema.sql 和 data.sql 两个文件。执行前用 head 瞄一眼脚本开头确认是不是带 CREATE DATABASE。# 先看脚本头部确认是建库脚本还是纯建表脚本 head -n 20 sql/init.sql # 如果脚本里有 CREATE DATABASE直接执行整个文件 mysql -u root -p sql/init.sql # 如果脚本只有建表语句先建库再按顺序导表 mysql -u root -p -e CREATE DATABASE IF NOT EXISTS admin_db DEFAULT CHARACTER SET utf8mb4; mysql -u root -p admin_db sql/schema.sql mysql -u root -p admin_db sql/data.sql执行顺序不能乱先建库再建表最后导数据。脚本里通常不会建物理外键但表之间的引用关系依赖初始化顺序顺序错了后面的 insert 会找不到主子表关系。字符集必须用 utf8mb4用户名、菜单名、字典值都可能出现生僻字或特殊符号utf8 在部分 MySQL 版本上连 emoji 都存不了会报 Incorrect string value。最容易翻车的操作是重复执行脚本。第二次跑同一个 init.sqlMySQL 会报 table already exists。这时候不要手动去删几张表——后续数据对不上更闹心。直接 drop database 再来一遍干净利落。这也是为什么初始化数据里如果包含默认管理员账号每次重置后密码都会回到初始值正式环境别频繁重置。3.3 修改 application.yml数据源、Redis、日志一个都不能漏多数系统的配置拆成了公共配置加环境配置两份本地开发改开发环境那份。核心配置项集中在这几块server: port: 8080 # 后端端口被占用就改这里 spring: datasource: # 数据库连接串serverTimezone 必须带否则启动报时区错误 url: jdbc:mysql://localhost:3306/admin_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root # 改成你的数据库账号 password: 123456 # 有特殊字符时用引号包起来 driver-class-name: com.mysql.cj.jdbc.Driver # MySQL 8 用这个5.7 换成 com.mysql.jdbc.Driver redis: host: localhost # 远程部署时改成内网 IP port: 6379 password: # 本地没密码就留空有密码必须填 database: 0 # 多个项目共用 Redis 时错开库号 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: # 开发期打印 SQL 到控制台上线记得删掉或改成不输出 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl改配置时下面五个位置是必须核对的重点配置项说明不改会怎样spring.datasource.username / password数据库账号密码启动报 Access denied 或连接超时JDBC URL 里的 serverTimezone时区参数报时区无法识别异常信息还是乱码spring.redis.passwordRedis 密码Redis 有密码时不填认证直接失败mybatis-plus 的 log-impl开发期 SQL 日志上线不删会刷爆日志文件server.port后端端口端口被占时启动失败YAML 里密码带特殊字符是个隐蔽坑。如果数据库密码里含 、#、: 这些字符不加引号会被 YAML 解析器截断或当作注释处理。建议统一用单引号包起来。另外mybatis-plus 的 log-impl 在开发时开着有用能看到每条 SQL 和参数但上线前一定要关掉否则高并发下日志输出本身就是性能瓶颈。3.4 启动验证日志、Swagger、默认账号三连看配置改完就能启动了。找到启动类通常叫项目名加 Application直接运行它的 main 方法。看控制台日志确认三件事# 启动成功的标志性日志注意端口号 # Started AdminApplication in 8.32 seconds # Tomcat started on port(s): 8080看到 Started 和 Tomcat started 这两行说明后端已经起来了。接下来验证接口层浏览器访问 Swagger 文档路径两种都试一下/doc.html 或 /swagger-ui/index.html哪个能开就用哪个。在 Swagger 里找到登录接口用初始化数据里的默认账号调一次。默认账号一般在 data.sql 的 insert 语句里常见是 admin 加一串初始化密码以脚本实际内容为准不要凭经验猜。前端是独立的 Vue 工程在 web 目录下执行 npm install 和 npm run dev。这里注意 node 版本个别系统对 node 版本敏感版本差太多会出现编译报错或页面白屏。前端 dev server 默认端口通常是 80 或 8081通过代理转发到后端 8080。前端登录后如果一直转圈优先看浏览器控制台里代理请求通不通这是前后端联调最常见的问题点。4. 二次开发实战给通用后台管理系统新增一个业务模块后台跑通只是开始真正要做的是加业务。拿订单管理举例完整链路是建表 - 代码生成器生成三层代码 - 补一个自定义查询 - 挂菜单和按钮权限。别跳步顺序乱了后面补起来很费劲尤其是权限挂载接口写完了菜单没挂前端根本点不到。4.1 建表与代码生成器一张业务表生成全套三层代码第一步建业务表。表名前缀最好和系统表区分开系统表是 sys_业务表约定用 biz_一眼就能认出归属。字段注释写清楚枚举值在注释里标明这些信息会直接带进生成的实体类和前端页面。-- 业务订单表前缀 biz_ 区分系统表 CREATE TABLE biz_order ( id BIGINT AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(32) NOT NULL COMMENT 订单编号, customer VARCHAR(50) NOT NULL COMMENT 客户名称, amount DECIMAL(10, 2) DEFAULT 0 COMMENT 订单金额, status TINYINT DEFAULT 0 COMMENT 0待处理 1已处理 2已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_order_no (order_no) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4 COMMENT 业务订单表;金额字段用 DECIMAL 不要用 DOUBLE这是财务类字段的硬规矩DOUBLE 的精度误差在累计计算时会放大。主键统一 BIGINT 自增状态字段加注释枚举值这些约定会影响生成器生成的实体类型。第二步用代码生成器。这套系统自带生成器老一代的形态是 MyBatis-Plus 的 AutoGenerator一个独立的 Java main 方法配置如下// 代码生成器MyBatis-Plus AutoGenerator一次生成三层代码 public class CodeGenerator { public static void main(String[] args) { AutoGenerator generator new AutoGenerator(); // 数据源必须和 application.yml 保持一致否则生成的代码连不上库 DataSourceConfig dsc new DataSourceConfig(); dsc.setUrl(jdbc:mysql://localhost:3306/admin_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai); dsc.setDriverName(com.mysql.cj.jdbc.Driver); dsc.setUsername(root); dsc.setPassword(123456); generator.setDataSource(dsc); // 包名配置parent moduleName 决定代码落在哪个包 PackageConfig pc new PackageConfig(); pc.setParent(com.example.admin); pc.setModuleName(order); // 模块名生成到 modules/order 下 pc.setEntity(entity); pc.setMapper(mapper); pc.setService(service); pc.setController(controller); generator.setPackageInfo(pc); // 表策略只生成 biz_order 这一张表去掉 biz_ 前缀 StrategyConfig strategy new StrategyConfig(); strategy.setInclude(biz_order); // 不指定就是全库生成容易把 sys_ 表也改了 strategy.setTablePrefix(biz_); // 去掉前缀实体类叫 Order 而不是 BizOrder strategy.setEntityLombokModel(true); // 实体用 Lombok省去手写 getter/setter generator.setStrategy(strategy); generator.execute(); } }四个关键参数要理解setModuleName 决定代码的包路径和前端页面目录改成 order 就是 modules/order 包setTablePrefix 去掉表前缀实体的命名更干净setEntityLombokModel 开了之后实体类只留字段声明getter/setter 由 Lombok 在编译期补全setInclude 指定生成哪几张表这是新手最容易翻车的地方——不指定的话默认生成库中所有表把 sys_user、sys_role 这些系统表也重新生成一遍覆盖掉原来的实体直接编译报错。生成产物很完整entityOrder、mapperOrderMapper 和 XML、serviceOrderService 和实现类、controllerOrderController如果生成器带了前端模板还会产出一套 Vue 的列表页、表单弹窗和 API 定义文件。这套代码是能直接编译运行的只是查询逻辑偏基础。4.2 手工补代码Controller 自定义查询与权限注解生成的 Controller 只有最基础的 CRUD满足不了真实业务查询。比如订单列表要按订单号模糊查、按状态过滤、按时间倒序就得手写一个分页查询接口RestController RequestMapping(/biz/order) public class BizOrderController { Autowired private BizOrderService orderService; /** 分页查询订单号模糊 状态精确过滤 */ GetMapping(/page) RequiresPermissions(biz:order:list) // 权限标识必须和 sys_menu 表里的 perms 一致 public ResultIPageBizOrder page(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, String orderNo, Integer status) { PageBizOrder page new Page(pageNum, pageSize); LambdaQueryWrapperBizOrder qw new LambdaQueryWrapper(); // 订单号模糊查询先判空再拼条件避免无条件 SQL if (StringUtils.hasText(orderNo)) { qw.like(BizOrder::getOrderNo, orderNo); } if (status ! null) { qw.eq(BizOrder::getStatus, status); } // 按创建时间倒序新订单排前面 qw.orderByDesc(BizOrder::getCreateTime); return Result.ok(orderService.page(page, qw)); } }LambdaQueryWrapper 的好处是字段名用方法引用写错了编译期就报错不会拖到运行时。like 和 eq 前必须判空前端不传参时不拼条件否则会出现查询条件被空值干扰的问题。RequiresPermissions 是权限检查点后面括号里的字符串必须和 sys_menu 表里配置的 perms 完全一致多一个空格都会鉴权失败。前端目录、菜单标识、接口注解三处要对应这是权限系统的铁律。业务逻辑不要堆在 Controller 里。以订单状态流转为例批量完成订单这种操作涉及状态校验和多条更新必须放在 Service 层并加事务Service public class BizOrderServiceImpl extends ServiceImplBizOrderMapper, BizOrder implements BizOrderService { /** 批量完成订单状态从 0 变为 1带事务保障 */ Transactional(rollbackFor Exception.class) public void finishOrders(ListLong ids) { ListBizOrder orders listByIds(ids); for (BizOrder order : orders) { // 业务校验只有待处理状态才能完成否则抛异常回滚 if (order.getStatus() ! 0) { throw new ServiceException(订单 order.getOrderNo() 不是待处理状态); } order.setStatus(1); } // 批量更新只更新 status 字段避免覆盖其他列 updateBatchById(orders); } }Transactional 的 rollbackFor 必须写成 Exception.class。不写的话Spring 默认只在抛出 RuntimeException 时才回滚如果你的业务异常被 try-catch 吃掉或者抛的是受检异常事务不会回滚数据就会处于半完成状态。updateBatchById 默认按主键更新而且只更新非空字段这是 MyBatis-Plus 的更新策略注意不要在实体上随手给字段赋默认值否则会把不想改的列也覆盖掉。4.3 挂菜单sys_menu 插入记录与前端路由同步后端接口写好了前端得能访问到。这套系统的菜单是从数据库加载的所以往 sys_menu 插记录左侧菜单和按钮权限会一起生效。插入顺序是先插菜单拿到菜单 id再插按钮。-- 1. 插入菜单parent_id0 表示顶级menu_type2 是菜单 INSERT INTO sys_menu (parent_id, menu_name, path, perms, menu_type, sort) VALUES (0, 订单管理, /biz/order, NULL, 2, 10); -- 2. 用 LAST_INSERT_ID() 拿到刚插入的菜单 id作为按钮的父节点 SET menu_id LAST_INSERT_ID(); INSERT INTO sys_menu (parent_id, menu_name, perms, menu_type, sort) VALUES (menu_id, 订单查询, biz:order:list, 3, 1), (menu_id, 订单新增, biz:order:add, 3, 2), (menu_id, 订单删除, biz:order:remove, 3, 3);menu_type 2 的菜单记录path 必须和前端路由文件里注册的路径一致不一致就会 404。menu_type 3 的按钮记录不参与路由渲染只进权限集合。perms 要和 Controller 注解严格对应比如 biz:order:list 对应分页查询接口。前端按钮显隐用的是指令如 v-hasPermi它判断的是当前用户 perms 集合里有没有对应值数据库里删掉这条按钮记录前端按钮立刻消失。前端部分要看这套系统是动态路由还是静态路由。判断方法登录后接口有没有返回一个类似 /getRouters 的菜单树接口有就是动态路由菜单表插完、页面文件创建好刷新就能看到没有就是静态路由还得去 router/index.js 里手动注册。这一步是新手最容易卡住的地方——菜单插了但页面 404先查路由类型。5. 高频坑排查记录启动失败与运行异常的五个案例下面五个问题是我处理这类系统时遇到最多的按现象 - 原因 - 解决写。遇到问题先对照别急着重装环境。绝大多数时候就是一个配置项的问题改完重启就好。5.1 数据库连接失败时区、驱动、密码三连坑现象启动日志报 Cannot create PoolableConnectionFactory 或 Communications link failure后面跟着一串连接 URL。原因三个原因按概率排——JDBC URL 没带 serverTimezone 参数、MySQL 版本和驱动类不匹配、账号密码写错。时区问题最隐蔽报错信息里会出现乱码时区名看着像编码问题实际就是缺参数。解决URL 里加上 serverTimezoneAsia/Shanghai核对驱动类——MySQL 8 是 com.mysql.cj.jdbc.Driver5.7 是 com.mysql.jdbc.Driver密码在 YAML 里有特殊字符就加单引号。改完重启正常情况下 10 秒内连接池就初始化成功日志里能看到连接池创建完成的提示。5.2 Redis 连接被拒与序列化异常现象启动报 Unable to connect to Redis或者登录成功后拉取用户信息时抛 SerializationException。原因前者十有八九是 Redis 没启动或者 application.yml 里的密码和实际不一致。后者是缓存序列化方式不匹配——系统默认用 JDK 序列化二次开发时有人改成 JSON 序列化但 Redis 里还留着旧的缓存数据读出来反序列化失败。这类报错最玄学因为换个环境可能就复现不了。解决先执行 redis-cli ping 确认 Redis 活着再核对 redis.password 配置。序列化异常不要纠结代码先把 Redis 缓存清掉执行 flushdb再重启应用。缓存相关的问题清缓存永远是最快的验证手段——清完就好的基本就是新旧数据格式冲突清完还报才是代码问题。5.3 登录后菜单空白或接口全 401现象前端登录成功但左侧菜单空荡荡调任何业务接口都返回 401 或 403。原因三层可能——用户没绑角色、角色没绑菜单、前端拿菜单树的接口报错。菜单空白通常不是前端 bug而是当前用户权限集为空后端返回了空菜单树。解决用 SQL 顺着用户把角色和菜单查一遍定位断在哪一层-- 从用户出发查角色 SELECT r.role_key FROM sys_user u JOIN sys_user_role ur ON u.id ur.user_id JOIN sys_role r ON r.id ur.role_id WHERE u.username admin; -- 从角色出发查菜单 SELECT m.* FROM sys_role r JOIN sys_role_menu rm ON r.id rm.role_id JOIN sys_menu m ON m.id rm.menu_id WHERE r.role_key admin;哪一层查出来是空的就补哪一层。另外检查 token 有效期配置。有的系统默认 token 过期时间极短比如 30 分钟前端页面放着不动再操作就过期表现也是 401。把有效期调长或者加上刷新机制问题就消失。5.4 代码生成器生成的代码编译不过现象生成完代码一编译报一串红——实体类找不到 getXxx 方法或者 Mapper XML 提示 Invalid bound statement。原因两个独立问题。实体类用了 Lombok 但开发工具没开启注解处理生成的 class 里就没有 getter/setterXML 的 namespace 或 resultMap 类型全限定名和实体类包名不一致。解决在 IDE 的编译设置里打开 Annotation Processors安装 Lombok 插件然后重新构建。XML 的 namespace 改成 Mapper 接口的全限定名比如 com.example.admin.modules.order.mapper.BizOrderMapper。这个坑在生成器换包名后特别常见——你在 CodeGenerator 里改了 moduleName生成的 XML 文件里的路径没跟着更新手动同步一下就行。5.5 Swagger 打不开或 token 没带上现象访问 /doc.html 白屏或 404另一种是登录后在 Swagger 里调业务接口一直报 401。原因路径不对——系统用的是 Swagger 增强版时路径是 doc.html原生 Swagger 则是 swagger-ui/index.html。401 是因为登录拿到的 token 没有填到全局认证参数里而是随手贴在了某个查询接口的参数框里其他接口自然没有携带。解决两种路径都试一下确定系统用哪个。调通登录接口拿到 token 后把 token 填到 Swagger 页面右上角的 Authorize 或全局参数设置里填完后所有接口请求会自动带上 Authorization 头。注意 token 是否需要带 Bearer 前缀以登录接口的要求为准填错了照样 401。6. 再进一步操作日志埋点与多环境打包的两个技巧6.1 用 AOP 注解给所有 Controller 加操作日志大部分后台系统都有操作日志表但默认只有登录、改密码几个接口在记录。给所有 Controller 方法手动加日志代码侵入性太强也不利于维护。常见做法是自定义一个注解加 AOP 切面业务代码零侵入// 自定义操作日志注解标在方法上value 描述操作 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface OperLog { String value(); // 操作说明如新增订单 } // 切面方法执行后记录操作人、耗时、参数摘要 Aspect Component public class OperLogAspect { Around(annotation(operLog)) public Object around(ProceedingJoinPoint pjp, OperLog operLog) throws Throwable { long start System.currentTimeMillis(); Object result pjp.proceed(); long cost System.currentTimeMillis() - start; String username SecurityUtils.getUsername(); // 从当前登录上下文取 String params buildParams(pjp.getArgs()); // 参数序列化前要过滤请求对象 saveOperLog(operLog.value(), username, cost, params); return result; } }两个设计要点参数序列化时HttpServletRequest、HttpServletResponse 这类对象不能直接 toString会报序列化异常或把无意义的内存地址写进日志要先过滤掉日志落库最好异步执行放在请求链路里会增加接口耗时用 Async 或消息队列都能解决。对应的日志表结构很简单CREATE TABLE sys_oper_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50), operation VARCHAR(100), cost_ms BIGINT, params TEXT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE InnoDB DEFAULT CHARSET utf8mb4;6.2 Maven Profile 多环境打包告别上线前改配置开发、测试、生产三套配置每次打包前手动改一遍早晚会出事故。用 Maven Profile 加占位符替换一条命令打出对应环境的包!-- pom.xml 里定义两个环境打包时用 -P 指定 -- profiles profile iddev/id propertiesactive.profiledev/active.profile/properties /profile profile idprod/id propertiesactive.profileprod/active.profile/properties /profile /profilesspring: profiles: active: active.profile # 这个占位符在打包时被替换成 dev 或 prod# 打包生产包并启动-DskipTests 跳过测试 mvn clean package -P prod -DskipTests java -jar target/admin-system.jar --spring.profiles.activeprod注意 pom.xml 里必须有 resources 的 filtering 配置否则占位符不会被替换启动时会报找不到配置文件。生产环境更稳的做法是在启动命令上加 --spring.config.location 指向服务器上的外部配置文件这样改配置不用重新打包。我现在拿到任何一套新脚手架都会按固定顺序做三件事先备份原版 SQL 和生产配置再删掉示例业务代码最后把代码生成器的配置单独存一份。前两件事保证随时能回退第三件事保证下一个模块不用重新调生成参数。这套流程帮我省掉的返工时间比写代码的时间还多。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Folia 顶栏歌词:lia.lines 插件接入 omarchy 4 桌面

Folia 顶栏歌词:lia.lines 插件接入 omarchy 4 桌面

Folia 顶栏歌词:lia.lines 插件接入 omarchy 4 桌面 【免费下载链接】folia-major 专注于绚丽的歌词动画效果的本地音乐/navidrome/第三方多平台在线音乐播放器 项目地址: https://gitcode.com/GitHub_Trending/fo/folia-major 用 lia.lines 插件&#xff0c…

2026/10/15 7:22:56 阅读更多 →
YourNextStore 性能优化:基于用户意图的 Bundle 预加载(Preload Based on User Intent)实践指南

YourNextStore 性能优化:基于用户意图的 Bundle 预加载(Preload Based on User Intent)实践指南

电商前端后端 【免费下载链接】yournextstore AI-Native Open-Source Next.js commerce. Powered by Stripe. Ultra fast with typesafe Commerce SDK. Built for AI development (Claude, Codex, Cursor). 项目地址: https://gitcode.com/gh_mirrors/yo/yournextst…

2026/10/15 7:22:56 阅读更多 →
YOLO26算法火灾场景火焰与烟雾目标检测+训练好的模型+8062张数据集+pyqt可视化界面

YOLO26算法火灾场景火焰与烟雾目标检测+训练好的模型+8062张数据集+pyqt可视化界面

YOLO26算法火灾场景火焰与烟雾目标检测训练好的模型8062张数据集pyqt可视化界面 这套数据共 8062 张图。用 YOLO26 训了 100 轮,mAP50 0.5484,mAP50-95 0.3301。Precision 0.5704、Recall 0.4935。下面按数据构成 → 训练曲线 → 预测效果的顺序过一遍&…

2026/10/15 7:22:56 阅读更多 →

最新新闻

低延迟趋势线LLT:从EMA到二阶滤波的交易择时Python实现

低延迟趋势线LLT:从EMA到二阶滤波的交易择时Python实现

简介:广发证券发布的《低延迟趋势线与交易择时》金融工程专题报告,面向技术分析者、量化策略研究员与短线交易者,针对传统移动平均线(MA)平滑性与延迟性难以兼顾的痛点,系统介绍低延迟趋势线(LL…

2026/10/15 7:52:01 阅读更多 →
224G PAM4收发器如何用3pJ/bit功耗突破40dB信道损耗

224G PAM4收发器如何用3pJ/bit功耗突破40dB信道损耗

简介:IEEE《固态电路期刊》2025年1月刊论文PDF,主题为3nm FinFET CMOS工艺的224 Gb/s PAM-4长距离收发器,面向高速串行接口、SerDes、集成电路与信号处理方向的研究人员与工程师。内容覆盖数据中心800G/1.6T交换机、AI加速器等高带宽需求&…

2026/10/15 7:52:01 阅读更多 →
HTTP与HTTPS核心差异及Python实现详解

HTTP与HTTPS核心差异及Python实现详解

1. HTTP与HTTPS基础概念解析HTTP(HyperText Transfer Protocol)和HTTPS(HyperText Transfer Protocol Secure)是每个Python开发者必须掌握的基础网络协议。我在实际面试候选人时发现,90%的初级开发者能说出"HTTPS…

2026/10/15 7:52:01 阅读更多 →
CentOS 7下vsftpd中文乱码根因与兼容性解决方案

CentOS 7下vsftpd中文乱码根因与兼容性解决方案

1. 项目概述:为什么vsftpd在CentOS 7里会“说错话”?你有没有遇到过这样的场景:用FileZilla连上一台CentOS 7服务器的vsftpd服务,上传一个叫“测试文档.txt”的文件,结果在服务器上ls一看,变成了一串问号或…

2026/10/15 7:52:01 阅读更多 →
JNI线程管理与同步机制深度解析

JNI线程管理与同步机制深度解析

1. JNI线程操作基础解析在JVM与本地代码交互的场景中,线程管理是JNI编程最关键的难点之一。不同于纯Java环境,JNI线程涉及Java线程与本地线程(如pthread)的双向交互,需要特别注意线程附着(Attach&#xff0…

2026/10/15 7:52:01 阅读更多 →
Caveman调试法:极简打印输出快速定位代码问题

Caveman调试法:极简打印输出快速定位代码问题

先交代一下背景:我最近在梳理自己这几年写的工具脚本和自动化流程时,发现有一类问题我一直在用最“笨”的办法解决,但效果反而最稳定。我想了想,这套思路挺像很多人说的“caveman debugging”,也就是极简调试法——不用…

2026/10/15 7:51:01 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →