news 2026/7/10 21:48:49

若依毕业设计实战:从模块定制到生产级部署的完整路径

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
若依毕业设计实战:从模块定制到生产级部署的完整路径


若依毕业设计实战:从模块定制到生产级部署的完整路径

摘要:许多学生在使用若依(RuoYi)框架完成毕业设计时,常陷入“能跑但不可维护”的困境:权限配置混乱、前后端耦合严重、缺乏真实业务场景适配。本文基于实战经验,详解如何在若依基础上进行模块化改造,实现角色-菜单-API三级权限解耦,并集成JWT令牌刷新、操作日志审计等生产级功能。读者将掌握一套可复用的二次开发模板,显著提升系统健壮性与答辩竞争力。


1. 学生常见痛点:为什么“能跑”≠“能交付”

  1. 权限模型僵化
    若依默认“角色-菜单”二级模型,在毕设场景里一旦遇到“同一角色不同数据范围”就抓瞎。例如“班主任”只能看本班成绩,但校级管理员要看全部,硬编码if(role==='admin')很快变成灾难。

  2. 代码冗余 & 逆向工程黑箱
    很多同学直接跑ruoyi-generator一键生成代码,结果Controller/Service/Mapper三层堆满重复getByIdlistAll,后期需求一改,牵一发动全身,Git 提交记录全是“fix again”。

  3. 前后端耦合
    把 Vue 文件直接塞进resources/static里,打包一次 3 min,上线发现路径 404,又回夹带baseUrl = window.location.origin这种“玄学补丁”。

  4. 缺失真实业务场景
    为了“有图有真相”,硬塞一个商城秒杀模块,但订单状态机、库存扣减全是伪代码,老师一问“超卖怎么解决”就原地爆炸。


2. 技术选型:MyBatis-Plus vs JPA 的适配成本

  1. 若依原生 MyBatis 的优劣

    • 优点:SQL 可控、分页插件成熟、二级缓存好配
    • 缺点:手写 XML 费手,字段一改全文搜索替换
  2. 换 MyBatis-Plus 的代价

    • 只需把mybatis依赖换成mybatis-plus-boot-starter,旧 XML 零改动即可运行
    • 代码生成器模板里把Mapper.java的基类换成BaseMapper<T>,即可砍掉 80% 的*Mapper.xml
    • 注意:若依自带的DataScopeInterceptor依赖原生MappedStatement,需要重写一个PlusDataScopeInterceptor,成本 1 h 以内
  3. 换 Spring Data JPA 的代价

    • 需要把BaseEntity@TableId(type = IdType.AUTO)改成@GeneratedValue
    • 所有*Mapper接口要新写@Entity@Repository
    • 若依的PageUtils与 JPA 的Pageable不是一一对应,分页封装得重写
    • 结论:毕业设计周期 3 个月以内,不建议折腾 JPA,MyBatis-Plus 是性价比之王

3. 核心改造 1:三级权限解耦

