简介:本资源是一套面向计算机专业本科生的高分毕业设计实战项目,聚焦高校入校申报审批业务场景,采用SpringBoot后端+Vue前端的主流技术栈实现全流程线上化管理,适用于毕业设计选题、课程设计及期末综合实训。压缩包共367个文件,包含83个Java核心业务类(如CommonController、YonghuController等)、32个Vue组件页面、161个SVG图标资源、15个JS交互脚本、10个XML配置文件及2个SQL数据库脚本,辅以bat一键部署脚本、演示视频与详细使用文档,整体大小28.54MB。已有82人学习下载,项目已在Windows 10/11环境实测通过,答辩评分高达97分,获导师指导认可;提供完整可运行源码、结构清晰的模块划分、开箱即用的部署方案及关键操作录屏,显著降低二次开发与调试门槛。 最近几年Java毕业设计选题很有意思,大家越来越不满足于“图书管理”“学生选课”这类老掉牙的系统,开始往更贴近实际业务场景的方向走。我接触到的选题里,基于SpringBoot+Vue的入校申报审批系统出现频率相当高,网上围绕这个题目的源码、数据库、使用文档、演示视频资源也特别多。今天把这套项目从设计到落地的完整链路拆开讲讲,包括为什么这么设计、数据库怎么写、前后端怎么对接、演示和答辩怎么准备,尽量把容易踩的坑一次说透。
1. 项目整体定位与设计思路
1.1 为什么“入校申报审批”适合当毕设题目
先说结论:这个题目属于“业务逻辑清晰、技术点够用、演示效果直观”的典型代表。入校申报审批的核心场景是校园对外来人员或本校学生的入校申请进行线上审核。访客填报申请信息,审批人员按角色做审核,状态从“待审批”变成“通过”或“驳回”,整个过程涉及登录鉴权、角色权限、表单提交、审批流转、数据统计等多个环节。
相比于商城、论坛这类偏通用的系统,入校申报审批的业务链条更垂直,评审老师在看的时候不用花太多时间去理解业务背景,一眼就知道你做的系统要解决什么问题。同时它又不算太简单,至少包含三类角色、一条审批流程、若干条核心状态,能够撑起毕业设计的全部要求。
另外这个题目在整合三方依赖上比较顺畅。后端用SpringBoot处理接口和业务逻辑,前端用Vue做页面交互,MySQL负责数据持久化。技术栈不花哨但覆盖了企业开发的主流组合。对毕业生来说,写完这个项目,你在简历上可以理直气壮地写“熟悉前后端分离开发”“掌握SpringBoot+Vue项目实践经验”,不会像某些偏算法或偏底层的题目那样做了半年却没法直接展示。
1.2 SpringBoot+Vue技术选型背后的考量
为什么这个组合能成为毕业设计的主流选择?我个人的理解是:它兼顾了“能力展示”和“可控性”两个点。
SpringBoot最大的价值是简化了Spring的配置,你不需要手动写一长串XML配置,一个Application类就能启动项目。对毕设而言,这意味着开发周期短、出活快。同时又因为项目本质上还是Spring家族,像Spring MVC、MyBatis这些主流框架你都会用到,面试时被问到相关原理也能接得上话,不会像纯增删改查那样没有深度。
Vue这边则是渐进式框架的代表。你用Vue2还是Vue3都行,但很多题目的参考代码和历史文章都是Vue2+Element UI的组合,遇到问题反而更好查。Vue的组件化让页面拆分成登录、申请表单、审批列表、详情弹窗等独立模块,写起来清晰,答辩时也好讲。
有人问为什么不直接用JSP加SpringBoot,节省跨域和部署的麻烦?这个问题我也想过。前后端分离虽然会有跨域、token管理这些额外工作,但这些恰恰是现在企业开发的标准姿势。既然做毕业设计,除了完成功能,还要让老师看到你具备现代Web开发的工程化思维。所以哪怕麻烦一点,也建议选前后端分离。
1.3 这类项目交付物的整体构成
标题里的“源码+数据库+使用文档+演示视频”,实际上就是毕业设计提交的标准四件套。
源码是核心,包括后端SpringBoot工程和前端Vue工程。数据库是一个SQL脚本文件,里面包含建库、建表、基础数据和测试数据。使用文档主要说明启动步骤、账号信息、功能目录和数据库说明。演示视频则是把项目跑起来后按流程录制的操作过程,用于在无法现场演示的时候给老师看。
这四件套构成了一个完整可复现的项目。我在实际做类似项目时,还有一个习惯:额外写一个“部署说明.txt”,把从安装JDK到启动Vue一步一步解释清楚,免得自己过一个月忘记怎么跑项目。这个习惯后面也会详细讲。
2. 功能模块拆解与数据库设计
2.1 三类核心角色与权限边界
把系统拆开看,最基础的设计是角色划分。入校申报审批系统通常包含三类角色:
- 申请端角色:学生或教职工。他们登录后可以填写入校申请、查看自己提交的申请记录、查看审批进度、撤销还未审批的申请。
- 审批端角色:根据学校实际流程,可能是班主任、系部管理员、保卫处人员。他们的核心操作是查看待审批列表、点击通过或驳回、填写审批意见。不同层级的人看到的数据范围不一样。
- 管理端角色:系统管理员。负责账号和部门维护、发布入校规则和公告、查询全部申请记录、导出统计结果。
权限边界要在前后端同时控制。前端通过路由守卫判断用户是否有权限进入某页面,后端通过拦截器和接口层权限校验防止越权。比如学生直接请求审批接口,后端必须返回无权限错误,不能只靠前端隐藏按钮来保护。
2.2 数据库表设计
数据库表设计是整个系统最基础也最关键的部分。我见过不少同学的数据表都设计得比较随意,审批记录和申请记录混在一起,结果做审批流的时候状态乱套。
推荐按下面的方式拆表:
- user表:用户基础信息,包含id、username、password、real_name、phone、department、user_type。
- role表:角色信息,包含id、role_name、role_key。role_key可以设计成student、approver、admin。
- user_role表:用户角色关联表,因为用户和角色是多对多关系,通过中间表维护最规范。
- apply_record表:入校申请表,核心字段包括id、user_id、apply_no、purpose、plan_in_time、plan_out_time、visitor_name、visitor_phone、accompany_num、car_number、status、current_node、create_time、update_time、deleted。
- approval_record表:审批记录表,包含id、apply_id、approver_id、approve_status、approve_comment、create_time。
- notice表:公告信息表,包含id、title、content、create_by、create_time。
申请表字段里最关键的是status和current_node。status表示当前申请处于什么状态,比如待审批、已通过、已驳回、已撤销;current_node表示当前审批到达哪个节点,比如班主任、院系、保卫处。所以status描述的是整体状态,current_node描述的是流程位置,两者配合才能让前端正确渲染进度条。
审批记录表的设计上要注意,一张申请单经过多个审批人时,不要直接把原记录的审批人字段改掉,而是每条审批动作插入一条新记录。这样历史留痕完整,答辩时你可以说“系统保留了完整的审批链路,方便追踪责任”。
2.3 数据库设计的几个关键细节
设计数据库时,我建议提前考虑逻辑删除而不是物理删除。比如用户误删一条申请记录,如果用delete语句直接删,数据就没了;如果用deleted字段标记,管理员还能在后台恢复。毕设项目规模不大,物理删除对性能的影响可以忽略,但逻辑删除这个意识本身就是加分项。
另外一个容易忽略的地方是状态枚举的统一。数据库里不要出现“待审”“已批”“驳回”这种口语化文字,最好存英文状态码:PENDING、APPROVED、REJECTED、REVOKED、COMPLETED。后端代码里定义一个枚举类统一管理,前端显示时再转换成中文。这样代码逻辑更清晰,也方便后续加状态。
申请编号也要单独设计。比如apply_no可以用日期加自增序列生成,或者用时间戳加随机数。不要用主键id去展示给用户,别人可以通过id遍历接口拿到数据。
3. 后端核心实现过程:SpringBoot篇
3.1 项目初始化与包结构规划
假设你拿到的是带源码的项目,第一步要做的就是看懂包结构。一个比较规范的SpringBoot后端工程应该是这样:
- com.example.campus.controller:接收前端请求,返回统一JSON结果。
- com.example.campus.service:业务逻辑层,负责处理审批流转、校验权限等核心逻辑。
- com.example.campus.mapper:数据访问层,使用MyBatis-Plus的BaseMapper。
- com.example.campus.entity:数据库对应的实体类。
- com.example.campus.dto:前端传入的请求参数对象。
- com.example.campus.vo:返回给前端的视图对象。
- com.example.campus.common:统一返回结果、异常处理、常量定义。
- com.example.campus.config:跨域配置、WebMvcConfigurer、拦截器配置。
pom.xml中需要的核心依赖大致有这些:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.19.2</version> </dependency>为什么推荐用MyBatis-Plus而不是纯MyBatis?因为毕业设计里大部分操作就是单表增删改查和简单联表,MyBatis-Plus自带分页插件、条件构造器、代码生成器,写代码效率高很多,而且对初学者来说,它的BaseMapper已经封装了常用方法,你只需要在复杂查询时手写SQL就行。
3.2 登录鉴权与接口权限控制实现
登录鉴权我建议使用JWT,而不是传统的Session。原因很简单:前后端分离环境下,后端不需要维护Session,前端每次请求把token放在请求头里,后端通过拦截器解析用户信息。
实现思路是这样的:
- 用户提交username和password,后端查询user表,通过BCrypt验证密码。
- 验证通过后,生成一个JWT token,把userId、userName和用户角色列表写进token的payload里。
- 返回前端一个登录结果对象,包含token和用户基本信息。
- 前端把token存到localStorage,axios请求拦截器自动在请求头加上Authorization。
- 后端写一个拦截器,对所有需要登录的接口校验token是否有效、是否过期。对于handlerMethod,再通过自定义注解判断是否有角色权限。
后端核心代码大致类似:
@Component public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (token == null || token.isEmpty()) { throw new BusinessException(401, "未登录"); } // 解析token,如果无效或过期则抛异常 LoginUser loginUser = JwtUtils.parseToken(token); // 把当前登录用户放入ThreadLocal,方便Service层获取 UserContext.set(loginUser); return true; } }角色权限可以用类似这样的自定义注解:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RequireRole { String[] value(); }然后在Controller的方法上加注解,比如审批接口只允许approver和admin访问。拦截器里拿到方法上的注解,再判断当前用户角色是否包含其中的某个值,不匹配就返回403。
这里有个细节:密码不能明文存储。哪怕答辩时只是演示,也建议用BCrypt或MD5加盐处理,不要在数据库里直接放明文。这样你可以在答辩时说考虑了基本安全设计。
3.3 审批流程状态机的核心逻辑
入校申报审批系统的核心功能就是审批流转,这块逻辑写得好不好,直接决定项目质量。
先定义状态枚举:
public enum ApplyStatus { PENDING("待审批"), APPROVED("已通过"), REJECTED("已驳回"), REVOKED("已撤销"), COMPLETED("已完成"); private final String description; ApplyStatus(String description) { this.description = description; } }审批接口处理逻辑一般是这样:
- 根据applyId查询申请记录,判断记录是否存在、是否处于当前审批人可操作的状态。
- 判断当前登录用户的审批节点是否匹配申请记录里的currentNode。
- 更新申请表的状态和节点,同时插入一条审批记录到approval_record。
- 如果审批通过且已经到达最后一个节点,就把申请状态改成APPROVED,此时可以生成对应的入校码或二维码标识。
- 如果驳回,就直接把状态改成REJECTED,流程终止。
- 学生端撤回申请时,只有status为PENDING时才能撤销,否则要提示“当前进度不可撤销”。
状态流转里最需要关注的是节点推进。假设流程是“班主任审批 -> 院系审批 -> 保卫处确认”,那么currentNode可以设计为数字或字符串标识,通过一个Map或常量类配置每个节点下一步要流转到哪个节点。
核心代码可能长这样:
public void approve(Long applyId, String comment) { ApplyRecord record = applyRecordMapper.selectById(applyId); // 校验登录用户身份 if (!canApprove(record, loginUser)) { throw new BusinessException("当前用户无权处理该申请"); } if (!"PENDING".equals(record.getStatus())) { throw new BusinessException("申请单当前状态不可审批"); } // 业务判断 int nextIndex = currentNodeIndex(record.getCurrentNode()); if (nextIndex >= nodeList.size() - 1) { record.setStatus("APPROVED"); record.setCurrentNode("DONE"); } else { record.setCurrentNode(nodeList.get(nextIndex + 1).getNodeCode()); } record.setUpdateTime(new Date()); applyRecordMapper.updateById(record); // 插入审批记录 ApprovalRecord approval = new ApprovalRecord(); approval.setApplyId(applyId); approval.setApproverId(loginUser.getId()); approval.setApproveStatus("APPROVED"); approval.setApproveComment(comment); approvalRecordMapper.insert(approval); }这个过程中要特别留意“重复审批”问题。如果前端并发请求或者用户连续点击两次“通过”,系统必须保证不会把审批状态重复推进。最简单的方案是使用乐观锁,在apply_record表增加version字段,更新时使用:
UPDATE apply_record SET status = 'APPROVED', version = version + 1 WHERE id = ? AND version = ?在代码里判断update返回的行数,如果为0就代表数据已被别人修改,直接返回“操作失败,请刷新后重试”。这个机制不复杂,但能展现你对并发安全有一定认识。
3.4 文件上传与消息提醒这类扩展功能
如果基础版本已经完成,可以考虑加文件上传功能。比如入校申请时上传健康码截图、行程证明或邀请函,用SpringBoot接收MultipartFile,存到本地指定目录,并把文件路径存到attachment表。
前端展示时使用图片预览组件,或者提供一个下载链接权限校验的接口。这一步能扩展系统的实用价值,答辩时也可以说“为申请材料提供了存档能力”。
消息提醒功能如果时间不够,可以用简单的站内消息实现。比如审批结果产生后,往message表插入一条记录,学生在首页右上角看到小红点。对毕设而言,不需要对接短信、邮件,站内消息已经够用。
3.5 统一返回结果与全局异常处理
接口返回格式尽量统一,前端处理起来会轻松很多。我常用的结构是:
{ "code": 200, "message": "操作成功", "data": {} }后端定义一个Result类,Controller返回时调用Result.ok(data)或者Result.fail(msg)。登录失效时返回401,权限不足返回403,业务规则校验失败返回500或自定义业务码。
再配合@RestControllerAdvice捕获所有异常,避免在Controller里到处写try-catch。这种全局异常处理会让代码干净很多,也方便向前端返回友好的错误信息。
4. 前端核心实现过程:Vue篇
4.1 Vue工程结构与基础配置
前端工程通常是用vue-cli创建,目录结构大致如下:
- src/api:封装axios请求模块,按功能拆成login.js、apply.js、approve.js、notice.js等。
- src/router:定义路由表,配置动态路由和前置守卫。
- src/store:Vuex模块,保存用户信息、登录状态、权限标识。
- src/views:页面组件,包括Login.vue、Dashboard.vue、ApplyList.vue、ApplyCreate.vue、ApproveList.vue、UserManage.vue。
- src/components:通用组件,比如上传组件、状态标签、分页组件、步骤条。
- src/utils:工具函数,包括token存储和解析。
axios封装要注意请求拦截器和响应拦截器的使用。请求拦截器在每个请求头里加上token,响应拦截器统一处理错误码。比如后端返回401时,清掉本地token并跳转到登录页;返回403时弹出“无权限访问”的提示;返回200时直接返回data字段,避免每层都写嵌套结构。
4.2 登录鉴权与路由守卫实践
前端登录页提交表单后,拿到后端返回的token和用户信息。推荐将用户信息存到Vuex中,同时将token放入localStorage,这样页面刷新后还能通过Vuex的初始化方法读回。
路由守卫是最关键的环节。在router.beforeEach中判断目标路由是否要求登录,如果已登录则继续,未登录则跳转到登录页。对于角色权限,可以在meta字段里声明allowedRoles,在守卫里检查当前用户角色。
Vue2里的写法大致如下:
router.beforeEach((to, from, next) => { const token = localStorage.getItem('token'); if (to.meta.requiresAuth && !token) { next('/login'); } else if (to.meta.allowedRoles) { const userRole = store.state.user.role; if (!to.meta.allowedRoles.includes(userRole)) { next('/403'); } else { next(); } } else { next(); } });这套逻辑写清楚之后,前端页面展示的菜单也会根据角色动态渲染。比如学生登录后看到“我的申请”“我要申报”,审批人登录后看到“待审批列表”,管理员看到“用户管理”“数据统计”。动态菜单可以通过后端返回的权限标识来过滤,也可以用前端写死角色判断,毕业设计阶段用前端判断就够了。
4.3 申请表单与审批列表的页面实现
申请表单页面是学生端使用频率最高的地方。建议用Element UI的el-form组件统一布局,字段尽量和后端数据库字段一一对应,这样联调时不容易出错。要做的校验包括必填字段是否为空、手机号格式、日期范围是否合理。
时间选择器选择“计划入校时间”和“计划离校时间”时,要设置校验规则确保离校时间晚于入校时间。这个细节看似简单,但非常体现程序的严谨性,答辩时能顺手讲出来。
审批列表页面建议用卡片式布局,而不是纯表格。每张卡片展示申请人的基本信息、入校时间、申请状态,点击“查看”弹出详情抽屉,在抽屉里展示完整表单、申报材料和审批时间线。审批人可以在抽屉底部选择“通过”或“驳回”,驳回时必须填写意见,前端用el-input的必填校验来控制。
状态列要用Tag组件区分颜色,比如PENDING显示为黄色标签、APPROVED显示为绿色标签、REJECTED显示为红色标签、REVOKED显示为灰色标签。这样视觉上一目了然,演示视频里也好看。
4.4 Axios调用与跨域联调技巧
前后端分离开发最绕不开的就是跨域问题。开发阶段最简单的方式是在Vue.config.js里配置devServer代理:
module.exports = { devServer: { port: 8081, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }这样前端请求"/api/login"时,实际上是请求后端"http://localhost:8080/api/login"。代理方式的好处是前端不用开启后端CORS也能联调,浏览器地址栏里都是同源的,业务代码里不用做额外处理。
如果后端也配了CORS,双保险也没问题。但要注意生产部署后,Nginx同样可以做反向代理,把前端静态资源和后端接口统一到同一个域名下。懂这个链路,答辩讲部署方案时就很加分。
4.5 演示视频录制时的前端细节
录演示视频时,Vue的前端体验感受会影响整体观感。建议提前准备几条测试数据,比如张三的申请状态是待审批,李四的申请状态是已通过,王五的申请状态是已驳回。演示的时候走一遍完整流程:学生登录提交申请,切换审批人登录处理申请,学生刷新状态看到审批结果。每一步都要等接口返回后再操作,不要在请求还没完成时疯狂点击。
视频里还可以展示一下前端响应式和断网提示。如果时间充足,也可以打开浏览器F12,展示Network里接口请求和响应,让老师看到前后端数据交互的过程。这对“演示视频”的质量提升非常大。
5. 项目运行环境配置与部署指南
5.1 本地开发环境准备
想把项目跑起来,第一步得把基础环境装好。最常见的版本组合是:
- JDK 1.8
- Maven 3.6+
- Node.js 12或14
- npm 6.14
- MySQL 5.7或8.0
- IDEA 或 Eclipse
先检查“java -version”“mvn -v”“node -v”能否正常输出。JDK是1.8的话注意Maven和IDEA的版本兼容,推荐用2019版之后的IDEA,高版本IDEA如果装了低版本JDK也没问题。
然后导入SQL脚本。有两种方式,一种是直接用Navicat或MySQL命令行执行整个.sql文件,另一种是在IDEA的Database面板里运行脚本。数据库名建议用campus_apply这种带项目特征的名称,避免用test。
执行完后确认表数量和基础数据,特别是用户表和角色表是否已经有admin账号、测试学生账号、审批人账号。很多项目的演示账号写在README或使用文档里,如果你是自己造数据,记得密码统一加密后在文档里注明原文密码。
5.2 后端启动步骤与常见配置修改
后端项目导入IDEA后,先等Maven把依赖下载完。如果项目是Gradle管理的,则等待Gradle同步。在启动之前,要打开application.yml确认数据库账号密码和当前环境一致。
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/campus_apply?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0配置里那个serverTimezone=Asia/Shanghai要特别注意,很多同学连接MySQL 8时报错“The server time zone value”就是少了这一段。另外MyBatis-Plus的逻辑删除字段配置,必须和数据库deleted字段对应上,否则你查数据时会发现删除的记录还在。
启动后端后,浏览器直接访问“http://localhost:8080/api/test”之类的接口,如果能返回JSON,说明后端已经正常运行。如果启动失败,优先看日志里是端口占用还是数据库连接失败。端口占用的话直接改server.port,不然就检查MySQL服务有没有启动。
5.3 前端启动步骤与常见配置修改
前端项目打开后,先执行npm install安装依赖,然后执行npm run serve启动开发服务器。如果node_modules已经存在,可以直接跳过安装步骤。启动后浏览器访问“http://localhost:8081”,看到登录页面就代表前端正常。
Vue前端端口和代理配置要查一遍,尤其是接口路径前缀。如果后端接口前缀是“/api”,前端所有请求都要带上这个前缀;如果代理目标不对,登录时浏览器Network里会显示404或500,这类问题十有八九是target配置错了。
如果前端启动报node版本过高导致node-sass安装失败,可以把node-sass替换成sass,或者升级成dart-sass。网上现成源码里很多是Vue2+node-sass搭配,在你自己的电脑上很容易翻车。
5.4 生产部署方案(可选加分)
想更进一步,可以把前后端分别打包,然后部署到云服务器。后端用Maven打成jar包,运行“java -jar xxx.jar”;前端用“npm run build”生成dist目录,然后用Nginx托管静态文件。
Nginx配置里需要做反向代理:
server { listen 80; server_name your_domain; location / { root /opt/campus/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }注意try_files这行,如果漏掉,前端刷新页面时会出现404,因为Vue是单页应用,路由由前端接管,Nginx必须把非资源请求全部指向index.html。
部署这部分不是必须的,但如果有时间,把项目部署到云服务器给老师一个访问链接,绝对能拉高答辩评分。很多老师会觉得只有本地跑的毕设“不通透”,有个公网链接就完全不一样了。
6. 常见问题排查与避坑实录
6.1 数据库连接失败的多种原因
最常见的错误是“Communications link failure”和“Access denied for user”。前者一般是数据库没启动、端口不对、url里IP写错、或者是本机防火墙拦截。后者是用户名或密码错误,需要逐一排查。
还有一种情况是MySQL 8的驱动名变了。SpringBoot 2.x里驱动名是com.mysql.cj.jdbc.Driver,而不是老的com.mysql.jdbc.Driver。如果使用老驱动,会报ClassNotFound。直接把driver-class-name改成新的即可。
另外只要你用的是MySQL 8,serverTimezone基本必须配置。配置好后,再连接就不会出现时区异常。
6.2 前端接口响应403/401的处理
接口401通常代表token失效或未登录,需要检查前端请求头有没有正确携带token。接口403代表当前用户没有该操作权限,这时要看数据库user_role表里当前用户绑定的角色是否正确。
有一种特别坑的情况是后端拦截器已经通过,但Vue页面还是显示无权限。这时要检查后端接口的@RequireRole注解和前端登录用户角色是否完全对应。比如数据库里的role_key是“admin”,后端注解写的是“ADMIN”,大小写不一致就会匹配不上。
6.3 上传文件失败或路径问题
文件上传功能如果做得不好,演示时最容易翻车。常见问题包括上传目录权限不足、请求头没有multipart/form-data、后端没有配置静态资源映射。
如果上传的是图片,前端需要回显,可以给后端加一个文件访问接口,比如“/file/{fileName}”,读取本地目录后输出到响应流。不要用传统方式直接拼一个file路径丢给前端,因为前端通过浏览器无法直接访问服务器本地文件。
6.4 MyBatis-Plus分页不生效
MyBatis-Plus的分页插件需要自己注册PaginationInnerInterceptor,否则调用selectPage时数据能返回,但total和pages都是0。很多同学在仿照网上教程敲代码时漏掉了这一步,导致前端分页组件显示的总页数始终不对。
注册方法是在配置类里加一个MybatisPlusInterceptor的Bean,并添加PaginationInnerInterceptor。执行完这一步,再分页查询就不会出问题。
6.5 演示前必做的检查清单
我强烈建议在录制演示视频或答辩之前,按这个清单过一遍:
- 数据库服务是否启动,SQL脚本是否执行成功。
- 测试账号能否正常登录,密码是否与测试文档一致。
- 后端接口是否能正常返回,是否因为代码改动导致编译错误。
- 前端页面刷新后是否正常,是否有跨域或404问题。
- 提前准备几条处于不同状态的申请数据,便于演示。
- 关闭电脑上的无关弹窗,避免录视频时出现尴尬通知。
- 准备一份备用数据库脚本,如果现场数据库被改乱了,重置一遍再跑。
7. 进阶优化与答辩亮点建议
7.1 从“能用”到“好看”的加分项
如果核心功能都已完成,可以有选择地加上一些容易出彩的功能。
第一个是审批时间线。这个功能其实是把approval_record表的记录按时间排序后渲染成一条时间线,显示每一步审批人、审批意见和时间。前端用Element UI的el-timeline组件,十几行代码就能实现,但视觉效果和项目完整度提升非常明显。
第二个是数据统计面板。管理员首页可以展示今日新增申请、今日待审批数量、近一周申请趋势。后端提供一个统计接口,返回几个数字,前端用卡片和简单折线图展示。如果不想用ECharts,用CSS画简洁的柱状图也行。
第三个是导出Excel。管理员查询申请记录后,可以点击按钮导出Excel表格。后端用EasyExcel或Apache POI生成文件,前端通过window.open或axios的blob方式下载。这样整个系统就从“演示型”变成了“工具型”。
7.2 申请表状态机优化
基础版本只有简单状态字段,进阶版本可以定义一张流程配置表或状态流转表,把“当前节点、下一步节点、可操作角色”配置在数据库里。这样做的好处是新增审批环节时不用改代码,只改配置记录就行。
答辩时可以讲“我通过配置驱动的方式实现了审批流程,后续如果学校要增加环节,只需管理后台配置新的节点和角色,不需要重启服务”。这句话虽然简单,但很多人做不出来,能说清楚就是加分项。
7.3 答辩PPT和讲解逻辑
答辩时间一般有限,讲解逻辑建议按“问题场景 -> 解决方案 -> 技术实现 -> 成果演示”四个步骤走。
先介绍当前学校入校申请存在的问题:线下流程慢、申请记录难查询、审批进度不透明。然后引出你的系统如何解决这些问题。接着按“后端如何设计接口”“前端如何展示数据”“数据库如何支撑流程”讲技术实现。最后演示一遍完整流程,从登录到提交申请再到审批通过,一路走下来。
解释项目的时候,不要只念类名和方法名。老师更关心的是“你为什么要这样设计”。比如“为什么用JWT而不是Session”,你就可以说“当前后端是前后端分离架构,JWT无状态,适合前端多端使用”。再比如“为什么审批记录要单独建表”,你可以说“为了保留完整的审批轨迹,支持追溯和复盘”。
7.4 写在最后的一点经验
从选题到答辩,这类系统的坑其实不算多,真正的难点反而在于前端和后端联调时信息同步。如果自己从头写,建议先画清楚数据库关系图,然后确定接口文档,再动手写前后端代码。如果用的是网上的源码,不要直接跑,先花半天把表结构和核心流程吃透,再改一改功能和样式,这样才能在答辩时做到有问必答。
尤其是那些标了“高分项目”的资源,原始代码不一定能直接在你电脑上运行。拿到手先看数据库脚本和配置文件,缺什么补什么。我见过太多同学因为node-sass装不上、MySQL版本对不上,结果项目根本跑不起来。程序跑起来了,后面一切都好说。
本文还有配套的精品资源,点击获取