SpringBoot+Vue银行OA系统毕设实战:从RBAC权限到部署全解析

SpringBoot+Vue银行OA系统毕设实战:从RBAC权限到部署全解析 简介基于SpringBoot和Vue开发的银行OA系统是一套面向Java毕业设计、课程设计及期末大作业的完整项目主要解决毕业设计选题难、从零搭建系统工作量大的问题前后端代码齐备注释清晰适合具备基本Java语法与前端基础的学习者快速上手无论是课程答辩还是毕业设计验收都能提供整套可落地的方案。压缩包共448个文件约11.31MB涵盖Vue前端页面、Java后端控制器与服务层、MySql数据库脚本、XML配置、构建与部署脚本另有svg、jpg、png等图标和图片资源辅助理解界面逻辑结构分类清晰方便按模块查阅。该项目在CSDN已有173人学习下载经过严格调试确保可运行并配有自动安装与启动脚本降低部署门槛对新手十分友好。包内除完整源码与数据库外还附带软件工具、组件备份及配置文件读者可据此完成环境搭建、功能扩展与二次开发备份文件亦可用于误操作恢复整体完整度和可维护性都较高具有较高的实战参考价值。1. 为什么银行OA毕设选了SpringBootVue这套组合拿到这个压缩包时先看到的不是代码而是IndexHeader.vue.bak、BreadCrumbs.vue.bak一类的备份文件和三个bat脚本初看会有点乱但拆开后其实是典型的银行内部OA系统SpringBoot负责用户、部门、审批流程的接口Vue负责导航布局和页面交互数据库脚本直接给出初始化数据。这类项目在毕业设计里评分稳定原因是它覆盖了权限管理、表单提交流程、文件上传、审批流这些OA业务里最高频的模块前后端分离的结构也方便答辩时讲清楚接口怎么设计。适合三类人拿它做毕设底子再二开的学生想快速看RBAC权限在SpringBoot里落地方式的工作三五年的Java后端以及准备前端面试时拿Vue路由守卫做例子的开发。把这套组合跑起来不稀奇理解各层之间为什么这样配合才是这里面值钱的部分。2. SpringBoot后端启动类、数据源与Maven打包细节2.1 启动类上的两个注解决定包结构成败项目解压后先找根目录的启动类这个资源的包结构是按com.bank.oa组织的启动类的标准写法是package com.bank.oa; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication MapperScan(com.bank.oa.mapper) // 扫描MyBatis的Mapper接口漏掉会启动失败 public class OaApplication { public static void main(String[] args) { SpringApplication.run(OaApplication.class, args); } }SpringBootApplication默认扫描启动类所在包及其子包也就是说controller、service、mapper这些包的路径必须都挂在com.bank.oa下面否则会出现接口404或者bean找不到。MapperScan的作用是把指定包下的Mapper接口注册到Spring容器这里如果漏配MyBatis会在启动时抛出Invalid bound statement一类的异常。接手这类毕设代码时优先检查controller、service、mapper三层是否在同一父包下很多项目跑不起来的根因不是业务逻辑写错而是包扫描漏了一层。这个项目因为用了MyBatis-Plus还需要让MapperScan的路径和application.yml里 mybatis-plus 的typeAliasesPackage保持一致否则实体类别名解析不到写SQL时返回类型会报错。2.2 application.yml里数据库连接参数的几个坑数据库连接配置在src/main/resources/application.yml核心内容如下server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/bank_oa? useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/Shanghai useSSLfalseallowPublicKeyRetrievaltrue username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl逐个说明关键参数。serverTimezoneAsia/Shanghai解决MySQL 8.0连接时的时间偏差不加会直接报The server time zone value异常useSSLfalse是本地开发关闭SSL握手避免控制台证书警告刷屏allowPublicKeyRetrievaltrue是MySQL 8.0特有的配置解决客户端默认不请求公钥时报Public Key Retrieval is not allowed的错。map-underscore-to-camel-case开启后数据库里的dept_id才能自动映射到实体字段deptId这是MyBatis-Plus项目最容易被忽略的一行配置漏掉它的直接表现是查询结果里凡是下划线字段全是null。log-impl配成StdOut后SQL会直接打到控制台排错时比看日志文件高效很多。关于driver-class-name的选择MySQL 8.0连接器用com.mysql.cj.jdbc.DriverMySQL 5.7两者都可以但旧类在新版驱动里已标记过时。这个资源建议用MySQL 5.7核心原因是初始化SQL里的排序规则在两个版本之间有差异8.0默认的utf8mb4_0900_ai_ci会让中文字段排序和预期不一致而5.7的utf8mb4_general_ci表现最稳定对毕设演示完全够用。如果本机只有8.0建库时执行CREATE DATABASE bank_oa DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;也能规避掉大部分兼容问题。提示如果本机只有MySQL 8.0建库时显式指定utf8mb4_general_ci驱动类用8.0那行两个版本都能稳定跑。2.3 Maven依赖版本选择避免踩坑pom.xml的parent版本建议锁在2.7.x和这个项目匹配度最高parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies为什么选2.7.18而不是SpringBoot 3.x有三点理由。第一银行OA业务里经常集成Activiti工作流引擎Activiti对SpringBoot 3.x的原生支持仍有兼容成本第二3.x把javax.servlet换成了jakarta.servlet老项目里凡是写HttpServletRequest的地方全部要同步改import第三3.x默认的MySQL驱动版本和连接参数变化较大照抄网上的配置容易连不上测试库。Lombok这行故意加optionaltrue/optional防止它被打进最终jar因为打包后并不需要编译期的注解处理器。构建时如果本机JDK是17而SpringBoot 2.7只支持JDK 8到17需要在pom里确认java.version1.8/java.version否则会报invalid source release。2.4 mvnw.cmd与跳过测试的打包命令项目根目录保留mvnw.cmd这是Maven Wrapper允许本机没装Maven的人直接执行.\mvnw.cmd clean package构建。打包建议跳过测试.\mvnw.cmd clean package -DskipTests-DskipTests跳过测试执行但继续编译测试类如果测试类编译也想跳过换成-Dmaven.test.skiptrue。毕设项目里src/test下通常有自动生成的OaApplicationTests它加载整个Spring上下文本地MySQL没启动或账号配置不对时这个测试必挂这不是代码问题是测试环境依赖问题所以打包命令带跳过参数是常规操作。如果学校要求部署到Tomcat 7.x或8.x环境需要额外把启动类继承SpringBootServletInitializer并把内嵌Tomcat的scope改成provided否则war包会和容器冲突。执行完打包后javajar在target目录用java -jar target/bank-oa.jar启动即可。后端启动阶段常见的几个报错按频率整理如下报错信息原因处理办法Invalid bound statementMapperScan未配置补全Mapper接口扫描包The server time zone value时区未设置URL加serverTimezoneAccess denied for userMySQL账号或密码不对检查yml配置和MySQL授权Could not open JDBC ConnectionMySQL服务没启动先启动mysql服务Port 8080 was already in use端口被占用netstat -ano | findstr 8080查PID并处理3. Vue前端IndexHeader改造、路由守卫与.bak文件3.1 四个.bak文件透露的信息资源列表里最有意思的是带了一组.bak文件文件在OA里的角色IndexHeader.vue.bak顶部导航栏备份IndexAsideStatic.vue.bak左侧静态菜单备份BreadCrumbs.vue.bak页面面包屑备份update-password.vue.bak修改密码弹窗备份.bak是作者改动前的快照拿到项目先做diff能直接看出功能迭代方向diff IndexHeader.vue.bak IndexHeader.vue比如IndexHeader旧版可能只显示用户名新版多了退出登录和修改密码入口IndexAsideStatic新版可能从完全静态的菜单改成根据路由动态生成。diff一遍比直接点运行更先理解系统结构。确认新版没问题后把.bak文件移出src目录避免Vue CLI在npm run build时把备份文件当作模块打包导致app.45655fe7.css这类产物文件名后面带上多余的hash体积也跟着翻倍。实际项目里见过因为备份文件混入打包后的css从几十KB膨胀到几百KB页面首屏明显变慢的情况。提示diff结束后务必把.bak文件移出src目录否则npm run build会把备份内容一并打包进产物。3.2 布局组件与路由的对应关系Vue端目录一般是src/views下放login、dashboard、approval、system等页面目录主布局由IndexHeader顶部、IndexAsideStatic侧边栏、BreadCrumbs面包屑和router-view组成。路由配置写法如下{ path: /, component: Layout, redirect: /dashboard, children: [ { path: dashboard, name: Dashboard, component: () import(/views/dashboard/index.vue), meta: { title: 工作台, icon: el-icon-s-home } // 面包屑标题来源 } ] }这段路由表达的含义是/挂载Layout父组件children里的页面渲染进Layout的router-view区域。meta.title会被前端拿出来设置浏览器标签标题和面包屑展示BreadCrumbs的数据就是从当前路由的matched数组里取meta.title拼出来的。旧版项目多沿用vue-element-admin的目录约定新加页面时在children里追加对象即可。注意子路由的path不要写/dashboard这种带斜杠的绝对路径写成相对路径dashboard即可写错了页面上会出现双斜杠导致匹配不到组件。3.3 axios拦截器和路由守卫的前后端联动OA系统要求登录后所有请求带token前端用axios统一处理import axios from axios const service axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截每次请求自动附带token service.interceptors.request.use(config { const token localStorage.getItem(bank-oa-token) if (token) { config.headers[Authorization] Bearer token } return config }) // 响应拦截401统一踢回登录页 service.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { localStorage.removeItem(bank-oa-token) window.location.href /login } return Promise.reject(error) } ) export default service请求拦截器在每个请求发出前从localStorage取bank-oa-token并写入Authorization头后端如果是Spring Security的JWT过滤器会从header里解析并校验签名和过期时间。响应拦截器单独捕获401表示token无效或过期直接清token跳登录页这样每个controller都不用写重复的登录判断。baseURL: /api是配合代理使用的开发环境下vue.config.js里把/api代理到http://localhost:8080部署时再由Nginx反代到后端服务。路由守卫再兜一层页面权限router.beforeEach((to, from, next) { const token localStorage.getItem(bank-oa-token) if (to.path /login) { next() return } if (!token) { next(/login) return } next() })这里只判断有没有token没有判断用户角色。常见的面试追问是“有token但没权限怎么办”解决思路是登录接口返回该用户的菜单路由表前端通过router.addRoutes动态注册只把当前角色能访问的路由挂进router。如果项目里菜单由后端返回登录后先拿菜单再渲染侧边栏IndexAsideStatic.vue遍历菜单数组生成el-menu这是OA系统从静态菜单升级到动态菜单的标准操作也是这个银行项目里静态菜单版本和后期版本的核心区别。3.4 install和build脚本里的镜像源问题1-install.bat的内容一般是npm install --registryhttps://registry.npmmirror.com指定了镜像源安装速度比官方源快很多。如果执行时卡在idealTree或报ECONNRESET删掉node_modules目录重新执行或者先执行npm cache clean --force再装。老项目里package.json依赖版本如果锁得太宽松npm install会把Vue拉到大版本导致Global is not defined一类兼容报错这时直接把package.json里的vue固定到2.6.14再重新安装。3-build.bat一般执行npm run build构建产物输出到dist目录银行OA类项目通常还会把dist目录复制进SpringBoot的src/main/resources/static下面这样打成jar后能直接访问前端页面省去单独部署前端的麻烦。4. 银行OA数据库设计RBAC表结构与初始化脚本4.1 用户、部门、角色三张核心表数据库脚本在压缩包的sql目录下导入后先看三张核心表-- 部门表先建用户表要引用dept_id CREATE TABLE sys_dept ( dept_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 部门ID, parent_id INT DEFAULT 0 COMMENT 上级部门ID, dept_name VARCHAR(50) NOT NULL COMMENT 部门名称, order_num INT DEFAULT 0 COMMENT 显示排序 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT部门表; -- 用户表 CREATE TABLE sys_user ( user_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 用户ID, dept_id INT COMMENT 所属部门ID, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT 密码, status CHAR(1) DEFAULT 0 COMMENT 状态: 0正常 1停用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, KEY idx_dept (dept_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;建表顺序先部门后用户因为用户表dept_id外键指向部门表。status状态位类似银行账号的“正常/挂失”概念用户停用后即使密码正确也无法登录管理员直接update这个字段而不删除数据符合金融业务里的审计要求。create_time用DEFAULT CURRENT_TIMESTAMP让数据库自动填时间Java代码里不需要手动set。这里体现的数据库增删改查基本功是先设计主键和关联字段再考虑索引idx_dept就是为了后续按部门查用户时避免全表扫描。4.2 角色、菜单与权限关联OA系统的权限核心是RBAC中间关系表如下CREATE TABLE sys_role ( role_id INT PRIMARY KEY AUTO_INCREMENT, role_name VARCHAR(30) NOT NULL COMMENT 角色名称, role_key VARCHAR(50) COMMENT 角色权限字符串, status CHAR(1) DEFAULT 0 ); CREATE TABLE sys_menu ( menu_id INT PRIMARY KEY AUTO_INCREMENT, parent_id INT DEFAULT 0 COMMENT 父菜单ID, menu_name VARCHAR(50) COMMENT 菜单名称, path VARCHAR(100) COMMENT 路由地址, perms VARCHAR(100) COMMENT 权限标识, menu_type CHAR(1) COMMENT M目录 C菜单 F按钮 ); CREATE TABLE sys_user_role ( user_id INT, role_id INT, PRIMARY KEY (user_id, role_id) ); CREATE TABLE sys_role_menu ( role_id INT, menu_id INT, PRIMARY KEY (role_id, menu_id) );sys_role_menu把角色和菜单关联起来当用户登录时后端根据user_id查出role_id再关联menu表拿到该用户所有可访问的路由返回给前端渲染侧边栏。perms字段是按钮级权限判断用的比如system:user:add这样的字符串后端配合PreAuthorize(hasAuthority(system:user:add))注解做接口鉴权。如果后面要在银行OA里加新模块仿照现有sys_menu的insert语句追加菜单即可注意parent_id指向父菜单层级一般不超过三级。这套模型的扩展性在答辩时是一个加分点可以提一句“权限只挂在角色上不直接挂用户方便批量调整”。4.3 初始化数据的密码加密方式看初始化脚本里管理员账号的写法INSERT INTO sys_user (user_id, dept_id, username, password, status) VALUES (1, 1, admin, MD5(123456), 0);密码字段用MD5存储说明早期的演示版本没有引入BCrypt。后端登录逻辑常见的做法是DigestUtils.md5DigestAsHex比对或者直接查出来比对密文。但实际项目里MD5不够安全会换成BCryptPasswordEncoder它的特点是同一密码每次加盐后的hash都不同数据库存的是$2a$10$...开头的字符串。如果后续要接Spring Security登录接口应该写成PostMapping(/login) public Result login(RequestBody LoginDTO dto) { SysUser user userService.getByUsername(dto.getUsername()); // matches自动解析盐值并比对不要手动对明文做MD5 if (user null || !passwordEncoder.matches(dto.getPassword(), user.getPassword())) { return Result.error(用户名或密码错误); } String token JwtUtil.createToken(user.getUserId(), user.getUsername()); return Result.ok(token); }matches方法第一个参数是明文密码第二个参数是数据库里查出来的密文内部自动解析盐值。这个项目原版脚本里的MD5密码能直接登录是因为登录比对时也用了MD5二者保持一致。升级成BCrypt后初始化脚本要同步改成BCrypt生成的密文串否则老账号全部登录不上。4.4 MySQL 5.7与8.0的兼容性对照数据库工具和版本问题在排错里占了较大比例核心差异整理如下对比项MySQL 5.7MySQL 8.0JDBC驱动类com.mysql.jdbc.Drivercom.mysql.cj.jdbc.Driver默认排序规则utf8mb4_general_ciutf8mb4_0900_ai_ci时区处理无强制要求serverTimezone必填公钥机制不需要allowPublicKeyRetrievaltrue密码插件mysql_native_passwordcaching_sha2_password用Navicat等数据库工具导入脚本时如果8.0报Authentication plugin caching_sha2_password在连接属性里把AllowPublicKeyRetrieval设为true。如果公司环境用的是Oracle、达梦这类数据库这里的SQL语法不完全通用但表设计思路可以照搬到对应数据库方言。这个资源的初始化脚本是按照5.7写的导入8.0时最容易报错的就是排序规则建库时手动指定DEFAULT COLLATE utf8mb4_general_ci就能绕开。提示导入脚本前先确认排序规则否则表建好后中文字段排序会不符合预期。5. 部署实战三个bat脚本与前后端联调验证5.1 脚本执行链路资源里的三个bat脚本执行顺序如下脚本作用对应命令1-install.bat安装前端依赖npm install --registryhttps://registry.npmmirror.com2-run.bat启动后端服务java -jar或mvn spring-boot:run3-build.bat前端打包产物npm run build实际执行的节奏是先建库导入bank_oa.sql再执行1-install.bat装依赖随后启动后端再执行2-run.bat或者直接在IDEA里运行OaApplication前端开发模式时执行npm run dev联调确认没问题再执行3-build.bat出生产包。这个顺序打乱了会出现一些迷惑现象先跑前端再启动后端页面能打开但接口全挂先build不installnpm会直接报缺依赖。5.2 前后端联调验证技巧启动后打开浏览器访问http://localhost:8080如果前端已打包进static目录页面直接显示登录框用admin账号和初始化脚本里的密码登录。验证接口通不通优先看浏览器DevTools的网络面板登录请求返回200response里带token后续请求header里带Authorization。如果登录后页面空白按顺序检查浏览器console报错、路由有没有被守卫拦截、控制台里MyBatis的SQL有没有查出来数据。这三个位置分别对应前端代码、路由权限、后端数据三层问题挨个看基本能定位。5.3 高频报错速查后端启动报数据库连接失败时第一反应不是改pom而是先把application.yml里的用户名密码和本机MySQL对上再用命令行敲mysql -uroot -p验证。前端打包后如果布局异常特别是vue打包后布局错乱检查打包出来的css有没有混入.bak文件内容、图片路径是不是被webpack改成了绝对路径在vue.config.js里配置publicPath: ./可解决多数相对路径问题。页面能打开但接口404确认controller类上有没有RestController注解以及RequestMapping路径和前端baseURL拼接后是否匹配。这些错误按顺序走一遍项目部署跑通后剩下的工作量基本就是业务页面和答辩文档了。本文还有配套的精品资源点击获取