目标:角色 → 菜单 → API 按钮,三表分离,支持“同角色不同数据行级权限”。

  1. 数据模型微调
    sys_rolesys_menu之间增加sys_role_menu_scope表,核心字段:

    id bigint PK role_id bigint FK→sys_role menu_id bigint FK→sys_menu dept_ids varchar 数据范围,逗号分隔部门 ID api_tag varchar 预留,给前端按钮用
  2. 自定义注解@RequiresPermissionsExt

    @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface RequiresPermissionsExt { String[] value() default {}; // 需要权限字符数组 DataScope dataScope() default DataScope.ALL; }
  3. AOP 拦截实现逻辑

    • 前置:读取SecurityUtils.getLoginUser().getUserId()
    • 查询sys_role_menu_scope拿到当前用户在该菜单下的dept_ids
    • dept_ids塞进ThreadLocal<List<Long>>
    • MyBatis-Plus 拦截器读取ThreadLocal,自动拼AND dept_id IN (...)
    • 后置:清理ThreadLocal防止内存泄漏
  4. 使用示例

    @RequiresPermissionsExt(value={"student:score:view"}, dataScope=DataScope.DEPT) @GetMapping("/score") public TableDataInfo list(ScoreDTO dto){ return scoreService.selectPage(dto); }

    至此,同一“班主任”角色登录,只能看到本班成绩;换成“教学秘书”角色,即可看到全院数据,代码零侵入。


4. 核心改造 2:JWT 刷新 & 并发登录控制

  1. 刷新拦截器(关键注释已写)

    public class JwtRefreshInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token = JwtUtils.getToken(request); Claims claims = JwtUtils.getClaims(token); // 1. 剩余时间 < 10 min 则刷新 if (claims.getExpiration().getTime() - System.currentTimeMillis() < 10 * 60 * 1000) { String userKey = claims.get("userKey").toString(); LoginUser loginUser = SpringUtils.getBean(TokenService.class) .getLoginUser(userKey); // 2. 颁发新令牌,旧令牌加入 Redis 黑名单 5 min String newToken = JwtUtils.createToken(loginUser); response.setHeader("Authorization", newToken); RedisUtils.setCacheObject(Constants.JWT_BLACK + token, "1", 5 * 60); } return true; } }
  2. 并发登录踢下线

    • 登录成功后RedisUtils.setCacheObject("user:kick:" + userId, token, 过期时间)
    • 每次请求比较Redistoken是否一致,不一致返回 401
    • 支持开关配置:single.login=true/false

5. 前端路由守卫:让按钮级权限也“动”起来

// permission.js router.beforeEach(async (to, from, next) => { // 1. 白名单 if (whiteList.indexOf(to.path) !== -1) { next(); return } // 2. 取用户信息 const userStore = useUserStore() if (!userStore.token) { next(`/login?redirect=${to.fullPath}`); return } // 3. 动态路由未加载则拉菜单 if (!userStore.hasRoutes) { const accessRoutes = await userStore.generateRoutes() accessRoutes.forEach(r => router.addRoute(r)) next({ ...to, replace: true }) } else { next() } })

按钮级权限指令:

app.directive('permission', { mounted(el, binding) { const { value } = binding const permissions = useUserStore().permissions if (value && !permissions.includes(value)) { el.parentNode && el.parentNode.removeChild(el) } } })

模板用法:

<el-button v-permission="'student:score:export'">导出成绩</el-button>

6. 安全加固:XSS + SQL 注入双重保险

  1. XSS 全局过滤器
    继承HttpServletRequestWrapper,重写getParametergetInputStream,用Jsoup.clean过滤白名单标签。

  2. SQL 注入
    MyBatis-Plus 自带#{}占位符已 99% 免疫,但代码里还有同学写order by ${sort}排序,务必把sort字段拉白名单校验:

    if (!ArrayUtils.contains(whitelist, sort)) throw new BadRequestException("非法排序字段");
  3. 操作日志审计
    利用@Log注解 + AOP,把ip、url、method、params、userId、time写表,答辩时老师最爱问“你怎么追踪误删数据”,直接甩日志表。


7. 生产环境避坑指南

  1. 数据库命名规范

    • 统一小写下划线,主键bigint(20)自增
    • 所有表必须create_time datetime DEFAULT CURRENT_TIMESTAMP
    • 索引命名idx_表名_字段名,唯一索引uk_表名_字段名,方便 DBA 快速定位
  2. Swagger 文档暴露
    生产环境务必加网关拦截:

    spring: profiles: active: prod knife4j: production: true # 关闭接口页面

    或者 Nginx 层直接return 404 /swagger-ui.html

  3. 冷启动提速

    • 关闭spring.devtools.restart
    • BeanValidation换成hibernate.validatorprecompile版本
    • 使用spring-context-indexer生成spring.components,本地启动从 12 s 降到 7 s,服务器 2 vCPU 4 G 首次启动 25 s → 15 s
  4. 日志切割
    使用logback-spring.xml按 100 MB 切割,保留 30 天,防止磁盘爆满把服务拖死。


8. 部署脚本:一条命令上云

#!/bin/bash # build.sh mvn clean package -Dmaven.test.skip=true docker build -t ruoyi-project:1.0.0 . docker run -d --name ruoyi-prod \ -e SPRING_PROFILES_ACTIVE=prod \ -e MYSQL_URL=jdbc:mysql://10.0.0.8:3306/ruoyi_prod \ -p 8080:8080 ruoyi-project:1.0.0

前端独立容器:

FROM nginx:alpine COPY dist/ /usr/share/nginx/html/ COPY nginx.conf /etc/nginx/conf.d/default.conf

vue.config.jspublicPath改成./,再用 GitHub Actions 自动打包推镜像,答辩演示时直接打开公网域名,老师点赞“像企业级”。


9. 结语:把 Demo 变产品的下一步

走完上面的改造,你的“若依毕业设计”已经脱离“课程级别”,具备可维护、可扩展、可交付的雏形。但别急着关机睡觉,最后留两个小作业:

  1. 把 README 写成产品白皮书:背景、功能、架构图、部署手册、接口示例,一个都不能少——导师一看就知道“这学生能毕业”。
  2. 做一次“假压力测试”:用wrk -t12 -c400 -d30s打 1 w 并发,把 QPS、RT、错误率截屏贴报告里,老师想挑刺都找不到角度。

毕业设计不是终点,而是你把“演示系统”转化为“可交付产品”的第一块跳板。动手重构你的若依项目,下次面试官问“有没有上线经验”,你可以把域名甩给他,顺便讲讲 JWT 刷新、并发踢人、数据权限三级解耦——offer 稳了。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/8 22:03:34

GitHub加速工具:企业级开源项目访问优化解决方案

GitHub加速工具&#xff1a;企业级开源项目访问优化解决方案 【免费下载链接】Fast-GitHub 国内Github下载很慢&#xff0c;用上了这个插件后&#xff0c;下载速度嗖嗖嗖的~&#xff01; 项目地址: https://gitcode.com/gh_mirrors/fa/Fast-GitHub 在全球化软件开发环境…

作者头像 李华
网站建设 2026/6/26 13:26:16

拯救你的MC世界:Minecraft Region Fixer完全自救指南

拯救你的MC世界&#xff1a;Minecraft Region Fixer完全自救指南 【免费下载链接】Minecraft-Region-Fixer Python script to fix some of the problems of the Minecraft save files (region files, *.mca). 项目地址: https://gitcode.com/gh_mirrors/mi/Minecraft-Region-…

作者头像 李华
网站建设 2026/7/1 22:27:19

如何让非NVIDIA显卡运行CUDA程序:ZLUDA实用指南

如何让非NVIDIA显卡运行CUDA程序&#xff1a;ZLUDA实用指南 【免费下载链接】ZLUDA CUDA on Intel GPUs 项目地址: https://gitcode.com/GitHub_Trending/zl/ZLUDA ZLUDA是一款革命性的兼容层工具&#xff0c;它能让Intel和AMD显卡也能运行原本只能在NVIDIA硬件上执行的…

作者头像 李华