资讯动态

SpringBoot+Vue企业OA管理系统实战:从需求拆解到部署上线全流程

发布时间:2026/10/22 14:17:17 来源:尧图企业网站定制
每年到了准备毕设的季节总能看到不少人对着SpringBootVue 企业OA管理系统这种题目发愁。要么是从网上拖下来一份前后端不分的烂摊子要么是自己吭哧吭哧写了一半发现审批流程绕得下不来台。我前前后后整理过两套类似的Java Web项目踩了不少坑也总结出一套比较顺的思路。这篇就围绕SpringBoot Vue 企业OA管理系统这个题目从需求拆解、技术选型、数据库设计、后端接口、前端落地一直讲到源码、SQL脚本和接口文档该怎么整理成一份拿得出手的完整交付物。不管是用来做毕设答辩还是想在公司内部搭一套轻量级的办公管理工具这篇文章都能给到一个可以直接照着走的方案。1. OA系统的需求边界先搞清楚你究竟在做什么系统很多同学拿到题目第一步就去敲代码这是大忌。OA系统看着简单不就是请假审批、员工管理嘛但真做起来业务边界不划清楚后面全是坑。1.1 为什么OA是毕设里的安全牌先说结论企业OA管理系统是Java Web方向里最不容易翻车的选题。原因有三个。第一需求足够立体。一个OA系统天然会涉及用户的增删改查、角色的权限控制、流程审批的状态流转、公告的发布与可见性这些都是面试官和答辩老师最爱问的技术点覆盖面广但不至于深到失控。第二演示效果直观。学生管理系统、图书馆管理系统这类题目太单薄答辩时三五分钟就讲完了。而OA系统有登录、有工作台、有待办审批、有统计图表演示起来层次感很强老师能看到的功能足够多。第三技术栈匹配度极高。SpringBoot负责接口Vue负责页面MySQL存数据这套组合正好是当前Java Web开发的通用搭配既有学习价值又有实用价值不会让人觉得是过时的玩具。1.2 核心功能清单与模块划分我在动手之前会把整个系统拆成下面这张表。这里不是拍脑袋而是根据OA系统的真实使用场景反推出来的。模块核心功能在毕设中的优先级登录认证账号密码登录、Token鉴权、退出登录必须员工管理员工信息的增删改查、部门归属必须部门管理部门的层级结构、增删改查必须角色权限角色分配、菜单权限控制必须请假审批发起请假、待办审批、审批记录必须报销审批报销单提交、财务审批加分公告管理公告发布、可见范围、置顶建议考勤统计签到签退、月度考勤报表加分我的建议是时间不够的情况下优先保证前五个模块这已经能撑起一个完整的业务闭环。公告管理做出来之后演示效果提升明显因为它是典型的所有人都能看到的功能。1.3 千万别一上来就做大而全我见过不少同学把OA系统做成了全家桶工作流引擎、在线聊天、日历日程、项目管理全往里塞。结果就是每个功能都半吊子答辩时被老师随便追问一个细节就答不上来。正确的姿势是MVP思维。第一版只要做到员工能登录、能看到公告、能提请假、领导能审批这个闭环就足够了。数据库里的表控制在十张以内代码量控制在合理范围反而更容易把每一块的质量打磨好。后面如果时间富余再考虑加模块。2. 技术栈选型SpringBoot和Vue是怎么配合的这个题目最核心的技术选型就是标题里的SpringBoot加Vue。但具体到版本、配套组件很多人栽过跟头我这里直接给一套实测稳定的组合。2.1 后端为什么是SpringBoot而不是SSH早期的Java Web项目喜欢用SSHSpring Struts Hibernate但现在再做新项目SpringBoot几乎是唯一解。原因很直白SpringBoot把繁琐的XML配置全部变成了约定和自动配置内置了Tomcat打个jar包就能跑开发效率不是一个量级的。具体到这个OA项目后端核心组件我建议这样选SpringBoot 2.7.x版本配合JDK 8。这里不推荐一上来就上SpringBoot 3.x因为3.x强制要求JDK 17很多老教程和老代码跑不起来对于毕设来说没必要给自己添堵。MyBatis-Plus作为ORM框架。它比原生MyBatis好用太多分页插件、代码生成器、LambdaQueryWrapper这些功能能省掉大量重复代码。MySQL 8.0作为数据库5.7也完全够用但8.0是目前的主流。JJWT或Hutool的JWT工具来做登录令牌。至于工作流引擎要克制不要引入Activiti、Flowable这种东西对OA毕设来说太重了。2.2 前端Vue加Element Plus后台管理系统的黄金搭档前端如果不考虑Vue那标题就名不副实了。Vue的核心价值在于组件化和响应式这特别适合后台管理系统这种表格加表单为主的界面形态。我推荐的具体组合是Vue 3 Vite Element PlusPinia做状态管理Vue Router做路由Axios做HTTP请求选Vite而不是Vue CLI因为Vite启动速度快而且现在官方主推Vite网上能查到的坑和解决方案也更多。Element Plus这个组件库内置了大量的表格、表单、弹窗、菜单组件基本就是给后台管理系统量身定制的你不需要自己写任何复杂的样式专注业务逻辑就行。2.3 环境与版本匹配里最容易踩的坑这套组合跑不起来90%的情况不是代码问题而是版本不匹配。我列几个亲测过的坑JDK版本和SpringBoot版本不匹配最常见的就是JDK 8配了SpringBoot 3.x启动直接报错。Node.js版本太老Vite 4以上版本要求Node 14.18如果你还是Node 12npm install都会失败。Maven依赖下载慢国内建议配置阿里云镜像否则光等依赖就能等半小时。MySQL 8的驱动类名是com.mysql.cj.jdbc.Driver和MySQL 5的com.mysql.jdbc.Driver不一样配置错了连数据库直接报ClassNotFound。3. 数据库设计与权限模型地基打牢后面才不慌数据库设计是整个OA系统的地基。地基打得歪后面写代码全是别扭的。我设计这套表的时候遵循的原则很简单表宁少勿多字段宁冗余勿过度范式化权限模型能用经典RBAC就别自创。3.1 核心表结构从员工到部门再到审批先给出一套核心表的清单这套结构我实测下来接近一个完整OA的最小可运行集合。sys_user用户表id、username、password、real_name、dept_id、phone、email、avatar、status、create_timesys_role角色表id、role_name、role_code、descriptionsys_menu菜单表id、parent_id、menu_name、path、component、icon、sortsys_user_role用户角色关联表id、user_id、role_idsys_role_menu角色菜单关联表id、role_id、menu_idsys_dept部门表id、parent_id、dept_name、sortoa_leave请假表id、user_id、start_time、end_time、days、reason、status、approver_id、approve_time、approve_commentoa_notice公告表id、title、content、publisher_id、publish_time、status几个容易纠结的地方我直接给结论。用户表不要和员工表拆成两张毕设场景下一张表搞定还省得联查。部门表用parent_id做层级结构不要用物理外键去约束逻辑关联足够插入数据时还能少踩外键顺序的坑。3.2 RBAC权限设计五张表搞定所有权限问题RBAC基于角色的访问控制是后台管理系统最经典的权限模型。核心思想就一句话用户不直接绑定权限而是通过角色间接获得权限。我实际采用的是用户-角色-菜单三层模型一共五张表。用户表和角色表是多对多通过sys_user_role关联角色表和菜单表多对多通过sys_role_menu关联。后端在用户登录成功后通过用户ID查出全部角色再用角色ID查出全部菜单最后把菜单渲染成前端路由。这里有个重要经验菜单权限只是最基础的你能看到什么页面按钮级别的权限比如审批通过按钮只有管理员能点要不要做我建议看时间而定。如果要做在sys_menu表里加一个perms字段存按钮权限标识前端用自定义指令v-hasPermission来控制按钮显示隐藏后端在接口上也做同样的权限校验。两段都做才算完整的权限控制只做前端等于没做。3.3 审批流的数据结构一张表加状态字段就够了很多同学一听到审批流就发怵以为要设计复杂的工作流引擎。实际上对于毕设场景下的OA系统单据存表加状态流转就完全够用。以请假审批为例理想状态是这样的员工提交请假申请表中插入一条status为0的记录直属领导登录后看到的待办列表就是status为0的记录。领导点击通过status变为1记录approver_id、approve_time和审批意见。点击驳回status变为2。这里有一个设计细节审批意见字段要在单据表上直接加而不是单独建一张审批记录表。原因是OA系统里最常见的审批是单级审批一张表就能完整表达。如果你设计了多级审批比如请假超过三天要总经理审批建议再建一张审批记录表来存每一步的流转历史否则单靠status字段无法表达中间过程。优先级上单级审批永远是第一版多级审批是加分项。4. 后端接口设计认证、返回体、异常处理器三板斧后端接口这部分决定了前端开发同学的体验也决定了答辩时老师问你接口怎么设计的你能不能答得漂亮。我的经验是先把三件事做好认证拦截、统一返回体、全局异常处理。这三板斧立住后面的业务代码只是往框架里填内容。4.1 JWT登录认证与拦截器的完整链路JWTJSON Web Token是目前前后端分离项目最常用的认证方案。它的核心逻辑是用户登录成功后后端生成一个包含用户ID、用户名、过期时间的Token返回给前端。前端把Token存在本地每次请求时放在请求头里带回来后端解析Token就能知道请求是谁发的。具体到SpringBoot里我的实现分三步第一步登录接口校验用户名密码成功后用JWT工具类生成Token。这里要注意密码必须加密存储用BCrypt格式不能让明文密码直接躺在数据库里。第二步写一个JwtInterceptor拦截器放行登录接口拦截其余接口。一旦请求头里没有Token或者Token解析失败直接返回401。第三步注册拦截器时把Swagger文档相关的路径、静态资源路径、错误页面路径都排除掉否则会出现文档打不开、前端页面刷新后被拦截的问题。核心代码可以是这样public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行跨域预检请求 if (OPTIONS.equals(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { token token.substring(7); } try { Long userId JwtUtil.parseToken(token); request.setAttribute(userId, userId); return true; } catch (Exception e) { response.setStatus(401); return false; } } }4.2 统一返回体和全局异常让前后端联调不吵架我见过最糟糕的接口设计是有的接口返回{code: 200, data: ...}有的接口返回{success: true, result: ...}还有的直接返回一个字符串。前端每次请求都要先猜返回结构这是联调效率低下的最大原因。标准做法是定义统一返回体我一般叫Resultpublic class ResultT { private Integer code; // 200成功500业务错误401未认证 private String message; private T data; // 省略getter/setter和静态方法success()、error() }配合全局异常处理器业务代码里连try-catch都可以少写。只需要自定义业务异常类BusinessException在Service层里遇到不正常情况就抛异常Controller层不需要任何处理RestControllerAdvice会统一捕获并转换成Result返回。这样做的好处是Controller层代码极其干净每个接口就三行一行调service一行返回Result.success。答辩时老师问起怎么保证接口风格一致你就能把这个设计说明白。4.3 典型业务接口的设计规范以请假审批为例OA系统的接口不少但核心模式是雷同的。我拿请假模块的四个接口举例你掌握模式之后报销、出差这些模块就是复制粘贴换字段。提交请假申请POST /api/leave入参是请假表单后端校验时间合法性后插入记录。我的请假列表GET /api/leave/my根据当前登录用户的ID查出自己的全部记录。待办审批列表GET /api/leave/todo查status为0的记录。审批操作PUT /api/leave/approve/{id}入参是approveResult和approveComment后端更新status等字段。接口设计里有几个细节值得反复强调。分页查询一定要封装PageQuery对象统一接收pageNum、pageSize、keyword这些参数列表接口默认按创建时间倒序方便前端直接展示最新数据审批操作里务必要做状态校验防止重复审批把数据状态改乱。5. 前端落地路由、请求封装、组件复用后端跑通了前端如果写得稀碎整个系统照样没法看。Vue这套体系里我重点讲三个东西动态菜单、axios封装、审批组件的复用。这三个都是后台管理系统前端的通用套路。5.1 动态菜单不同角色登录看到的菜单不一样权限控制在页面上最直观的体现就是不同的人登录后菜单不同。管理员能看到员工管理和角色管理普通员工只能看到请假和公告这就是动态菜单要做的事。我的实现方案是用户登录成功后后端接口返回这个用户有权访问的菜单树前端把这个菜单树渲染成侧边栏同时动态注册路由。这里要特别叮嘱一句前端隐藏菜单只是体验层面的处理真正的权限校验必须依赖后端接口的拦截器否则懂行的人改一下前端路由就能访问没有权限的页面。用Vue Router的动态注册核心思路是遍历后端返回的菜单数组逐个调用router.addRoute()。这个逻辑在权限控制里非常关键建议提前封装好。5.2 axios封装请求拦截器与401的统一处理前端每个接口都裸调axios会让代码非常难维护。我习惯在utils/request.js里做一层统一封装两个拦截器就够请求拦截器里每次发请求前从Pinia或本地存储中取Token加到请求头service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config })响应拦截器里统一处理后端返回的Result格式。code为200时直接返回data业务错误码弹出错误提示401则清空Token并跳转回登录页service.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message) return Promise.reject(new Error(res.message)) } return res.data }, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) } )这套封装做完之后业务代码里的接口调用就极其清爽比如请假列表只需要写const data await getLeaveList(params)不用关心Token、状态码这些杂乱细节。5.3 组件化思路审批列表和表单的复用价值前端最怕的情况是每个页面复制粘贴一大段重复代码。OA系统里最容易复用的是两块一是带状态的列表页二是审批表单的展示。我的做法是可以让审批模块页面配置化也可以做一个PublicList公共组件传入接口地址和列配置就可以渲染表格。请假、报销、公告列表都能用同一套组件只是列配置不同。单文件组件的数据驱动方式非常灵活。还有一个值得抽出来的组件是状态标签。请假状态只有三种待审批用warning色已通过用success色已驳回用danger色。把这封装成StatusTag组件传入status就自动渲染对应的标签三行代码调用全系统通用。这种小细节会让整个前端的代码质量提升一个档次。6. 交付物整理一份能直接用的完整源码该是什么样回到这个项目的交付要求完整项目源码加SQL脚本加接口文档。这三样缺一样项目都算不上完整。6.1 前后端源码目录结构怎么组织才专业一份专业的源码目录应该让接手的人看一眼就知道哪里是前端哪里是后端怎么启动。我最推荐的顶层结构是这样的oa-system/ ├── backend/ # SpringBoot后端工程 │ ├── src/main/java/com/xxx/oa/ │ │ ├── controller/ # 控制层 │ │ ├── service/ # 业务层 │ │ ├── mapper/ # 数据访问层 │ │ ├── entity/ # 实体类 │ │ ├── common/ # 通用类(Result、异常、常量) │ │ └── config/ # 配置类(拦截器、跨域) │ └── src/main/resources/ │ ├── application.yml │ └── mapper/ # XML文件 ├── frontend/ # Vue前端工程 │ ├── src/ │ │ ├── api/ # 接口请求封装 │ │ ├── views/ # 页面 │ │ ├── router/ # 路由配置 │ │ ├── stores/ # Pinia状态管理 │ │ ├── components/ # 公共组件 │ │ └── utils/ # 工具类 │ └── vite.config.js ├── sql/ │ ├── 01_schema.sql # 建库建表脚本 │ ├── 02_data.sql # 初始数据脚本 │ └── 03_test_data.sql # 测试数据脚本 └── docs/ ├── 接口文档.md └── 部署说明.md两个工程分开放在backend和frontend目录下这是最直观的方案。SQL脚本单独放一个目录并且按建表、初始数据、测试数据拆分三个脚本是专业度的直接体现。启动说明写在README里不要写在代码注释里。6.2 SQL脚本的三个文件怎么分工我见过很多项目的SQL脚本只有一个几百行混在一起表结构和数据全都揉成一团。这样很不好维护。专业的做法是拆成三个文件01_schema.sql负责建库建表里面只有CREATE DATABASE和CREATE TABLE语句。表脚本要按依赖顺序写先建部门表再建用户表最后建关联表和业务表。如果顺序乱了后执行建表语句会报表不存在的错误。02_data.sql负责初始化必要数据包括一个管理员账号、基础角色、系统菜单等。这里有一个关键点所有数据插入的ID必须写死且保证唯一而且菜单表的parent_id要和菜单一致。如果用的是自增ID反复执行脚本可能导致ID对不上。03_test_data.sql负责造测试数据目的是让系统一跑起来就有数据可以展示。建议至少插入几个用户、部门、多条不同状态的请假记录。演示效果靠的就是这份测试数据数据越真实老师看着越舒服。6.3 接口文档在线Swagger和离线Markdown怎么选接口文档是很多同学最容易忽视但实际非常重要的交付物。我推荐做两种形态。形态一集成Swagger自动生成在线文档。只需要在SpringBoot里引入springfox或springdoc依赖加几个注解就能在浏览器里访问/swagger-ui/index.html每一个接口都可以在线调试。这个方案的好处是零维护成本代码即文档每次改代码接口文档自动更新。形态二写一份简洁的Markdown接口文档。这是给没有运行环境的人看的比如论文评审老师。每页包含接口名称、请求方式、请求路径、请求参数表格、返回参数示例、备注说明。不需要逐字写完所有接口但核心的登录认证、员工管理、请假审批这三个模块必须写全。我的建议是两者都做Swagger用于开发期联调Markdown用于答辩交付。哪怕都会用不到一半但我有文档意识这件事就能在答辩老师那里留下好印象。7. 部署上线的实操经验与踩坑记录最后这部分聊聊部署和上线的坑。一个项目代码写得再好部署不起来就一切归零。7.1 本地运行的正确顺序跑通这个项目的正确顺序是数据库优先创建和初始化再启动后端最后启动前端。很多人一上来先npm run dev结果页面从白屏到报错一直怀疑是前端的问题其实后端根本没起来。第一步执行SQL脚本注意顺序。先执行01_schema.sql再执行02_data.sql。如果已经执行过一遍可以删库再重建保持数据干净。第二步修改后端application.yml中的数据库账号密码确保和本机MySQL一致。把端口固定为8080上下文路径不要设置保持默认最好。第三步启动后端类里面的main方法看到Started Application in xxx seconds即成功。第四步启动前端前先确认node_modules已安装然后npm run dev打开浏览器访问localhost:5173即可。7.2 跨域问题前端代理和后端CORS不要同时开前后端分离项目开发时必然遇到跨域问题。前端跑在5173端口后端跑在8080端口两个端口不同浏览器会拦截跨域请求。解决方案有两种一是前端配置Vite代理将请求前缀/api代理到localhost:8080利用开发服务器转发请求绕开浏览器跨域限制。二是后端配置CORS在拦截器或配置类里允许跨域访问。这里一定要记得两种方案选一种不要同时开。如果同时开了会出现预检请求被拦截器拦截把CORS响应头去掉结果明明做了跨域配置请求还是报错。这是所有跨域坑里最隐蔽的一个。7.3 时区、编码、文件上传三个隐形的雷最后三个坑任何一个都能让人排查一下午。第一数据库连接串里加上serverTimezoneAsia/Shanghai否则日期时间查询可能差8小时。第二数据库建表时统一用utf8mb4字符集否则前端传了表情符号直接报乱码。第三如果你做了头像上传功能文件保存路径一定要在配置里写一个绝对路径并在本地创建好否则上传时会报目录不存在。部署时后端打包用mvn package生成jar包前端用npm run build生成dist目录。如果要做合并部署把dist目录复制到后端src/main/resources/static目录下重新打包就能实现一个jar包同时包含前端页面和后端接口这种部署方式在毕设答辩演示时特别方便不用开两个终端跑两个服务。从我个人的体会来说做完这个OA项目最大的收获不是跑通了某个功能而是建立起了一套需求拆解-数据库设计-接口约定-前端落地-交付整理的完整链路思维。尤其是把SQL脚本和接口文档当作一等交付物来对待这种习惯在之后的实际工作里非常受用。最后再分享一个实用的扩展方向如果你做完这些之后还有精力试着在公告模块里加一个Redis缓存把热点公告放到缓存里然后去文档里解释为什么用Redis这个点足够让答辩老师眼前一亮。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