简介:这份资源面向计算机专业毕业设计学生与需要完整项目实战的开发者,提供一套基于Spring Boot与Vue的宠物领养系统全套实现方案,涵盖从需求分析到部署上线的完整链路。压缩包共4个文件,包含2个zip源码包、1个sql数据库脚本和1个docx部署文档,整体约664KB,源码、建表脚本与部署说明一应俱全。系统功能划分清晰:前台提供宠物列表、宠物详情、领养公告与个人中心;普通用户可管理我的申请、陪伴记录与宠物知识;管理员则负责种类与品种管理、宠物管理、领养审核、宠物回访及公告维护。项目还配套系统架构图、用例图、顺序图与E-R图等专业绘图,便于直接用于论文撰写与答辩展示。目前已有42人学习下载,适合希望快速搭建可运行项目、对照文档完成部署并整理毕设材料的读者参考。
1. 从一份「能跑起来」的宠物领养系统说起
宠物领养系统这个题目,在毕设和练手项目里出现频率极高,但真正能拿得出手、能部署上线、能让别人照着跑通的并不多。大部分仓库卡在三个地方:SpringBoot 版本选得太新导致依赖冲突、Vue 打包后塞进 SpringBoot 静态资源目录路径对不上、数据库表结构和后端实体类字段对不齐。这套「SpringBoot+Vue 宠物领养系统」要解决的就是把这三件事一次性做干净——后端提供领养申请、宠物信息、用户权限、审核流转的接口,前端做用户端浏览与申请、管理端审核与统计,数据库落 MySQL,最后打成可部署的产物。
它适合两类人:一类是正在做毕设、需要一套结构清晰且能讲清楚设计思路的参考实现;另一类是想练手前后端分离、但不想在环境配置上耗掉一周的开发者。下面按「先立住技术选型、再动手复现、最后说坑」的顺序展开,中间会给到能直接抄的配置、建表语句和部署命令。
2. 技术选型与工程骨架:为什么是 SpringBoot + Vue 这套组合
2.1 后端选 SpringBoot 而不是 SSM 的理由
宠物领养系统的业务复杂度不高,核心实体就用户、宠物、领养申请、公告这几张表,但接口数量不少:登录鉴权、宠物分页查询、申请提交、审核状态流转、图片上传。用传统 SSM 要手写大量 XML 映射和 web.xml 配置,而 SpringBoot 的自动配置能把这些样板代码压到最低。
具体到版本,我一般会锁在 SpringBoot 2.7.x 这一档。原因很直接:2.7 是 2.x 最后一个稳定分支,对 MyBatis-Plus、Druid、JWT 这些常用库的兼容性经过大量项目验证,网上能搜到的整合教程也最全。热搜里出现的「springboot版本太高」正是很多人踩过的坑——直接上 3.x 后,javax.servlet全部换成jakarta.servlet,老教程里的拦截器、过滤器代码全部报错,对新手极不友好。
工程骨架用标准分层:controller接请求、service写业务、mapper走数据库、entity对应表、config放拦截器和跨域配置。这个结构不是形式主义,而是当你要加「领养审核超时自动取消」这类定时任务时,能清楚知道代码该落在哪一层。
2.2 前端选 Vue 2 还是 Vue 3 的取舍
Vue 这边,如果目标是快速出活、少踩构建工具的坑,Vue 2 + Element UI 依然是稳妥选择;如果想练 Vue 3 的 Composition API 和 Vite,那就上 Vue 3 + Element Plus。两者在宠物领养系统里的差别主要体现在路由和状态管理写法上。
热搜里的「vue安装及环境配置」「vue路由」「vue打包放进springboot中」是这条链路上最高频的三个问题。我的建议是:前端独立开发时用npm run serve起 8080 端口,通过代理转发/api到后端 8081;上线时用npm run build产出dist,再把dist里的文件整体拷进 SpringBoot 的src/main/resources/static目录。这样只需要维护一个可执行 jar,部署时不用同时起 Node 服务。
2.3 数据库表设计的最小可用集合
宠物领养系统的表不用多,但字段要想清楚。下面这张表列出核心四张表的关键字段和设计意图:
| 表名 | 关键字段 | 设计意图 |
|---|---|---|
| user | id, username, password, role, phone | role 区分普通用户和管理员,避免建两张用户表 |
| pet | id, name, type, age, status, cover_img | status 用 0/1/2 表示待领养/审核中/已领养 |
| adopt_apply | id, user_id, pet_id, apply_time, audit_status | audit_status 独立于 pet.status,便于追溯 |
| notice | id, title, content, create_time | 公告表,管理端发布、用户端展示 |
字段命名统一用下划线,实体类用驼峰,靠 MyBatis-Plus 的map-underscore-to-camel-case自动映射。这一步如果偷懒不统一,后面查出来的对象字段全是 null,排查起来非常费时间。
3. 后端落地:从建表到接口跑通的最小闭环
3.1 建库建表与初始化数据
先在 MySQL 里建库,字符集用utf8mb4,否则宠物名字里带 emoji 会插入失败。下面是核心建表语句:
CREATE DATABASE pet_adopt DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE pet_adopt; CREATE TABLE `user` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL UNIQUE, `password` VARCHAR(100) NOT NULL, `role` TINYINT DEFAULT 0 COMMENT '0普通用户 1管理员', `phone` VARCHAR(20), `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE `pet` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(50) NOT NULL, `type` VARCHAR(20) COMMENT '猫/狗/其他', `age` INT, `status` TINYINT DEFAULT 0 COMMENT '0待领养 1审核中 2已领养', `cover_img` VARCHAR(255), `description` TEXT ); CREATE TABLE `adopt_apply` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `user_id` INT NOT NULL, `pet_id` INT NOT NULL, `apply_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `audit_status` TINYINT DEFAULT 0 COMMENT '0待审核 1通过 2驳回', `remark` VARCHAR(255) );建完表后插一条管理员账号,密码存 BCrypt 加密后的值,不要存明文。初始化数据用INSERT语句写进data.sql,配合 SpringBoot 的spring.sql.init.mode=always可以在启动时自动执行,但生产环境记得关掉,避免每次重启重复插入。
3.2 application.yml 里必须调对的几个参数
配置文件是后端能不能连上数据库的关键。下面这份application.yml是我在多个项目里验证过的模板:
server: port: 8081 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/pet_adopt?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_password servlet: multipart: max-file-size: 5MB max-request-size: 10MB mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto几个参数说明:serverTimezone=Asia/Shanghai不加会报时区错误;max-file-size控制宠物图片上传大小,默认 1MB 传手机拍的照片必失败;log-impl打开 SQL 打印,调试阶段能直接看到执行的语句,上线前注释掉。id-type: auto对应数据库自增主键,如果用了雪花 ID 要改成assign_id。
3.3 领养申请接口的完整实现
领养申请是这套系统的核心业务,涉及「提交申请 → 校验宠物状态 → 更新宠物状态 → 写申请记录」四步,必须放在同一个事务里。下面是 Service 层的关键代码:
@Service public class AdoptApplyServiceImpl extends ServiceImpl<AdoptApplyMapper, AdoptApply> implements AdoptApplyService { @Autowired private PetMapper petMapper; @Override @Transactional(rollbackFor = Exception.class) public Result submitApply(Integer userId, Integer petId, String remark) { // 1. 校验宠物是否存在且处于待领养状态 Pet pet = petMapper.selectById(petId); if (pet == null) { return Result.fail("宠物不存在"); } if (pet.getStatus() != 0) { return Result.fail("该宠物已被申请或已领养"); } // 2. 校验该用户是否重复申请 Long count = lambdaQuery() .eq(AdoptApply::getUserId, userId) .eq(AdoptApply::getPetId, petId) .eq(AdoptApply::getAuditStatus, 0) .count(); if (count > 0) { return Result.fail("你已提交过申请,请等待审核"); } // 3. 写申请记录 AdoptApply apply = new AdoptApply(); apply.setUserId(userId); apply.setPetId(petId); apply.setRemark(remark); apply.setAuditStatus(0); save(apply); // 4. 更新宠物状态为审核中 pet.setStatus(1); petMapper.updateById(pet); return Result.ok("申请已提交"); } }逻辑说明:@Transactional保证第 3、4 步要么都成功要么都回滚,否则会出现「申请记录写了但宠物状态没变」的脏数据。重复申请校验用count而不是查列表,减少数据传输。rollbackFor = Exception.class是必须的,默认只回滚运行时异常,受检异常不会触发回滚。
参数说明:userId从 JWT token 里解析,不要从前端传,否则用户可以伪造他人身份提交申请。petId前端传,但服务端必须重新查库校验状态,不能信任前端传来的 status。
3.4 跨域与登录拦截器的配置
前后端分离开发时,前端 8080 调后端 8081 必然跨域。配置类里加一个WebMvcConfigurer:
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private JwtInterceptor jwtInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) .addPathPatterns("/**") .excludePathPatterns("/user/login", "/user/register", "/pet/list"); } @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowCredentials(true) .maxAge(3600); } }拦截器排除登录和注册接口,宠物列表也放行,让未登录用户能先浏览。allowedOriginPatterns("*")配合allowCredentials(true)在 SpringBoot 2.4 之后是允许的,早期版本要用allowedOrigins且不能写*,这是版本差异导致的常见报错点。
4. 前端落地:Vue 页面、路由与打包进 SpringBoot
4.1 环境配置与依赖安装的稳定路径
Vue 环境配置翻车最多的是 Node 版本和 npm 源。Node 建议用 16.x 或 18.x LTS,不要用最新的奇数版本,某些依赖包还没适配。安装依赖前先换源:
npm config set registry https://registry.npmmirror.com npm install如果npm install卡在某个包上不动,先删掉node_modules和package-lock.json再重装,比反复重试有效。Vue 2 项目用vue-cli创建,Vue 3 用npm create vite@latest,两条路线的目录结构不同,但业务代码写法差异不大。
4.2 路由设计与页面权限控制
宠物领养系统的路由分两块:用户端和管理端。用 Vue Router 的meta字段标记是否需要登录和管理员权限:
const routes = [ { path: '/', component: Home }, { path: '/pet/:id', component: PetDetail }, { path: '/apply', component: ApplyForm, meta: { requireAuth: true } }, { path: '/admin', component: AdminLayout, meta: { requireAuth: true, requireAdmin: true }, children: [ { path: 'pet', component: PetManage }, { path: 'audit', component: ApplyAudit } ] } ] router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') const role = localStorage.getItem('role') if (to.meta.requireAuth && !token) { next('/login') } else if (to.meta.requireAdmin && role !== '1') { next('/') } else { next() } })路由守卫里从localStorage读 token 和角色,每次跳转前判断。注意role存的是字符串,后端返回数字 1 时要转成'1'再比较,否则1 !== '1'恒为真,管理员永远进不去后台,这个坑我踩过。
4.3 打包产物放进 SpringBoot 的正确姿势
前端开发完成后执行npm run build,Vue CLI 默认输出到dist目录。把这个目录下的index.html和static文件夹整体拷到 SpringBoot 的src/main/resources/static下。如果前端路由用了 history 模式,刷新页面会 404,需要在 SpringBoot 里加一个转发配置:
@Controller public class IndexController { @RequestMapping("/{path:[^\\.]*}") public String forward() { return "forward:/index.html"; } }这个正则排除带点的静态资源请求,只把前端路由路径转发到index.html。如果不想加这段代码,就把 Vue Router 改成 hash 模式,URL 带#,刷新不会 404,但地址栏不好看。两种方案按需选。
5. 避坑与排查:部署和联调阶段最容易翻车的五件事
5.1 现象:启动报Table 'xxx' doesn't exist,但数据库里明明有表
原因通常是application.yml里的数据库名拼错,或者建表时用了另一个库。还有一种情况是 MyBatis-Plus 的@TableName注解写的表名和实际表名不一致,比如实体类叫Pet,默认找pet表,但建表时写成了pets。
解决:先看启动日志里打印的 JDBC URL,确认连的是哪个库;再检查实体类注解。打开log-impl后,报错前会打印实际执行的 SQL,把 SQL 复制到数据库客户端里跑一遍,问题立刻定位。
5.2 现象:前端请求后端返回 200,但数据是空的,控制台无报错
原因多半是跨域配置没生效,浏览器实际发的是 OPTIONS 预检请求,被拦截器拦下返回了 401,但前端只看到业务请求失败。另一种可能是后端返回的字段名和前端接收的不一致,比如后端返回coverImg,前端写的是cover_img。
解决:打开浏览器 Network 面板,看请求的 Response 里到底有没有数据。如果有数据但页面不显示,就是字段映射问题;如果 Response 是空的,检查拦截器是否放行了 OPTIONS 请求,在拦截器preHandle里对OPTIONS方法直接返回true。
5.3 现象:图片上传成功但访问 404
原因:文件存到了服务器本地磁盘某个目录,但 SpringBoot 没有配置静态资源映射,浏览器访问的 URL 找不到对应文件。很多人把图片存到项目根目录的upload文件夹,但 SpringBoot 默认只暴露static、public等几个目录。
解决:加一个资源配置,把磁盘路径映射到 URL 路径:
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceLocations("file:" + System.getProperty("user.dir") + "/upload/"); }上传时把文件写到user.dir/upload/下,返回的 URL 拼成/upload/文件名,前后端约定好这个前缀。
5.4 现象:Vue 打包后页面白屏,控制台报Unexpected token '<'
原因:vue.config.js里的publicPath配置不对。默认是/,如果部署时不是根路径,静态资源请求会 404,返回的是index.html的内容,浏览器按 JS 解析就报这个错。
解决:在vue.config.js里设publicPath: './',用相对路径引用资源。同时确认dist目录下的文件确实被拷进了static,而不是拷成了static/dist多一层。
5.5 现象:审核通过后宠物状态没变,或者变了但申请记录还是待审核
原因:审核接口没有加事务,更新宠物状态和更新申请状态两步中有一部失败,数据就不一致。或者审核逻辑写在了 Controller 里,先更新了申请状态,再调 Service 更新宠物,中间抛异常没有回滚。
解决:把审核逻辑整体挪到 Service 层,加@Transactional,先更新申请状态,再根据审核结果更新宠物状态(通过则设为已领养,驳回则设回待领养)。两步操作要么全成功要么全回滚,保证数据一致。
6. 进阶技巧:用接口文档和定时任务把系统做扎实
系统能跑通只是及格线,真正让这套宠物领养系统拿得出手的,是两个容易被忽略的细节:接口文档和状态自动流转。
接口文档我一般用 Knife4j,它是 Swagger 的增强版,界面更友好。引入依赖后在配置类上加@EnableKnife4j,Controller 方法上加@ApiOperation注解,启动后访问/doc.html就能看到所有接口的参数和返回值。这样做的好处是前端不用反复问后端「这个接口传什么」,联调效率至少提升一半。注意生产环境要加访问密码或直接关闭,避免接口信息暴露。
状态自动流转解决的是「用户申请后管理员长期不审核,宠物一直卡在审核中」的问题。用 SpringBoot 的@Scheduled定时任务,每天凌晨扫描超过 7 天未审核的申请,自动驳回并把宠物状态改回待领养:
@Component public class ApplyTimeoutTask { @Autowired private AdoptApplyMapper applyMapper; @Autowired private PetMapper petMapper; @Scheduled(cron = "0 0 2 * * ?") public void cancelTimeoutApply() { LocalDateTime deadline = LocalDateTime.now().minusDays(7); List<AdoptApply> list = applyMapper.selectList( new LambdaQueryWrapper<AdoptApply>() .eq(AdoptApply::getAuditStatus, 0) .lt(AdoptApply::getApplyTime, deadline) ); for (AdoptApply apply : list) { apply.setAuditStatus(2); apply.setRemark("超时未审核,系统自动驳回"); applyMapper.updateById(apply); Pet pet = petMapper.selectById(apply.getPetId()); if (pet != null && pet.getStatus() == 1) { pet.setStatus(0); petMapper.updateById(pet); } } } }cron表达式0 0 2 * * ?表示每天凌晨 2 点执行。启动类上要加@EnableScheduling注解,否则定时任务不生效,这个注解漏加是新手最常见的翻车点。批量处理时如果数据量大,要分批查、分批更新,避免一次性加载过多数据撑爆内存。
最后说一个验证方法:部署完成后,用 Postman 或浏览器把「注册 → 登录 → 浏览宠物 → 提交申请 → 管理员审核 → 查看结果」这条主链路完整走一遍,每一步都看数据库里的数据变化。这条链路通了,系统就真正可用了。我自己做这类项目时养成的习惯是,每加一个功能就先手动走一遍主流程,再写自动化测试,比事后补测试省力得多。希望帮到你。
本文还有配套的精品资源,点击获取