SpringBoot + Vue 写乡政府管理系统平台,是 Java Web 毕业设计里非常典型的一类题目。每年都有不少同学拿到的是这样一套完整项目包:源码、SQL 脚本、接口文档全都配齐,看起来什么都有,可真正动手跑起来、弄明白里面逻辑的人却不多。这篇文章就按我实际调试这类系统的思路,把这个项目从目录结构、数据库设计、接口设计到本地跑通、答辩准备完整拆一遍,希望能让你从“能打开”变成“讲得清”。
这套系统要解决的问题其实很接地气:乡镇基层工作人员需要管理居民信息、发布通知公告、受理群众上报事项、走流程审批,还要能对数据进行统计展示。手工台账时代这些事情分散在多个本子里,查起来费劲;用系统管理之后,所有操作留痕、可检索、可统计,这就是毕设展示时最核心的亮点。如果你是准备做 Java Web 毕设的学生,或者在学前后端分离但缺少一个综合练手项目,这个题目都非常合适。
1. 拿到项目包,先别急着双击运行
1.1 为什么 SpringBoot + Vue 是毕设最稳的选择
很多同学会问,为什么这类管理系统不是用 JSP 写,也不是用 SSM 手写一堆配置,而是普遍选择 SpringBoot 加 Vue?答案很朴素:资料多、坑少、分工清楚。
SpringBoot 解决了传统 Spring 项目里大量 XML 配置的问题,内嵌 Tomcat,打成一个 jar 包就能跑,这对毕设来说非常重要。你不需要去纠结容器怎么配,把精力放在业务逻辑上。Vue 的好处则是页面组件化,前端代码结构清晰,配合 Element UI 这类组件库,后台管理页面很快就能搭出来。前后端分离后,前端归前端、后端归后端,联调时只需要约定好接口格式,答辩演示时也更容易讲清楚“某个按钮干了什么”。
这种管理系统的本质,其实就是增删改查加权限控制,外加少量统计图表。它不需要高并发方案,不需要分布式架构,但要求每一层你都讲得明白。SpringBoot 加 Vue 这个组合,恰好能覆盖从数据库、接口、页面到部署的完整链路,这也是它成为 Java Web 毕设主流选择的原因。
1.2 项目包里的目录结构,决定你从哪看起
拿到一套完整源码,很多人第一反应是直接导入 IDEA 然后按启动键。我建议先花十分钟把项目包整体看一遍,确认里面的模块划分。典型的结构长这样:
township-admin/ ├── backend/ # 后端工程 │ ├── src/main/java │ ├── src/main/resources │ └── sql/ │ ├── township_admin.sql │ └── init_data.sql ├── frontend/ # 前端工程 │ └── src/ │ ├── api/ │ ├── router/ │ ├── views/ │ └── utils/ └── 接口文档.md如果里面有 README,一定要先读。README 通常会写明 JDK 版本、MySQL 版本、Node 版本、默认账号密码,这些信息直接决定了你后面会不会踩版本坑。我见过太多人因为不看 README,把 JDK 17 的项目环境配成 JDK 8,结果一启动就报错,还以为是源码有问题。
后端 part 一般分为 controller、service、mapper、entity 这几层,resources 里放着 application.yml 或 application.properties。SQL 目录通常是整个项目的地基,先建库、再导表、最后插数据。前端部分主要关注 views 和 api 两个目录,一个对应页面,一个对应接口调用。
1.3 技术栈选型背后,其实是权衡的结果
下面这张表是这类项目最常见的选型,你可以对照手里源码看是否一致:
| 分层 | 常见选择 | 选择理由 |
|---|---|---|
| 后端框架 | SpringBoot 2.7.x | 稳定、资料多,与大部分前端组件兼容 |
| ORM 框架 | MyBatis-Plus | 减少大量 CRUD 代码,分页查询方便 |
| 权限方案 | JWT + 拦截器 | 无状态、演示方便、思路容易讲清楚 |
| 前端框架 | Vue2 + Element UI | 成熟稳定,毕设模板多;Vue3 + Element Plus 同样常见 |
| HTTP 请求 | Axios | 统一封装请求、拦截器处理 token |
| 图表 | ECharts | 数据统计看板基本都在用它 |
有几个点特别值得说。MyBatis-Plus 之所以比原生 MyBatis 更受欢迎,是因为它自带条件构造器和分页插件,写单表查询时基本不用手写 SQL;像用户列表、公告列表这种全是单表 CRUD 的场景,代码量能少一半。权限方面,很多毕设不用 Spring Security 是因为学习成本偏高,配置过滤器链、用户认证、方法级注解,对项目周期只有几个月的学生来说并不友好。JWT 加拦截器的方式足够应付演示,而且答辩时“前端带 token、后端拦截校验”这句话也非常好解释。
前端选 Vue2 还是 Vue3,主要看项目本身用的什么。如果源码就是 Vue2,不要手痒升级成 Vue3,Element UI 和 Element Plus 的组件用法、兼容性差异会浪费你大量时间。先把项目跑起来,后续想升级另说。
2. 先看懂业务模块和 SQL 脚本,再谈跑项目
2.1 业务模块按四类来理解,记起来特别快
乡政府管理系统听起来业务很多,其实归纳下来就是四块:系统基础、居民档案、协同办公、数据统计。
系统基础包括用户管理、角色管理、菜单管理,解决的是“谁能登录、能看到什么”。居民档案是这类系统的特色模块,一般是户信息加家庭成员,包含户主姓名、身份证号、住址、家庭人口数量、土地面积等。协同办公是功能最密集的部分,有公告通知、事件上报、审批流程,事件上报可以理解成群众有事找政府,工作人员收到后进行分派、处理和反馈。数据统计则是首页看板和图表,比如各状态事件数量、每月新增居民数、公告发布统计。
把业务分成这四类后,你再去看代码就轻松多了。控制器类名基本能和模块对上,比如 UserController、HouseholdController、NoticeController、EventController、ApprovalController。这样看代码不是一行行读,而是按模块跳着看,效率高很多。
2.2 建库导数据之前,先把脚本读明白
SQL 脚本是整个项目的地基。拿到脚本后不要急着双击运行,先打开看几眼。一般来说脚本里会依次出现这么几部分:创建数据库、使用数据库、创建表、插入初始数据。
因为表之间有关联,比如用户表关联角色,事件表关联用户,所以导入的时候要注意顺序和依赖关系。如果脚本里直接是完整的建库和建表语句,那用命令行或者数据库工具直接跑就好。我习惯用命令行方式:
mysql -u root -p登录后先执行:
CREATE DATABASE IF NOT EXISTS township_admin DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE township_admin; SET NAMES utf8mb4; SOURCE /your/path/township_admin.sql;这里有三个细节值得注意。第一,数据库排序规则建议用 utf8mb4_general_ci,支持的字符集更全,能避免中文乱码和个别生僻名字存储报错。第二,导入之前先执行SET NAMES utf8mb4;,否则脚本里的中文注释和初始数据很容易变成乱码。第三,如果你用的是 MySQL 8.0,而 SQL 脚本是在 MySQL 5.7 里导出的,遇到utf8mb4_0900_ai_ci相关的报错时,需要手动把排序规则改成utf8mb4_general_ci,这是兼容性最省事的处理方式。
看完脚本后,你可以把表名、表注释、核心字段在纸上画一个简版关系图。不需要画得很复杂,重点是明确用户表、事件表、审批表之间的外键关系,后面答辩画系统架构图时也用得上。
2.3 核心表的字段设计思路
以我调试过的这类项目为例,有张用户表长得差不多是这样:
CREATE TABLE `sys_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '登录账号', `password` varchar(255) NOT NULL COMMENT '加密后的密码', `real_name` varchar(50) DEFAULT NULL COMMENT '姓名', `role_code` varchar(30) DEFAULT 'staff' COMMENT 'admin/staff/resident', `phone` varchar(20) DEFAULT NULL COMMENT '联系电话', `status` tinyint(4) DEFAULT '1' COMMENT '1启用 0禁用', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `deleted` tinyint(4) DEFAULT '0' COMMENT '逻辑删除 0未删 1已删', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表';这类字段设计有它固定的逻辑。deleted字段是逻辑删除标记,表面上叫删除,实际只是打一个标记,数据还在表里。这样做的原因很实际:居民的办事记录、事件的处理历史,一旦物理删除就没法追溯,乡镇政务场景下留痕比删除重要得多。create_time和update_time属于公共字段,几乎每张表都有,后端可以通过 MyBatis-Plus 的自动填充功能统一处理,写代码时就不用每次手动 set 时间。
再看居民信息表,通常有户主信息和家庭成员信息。户主表字段包含household_code户号、head_name户主姓名、head_id_card身份证号、address地址、population人口数、land_area土地面积。这里的关键是身份证号要唯一,否则数据会重复。事件上报表则包含事件编号、事件类型、标题、内容、上报人、上报时间、处理人、状态和处理结果。状态字段是它最核心的设计所在,一般用数字代表不同阶段,比如 0 待受理、1 处理中、2 已完成、3 已驳回。
表格形式整理一个常见版本,方便对照:
| 表名 | 核心字段 | 业务含义 |
|---|---|---|
| sys_user | username, password, role_code, status | 登录用户与角色 |
| household | head_name, head_id_card, address, population | 居民户信息 |
| event_report | event_type, title, status, handle_result | 群众上报事项 |
| notice | title, content, top_flag | 公告信息 |
| approval | business_type, business_id, status, approver | 通用审批记录 |
2.4 初始数据里的默认账号,别忽视
SQL 脚本除了建表,一般还会插入几个初始账号。最常见的是超级管理员 admin,密码要么是 123456,要么是 admin123,但不同项目不一样,一切以接口文档或 README 里写的为准。除管理员外,通常还有普通工作人员和居民身份的测试账号。
这些账号对应的角色不同,登录后看到的菜单和能操作的按钮也不一样。比如 admin 可以查看全部数据和所有菜单,工作人员只能维护对应模块,居民账号只能上报事件和查看自己的处理进度。第一次登录后先把默认密码改掉,尤其是准备演示时,不要让评审老师看到你还在用默认密码,这个细节很加分。
如果你发现初始数据里没有测试账号,也不要慌,可以直接复制一条 sys_user 记录改成自己的手机号,再把密码字段改成项目里相同的加密方式。这里要注意,如果密码存储是 BCrypt,不能直接在数据库里写明文,需要通过后端注册接口或写个临时测试代码生成加密串,否则怎么改都登录不上去。
3. 接口文档与后端核心代码:带着问题看,不要从头读到尾
3.1 接口文档先看这三处
接口文档是连接前后端的契约。看接口文档不要从头到尾背,重点看三块内容:接口路径前缀、鉴权方式、分页参数。
这类项目接口路径一般统一以/api开头,比如/api/user/page、/api/event/page、/api/notice/list。看到这个前缀,你就知道所有业务接口都在它后面,前端 request 封装里也会统一加上这个基础路径。鉴权方式基本都是请求头加一个Authorization字段,值是一串 JWT token。分页参数则通常叫pageNum和pageSize,对应第几页和每页几条。
下面是一个典型的接口描述格式:
| 项 | 内容 |
|---|---|
| 接口路径 | GET /api/event/page |
| 请求参数 | pageNum, pageSize, status, keyword |
| 请求头 | Authorization: Bearer token |
| 响应格式 | Result<PageResult > |
| 功能说明 | 分页查询事件上报列表 |
看完这三个信息,再去对照前端 API 目录里的调用代码,基本就能把一条数据从前端到后端的链路串起来。遇到接口调不通,也优先排查这三个点,路径是否一致、token 是否带上、参数名是否写错。
3.2 统一响应体是你调通接口的第一道门槛
前后端分离项目里,后端返回的数据格式必须统一,前端才能写一套判断逻辑通吃所有接口。这类项目通常会用 Result 或 Response 类包裹返回数据,结构是 code、message、data 三个字段。
一个简化版本长这样:
public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> r = new Result<>(); r.setCode(200); r.setMessage("操作成功"); r.setData(data); return r; } public static <T> Result<T> error(String msg) { Result<T> r = new Result<>(); r.setCode(500); r.setMessage(msg); return r; } }前端拿到响应后,一般会先判断 code 是否等于 200。这里有个容易踩坑的地方,不是所有非 200 的 code 都代表业务异常,比如 401 通常代表 token 失效,需要在请求拦截器里统一处理并跳转到登录页。你在看接口文档时,如果看到响应示例里只有 success 的,最好再去后端全局异常处理类里看一眼 error 和 unauthorized 对应的 code,这样排查问题时会很顺。
3.3 权限认证:看拦截器的排除路径就能懂登录原理
理解了 JWT 后,这类项目的权限逻辑能看懂八成。流程是这样的:用户输入账号密码,后端验证通过后生成一串 token 字符串返回给前端;前端把 token 存到 localStorage;后续每次请求都带着 token;后端写一个拦截器,对所有接口请求检查 token 是否存在且合法,不合法直接返回 401。
拦截器的核心逻辑并不复杂:
public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); // 校验 token,失败则返回 401,成功则放行 if (StringUtils.isBlank(token) || !JwtUtil.verify(token)) { response.setStatus(401); return false; } return true; } }这里最值得研究的是拦截器配置里的排除路径。正常都要把登录接口、静态资源、Swagger 文档页面、前端跨域预检请求 OPTIONS 放行掉,否则登录接口自己都被拦截了,系统就会陷入“不登录无法访问、不访问无法登录”的死循环。这也是这类项目最常见的启动后问题之一。
理解这套认证机制后,答辩时被问到底层逻辑也不用怕,按“用户认证发 token、请求携带 token、拦截器校验 token、前端路由跳转”这条线讲就行。相比引入 Spring Security,这种自己实现的轻量方案更适合在十分钟内把原理讲透。
3.4 业务状态流转:审批逻辑最容易在答辩时被追问
像事件上报、审批这类模块,业务上最核心的就是状态流转。状态流转说白了就是用一个字段记录当前所处阶段,然后按既定规则更新它。
以事件上报为例,初始状态可能是 0 待受理,工作人员处理时更新为 1 处理中,办结后更新为 2 已完成,如果不符合要求则驳回为 3 已退回。这些状态值在后端往往是常量或枚举,前端页面显示时按钮的可用性会根据状态判断,比如已完成的不能再次处理,已退回的上报人可以重新提交。
这里有两个点很容易被答辩老师追问。第一个是为什么用数字而不是直接存文字?因为业务状态可能改名,比如“处理中”改成“办理中”,如果存的是数字,只需要改前端的字典翻译即可,不需要动数据库。第二个是状态更新时要不要保证多个操作同时成功?比如事件办结时要同时改事件状态和写入处理结果,这两步必须在一个数据库事务里,否则可能出现事件状态改了但结果没保存的脏数据。你在代码里看到@Transactional注解时,知道它是用来保证事务的,回答基本就能到点子上。
更新状态的方法里,经常会伴有一段统计 SQL,比如按状态统计事件数量。一般写法是:
SELECT status, COUNT(*) FROM event_report GROUP BY status;这种统计结果直接喂给前端 ECharts 饼图,就是首页看板上的数据来源。所以数据统计模块并不神秘,它本质就是几个分组查询加前端图表渲染。
4. 前端页面与前后端联调的关键细节
4.1 前端目录按功能找,比按组件找更快
Vue 项目打开后,先不要看components目录里有什么通用组件,直接看views目录。因为 views 下的文件夹名称通常与后端模块一一对应,比如views/system/user对应用户管理,views/event对应事件上报。找到页面文件后,再看这个页面里 import 了哪些 API,然后去 api 目录里找对应的接口定义。
utils/request.js是另一个重要文件,它封装了 Axios 实例,统一设置了请求超时时间、请求头、响应拦截器。几乎所有接口调用都会走这个统一入口,所以排查问题时先确认这个文件里的配置是否正确。router 目录则是路由配置,里面能看出系统有哪些页面、哪些页面需要登录才能访问、哪些页面是管理员专属。
4.2 一个列表页的完整调用链
列表页面是最常见的页面形态,理解它等于理解了系统一半的前端逻辑。以事件列表为例,页面加载时会调用一个分页接口:
export function fetchEventPage(params) { return request({ url: '/api/event/page', method: 'get', params }) }页面里的话,通常是created或mounted生命周期里调用这个函数,把返回结果里的 records 赋给表格数据源,把 total 赋给分页组件的总数。这里要注意一个高频 bug:后端参数叫pageNum,前端 params 里如果写成了page,后端拿不到分页参数,返回的就是默认第一页或直接报错。联调时遇到“明明有数据但表格不显示”的问题,第一反应就是抓包看请求参数名是否和后端一致。
编辑和删除也是同样的套路。点编辑按钮后,把当前行数据回显到弹窗表单,提交时如果是新增就调用增加接口,如果是编辑就调用更新接口。接口路径、请求方法、参数结构这三项对应上,整个页面基本就通了。
4.3 跨域问题:用代理解决,别只在后端开 CORS
前后端分离开发时,前端跑在 8080,后端跑在 8081,端口不同就存在跨域问题。解决跨域的常规方案有两种:后端开启 CORS,或者前端配置代理。
这类毕设项目我强烈建议用前端代理方式,代理配置在 Vue CLI 项目里是vue.config.js,在 Vite 项目里是vite.config.js,写法略有差异但思路一致:
module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8081', changeOrigin: true } } } }这个配置的意思是,前端只要往后端发请求路径以/api开头,就自动转发到 8081 端口的后端服务。这样浏览器看到的请求都是同源的,不会触发跨域限制。如果项目里后端已经写了 CORS 配置,前端代理又在做转发,某些情况下会重复设置跨域头,反而产生怪问题,所以我通常建议前后端保持统一,要么用代理,要么用 CORS,不要两边都开。
改完代理配置后记住重启前端服务,proxy 配置不影响 js 语法的热更新,不重启不会生效。改完还不行的话,就打开浏览器的开发者工具,看请求的路径到底被转发到了哪里,排查起来很快。
4.4 前端权限:路由守卫和菜单显示
前端权限控制主要体现在两个方面,一个是路由守卫,一个是菜单动态显示。
路由守卫的逻辑是在跳转路由前判断用户是否登录,未登录就强制跳转到登录页。代码大致是这样:
router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.path !== '/login' && !token) { next('/login') } else { next() } })菜单动态显示则根据角色来判断。比如 admin 可以看到系统管理菜单,staff 看不到用户管理,resident 只能看到事件上报。常见做法是在路由的 meta 里配置角色要求,渲染菜单时根据当前用户的角色过滤一遍。还有一种更完整的方式,是后端在登录成功后直接返回当前用户可访问的菜单列表,前端根据这个列表动态生成路由。两种都行,关键要能讲清楚。
需要注意,前端隐藏菜单只是体验上的优化,真正的权限控制必须在后端接口完成,否则会变成“绕过前端就能越权访问”。后端拦截器可以限制未登录访问,但更细的接口级角色校验还需要额外做,这个点答辩老师经常追问,你提前想好答案就稳了。
5. 把项目从零跑起来:可直接照抄的步骤和避坑清单
5.1 环境版本先对齐,再动手
很多启动失败不是代码问题,而是环境版本不匹配。启动之前先确认几个关键版本,比写代码更重要。
| 环境 | 推荐版本 | 说明 |
|---|---|---|
| JDK | 8 或 11 | SpringBoot 2.x 项目最常见 |
| Maven | 3.6+ | 依赖管理必备 |
| MySQL | 5.7 或 8.0 | 看 SQL 脚本语法兼容性 |
| Node.js | 14 或 16 | Vue2 项目常见版本区间 |
| npm / yarn | 随 Node | 建议使用 npm |
如果你手头项目是 SpringBoot 3.x,那 JDK 必须 17 以上,这个要特别注意。MyBatis-Plus 版本、Spring Boot 版本、JDK 版本三者是有配套关系的,强行混用会在启动时报UnsupportedClassVersionError或依赖冲突。最简单的办法是打开后端pom.xml,看<java.version>标签写的是什么,再按这个版本配 JDK。
5.2 后端启动四步走
第一步,创建数据库并导入 SQL。这一步可以完全照前面的命令操作,导入完成后在数据库工具里刷新,确认表数量和初始数据都在。
第二步,打开application.yml修改数据源配置。重点关注这三个配置:
server: port: 8081 spring: datasource: url: jdbc:mysql://localhost:3306/township_admin?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver这里的serverTimezone=Asia/Shanghai是用来避免 MySQL 时间差八小时的常见问题,characterEncoding=utf8是保证中文不乱码的关键参数。如果你用的是 MySQL 5.7,驱动类名可能是com.mysql.jdbc.Driver,换成 8.0 就改成com.mysql.cj.jdbc.Driver。密码务必填对,这里错误最典型的表现就是启动日志里报Access denied for user。
第三步,启动后端。可以用 IDEA 直接运行启动类的 main 方法,也可以在项目根目录执行:
mvn spring-boot:run启动后看到类似Started Application in x.xxx seconds的日志就说明成功了。
第四步,验证后端。找一个公开的接口,比如登录接口,用 Postman 或浏览器访问,能返回 JSON 就说明后端没问题。如果项目集成了 Swagger,访问http://localhost:8081/doc.html或/swagger-ui/index.html能看到接口列表,那就更方便了。
5.3 前端启动三步走
前端启动比后端简单,但依赖安装有时候很折磨人。
第一步,进入前端目录后执行依赖安装。如果默认源速度很慢或卡住,用国内镜像快很多:
npm install --registry=https://registry.npmmirror.com这里有个常见坑:如果项目里用了node-sass,它的安装依赖 Node 版本,Node 版本过高会直接编译失败。遇到这种情况,优先把 Node 降到项目 README 指定的版本,再用npm rebuild node-sass修复,而不是硬着头皮升级依赖,升级后很容易出现一堆兼容性问题。
第二步,启动开发服务器。Vue2 项目执行npm run serve,Vue3 项目一般是npm run dev:
npm run serve看到App running at: Local: http://localhost:8080/这类输出就成功了。
第三步,浏览器打开前端地址,用默认账号登录,能进入首页看到图表数据,就说明前后端已经通了。如果页面空白或接口报错,不要急着改代码,先打开开发者工具的 Network 面板,看接口请求是否发出、返回状态码是多少。
5.4 高频报错速查表
调试这类系统时,有几个报错出现频率极高。我把实际遇到过的情况整理成了速查表:
| 报错现象 | 常见原因 | 处理方式 |
|---|---|---|
| Access denied for user | 数据库账号密码错误 | 检查 application.yml 配置 |
| Unknown database | 数据库没建或名字不对 | 先执行 CREATE DATABASE |
| Port 8080 was already in use | 端口被占用 | 换端口或结束占用进程 |
| 登录接口返回 401 | token 未放行或前端没带 token | 检查拦截器排除路径和前端请求头 |
| 前端页面接口跨域 | 代理未配置或未重启 | 重新配置 proxy 并重启服务 |
| node-sass 编译失败 | Node 版本过高 | 降级 Node 或重建本地依赖 |
| 页面中文乱码 | 数据库编码不是 utf8mb4 | 建库时指定 utf8mb4 并重新导数据 |
| 接口返回 500 但日志无异常 | 日志级别过滤或异常被吞 | 调整日志级别为 debug 再复现 |
排查时记住一个原则:先看日志,再看网络请求。后端报错信息里通常会精确到哪一行,前端接口调用失败则优先看请求路径、参数名和状态码。不要一上来就怀疑源码有问题,整套项目跑通之前,十个报错里至少有七个是环境和配置层面的问题。
6. 答辩准备、二次扩展和一点过来人经验
6.1 答辩前把这三个问题练熟
答辩时间通常不长,老师不可能把每行代码都问到。但有三个问题几乎跑不掉:为什么选这个技术栈、数据库为什么这么设计、权限是怎么控制的。
回答第一个问题时,不要只罗列技术名词,要从需求出发讲。比如 SpringBoot 简化了配置部署,Vue 让页面开发更高效,MyBatis-Plus 提升了 CRUD 开发效率,JWT 适合前后端分离场景。把这些理由串成一段“需求决定选型”的话,比背名词强得多。
回答第二个问题时,要从业务出发。比如用户表为什么要逻辑删除,可以说因为群众上报记录和处理历史需要追溯;事件状态为什么用数字而不用文字,可以说状态名称可调整且节省存储、方便统计。只要你能解释“为什么这么设计”,老师就会认为你是真的理解了业务,而不是抄了套代码。
权限问题是最容易被追问的。建议先讲整体流程,再讲前端路由守卫和后端拦截器的分工,最后强调“前端控制是为了体验,后端控制才是安全底线”。如果你能在项目里指出具体代码位置,再补充一句“密码存的是加密后的密文而不是明文”,这个问题的印象分会明显提升。
6.2 二次开发:从毕设到能用的系统,一般加这四个模块
如果时间充裕或者想提升项目的完整度,有四个方向改动成本低、展示效果好。
第一个是操作日志模块。用 AOP 或拦截器记录谁在什么时间做了什么操作,这是政务类系统非常看重的“审计留痕”能力。第二个是文件上传模块,比如公告附件、事件处理的证明材料,用本地存储实现即可,答辩时能演示上传和下载就是加分项。第三个是 Excel 导入导出,比如居民信息批量导入,使用 EasyExcel 比 POI 简单很多。第四个是定时统计任务,可以用 Spring 的@Scheduled注解,每天自动生成前一天的数据统计,让首页看板更动态。
这四个方向不需要全部都做,挑一个和当前项目关联最强的实现即可。做得深一点、能演示,比做得多了但每个都很浅更好。加模块时注意遵循原有代码风格,比如 controller 返回 Result、service 接口与实现类分层,这样代码看起来才像一个完整的工程。
6.3 调试这类项目,我养成的几个习惯
调试这套项目时,我踩过几次坑之后养成了几个固定习惯。
第一,任何修改之前先备份 SQL。别小看这一步,改错字段、删错数据是常有的事,尤其当你在测试逻辑删除功能时误操作把真实数据标记删除,没有备份就只能重新导一遍初始数据,所有测试记录全部归零。第二,改完一个功能先看后端日志再上前端。前端页面报错往往只是表象,真正的堆栈信息在后端控制台,把日志看明白了再动手改,能少走很多弯路。第三,接口先独立测试再连页面。用 Postman 把登录、查询、新增、删除这些接口单独过一遍,确认后端没有问题,再去页面上点按钮,这样定位问题时的范围会小很多。第四,演示环境固定一套数据。答辩前把演示数据准备好,不要现场导数据、现场注册账号,避免数据库连不上或者数据没初始化这类低级意外。
最后说一句个人体会。SpringBoot + Vue 的乡政府管理系统本身并不难,难点在于把每个技术点从“会跑”升级到“会讲”。项目是你做的,但真正留给评审老师的印象,取决于你能否把设计与实现讲得清楚,这也是毕业设计这个环节真正想训练你的能力。希望这篇拆解能帮你少走些弯路,在答辩前少焦虑几天。