每次看到后台有人问我“毕设怎么选题”,我都很想说:别去卷那些烂大街的商城系统了。今天把一个我做过的、帮过好几个小伙伴通过答辩的项目完整拆出来给你们看——SpringBoot+Vue 流浪动物救助网站平台。这个选题好在哪?第一,它属于公益性质,业务场景有温度,答辩老师天然有好感;第二,它不是一个简单CRUD,里面有权限、文件上传、流程状态流转、统计报表这些硬知识点,能展示你的后端功底;第三,前后端分离用现在招聘市场最常见的SpringBoot+Vue组合,做完简历上也拿得出手。
这篇帖子我把项目的业务设计、表结构、接口文档、前端实现思路、以及我踩过的坑全部展开讲一遍。不管你是拿来当毕设还是纯粹想练手,按这个思路走,至少能少熬五个通宵。
1. 项目整体拆解与设计思路
1.1 业务需求梳理:从救助场景看系统边界
先说业务。流浪动物救助这件事,外行听起来好像就是“发帖-领养”两个动作,但真正落成系统你会发现,参与的人远不止领养者一方的。
我在做第一版需求梳理时,把角色拆成了三类:普通游客/注册用户、救助站管理员(或社团管理员)、超级管理员。围绕这三个角色,整个平台的业务闭环长这样:有人在外面发现了流浪猫狗,拍照发布到平台;救助站看到消息后介入,把动物收容进来,更新它的健康状态和领养条件;有意向的普通用户在线提交领养申请,管理员审核通过后线下交接;同时平台上还要跑志愿者招募活动和捐赠记录,不然资助方没地方看到钱的去向。
所以系统的功能边界就很清楚了:动物信息发布与审核、领养申请流程、活动发布与报名、捐赠记录管理、用户管理、首页轮播与资讯推送。这六个模块不要多,做精细了比堆十个功能强得多。
1.2 核心模块的功能清单与优先级排序
很多人一开始就急着写代码,结果做到一半发现表关系乱了。我习惯先把功能清单列出来,标上优先级,这样做出来的系统才有主次:
- 动物信息模块:发布、编辑、上下架、详情展示、健康状态标签。这是系统的脸面,优先级最高。
- 领养申请模块:用户提交申请、管理员审核、审核状态(待审核/通过/驳回)、领养成功后标记。这是整个系统的业务闭环核心,也是一张体现你“会处理状态流转”的名片。
- 用户中心:注册登录、个人信息修改、我的申请记录、我发布的动物信息。权限控制的主要阵地。
- 志愿者活动模块:活动发布、报名参加、人数限制。加分项,但又能和动物模块产生关联。
- 捐赠管理模块:线上登记捐赠记录、后台展示捐赠列表。这里可以不做真实支付,但要把“支付回调”的状态机设计讲明白,答辩就能出彩。
- 资讯与轮播图:日常内容维护入口,用来展示公告、领养故事等。
我把优先级排序放在最前面讲,是因为很多新手一上来就把时间耗在轮播图这种边缘功能上。记住,领养审核流程才是面试官和答辩老师第一眼看的地方。
1.3 我从零搭这套系统的技术选型逻辑
这个项目的技术栈选得比较主流:后端SpringBoot 2.7 + MyBatis Plus + MySQL 8.0 + JWT,前端Vue 2 + Element UI + Axios + ECharts。为什么这么选,我逐个说下我的考量。
SpringBoot 2.7 这个版本生态最成熟,网上资料最多,出了问题搜索都有现成答案,比3.x踩坑少;持久层用 MyBatis Plus,既保留了SQL的掌控力又解决了80%的CRUD模板代码,写起来很快;鉴权用 JWT 而不是 Session,是因为前后端分离架构下JWT是现在企业里最常见的选择,也能证明你理解“无状态认证”这个概念。
前端没有选 Vue 3,是因为 Element UI 在 Vue 2 上的支持最稳,对毕设场景来说交互复杂度远没到需要 Composition API 的地步。等你想升级Vue 3的时候,这套项目的后端接口完全不需要动,这个“前后端分离”的优势一定要记牢,答辩时是加分项。
2. 数据库设计与核心接口实现
2.1 数据表规划:九张核心表的职责划分
数据库是整个项目的根本,表结构没设计好,后面写接口会痛苦到怀疑人生。我最后定稿用了九张表,每张表的职责都很明确:
| 表名 | 存放内容 | 关键字段 |
|---|---|---|
| user | 用户账号信息 | id, username, password, avatar, phone, role, status |
| animal | 动物信息 | id, name, type, breed, health_status, images, address, status |
| adoption_apply | 领养申请记录 | id, animal_id, user_id, reason, status, create_time |
| volunteer_activity | 志愿者活动 | id, title, content, start_time, max_people, enrolled_count |
| activity_signup | 活动报名记录 | id, activity_id, user_id, signup_time |
| donation | 捐赠记录 | id, user_id, amount, message, donate_time |
| article | 资讯/公告 | id, title, content, cover, create_time |
| banner | 首页轮播图 | id, image, link, sort |
| feedback | 留言反馈 | id, user_id, content, reply, create_time |
三张业务主表(user、animal、volunteer_activity)配两张关联表(adoption_apply、activity_signup),再加三张支撑表(donation、article、banner、feedback),每个表之间关系清晰、不冗余,这在数据库设计评审时是站得住脚的。
特别说一下animal表里的status字段,我设计了四个值:0-待审核,1-待领养,2-已被申请,3-已领养。这个字段是整个系统的状态机核心,页面显示、操作按钮、数据统计全部依赖它。前期必须把这张表的默认值约束建好,不然后面写SQL判断逻辑时会有一堆脏数据。
2.2 SQL脚本里容易被忽略但很关键的细节
SQL脚本是这个项目的交付物之一,很多人的脚本只有建表和造数据,其实远远不够。我在写脚本时特别注意了以下几点。
字段类型选择上,金额字段amount要用decimal(10,2)而不是double。double在精度上会有误差,答辩时如果老师问起来,你能说出“钱相关的字段必须精确小数,避免浮点精度问题”这句话,专业感一下就上来了。
时间字段我统一用datetime类型,默认值设为CURRENT_TIMESTAMP。这样插入数据时不用手动塞时间,也方便后续按时间排序统计。
索引方面,外键关联字段(user_id、animal_id、activity_id)和状态字段(status)都加了普通索引。这个动作很小,但能体现你的性能意识。现在数据量小看不出来,将来几十万条记录查询时,有没有索引差别可能是几毫秒和几百毫秒的区别。
最后脚本里一定要带上初始数据:一个管理员账号、两个普通用户、十来个动物信息、几条领养申请记录。这样别人拿到项目导入SQL就能直接登录看到效果,不用自己去后台一个个录数据。这也是“完整项目源码+SQL脚本”这个交付体验感的关键。
2.3 领养申请全流程接口的状态流转设计
这个模块是项目的核心卖点,我单独拿出来细说。领养申请流程一共四步:提交申请、管理员审核、用户确认领养、完成回访记录。
先看提交申请接口,前端传animalId和userId,后端要做三件事:校验用户是否已经申请过这只动物(防止重复申请)、判断 animal 当前 status 是否为1(待领养状态才允许申请)、插入申请记录并把动物状态改成2(已被申请)。这三件事必须在同一个事务里完成,否则会出现“申请记录插入成功但动物状态没改”这种恶心bug。
管理员审核接口接收applyId和审核结论。通过就把申请状态改成1,并在动物表把状态改成3;驳回就只改申请状态,不动动物表状态。注意这里也要加事务,虽然逻辑简单,但漏了事务在并发请求下就可能出问题。
用户确认领养接口要重新查一次 animal 状态,确认还是“已被申请”且申请人是当前用户,才把状态改为“已领养”。这一步属于“用户身份与资源归属校验”的典型场景,技术含量不高但非常重要,很多数据库越权漏洞就是这类接口没做归属判断。
2.4 文件上传与图片回显的本地落地方案
- application.properties 里要配置两个关键的属性:
spring.servlet.multipart.max-file-size=10MB # 自定义的静态资源映射路径 file.upload-path=/data/upload/- 需要写一个 WebMvcConfigurer 做虚拟路径映射:把浏览器访问的
http://localhost:8080/images/**映射到磁盘物理目录。这一步懒不得,不做配置前端图片全部加载不出来。 - 文件命名规则建议用 UUID 拼上源文件后缀,这样能避免中文文件名乱码和重名覆盖两个问题。用
System.currentTimeMillis()拼接用户ID也可以,但并发场景下有一定概率重名,UUID 最稳。 - 图片访问地址存到数据库时要存完整的相对路径,例如
/images/20240315/xxx.jpg。前端直接用这个值拼上接口域名就能访问,好排查问题。
提示:踩过一次坑,本地跑得好好的上传就报错,最后发现是 Linux 服务器的
/data/upload目录没有写权限。部署时记得chmod -R 777或按实际用户授权。
2.5 数据统计报表的小亮点:ECharts 看板
我在后台加了一个统计看板:按月份统计新增动物数量、领养成功率、志愿者活动报名趋势、捐赠金额Top榜。数据来源都是按 SQL group by 查询出来的临时统计结果,前端用 ECharts 折线图和柱状图展示。
这个模块有两个隐藏价值。第一个是简历上可以写“负责数据可视化模块设计”,第二是答辩时老师问“你项目有什么亮点时”,展示图表和展示表格的感觉完全两样。
实现上有一个小技巧:后端接口返回的数据结构不要嵌套太多层,比如月份列表和数量列表分开返回:
{ "months": ["2024-01", "2024-02"], "counts": [12, 25] }前端直接喂给 ECharts 的 axis 和 series 就行,少了很多数据转换的逻辑。
3. 前端页面与交互实现重点
3.1 首页设计:信息流如何吸引用户停留
首页是访客进入系统看到的第一面,纯粹做个轮播图加列表就太素了。我把首页拆成五块:顶部导航、轮播图、动物展示网格、最新资讯、捐赠入口。重点说一下动物展示网格的部分。
每个动物卡片我放了照片、名称、品种、所在地、健康状况标签、申请领养按钮。卡片用 Element UI 的el-card组件做hover效果,鼠标移上去时轻微上浮并显示阴影,这个小交互在答辩演示时非常抓眼球。实现只需要加一行transition: all 0.3s ease;和transform: translateY(-8px);,但是效果立刻就不一样了。
首页数据不要求实时,后端接口可以做个“只查询status为1的动物、按发布时间倒序、limit 8条”的简单处理。分页的负载给列表页,首页只展示最新八只,加载速度体验好得多。
3.2 登录鉴权:Axios 拦截器与 JWT 的配合
登录这块我调研过两种方案:一种是登录后把用户信息全存到 localStorage,每次请求不带头,后端靠 Session;另一种是 JWT。我建议毕设直接上 JWT,理由很简单——这是现在企业里最常用的方案,你写在简历上是加分项。
具体流程:登录成功后后端返回一个 token 字符串,前端存到 localStorage;然后给 Axios 配置请求拦截器,每次发请求都在 header 里带Authorization: Bearer token;后端自定义一个拦截器,解析 token、校验过期时间,把 userId 放进 ThreadLocal。
前面所有页面访问公共接口(比如浏览动物列表)可以不放拦截,但涉及个人数据的接口必须校验。有一个细节要注意:Axios 响应拦截器要加一层统一处理,遇到后端返回401时强制清掉 localStorage 并跳转回登录页,不然用户token过期后页面会一直白屏或报错,体验特别糟糕。
3.3 后台管理界面三个必做的布局组件
后台管理端我用的是经典布局:左边侧边栏菜单,右边内容区。这块有三个组件样式很关键。
第一个是表格筛选区。后台的动物列表、申请列表、活动列表都有搜索条件,我统一在表格上方加了一个el-form inline的筛选栏,支持按关键词和状态筛选;筛选条件变化时重新调用分页查询接口。这个组件看起来简单,但它是“后台系统”区别于“普通页面”的重要标志。
第二个是状态标签组件。用el-tag配合 type 属性区分不同状态,比如待审核用warning、已通过用success、已驳回用danger。一眼扫过去状态清晰,答辩演示时视觉效果非常加分。
第三个是弹窗表单。新增和编辑操作统一用el-dialog嵌套el-form,设置好rules校验规则。我踩过的一个坑是弹窗关闭后再次打开会残留上次的数据,所以必须在onOpen事件里调用resetFields()重置表单数据。
后台管理这部分代码量不小,但思路很机械:表格用于展示、表单用于录入、弹窗用于承载,尽量用代码生成器把重复代码快速铺出来,省出的时间全部用来打磨核心流程。
3.4 前端路由守卫的两种写法,推荐第二种
路由守卫是实现“未登录不能访问个人中心”的常用手段。第一种是在每个页面的created钩子里检查 token,代码冗余且容易漏;第二种是用 Vue Router 的全局前置守卫,统一判断:
router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !localStorage.getItem("token")) { next("/login"); } else { next(); } })每一条路由定义时在 meta 里标记requiresAuth: true,前端就能统一处理。这个写法虽然多学一个概念,但代码干净、扩展性好,还能顺带做管理员和普通用户的角色判断,是能写到简历上的技能点。
4. 常见问题与排查技巧实录
4.1 跨域问题:前后端分离第一天就会撞上的坑
前后端分离开发时,前端跑 8080 端口、后端跑 8081 端口,浏览器跨域拦截几乎立刻就可能出现。解决办法有两种:后端加CorsFilter做全局跨域配置,或者前端用 Vite 代理。
我推荐后端配置全局 CORS 过滤器,因为这样前端生产环境不需要额外处理。关键代码就一个@Configuration类,注册CorsFilterBean,允许的源地址写http://localhost:8080,允许所有请求头和方法。要注意如果写了allowCredentials(true),那allowedOrigins就不能用*替代,必须写具体地址,这是配置里的一个常见报错点。
4.2 JWT 过期后前端白屏问题
这是一个很典型又隐蔽的问题。我做过一个模拟项目X,当时后端 token 过期后接口返回了401,但前端响应拦截器只处理了200和错误码500,没有单独处理401,结果页面一直显示空数据、控制台报错,用户完全不知道自己该重新登录。
解决思路:响应拦截器里对 HTTP 状态码做完整分组——200正常返回数据,401清除本地登录态并跳转/login,500统一弹出服务端异常提示。这个处理逻辑放在一个地方,后续所有接口就都自动带上这层保护了。
4.3 本地访问图片404,原因竟是虚拟路径没配置
前端展示的动物图片几乎都是本地磁盘文件,我在给一个同学排错时发现他的img标签src写的是绝对路径/usr/local/upload/xxx.jpg。浏览器地址栏输入这个路径当然访问不到,因为静态文件必须在后端工程里做映射才能被访问。
正确做法是:磁盘文件放在任意目录(比如/data/upload),后端配置一个资源映射把/images/**指向这个物理目录,然后数据库里存/images/xxx.jpg,前端src="/images/xxx.jpg"访问。这里有个细节:不要在src上写死localhost:8080,要用相对路径,这样换环境部署时不用改前端代码。
4.4 MyBatis Plus 分页查询时“查不到新插入的数据”
这是 MyBatis Plus 分页插件的一个经典坑:只引入了分页依赖,但没配置 PaginationInnerInterceptor。结果就是你写的new Page<>(pageNum, pageSize)返回的 total 一直是0,数据永远只显示第一页,翻页无效。
复现与解决:检查配置类里是否创建了MybatisPlusInterceptor的 Bean,并且在innerInterceptors列表中加入PaginationInnerInterceptor(DbType.MYSQL)。加了这一步,分页参数才会被自动填充到 SQL 里。
4.5 常见问题速查表
| 异常现象 | 排查方向 | 解决要点 |
|---|---|---|
| 前端请求全部403 | 跨域、CSRF、token失效 | 先看控制台 Network 的具体响应,再检查过滤器和拦截器顺序 |
| 上传图片失败 | 磁盘权限、文件大小限制 | multipart.max-file-size调大,检查物理目录可写 |
| 列表接口报SQL语法错误 | 表名或字段名与MySQL关键字冲突 | 用反引号标注,比如order字段要写成`order` |
| 登录成功但个人中心空白 | 路由守卫或响应拦截器逻辑问题 | 检查 token 是否写入 localStorage、请求头是否带回 |
| 后台管理表格数据不刷新 | 前端外部同步问题 | Element UI 表格要重新赋值数组触发更新,不能直接改原数组某项 |
5. 答辩准备与简历展示的关键细节
5.1 答辩演示的黄金15分钟
这套系统功能不少,但答辩时间通常只有10到15分钟。千万不要从头到尾把每个页面点一遍,那是学生思维。我建议的演示顺序是:首页快速展示项目视觉效果,然后直奔主题演示“用户提交领养申请-管理员审核-状态变更-列表刷新”这整条流水线,中间穿插两次疑问设计:一次是看图片如何上传,一次是看ECharts统计图表。
重点讲清楚你的数据库是怎么设计的,领养状态是怎么流转的,JWT拦截器是怎么控制访问权限的。遇到老师问你“这个系统的并发能力如何”这类问题时,一定不要慌,就明确说是“单体架构、适合小规模公益组织,后期可以引入缓存和微服务拆分”即可,坦诚比吹嘘得分高。
5.2 简历上应该怎么写这个项目
毕业求职时,这个项目的简历写法也要讲究,不能只写“参与开发流浪动物救助网站”。我帮人改过简历,把纯CRUD描述改成“负责设计9张核心业务表并完成领养申请全流程状态机设计与实现”,感觉完全不一样。
直接放一个可以抄的简历模板段落:独立完成一个SpringBoot+Vue前后端分离的流浪动物救助平台,包含动物信息管理、领养审核、志愿者活动、捐赠记录、数据报表五大模块;负责数据库设计、JWT鉴权、文件上传、接口文档编写;采用RESTful接口风格,使用MyBatis Plus实现高效持久层操作。
5.3 做完这个项目后还能怎么扩展
如果你学有余力,我给几个方向:一是加一个“救助站地图”模块,用地图API把发布动物的地理位置标出来;二是给捐赠模块接一个真实支付(最稳妥的是沙箱环境),这就触及“支付回调”这个高级话题;三是用 Redis 做首页数据的缓存,降低对数据库的查询压力。做完任何一个,项目高度和答辩时的自信程度都会再上一个台阶。
我个人实际操作中很大的体会是:这个项目不只是一堆代码,更是一套完整理解“业务分析-表设计-接口开发-前端联调-部署交付”的过程。把表关系和状态流转理清楚,功能层面做到“能用、好看、好演示”,答辩和面试就会很有底气。最后分享一个小技巧:源码到手后不要急着改功能,先把SQL导入、把项目启动起来、把整个业务流程点一遍,再开始动手改造,这能省下你大量本来会花在“环境配置”“联调排错”上的时间。