
“SpringBoot Vue MySQL陕西民俗网”这个题目标题我见过太多次了。作为这些年帮不少学弟学妹跑过毕设代码的人我第一眼看到它就能脑补出完整的项目形态一个集民俗文化展示、活动发布、用户互动于一体的前后端分离网站。它不花哨但足够典型几乎所有毕业设计该有的东西——表设计、增删改查、分页搜索、登录注册、文件上传、前端路由、联调部署——全都覆盖了。这篇文章我不打算给你讲那种“概念上很美”的东西直接按我实际跑这类项目的经验把技术选型、数据库设计、前后端关键代码、部署流程、以及我踩过最深的几个坑全部掰开揉碎讲一遍。不管你是准备拿这个题目做毕设还是想找一个能练手的实战项目这篇都能让你少走很多弯路。1. 项目解读与设计思路1.1 民俗文化网站究竟在做什么陕西民俗网这个题目本质上做一个“文化内容管理 用户互动”的网站。它跟电商、博客、新闻站点在架构上没什么本质区别区别只在于业务实体不一样这里的核心不是商品和订单而是民俗分类、文化条目、活动信息、用户收藏和评论。所以拿这个题目有一个天然优势——它不需要你发明一套新业务规则所有逻辑都是“生活常识可解释”的论文里好写答辩时好讲。你做一个民俗列表页就是展示皮影、剪纸、社火、秦腔这些分类下的内容点击进去是详情页有图文、有简介、有浏览次数。你做一个活动模块就是发布“端午节民俗展演”“庙会安排”这类信息用户可以查看详情、参与报名。再配上注册登录、收藏、评论一个可展示的完整系统就出来了。这个题目的“文化属性”还有一个隐性好处美化界面时素材多。B站、图库、各地文旅网站的公开素材都能用来填充演示数据视觉效果好老师看了印象分自然不会低。我在给别人做演示数据时通常会把每个分类下放 8 到 12 条内容配图用统一风格首页轮播图放 3 到 5 张一眼看过去像个真在运营的网站。1.2 为什么是 SpringBoot Vue MySQL而不是别的这套组合在国内毕设里已经算“基础设施”了。你问十个人八个人用的是这套剩下两个用的是改了壳的 SpringBoot Thymeleaf。但它能成为默认选项是有实际原因的第一SpringBoot 把后端开发的门槛降到了非常低的水平。不需要你再折腾一大堆 XML 配置一个类就能启动 Web 服务内置 Tomcat打完 jar 直接扔服务器上跑这对大多数非科班或者基础一般的同学来说非常友好。我见过不少用 SSMSpring SpringMVC MyBatis改 SpringBoot 的项目改动量并不大但写起来舒服太多。第二Vue 是纯前端项目中最容易上手的框架之一。拿 Vue 做页面不需要手写原生 DOM 操作数据绑定是声明式的。你要展示民俗列表只要把后端返回的数组丢给页面然后在模板里用 v-for 渲染一下就行逻辑非常直观。第三MySQL 免费、常见、资料多。从安装到建库建表遇到任何问题基本一搜就有答案。对于一台学生电脑、一个开源项目来说不花钱能跑起来比什么都重要。真正需要小心的只有版本。我明确建议后端用 SpringBoot 2.7.x 配 JDK 1.8数据库用 MySQL 5.7 或 8.0前端如果是 Vue 2 就配 Element UI如果是 Vue 3 就配 Element Plus。千万不要手一抖直接下 SpringBoot 3.x——那东西要求 JDK 17 以上很多老教程、老包、老写法全都对不上运行报错时查博客的时间够你再跑一个模块了。1.3 项目功能拆分与整体模块设计从需求上讲我把这套系统拆成两大部分前台展示端和后台管理端。前台面向游客和注册用户后台面向管理员。前台端核心模块首页轮播图、分类入口、推荐内容展示。民俗文化列表按分类查看所有文化条目皮影、剪纸、民歌、社火、饮食等支持分页、搜索。民俗详情展示图文、来源、简介、浏览次数登录用户可以收藏和评论。活动动态展示民俗活动列表和详情登录用户可以报名。个人中心查看自己的收藏、评论、报名记录修改基本信息。后台端核心模块管理员登录通常初始化一个 admin 账号。民俗内容管理增删改查、上传封面图、设置所属分类。分类管理管理民俗文化的大类和小类。活动管理发布、编辑、删除活动。用户管理查看注册用户禁用/启用账号。这个功能清单别贪多。很多同学想加“在线预约座位”“VR看展”“视频点播”听起来高级但毕设周期就那么长功能越多写代码和写论文的工程量越大越容易烂尾。把上面这几个模块做扎实表设计合理、代码结构清晰、部署能跑通就是一个轻轻松拿中等偏上分数的合格项目。2. 数据库设计与后端核心实现2.1 数据表怎么设计才合理表关系和字段细节数据库设计直接决定后端写起来顺不顺手。我按这个项目给你列一个最精简但完整的表方案一共 6 张表用户表、分类表、民俗内容表、活动表、评论表、收藏表。用户表 user 是基础字段至少要有CREATE TABLE user ( id INT NOT NULL AUTO_INCREMENT COMMENT 用户ID, username VARCHAR(50) NOT NULL COMMENT 用户名, password VARCHAR(255) NOT NULL COMMENT 密码(BCrypt加密), nickname VARCHAR(50) DEFAULT NULL COMMENT 昵称, avatar VARCHAR(255) DEFAULT NULL COMMENT 头像URL, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, role TINYINT DEFAULT 0 COMMENT 角色 0普通用户 1管理员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;密码我强烈建议不要用 MD5直接用 Spring Security 里的 BCryptPasswordEncoder 或者 hutool 工具类做 BCrypt 加密。MD5 背后有没有安全缺陷这里不展开但答辩老师很可能会问“密码怎么存的”你答 BCrypt 加密比答 MD5 至少在态度上高一个等级。分类表和民俗内容表是项目里的核心两张表。分类表 category 要能上下级分层所以设计成父子结构CREATE TABLE category ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 分类名, parent_id INT DEFAULT 0 COMMENT 父分类ID0表示顶级, sort_order INT DEFAULT 0 COMMENT 排序, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;民俗内容表 culture 承载主业务数据字段设计时注意几点封面图和详情图要分开存 URL内容不能只存纯文本建议用 TEXT 类型存富文本 HTML状态字段控制上下架排序和浏览量用来做推荐逻辑。参考建表脚本CREATE TABLE culture ( id INT NOT NULL AUTO_INCREMENT, category_id INT NOT NULL COMMENT 所属分类ID, title VARCHAR(100) NOT NULL COMMENT 标题, cover VARCHAR(255) DEFAULT NULL COMMENT 封面图URL, summary VARCHAR(500) DEFAULT NULL COMMENT 摘要, content TEXT COMMENT 正文(富文本), view_count INT DEFAULT 0 COMMENT 浏览次数, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, status TINYINT DEFAULT 1 COMMENT 1上架 0下架, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;活动表 activity 类似 culture重点加活动时间、地点、报名人数上限CREATE TABLE activity ( id INT NOT NULL AUTO_INCREMENT, title VARCHAR(100) NOT NULL, cover VARCHAR(255) DEFAULT NULL, start_time DATETIME NOT NULL COMMENT 开始时间, end_time DATETIME DEFAULT NULL, location VARCHAR(200) DEFAULT NULL COMMENT 活动地点, content TEXT, max_people INT DEFAULT 0 COMMENT 报名上限0不限, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;评论表和收藏表属于互动数据。评论表直接关联 user_id 和 culture_id收藏表除了关联用户和内容还要加一个唯一索引防止重复收藏。报名记录我通常单独建一张 activity_signup 表字段就三样id、user_id、activity_id、报名时间唯一索引 (user_id, activity_id)。这个表结构最大的特点是“每个表都是为了页面而存在的”。前台展示什么页面后台管理什么列表都能在表里找到对应关系论文画 E-R 图时也特别好画。反正我辅导别人画图时发现一个规律表太散E-R 图乱成一团老师一看就觉得没设计这六张表整整齐齐从用户出发的收藏评论报名关系一目了然答辩的底气完全不一样。2.2 SpringBoot 项目结构、关键配置与统一返回后端项目我推荐直接用 Spring Initializr 生成不要手工建 Maven 项目。包结构用标准的 controller / service / mapper / entity 四层这样最直观老师看起来也最熟悉com.example.folklore ├── controller # 接收前端请求 ├── service # 业务逻辑 │ └── impl ├── mapper # MyBatis-Plus 的 Mapper 接口 ├── entity # 数据库实体类 ├── config # 跨域、静态资源映射等配置 ├── common # 统一返回类、异常处理、工具类 └── FolkloreApplication.java关键的 application.yml 配置我用 MyBatis-Plus 做数据访问配置文件长这样server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/folklore?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse 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 global-config: db-config: id-type: auto这里的 jdbc url 是很多新人第一次跑不起来的重灾区MySQL 8.0 必须在 url 后面带 serverTimezone否则报时间区错误useSSL 要设成 false否则启动时会有告警characterEncoding 要保证中文不乱码。如果你装的是 MySQL 5.7driver-class-name 写 com.mysql.jdbc.Driver 也能跑但我统一用 8.0 的驱动因为新版驱动向下兼容 5.7 的连接。统一返回结果类是后端所有接口的“通用外壳”没有它前端解析数据会非常难受。我习惯这样写Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.setCode(200); r.setMessage(操作成功); r.setData(data); return r; } public static T ResultT error(String message) { ResultT r new Result(); r.setCode(500); r.setMessage(message); return r; } }所有 Controller 的返回值都包成 Result前端 axios 拦截器只看 code 就能判断请求成功失败。我在码代码时也会顺手写一个全局异常处理器用 RestControllerAdvice 捕获业务异常统一返回 Result.error避免异常堆栈直接裸奔给前端。2.3 后端接口实现的几个关键细节先说分页和搜索。MyBatis-Plus 提供了内置分页插件配置一个 MybatisPlusInterceptor 就行。民俗列表接口大概是这样的模式GetMapping(/culture/page) public ResultIPageCulture page( RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 8) Integer pageSize, RequestParam(required false) Integer categoryId, RequestParam(required false) String keyword) { LambdaQueryWrapperCulture wrapper new LambdaQueryWrapper(); if (categoryId ! null) { wrapper.eq(Culture::getCategoryId, categoryId); } if (StringUtils.hasText(keyword)) { wrapper.like(Culture::getTitle, keyword).or().like(Culture::getSummary, keyword); } wrapper.orderByDesc(Culture::getViewCount); PageCulture page cultureMapper.selectPage(new Page(pageNum, pageSize), wrapper); return Result.success(page); }这种写法几乎不用手写 XML SQL逻辑都在代码里方便你写完以后在论文中解释“查询条件如何动态拼接”。注意 like 和 or 联用时优先级要考虑条件多的时候最好改成两个独立 wrapper 嵌套否则容易查出不该出现的数据。再一个是图片上传接口。民俗内容的后台管理肯定要传封面图我用的方案是把图片保存到本地磁盘然后把磁盘目录映射成可访问的 URLValue(${file.upload-path}) private String uploadPath; PostMapping(/upload) public ResultString upload(RequestParam(file) MultipartFile file) { String originalFilename file.getOriginalFilename(); String suffix originalFilename.substring(originalFilename.lastIndexOf(.)); String newFileName System.currentTimeMillis() suffix; File dest new File(uploadPath File.separator newFileName); file.transferTo(dest); return Result.success(/images/ newFileName); }同时在配置类里加一个静态资源映射让 /images/** 指向上传目录Configuration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/images/**) .addResourceLocations(file:D:/upload/); } }这里有个容易踩的坑如果你把映射路径写成了 file:///D:/upload/ 少了东西或者目录不存在都会导致前端图片直接 404。我建议在做项目时先手动建好目录并且在启动日志里打印一下上传路径便于排查。登录接口我用 JWT 做无状态认证。用户登录成功后后端生成一个 token 返回给前端前端存在 localStorage 里之后每次请求通过拦截器放在请求头中。后端用拦截器校验 token并将当前用户信息放入请求上下文。这个设计好处是不用操作 session前后端完全分离而且部署到集群环境也不会出问题。权限上管理员接口加一个角色判断普通用户访问管理接口直接返回 403。这个后端写下来大概 20 多个接口分类列表、内容分页、内容详情、收藏、评论、活动列表、报名、登录注册、后台 CRUD。不多不少正好覆盖一个合格毕设需要的接口量。3. 前端 Vue 从搭建到页面落地3.1 环境准备与项目初始化前端部分是很多同学进度卡住的地方其实卡点基本都在环境上不在代码本身。先把 Node.js 装好我建议装 Node 16.x不要一上来装最新的 20 或 22因为旧项目依赖尤其是 node-sass在新版本 Node 上编译容易报错。如果你机器上已经装了新版 Node那也别慌用 nvm 装一个 16 再切换即可。检查环境的命令就三条node -v npm -v npm config get registry如果 npm 下载包特别慢把镜像源换成淘宝省下的时间够你写两个页面npm config set registry https://registry.npmmirror.com然后创建 Vue 项目。这里我给两条路线你自己选路线一Vue 2 Element UI使用 Vue CLI 创建。npm install -g vue/cli vue create folk-web # 选择 Vue 2 预设 cd folk-web npm install axios element-ui vue-router3 vuex3这条路线资料最多网上 90% 的毕设代码都是这种结构遇到问题最好搜答案。路线二Vue 3 Element Plus使用 Vite 创建。npm create vuelatest # 选择 Vue3、Router、Pinia cd folk-web npm install axios element-plus这条路线更现代但如果你的论文要求不多没必要为了追新而给自己增加学习成本。我见过太多人卡在 Element Plus 的按需导入配置上白白浪费一个周末。说白了毕设的核心是“稳定跑通”不是“技术最潮”。我自己给人跑项目时一律推荐路线一。3.2 页面结构、组件拆分与路由设计前端页面不需要一百个文件堆在那里拆成 8 个左右页面就够Layout 布局页包含顶部导航栏、中间内容区、底部版权区。Home 首页轮播图 推荐分类入口 最新内容列表。CultureList 文化列表页分类切换 搜索框 分页列表。CultureDetail 民俗详情页内容展示 浏览次数 收藏/评论操作区。ActivityList 与 ActivityDetail活动列表和活动详情。Login 与 Register登录注册页。Admin 管理后台内含内容的增删改查表单和列表。路由设计上采用嵌套路由前台 Layout 包着 Home、Culture、Activity后台单独一个 Admin 路由并在进入前做权限校验。核心路由代码大概长这样const routes [ { path: /, component: Layout, children: [ { path: , component: Home }, { path: culture/list, component: CultureList }, { path: culture/detail/:id, component: CultureDetail }, { path: activity/list, component: ActivityList }, { path: activity/detail/:id, component: ActivityDetail } ] }, { path: /login, component: Login }, { path: /register, component: Register }, { path: /admin, component: Admin, meta: { requiresAuth: true, role: 1 } } ]路由守卫在跳转前检查本地存储的 token 和用户角色没登录就重定向到登录页。这里细节就是管理员信息不能只存个用户名要把角色一起存进 localStorage后端接口再校验一次做到“前端控制展示后端控制数据安全”。组件复用这块我习惯把分页列表抽成一个组件传入接口地址、搜索参数、渲染插槽列表页和后台列表页共用把评论区抽成另一个组件传入 contentId前台详情页和后台都用它。这个设计在写论文的时候也能单独提一节“公共组件设计”是加分项。3.3 前后端联调Axios 封装与跨域前端所有请求我都封装在一个 request.js 里避免每个页面写一堆重复代码import axios from axios const request axios.create({ baseURL: process.env.VUE_APP_BASE_URL, // 通常是 http://localhost:8080 timeout: 15000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) request.interceptors.response.use( response { const res response.data if (res.code 200) { return res.data } if (res.code 401) { localStorage.removeItem(token) window.location.href /login } return Promise.reject(new Error(res.message || 请求失败)) }, error { return Promise.reject(error) } ) export default request这里 baseURL 不硬编码死而是放在 .env.development 环境变量里部署时只需改环境变量不用改代码。这是一个很小的工程化习惯但很多教程不会教。跨域问题我推荐两种方案同时用上第一后端加一个 CORS 配置类允许前端所有请求来源第二如果部署时用 Nginx 做反向代理直接把 /api 前缀代理到后端端口前端所有请求都走相对路径彻底避开跨域。开发阶段用第一种部署阶段用第二种两全其美。我在这里单独说一下开发时的联调过程通常你写完一个接口先在后端浏览器直接访问看返回 JSON确认没问题后前端页面里调 request.get(/culture/page)看到控制台 Network 标签里请求返回 200再检查渲染结果。按这个节奏走前后端联调基本半小时就拿下一批接口。4. 部署与交付从本机到服务器4.1 本地跑通项目的完整流程这部分对你来说是最实际的。按照下面顺序一步步来大概率一次跑通第一步初始化数据库。用 Navicat 连接本地 MySQL新建数据库名字和 yml 里保持一致比如 folklore字符集选 utf8mb4然后导入项目里附带的 folklore.sql 脚本。导入成功后重点检查三件事表数量对不对、有没有数据、admin 账号在不在。第二步修改后端配置。打开 application.yml把数据库密码改成你自己的密码MySQL 端口如果是 3307、3308 这种也要同步改。upload-path 改成自己机器上存在的目录比如 D:/upload并且手动把这个目录建出来。第三步启动后端。用 IDEA 打开后端项目等 Maven 把依赖全部下载完直接运行 FolkloreApplication。看到控制台出现 Tomcat started on port 8080就说明后端起来了。建议这时先在浏览器访问 http://localhost:8080/culture/page?pageNum1pageSize8看有没有 JSON 返回。这一步要是 404 或者 500先别碰前端把后端调通再说。第四步启动前端。进入前端目录npm install 装依赖npm run serve 启动。看到 Compiled successfully 后浏览器打开 http://localhost:8081Vue 默认端口页面能出数据整个项目就算本地跑通了。我第一次给别人跑这个流程时卡最久的往往是两个地方Maven 依赖下载太慢以及数据库密码不对反复连接失败。Maven 依赖慢的话在 Maven 的 settings.xml 里加阿里云镜像速度立竿见影。数据库连接失败的话优先去看 MySQL 服务有没有启动排除法一个个测控制台能敲 mysql -uroot -p 进库说明数据库本身没问题问题一定在 yml 配置。4.2 打包部署到服务器的两种常用方式如果你只是毕业答辩本地跑已经很够用。但你手头正好又是一套带“部署文档”的项目所以至少要知道服务器是怎么跑的。方式一前端 Nginx 后端 Jar这是目前的主流方案。后端先打包mvn clean package -DskipTests打好后 target 目录下会有一个 folklore-0.0.1-SNAPSHOT.jar。上传到服务器用 nohup 后台跑nohup java -jar folklore.jar folklore.log 21 然后前端构建npm run build构建完成后项目根目录生成 dist 文件夹。把 dist 传到服务器 Nginx 的站点目录下再配置 Nginx 反向代理server { listen 80; server_name your_domain_or_ip; root /home/www/folk/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /images/ { proxy_pass http://127.0.0.1:8080/images/; } location / { try_files $uri $uri/ /index.html; } }这里有几个容易出错的小细节try_files 那一行必须写否则 Vue Router 用 history 模式时刷新非首页路径会直接 404这个坑坑过无数人api 和 images 两个 location 是关键前端请求走 /api 开头图片访问走 /images 开头都能转到后端端口别漏掉任何一个。方式二更轻量一点就是把前端 dist 直接拷贝到 SpringBoot 的 src/main/resources/static 目录下重新打包运行。这样一个 jar 包同时包含前端页面和后端接口启动一个端口全搞定。这种方式胜在简单不需要服务器上装 Nginx但缺点是不好区分前后端也不利于以后扩展我通常只是拿来应急或者做给老师演示的时候用。4.3 部署文档要写清楚哪些内容你的标题里包含“部署文档”四个字这个文档在交付时很重要因为老师或者下一个接手人首先看的就是部署文档。一份合格的部署文档至少包含这些内容环境要求清单JDK 版本1.8、Maven 版本、Node 版本、MySQL 版本都写成表格明确“推荐版本”和“最低版本”别只写一句“环境自备”。数据库导入部分写清楚新建数据库的字符集、SQL 文件的导入步骤、账密在哪里修改。最好配三张图新建数据库的截图、导入成功后的表列表截图、修改 yml 的截图。后端启动部分写清楚如何用 IDEA 打开项目、等待依赖下载、运行启动类、确认启动成功的日志特征。这一步很多人会忽略“确认成功”的标志你要明确告诉读者看见哪一行日志算成功。前端启动部分写清楚 npm install 和 npm run serve 两条命令的作用以及如果 8081 端口被占用怎么办。常见异常排查表比如数据库连接拒绝、端口占用、Maven 依赖下载失败、npm 安装失败等每个问题配一句最简单的解决命令。我第一次写部署文档时总是觉得“这还用写直接跑不就行了”后来被问过太多次“我按照你说的步骤做了还是不行卡在第 X 步”才明白文档的价值就在于把别人容易出错的地方提前标出来。宁可啰嗦不要默认别人和你有一样的知识背景。5. 高频踩坑与排查速查5.1 环境类问题大多数项目跑不起来都怪这里我把这几年看得最多的问题整理成一个速查表你在跑项目时碰到的 80% 问题基本都能在里面找到答案现象根本原因解决办法后端启动报 Port 8080 was already in use端口被占用换端口server.port 改 8081或找到占用进程结束它启动时报 Access denied for user rootMySQL 密码错误核对 yml 里的密码与本地 MySQL 密码是否一致启动后中文乱码数据库字符集不是 utf8mb4建库时选 utf8mb4url 加 characterEncodingutf8npm install 一直卡住或失败网络问题或镜像源问题设置淘宝镜像 registry.npmmirror.comnode-sass 编译报错Node 版本太高用 nvm 切到 Node 16或者换成 dart-sass前端页面白屏控制台报 404路由模式与 Nginx 不匹配配置 try_files $uri $uri/ /index.html前端请求接口报跨域 NYC后端未允许跨域加 CorsConfig 配置类图片上传后页面图片裂开静态资源映射路径错误检查磁盘目录是否真实存在检查 /images/** 映射这里面最值得单独拿出来说的是 Node 版本。我见过一个学弟Vue 项目 npm install 报了一长串 error最后发现他机器上装的是 Node 22而项目依赖里用的是旧版 node-sass二者根本不兼容。解决方式非常简单用 nvm 装一个 Node 16然后 npm install两分钟内问题消失。所以碰到奇怪的 npm 报错第一步永远先查版本。还有一个很容易被忽略的坑是 MySQL 8.0 和 5.7 驱动的写法差异。如果你用 MySQL 8.0driver-class-name 必须是 com.mysql.cj.jdbc.Driverurl 必须带 serverTimezone如果你的项目是从网上找的它原本用的可能是 5.7 的写法不修改就会启动失败。这种从别处拷来的项目第一件事不是跑而是打开 application.yml 比对数据库配置和本机环境。5.2 后端运行与数据访问问题后端启动成功但接口报错问题往往集中在 MyBatis-Plus 配置和数据访问上。比如一个最常见的问题查询结果所有字段都是 null。这种情况十有八九是实体类属性名和数据库列名对不上。MyBatis-Plus 默认开启了下划线转驼峰只要数据库列名叫 create_time实体属性叫 createTime就能自动对上。如果你把列名写成了 createTime数据库里下划线才是惯例或者实体字段命名不规范查出来就会是 null。排查方法很简单看控制台 SQL 日志把 SQL 复制到 Navicat 里跑一遍再和返回 JSON 对比一眼就看出问题在不在映射。还有一个问题分页查不出数据但列表总数是对的。这种情况通常是 Page 对象参数写错了。MyBatis-Plus 的 Page 构造函数第一个参数是当前页从 1 开始第二个是每页条数。如果你 pageNum 传的是 0第一页数据就会为空因为你查的是第 0 页。前端默认从 1 开始传后端千万别自作聪明去减 1。数据访问这块再提醒一句不要在生产环境里使用 MyBatis-Plus 的 selectList 一次性把整张表查出来。民俗网站数据量不大倒是无所谓但如果活动表、用户表以后涨到几万条这种写法直接卡挂服务器。凡是列表页一律用分页即使只有十几条数据分页也不会错。5.3 前端构建与部署问题前端的问题要比后端隐秘得多因为它报错时往往是“页面白了”而不是“程序崩了”。打包后刷新 404 的问题我前面已经提过Nginx 的 try_files 是标配。但还有一个类似的问题直接双击 index.html 打开页面发现 JS 加载后路由正则匹配不上所有资源走相对路径找不到。解决办法是在 vue.config.js 里设置 publicPath 为 ./或者写成服务器根目录路径。比如部署到域名根路径下publicPath 可以直接设成 /部署到子路径就设成 /子路径/。很多同学只知道改这一个配置却不知道为什么Vue 构建时默认 publicPath 是 /如果你的静态资源托管在子目录找不到资源自然白屏。再有一个烦人但常见的问题前端构建后图片跨域或者图片加载不出来。先确认图片 URL 是完整的能直接在浏览器新标签打开如果后端返回的是相对路径 /images/xxx.jpg那这个路径要能被当前域名直接访问也就是需要 Nginx 或者后端把 /images 映射好。我遇到过有人把图片 URL 存成了 uploads/xxx.jpg前端请求时相对于当前路由发出去结果路径七拐八拐就是不对最后把存储时统一加 / 前缀或者完整 URL 才解决。6. 如何把一个普通毕设做出亮点6.1 功能上可以做哪些低成本高回报的增强核心功能做完以后如果你想在答辩时多一点讲头不需要重写项目只需要加几个“小而巧”的能力。数据统计是性价比最高的一项。比如在后台加一个首页数据看板用 ECharts 展示一个月内每日内容发布数量、分类占比柱状图、用户增长折线图。这个功能的代码量不大但论文里可以写一章“系统数据可视化分析”答辩时老师看到图表会明显觉得项目有深度。实现上就是在后端写几个统计查询语句在后台页面引入 ECharts一个周末就能搞定。内容评论的多级嵌套也算一个亮点。现在多数毕设的评论都是一级列表你把它做成二级回复前端展示出来会有层次感数据库设计上多一个 parent_id 字段即可。这个改动非常小但能在论文中写“实现了基于楼中楼模式的多级评论交互”比平铺评论高级不少。还有一个很实用的小功能就是文本搜索的热词统计。前端搜索关键词以后后端把关键词记录下来后台可以查看搜索排行榜。这个功能不仅让系统看起来有运营思维而且实现起来不过是多一张表 一个计数更新非常划算。6.2 论文里这些内容能怎么组织论文写作是有技巧的。一个典型的毕业论文结构跟这个项目几乎是完美匹配的绪论里写研究背景和意义可以提民俗文化数字化传播需求分析里把前台和后台功能用用例图表达总体设计里放架构图、技术选型对比表和数据库 E-R 图详细设计里挑几个核心模块内容管理、收藏评论、活动报名按流程图和核心代码讲测试部分写功能测试用例表和性能测试结果。这样一个框架下来论文的章节脉络天然清晰因为项目本身就是按这个逻辑设计和实现的。这里给你提个醒论文中的截图一定要真实。我见过有人为了好看直接从网上找别人的系统截图贴进自己论文答辩时老师让他现场演示同样的页面当场露馅。宁可你的界面朴素一点但是每一个截图都是从自己系统里真正截出来的这才是安全的。6.3 答辩前一定要完成的准备动作答辩前一周建议按下面的清单做最后一次自查第一后端能打包吗如果老师让你现场演示部署你至少要能在十分钟内打出 jar 包并且在另一台电脑上跑出数据。我建议你用虚拟机或者另一台电脑完整走一遍部署流程而不是只在自己开发机上演示否则过于依赖本机环境。第二数据库脚本是新的吗有时候你开发过程中改了表结构但导出的 SQL 还是老版本别人导入后跑起来数据缺失或者直接报错。确保 folklore.sql 是从最新数据库导出的并且在空的 MySQL 实例上实测导入一遍。第三演示数据够不够丰满每个分类至少要有 5 条以上内容用户账号要有 3 到 5 个并且这些账号要有不同的收藏和评论记录。一个空荡荡的民俗网站就算功能再全也会让老师觉得开发工作没做完。第四准备好两套账号密码。一套管理员admin / 初始密码演示后台功能一套普通用户演示收藏、评论、报名流程。答辩现场最忌讳临时想起某个账号然后翻找笔记那体验很糟糕。按我个人经验答辩时老师问得最多的不是“你这技术有多难”而是“你为什么要这么设计”“表结构为什么这么建”“部署时你遇到什么问题”。所以你要做的不是把代码背下来而是能流利地讲清楚自己每一步的取舍。只要整个流程完整跑过一遍这些问题对你来说都是送分题。这个项目后续的扩展空间也不小。比如把静态的民俗内容改成支持视频播放或者把单机部署改成前后端分离加对象存储或者引入 Redis 做热点内容的缓存。但那是以后的事。先把现在这套完整地跑通、写好文档、顺利答辩比什么都重要。