首页 / 资讯中心 / 文章详情

SpringBoot+Vue企业OA管理系统:从权限设计到项目部署

SpringBoot+Vue企业OA管理系统:从权限设计到项目部署 ★ FEATURED ARTICLE
1. 项目概述与定位SpringBootVue 企业OA管理系统这类标题经常出现在毕业设计相关的资源站里但“完整项目源码SQL脚本接口文档”这几个字才是真正值得琢磨的地方。它不只是一个演示用的crud拼盘而是一套涵盖了用户认证、权限控制、流程审批、行政办公、数据统计的完整业务闭环。对正在做Java Web毕设的同学或者刚入职想快速了解企业级项目长什么样的新人来说这是一份可以直接上手、借鉴、二次开发的学习资料。这个系统能解决的问题很明确把公司日常的审批、公告、会议、考勤、日程这些零散事务从微信群里的人工接龙、Excel表格传来传去收拢到一个统一的Web平台里。使用者分管理员、部门主管、普通员工几种角色不同的人登录进去看到的功能菜单、可操作的按钮、能访问的数据范围都不一样。这种“千人千面”的效果就是典型的RBAC基于角色的访问控制权限模型在落地。适合谁来参考呢第一类是计算机相关专业、正在选题或已经选了OA方向的学生你需要的是一个结构完整、代码规范、有数据库设计文档的参照物而不是网上支离破碎的代码片段第二类是初入行的Java开发新人想搞明白Spring Boot怎么和Vue配合、JWT登录到底怎么串起来、权限拦截器在真实项目里长什么样第三类是那些“接私活”或在小公司做内部系统的开发者OA系统的很多模块可以抽出来直接复用。整个项目从技术架构上看走的是当前Java Web领域最主流、也最适合用来做毕设演示的路线前端VueElement UI后端Spring BootMyBatis Plus数据库MySQL权限用Spring Security或拦截器JWT。这套组合的好处是生态成熟、资料多、部署简单更关键的是它足够“典型”——面试聊项目、答辩讲架构都能拿出实实在在的东西来讲。2. 技术选型解析为什么是Spring BootVue而不是别的2.1 前后端分离架构带来的开发与部署优势如果放在七八年前一个JavaWeb项目通常是JSPSpring MVC页面和Java代码混在一起前端改个按钮样式要重启服务。而现在这套系统的前后端分离本质上是把“页面渲染”和“数据提供”拆成了两个独立工程。前端Vue项目通过Axios发HTTP请求拿到JSON数据后在浏览器里渲染DOM后端Spring Boot只负责接收请求、处理业务逻辑、返回JSON完全不关心数据最终长什么样。这种拆分带来一个直接好处前后端可以并行开发。前端同学拿着接口文档先写页面用Mock数据调通后端同学同时建表写接口两边互不阻塞。对个人开发者来说虽然不存在“团队协作”的问题但分离之后代码结构清楚很多——Vue的src目录下就分views、components、router、store、api几个层次后端Controller、Service、Mapper按业务模块划分找Bug的时候不用在HTML和Java之间来回跳。部署上也更灵活。前端打包后是一堆静态文件js、css、index.html可以用Nginx直接托管后端是一个可执行jar包跑在8080端口。生产环境里Nginx监听80端口把/api/开头的请求反向代理到Java服务其他请求直接返回前端静态文件。这样一套简单的拓扑就避免了跨域问题也把静态资源和动态服务的负载分开了。2.2 后端Spring Boot约定优于配置快速构建稳定服务Spring Boot最核心的价值是“约定优于配置”。以前用SSH框架搭一个项目要写web.xml、spring.xml、springmvc.xml还要配数据源、事务、扫描包光配置文件就能把人绕晕。Spring Boot通过自动配置和起步依赖把这些琐碎的东西大部分抹平了。你只需要在pom.xml里引入spring-boot-starter-web写一个main方法启动一个标准的Web服务就跑起来了。OA系统的后端模块用Spring Boot可以拆得很干净。SprinSecurity负责认证和授权JWT做无状态令牌MyBatis Plus做数据持久化Redis用来缓存用户会话和部分热点数据。整体分层架构我们下面会细说这里先记住一个结论这套组合不是哪个人拍脑袋定的而是经过大量生产环境验证、学习资料最全、出问题最容易搜到解决方案的一套“标准答案”。对毕设来说“标准”往往就意味着稳定和安全。另外Spring Boot生态里有个特别适合OA场景的特性——Spring Task定时任务。考勤提醒、会议开始前通知、审批超时催办这些都可以用Scheduled注解配上cron表达式搞定实现一个简单的定时任务服务。比起单独引入Quartz等框架用Spring自带的定时任务足够应付OA这种轻量级场景代码也更好理解。2.3 前端VueElement UI组件化开发提高页面搭建效率Vue之所以比传统jQuery开发效率高核心是它的响应式数据绑定和组件化体系。在OA系统里审批列表、公告栏、日历、表格这些都是高度复用的UI块。Element UI把表格、弹窗、表单、菜单、日期选择器这些常用组件都封装好了你只需要数据绑定好表格自动渲染校验规则写清楚表单自动提示。实际开发中Element UI的el-table加分页、el-dialog加表单校验、el-menu搭左侧菜单、el-tabs做详情页Tab切换这几个组合几乎能覆盖OA系统90%的页面形态。配合Vue Router的懒加载路由、Vuex存储用户信息和菜单权限前端要写的东西其实就是“把接口返回的数据按UI要求展示出来把用户填的数据按要求传给后端”。有一个细节值得注意Vue项目的目录结构会影响后续的可维护性。我看到过很多毕设代码把所有的页面都堆在views下面Api请求散落在各个组件里。好的做法是统一用src/api/module/来管理接口每个业务模块一个JS文件export对应的方法src/router里配置动态路由根据后端返回的菜单权限来挂载路由。这样做之后项目能承载的功能量和可维护性完全不一样。2.4 数据库与中间件MySQLRedis的组合性价比最高的选择MySQL作为主数据库是毋庸置疑的选择。OA系统的数据特点是以结构化数据为主比如用户表、部门表、审批表、公告表它们之间有明确的外键关系需要事务保证一致性。虽然理论上可以用PostgreSQL或者国产数据库但MySQL仍然是Java Web里普及率最高的选择网上资料、面试题、云数据库服务都是围绕它展开的用它对毕业生最友好。Redis在这套系统里的角色主要是“加速器”和“会话存储”。登录成功后生成的JWT虽然本身是无状态的但用户的角色信息、权限标识、部分热点配置可以放到Redis里做缓存减少数据库压力。另外一个典型场景是验证码存储——图形验证码或短信验证码通常设置5分钟有效期放在Redis里设置过期时间非常合适配合Spring Boot的RedisTemplate操作也简单。需要重点说明的是Redis不是这套系统“必须”的组件但加了Redis的版本在答辩时明显更有说头。你可以在“系统设计”里讲清楚“为什么需要Redis”、“缓存了什么数据”、“缓存和数据库的一致性怎么保证”这些就是常见的面试追问点。2.5 数据访问层MyBatis Plus提升开发效率避免重复劳动很多人纠结到底用MyBatis还是JPA。对于OA这种以单表操作和简单关联查询为主的系统MyBatis Plus在CRUD上比原生MyBatis省太多事。单表增删改查、分页查询、批量插入、逻辑删除这些都内置了不需要写XML。条件构造器QueryWrapper可以让动态SQL的拼装实现得非常优雅代码可读性也高。举个例子做公告列表的搜索功能时通常要根据标题模糊查询、发布时间范围过滤、状态过滤如果手写SQL要在XML里拼动态where条件容易漏条件或出错。用QueryWrapper的话几行代码搞定还不用担心SQL注入。如果你之前在原生MyBatis里写过那些又臭又长的动态SQL对比一下就明白省了多少体力。当然多表关联比较复杂的地方还是建议写好SQL映射。比如审批表要关联申请人信息、审批人信息、部门名称这个时候用注解Select写SQL或者建一个专用的VO类接收关联查询结果比用ORM硬靠外键对象更直观性能也可控。3. 系统核心模块拆解与业务设计3.1 组织架构与RBAC权限模型整个系统的地基一个企业OA系统最先要设计好的不是某个功能页面而是组织架构。组织架构决定了数据怎么归类、权限怎么下放、审批流怎么找人。典型的表结构设计有五张核心表用户表、部门表、角色表、用户角色关联表、角色菜单关联表。如果把菜单表也算上就是六张。用户表和部门表之间是多对一关系一个部门下有多个员工。用户表的关键字段包括用户名、密码BCrypt加密存储、真实姓名、手机号、邮箱、状态启用/停用、入职时间等。部门表有部门名称、父部门ID、负责人ID、排序号用父部门ID可以做递归树形结构实现多级部门层级。角色在OA里通常分为系统管理员、部门主管、普通员工、HR、财务这几类不同角色对应不同的菜单树和数据权限范围。系统管理员能看到全部菜单部门主管只能看本部门相关的数据普通员工只看到和自己相关的待办审批、公告、日程。这种数据权限的区分光靠菜单权限还不够通常还需要在后端查询时根据当前用户的部门ID做数据隔离。很多毕设只做了菜单权限忽略了数据权限这恰恰是答辩时容易暴露短板的地方。权限模型里还有两个细节要处理干净。一是在用户关联组织角色之外还可以引入“兼任角色”比如某个人在A部门是员工但在B项目里是负责人如果系统需要支持这种场景用户-角色关联表就要带上一个默认角色标记。二是管理员角色通常需要硬编码一个“超级管理员”标识防止所有人把系统管理员的菜单授权出去之后系统失去管理入口。3.2 用户认证与登录JWT无状态认证前端路由守卫配合现在前后端分离项目的常用登录方案是JWT。用户提交用户名密码后端校验成功后生成一个签名后的token返回给前端。前端把token存在localStorage里每次请求在Axios拦截器里加上Authorization头后端写一个拦截器或过滤器来校验token没带token或token过期就返回401前端收到401后跳回登录页。JWT的好处不用多说简单、无状态、易于水平扩展。但要注意几个容易被忽视的坑一是密钥不能硬编码在代码里要放到配置文件中生产环境通过环境变量注入二是token过期时间不要设置太长一个合理的值可能是2小时前端配合刷新token的机制三是JWT一旦签发服务端无法主动让它在过期前失效所以对于用户被禁用、修改密码这种场景单纯靠JWT是不够的需要结合Redis黑名单或token版本号机制。在实际代码里Spring Security的UsernamePasswordAuthenticationFilter会拦截登录请求我们重写AuthenticationManager的authenticate流程成功之后用JwtUtil生成token返回。后续每次请求走OncePerRequestFilter从header里解析token、解析出用户名和权限信息然后塞进SecurityContext里。这样的好处是Controller里可以直接通过AuthenticationPrincipal拿到当前登录用户不用每次手动去token里解析。Vue前端这边的配合也很关键。路由配置里meta字段标记requiresAuthVue Router的beforeEach守卫里检查是否有token和用户信息没有就跳登录页Axios的响应拦截器里判断HTTP状态码401时清除本地登录态、跳登录页。另外为了避免刷新页面时Vuex里的用户信息丢失需要把用户信息做持久化或者刷新时再调用一个getInfo接口重新拉取。3.3 审批流程设计从简单状态机到多级审批的演进审批流是OA系统里最能体现“系统性思维”的模块。最简单的设计是给审批表加一个status字段0待审批、1已通过、2已驳回。但如果只有这个你会发现无法满足“部门主管先审、总经理再审”这样的多级审批需求。所以正规的做法是建两张表审批实例表和审批任务表。审批实例表记录一次完整的申请比如请假申请包含申请人ID、申请类型、开始时间、结束时间、原因、当前状态审批任务表记录流程中的每一个待办任务包含任务所属实例ID、当前处理人ID、审批顺序号、审批状态、审批意见、审批时间。当用户提交一份请假申请系统根据配置好的审批链生成多个审批任务按部门主管→HR→总经理这样的顺序依次插入。每个审批人登录系统后查询“我待办的审批任务”处理后更新当前任务状态同时判断是否还有下一个待办人。如果没有下一个任务了主实例状态更新为最终通过。这个设计相当于一个极简版的“工作流引擎”不需要引入Activiti这种重框架但业务逻辑完整代码也好讲。另有一个细节是加签、转签、会签这些扩展场景。加签指当前审批人把任务转给另一个人处理转签是自己处理完后交给下一个人会签是多个审批人都要同意。毕设能做到“顺序审批”已经合格如果还想加分可以在审批任务表里增加“任务类型”字段来区分说明你考虑到了流程的灵活性。3.4 行政办公模块公告、会议、日程、考勤的功能设计除了审批OA系统的日常办公模块通常围绕公告通知、会议管理、日程安排、考勤打卡展开这几个模块涉及的字段和业务规则各不相同正好能体现数据库设计的功力。公告模块的核心是“发布范围”。一张公告表里有标题、正文、类型通知/制度/新闻、发布人ID、发布时间、置顶标志、附件地址。为了保证不同部门看到不同公告有两种设计方式一是公告表里直接存可见部门ID列表逗号分隔或引用关联表二是给公告设置一个可见范围类型全体/指定部门然后关联可见部门表。第二种方式更正规查询时用join过滤。会议管理相对复杂一些涉及会议主题、预定时间、会议室、参与人、会议纪要。比较容易忽略的是“时间冲突”同一个会议室在同一时间段不能被两个人同时预订。在数据库层面没法简单用唯一约束解决典型做法是查询时判断会议开始时间和结束时间是否有重叠。这里要跟面试官讲清楚解决的是并发预约场景下的合理性校验不是简单的字段约束。考勤打卡模块如果只是打卡时间和状态的记录其实不复杂但要注意两个点一是打卡的时间边界校准比如上午9点前算正常9点到9点30迟到9点30后算旷工这种规则建议放到配置表里不要硬编码二是有多个办公地点时需要让用户选择一个地点再打卡或者通过IP和定位来校验高校毕设通常不必做这么深但可以在设计文档里留一个扩展点。日程安排模块则是典型的前端事件与后端日历数据结合。前后端用FullCalendar或Element UI日历组件后端提供按月份查询日程列表的接口。日程表的关键字段有标题、开始时间、结束时间、提醒时间、类型个人/工作/纪念日、是否全天、创建人ID。提醒功能可以做成定时任务扫描到了提醒时间的记录给用户发站内信或推送。3.5 数据统计与可视化让管理系统具备展示价值一个OA系统如果全是增删改查页面答辩时多少有点单薄。可以把公告阅读量统计、各部门审批数量统计、考勤异常统计、员工入职离职趋势放到一个“数据看板”里配几张ECharts图表项目的整体观感和技术含量立刻不同。后端的统计接口一般是写GROUP BY的聚合SQL。比如统计各部门的员工数量select dept_id, count(*) from sys_user group by dept_id统计每个月审批通过的数量要对创建时间的月份做date_format处理。MyBatis里写好对应的Mapper方法返回List或者自定义VO前端拿到数据用一个折线图或者饼图组件直接渲染。这一块比较容易出问题的地方是时间分组跨年和空值处理。比如统计近12个月的数据某个月可能没有数据导致后端返回的是11条前端图表就会少一条需要在后端做“补零”处理——先把12个月份列表初始化好再遍历查询结果填充。处理完之后图表就顺滑了。4. 项目环境搭建与核心实现过程4.1 从源码到本地运行环境准备和配置的四步走拿到一套“完整项目源码”之后第一步不是急着点运行按钮而是先把环境梳理清楚。通常需要准备的东西有JDK 1.8或11、17根据项目pom的版本决定、Maven 3.6、MySQL 5.7或8.0、Redis如果项目用到、Node.js 14Vue2项目对应版本要留意、以及开发IDE后端用IDEA前端用VSCode或者WebStorm都行。环境准备好之后按下面这个顺序来操作最不容易出错先初始化数据库。用Navicat或命令行执行SQL脚本注意核对脚本里是否包含创建数据库的语句。如果只有建表语句需要自己先create database比如create database oa_system default charset utf8mb4再切换到该库执行脚本。处理后端配置文件。打开application.yml或application.properties逐个核对数据源地址、端口、Redis地址、JWT密钥等配置项。最常见的坑是密码中包含特殊字符、#等没有转义导致连库失败。启动Redis如项目用到然后启动后端服务。IDEA里运行Application类观察控制台日志看到“Started Application in xxx seconds”才算启动成功。启动前端。进入前端项目目录npm install安装依赖npm run serve启动开发服务器。如果安装依赖特别慢可以用镜像源把registry切到国内镜像基本一两分钟就能装好。4.2 Spring Boot后端分层Controller、Service、Mapper之间的协作模式一个好的后端项目代码分层一定要让外行一眼就能看懂。典型的包结构如下controller放接口入口service放业务逻辑接口和实现mapper放数据库操作entity放数据库表映射实体dto放接收前端参数的传输对象vo放返回给前端的视图对象config放配置类security放认证授权相关utils放工具类common放统一返回包装、异常处理、常量定义。Controller层不要写业务逻辑只做参数接收、调用service、返回结果这3件事。Service层要处理核心业务比如审批流程的状态流转、用户登录认证、部门树形结构的组装。Mapper层如果用了MyBatis Plus单表CRUD一律不用写XML继承BaseMapper即可。多表关联或者复杂统计查询在Mapper接口上写Select注解或者在resources/mapper目录下写对应XML。一个值得学习的最佳实践是“统一返回结果包装”。Controller里的每个接口返回的不再是原始对象而是一个Result对象包含code200成功500失败401未认证、message、data三个字段。前端Axios统一按这个结构解析错误提示也统一从message字段读取。这个模式一建立整个项目的异常处理和前端交互就规范了。4.3 前端工程结构路由、状态管理、接口封装三板斧前端Vue项目的结构一般分这么几块。router目录定义路由表和导航守卫storeVuex管理用户信息、菜单权限、应用状态api目录封装所有后端接口调用utils目录放Axios实例、工具方法、auth相关方法views目录按模块存放页面组件components目录放可复用的公共组件比如上传组件、富文本编辑器、审批表单等。在api封装这一层建议统一导出一个封装好的request对象。这个对象基于axios.create创建实例设置baseURL为/api设置超时时间。在请求拦截器里从localStorage取出token放到header的Authorization字段。在响应拦截器里统一处理HTTP错误和业务码错误。比如返回401就清除登录信息并跳转登录页返回业务失败就弹出错误提示。这样业务页面代码就不用每个都写try catch做错误处理了。路由这块要注意如果权限是后端返回的前端就要做成动态路由。也就是登录成功后先调用getUserInfo接口拿到当前用户的菜单列表然后把路由表通过router.addRoutes动态添加。这个方案比提前在前端写死全部路由再在守卫里判断权限要安全得多因为每个用户的可访问页面是后端控制的。还有一个细节是404页面要放在路由表最后并且动态路由添加之后需要手动用router.redirect方法或next({...to, replace: true})保证刷新后页面不白屏。4.4 数据库脚本导入与初始化数据为什么SQL脚本那么重要拿到源码包时SQL脚本是整个项目能不能跑起来的关键。一个合格的SQL脚本应该包含四部分建库语句或注释说明、建表语句、初始数据、注释说明。初始数据尤其重要因为如果脚本里没有管理员账号你连登录界面都进不去。在导入SQL脚本时有几个容易踩的坑要提醒一下。一是字符集问题如果建表语句里没指定utf8mb4而环境默认不是utf8中文容易出现乱码建议核对所有表的字符集二是执行顺序问题如果是多个SQL文件要先执行结构文件再执行数据文件有外键关联的表先建主表再建子表三是MySQL8.0和MySQL5.7兼容性如果SQL脚本里有某些旧语法可能在8.0执行没问题但反过来会有问题具体报错信息要看清楚。还有一个好习惯导入完成后用desc关键字套一下核心表结构或者打开数据库客户端看一眼表数量、行数据量。很多实战项目里遇到过“SQL脚本是半个版本”“少了几张表”的情况提前检查总比运行起来再发现强。4.5 接口文档的阅读技巧和联调方法源码包里的接口文档通常有两种形态一种是SwaggerKnife4j在线接口文档项目启动后访问/swagger-ui.html或者/doc.html就能看到所有接口另一种是Markdown或Word形式的手写接口文档。不管哪种核心要关注的是请求路径、请求方法、请求参数含是否必填、响应数据结构和错误码含义。动手联调的时候建议先用Postman或Apifox把后端的核心接口跑通。最好的顺序是先调登录接口拿到token再调获取用户信息接口确认权限数据然后把审批、公告等其他接口一个个验证。Postman里可以新建一个环境变量token在登录接口的Tests里写脚本自动保存后面的每个接口在Authorization里引用这个变量联调效率会高很多。联调过程中接口文档和实际返回不一致是常见问题。比如文档里写返回data字段是数组实际是null。这时候要看后端控制台日志和代码不要盲目改前端兼容这种错误。因为如果是后端逻辑有问题你改了前端虽然能跑但数据逻辑就是错的答辩的时候反而说不清楚。5. 核心功能模块的开发细节与代码实现5.1 登录鉴权的完整实现从后端签发Token到前端路由守卫登录鉴权这块几乎每个面试官都会揪住细节问必须吃透。后端逻辑大概这么几步第一步Controller层的LoginController接收LoginDTO用户名、密码、验证码。登录前先校验验证码是否正确Redis里的验证码和用户输入的做比对比对通过再继续走认证逻辑。第二步调用AuthenticationManager的authenticate方法。这里要自定义一个UserDetailsServiceImpl实现loadUserByUsername方法从数据库查询用户把用户的密码、状态、角色权限信息封装进UserDetails对象返回。Spring Security内部会用BCryptPasswordEncoder校验前端传来的密码和数据库里加密后的密码是否匹配。第三步认证成功后用JwtUtil生成token。token的payload里放userId、username这些核心信息用JWT的signWith方法进行HMAC-SHA256签名设置过期时间。同时再查一次用户角色权限把权限列表放到redis里方便后续的权限校验接口使用。返回给前端的数据一般是{ token, userInfo }。第四步前端收到token后存到localStorage调用getInfo接口拉取用户详情和菜单权限存到Vuex里然后动态添加路由跳转到首页。之后每次请求Axios拦截器自动携带token后端通过JwtAuthenticationTokenFilter这个OncePerRequestFilter解析出登录身份放行后续流程。在做这块时有几点经验很关键。一是密码加密一定要用BCrypt不要用MD5加盐这种已经被证明不够安全的方案二是AuthenticationManager的配置要允许登录接口匿名访问但拦截要配置成除了登录接口以外的所有接口都需要认证三是JwtUtil里解析token的代码要处理签名异常和过期异常分别返回不同的错误信息这样前端可以根据不同的错误码给出对应提示。5.2 动态菜单与按钮权限前端权限控制的后端支撑前面说的动态路由后端的核心是根据当前登录用户的角色查询出该角色对应的菜单树返回给前端。菜单表设计通常有菜单ID、父菜单ID、菜单名称、路由地址、组件路径、权限标识、菜单类型目录/菜单/按钮、排序号、图标等字段。后端生成菜单树的代码如果数据量不大几十条菜单直接查全量然后在内存里组装树形结构就行。常见递归方法先找所有顶层节点parentId为0然后对每个节点递归找它的子节点。可以用Java的Stream分组也可以用循环加Map的写法让时间复杂度控制在O(n)。组装好之后返回给前端前端根据这个菜单树去渲染el-menu的侧边栏菜单。按钮权限这块通常用权限标识的方式控制。后端在接口上用PreAuthorize(hasAuthority(system:user:add))这样的注解前端拿到用户拥有的权限标识list之后用自定义指令v-permissionsystem:user:add来判断某个按钮是否渲染。这样就做到了页面上的按钮都能受控不同角色看到的界面差异也体现在这里。一个常见的坑是超级管理员角色要跳过所有权限校验。很多项目里RBAC的menu表里没给超级管理员配置满权限会导致新版后台连不上。解决方式是在权限判断工具类里硬编码一个判断如果当前用户名是admin直接返回拥有所有权限标识。5.3 审批流的实现状态机流转与待办任务生成审批流的数据模型前面已经说了审批实例表审批任务表。在SubmitService留底这类业务代码里核心逻辑是事务性的必须加Transactional注解保证“插入实例”和“生成多个待办任务”要么同时成功要么同时失败。具体流程如下用户提交审批前前端把申请表单数据传到后端。后端生成一个审批实例记录状态设为“审批中”。然后根据申请类型请假、报销、加班等查询对应的审批链配置比如请假3天以内只需直属主管审批3天以上需要主管HR审批。这个配置放到数据库配置表里交给后台维护而不是写死在代码里是成熟系统的一个标志。接着遍历审批链节点逐个插入审批任务记录。每个任务包含流程节点类型、处理人ID、排序、状态待办。插入完成后返回给前端“提交成功当前等待XX审批”。当审批人处理任务时后端根据操作同意/驳回/退回更新当前任务状态记录审批意见和时间。如果是同意且存在下一节点生成下一节点的待办任务如果是驳回整个实例状态置为已驳回如果是同意且没有下一个节点实例状态置为已通过。实际上这套流程本身并没有用上什么复杂的状态机框架关键是把状态变更的分支处理清楚。我的建议是把状态流转的逻辑单独抽成一个审批流服务来处理配合策略模式比如“同意策略”“驳回策略”“撤回策略”这样每新增一种审批操作不需要改原来方法的代码只需新增一个策略类代码整洁度也高。5.4 文件上传与附件管理本地存储和对象存储的取舍OA系统里公告附件、报销凭证附件、头像上传这些场景都涉及文件上传。前端用el-upload组件配合Element UI的action属性或者自定义http-request方法把文件流POST到后端的/upload接口。后端接收文件的实现方式通常用MultipartFile参数。处理逻辑是先校验文件大小、文件类型后缀白名单或者MimeType然后用UUID生成新文件名避免文件名冲突和中文乱码保存到配置的上传目录下比如/opt/oa/upload再在数据库的附件表里插入一条记录记录原始文件名、存储路径、大小、上传人ID、关联业务ID。存储路径这里有一个经验不要把文件直接存到jar包所在的目录下也不要存到src/main/resources下因为打包部署后那是个只读的jar文件。正确做法是存到外部目录通过配置项upload.path指定。同时文件访问最好通过一个/download端口映射到磁盘目录而不是直接把绝对路径暴露给前端。如果考虑到生产部署的扩展性可以把这一层的接口抽象出来后面切换到OSS这类对象存储只需要改UploadService的实现Controller不用动。5.5 数据可视化看板的接口设计数据看板这块后端接口的写法其实是在考验你对SQL聚合函数的熟练度。比如统计各部门人数select d.dept_name, count(u.id) as cnt from sys_dept d left join sys_user u on u.dept_id d.dept_id group by d.id, d.dept_name比如统计近7天申请审批的数量可以用DATE_FORMAT把创建时间转成日期字符串再按天分组。如果你的MySQL时间是datetime格式对应的子节点就是DATE或DATE_FORMAT。注意带时区的情况如果项目部署的机器时区不是东八区日期可能会差一天最好在连接串里配置serverTimezoneAsia/Shanghai。前端这一侧就可以用ECharts的饼图和折线图来展示。饼图适合展示组织架构中各部门人数占比折线图适合展示近几个月审批数量的趋势。这些都是非常直观的展现对毕设演示来说效果很好逻辑也不难。6. 常见问题与排查技巧实录6.1 数据库连接失败配置文件最容易踩的坑很多同学拿到源码后后端一启动就报数据库连接错误。先看报错信息里的关键字段如果是Communications link failure通常是数据库没启动或者端口不对如果是Access denied for user通常是用户名密码不正确如果是Unknown database说明数据库名不对或数据库还没建。排查的时候三步走第一步用Navicat单独测试连接第二步检查后端yml配置里的url、username、password是否和Navicat一致第三步确认MySQL服务已经监听3306端口或你配置的端口。如果密码里含特殊字符记得在yml里用引号包起来比如password: abc123。MySQL8.0的驱动driver-class-name是com.mysql.cj.jdbc.Driver如果配置里写的还是旧版com.mysql.jdbc.Driver也会报错。6.2 前端npm install失败或启动报错npm install失败最常见的原因是网络问题导致某些依赖下载不下来。解决办法就是切换镜像源npm config set registry https://registry.npmmirror.com如果项目里有node-sass这个老依赖安装时经常和Node版本冲突一个快捷方式是把node-sass替换成sassdart-sass并在vue.config.js里配置css.loaderOptions.scss.additionalData或者减少对scss的依赖直接使用预编译好的css。npm run serve启动后如果端口被占用会提示端口错误可以在package.json的scripts里改成vue-cli-service serve --port 8081。如果页面报错Uncaught SyntaxError多半是依赖没装干净建议删除node_modules和package-lock.json后重新安装。6.3 跨域问题登录成功但后续请求全部报错前后端分离最经典的跨域报错是CORS。表现为前端页面能打开但Ajax请求报No Access-Control-Allow-Origin header is present。解决办法有几种最简单的在后端写个配置类实现WebMvcConfigurer重写addCorsMappings方法允许前端地址跨域。但更正规的做法是不通过后端解决跨域而是在Nginx层配置反向代理达到前后端同源。本地开发时结合Vue的代理功能在vue.config.js里配置devServer.proxy把所有/api的请求代理到后端地址。这样做不仅能避免跨域还能隐藏后端真实地址生产环境同样适用。最简单而正确的姿势是前端跨域问题尽量让前端处理掉不要推到后端去CORS。6.4 权限不足或页面空白动态路由和Vuex数据不同步有时候登录之后页面白屏控制台报错“Cannot match any routes”或“No match found for location”。原因是动态路由还没添加成功就访问了当前路径。解决办法是router.beforeEach里确保等动态路由addRoutes完成后next到目标地址前再放行判断方式是借助一个全局变量比如白名单或Vuex的isRoutesLoaded字段如果还没加载过路由就重新拉取。还有一种情况是权限标识对不上前端v-permission指令写的是system:user:add但后端角色权限返回的是system:user:add:create或者菜单表里权限标识有空格导致匹配失败。排查时后端的getInfo接口返回的permissions要打印出来逐一比对。6.5 事务失效审批流的隐患点审批流相关代码必须加Transactional但事务失效有几个经典场景要避免一是方法内部递归调用比如在同一个ServiceImpl里一个方法调用另一个方法Transactional不会生效因为事务是基于代理类的内部自调用走的是this指针不是代理对象二是异常被catch吞掉了事务感知不到RuntimeException就不会回滚三是方法不是public的Spring默认不代理非public方法。排查事务失效时可以先在测试里故意抛一个运行时异常看数据库是否回滚。如果没回滚就要从上面提到的几个方面逐一检查。这个问题在答辩时经常被问你可以主动演示一下“我在这里遇到过事务失效后来是这样解决的”反而很加分。7. 项目扩展思路与二次开发建议7.1 引入工作流引擎从极简流程到Activiti/Flowable前面设计的审批链其实是一个“阉割版工作流”。如果项目想更进一步比如支持流程节点条件分支金额大于5000走总经理否则仅部门经理、会签、或签、抄送等可以考虑引入Flowable或Activiti工作流引擎。它们都提供了可视化的流程设计器支持BPMN2.0规范部署流程文件和Java代码衔接也很成熟。不过要提醒的是工作流引擎的学习曲线比较陡引入后项目复杂度会明显上升。如果是毕设除非题目明确要求工作流否则不建议替换掉手写的极简审批流。可以在设计文档里留下一小节“流程引擎扩展性分析”画一下引入引擎后的模块依赖图已经能体现思考深度了。7.2 加入WebSocket实现实时消息通知OA系统里的待办审批、公告提醒、会议提醒用WebSocket做服务端主动推送体验会好很多。Spring Boot对WebSocket的支持比较完善可以通过ServerEndpoint实现一个简单的WebSocket服务端前端在Vue组件里new WebSocket。结合JWT做连接鉴权的思路是WebSocket的URL上带token参数握手阶段验证token如果合法才建立连接。连接建立后后端可以把该用户加入一个在线用户MapuserId-session。当审批流里产生新的待办任务时根据处理人ID找到对应的session推送一条消息过去前端实时更新右上角的待办红点数。这个功能的实现过程不复杂但演示效果非常好答辩时非常能吸引目光。7.3 单元测试与集成测试的补充大多数毕设项目最缺的就是测试代码。你可以在核心模块比如用户登录、审批流转用JUnit和Mockito写几个单测。登录逻辑还可以用Spring Boot Test的SpringBootTest拉起上下文做一个集成测试测试数据填充用H2或测试库。写测试不一定能让你在答辩时多讲几分钟但你可以在“项目质量”这一页PPT里放上一个“单元测试覆盖率达到xx%核心接口测试用例xx个”这样的图这比口头说“我测过”有说服力得多。而且面试官有时候就吃这一套因为大多数应届生没有意识写测试你能写就已经拉开差距了。7.4 部署发布从本地到云服务器的完整流程本地跑起来只是第一步能部署到云服务器上让面试官直接用手机访问毫无疑问是一个加分项。部署步骤大致是准备一台云服务器装好JDK、MySQL、Redis、Nginx把后端项目打包成jar包用nohup java -jar xxx.jar启动把前端项目打包成dist目录把静态文件放到Nginx的html目录下配置Nginx的location /api反向代理和location /静态文件最后用域名或IP访问。这里有一个非常容易踩的坑后端服务监听的地址默认是localhost外部访问永远连不上配置文件里要用0.0.0.0。另外云服务器的安全组要在防火墙面板里放行相关端口不然怎么跳都跳不通。部署过程的坑不少但整套走一遍之后你对这份源码的理解会高出好几个层级因为很多依赖关系不看真机运行环境根本体会不到。8. 写在最后的经验与建议这套SpringBootVue企业OA系统我实际跑下来最大的感受是它真正值钱的不是那几千行CRUD代码而是背后这套“怎么把一个真实业务场景抽象成数据模型和接口服务”的思路。你照着它敲一遍、改一遍、再亲手部署一遍比闷头看十篇“Spring Boot入门教程”管用得多。如果你现在手上已经有源码我的强烈建议是不要只是把项目跑起来截几张图就完事。试着改一个功能——比如把审批流程从两级改成三级或者给公告模块加一个阅读统计功能然后在原来代码基础上实现它。改动的过程中你才会发现哪些地方设计得巧妙哪些地方其实有隐患这些真实感受就是你答辩时最生动的素材。最后再分享一个小技巧。拿到一个不熟悉的项目源码时先别急着打开代码细节先用数据库客户端把表和初始数据过一遍再拿着接口文档对照着把核心接口用Postman跑一遍然后再回来看代码理解速度会快很多。这个流程我自己每次接手新项目都用稳得很。
阅读完成 · 觉得有帮助?
咨询建站