☰
SpringBoot+Vue宠物领养系统答辩PPT:前后端分离项目实战解析
2026/10/15 14:06:02 网站建设 项目流程

简介:一套基于Spring Boot和Vue.js的宠物领养系统答辩PPT,面向计算机专业毕业生及准备毕业设计答辩的开发者,以项目答辩汇报的视角,涵盖了从选题背景、开发环境到系统实现的全过程。内容涉及Java后端框架、MySQL数据库、B/S三层架构及UML建模分析,并梳理了管理员对宠物领养、宠物认领、教学视频、感谢信和公告的管理与审核,以及用户申请领养/认领、发布感谢信、收藏评论教学视频等核心功能,适合用于快速理解系统设计思路或作为答辩框架参考。资源包内只有1个pptx演示文件,大小2.65MB,体量轻巧却完整呈现了登录流程、各角色操作界面以及结论致谢等内容。目前已有1482人学习下载,说明其在毕业设计同类资源中具有较高参考价值。通过浏览该PPT,读者可以很快抓住宠物领养系统的需求分析、模块划分、数据库表设计和业务处理流程等要点,同时借鉴其结论与致谢表达,完善自身汇报材料,为后续项目开发提供指引。

1. 答辩PPT背后的系统:先想清楚你要讲的是什么

做宠物领养系统的人,往往把力气全花在功能能不能跑通上,直到答辩现场被问了一句“领养和购买到底有什么区别”才卡壳。这份SpringBoot+Vue宠物领养系统答辩PPT要解决的问题,不是把代码截图堆满每一页,而是把一套“申请—审核—回访”的业务闭环讲圆。后端用SpringBoot提供接口和权限控制,前端用Vue负责页面交互,两边通过JSON联调;系统的使用者是两类人,想领养宠物的普通用户和负责审核的管理员。适合正在准备毕业答辩的开发者,也适合想用一个完整项目入门前后端分离的从业者。你不需要把系统做多大,但流程必须自洽。

2. SpringBoot后端:三张核心表与一条状态链路

2.1 宠物、用户、领养申请:后端先建好这三张表

先想一个问题:这个系统到底存什么数据?宠物信息、用户信息、领养申请记录,缺一不可。把这三张表建好,后面所有接口都是在它们之间的读写和状态变更。常见做法是给每个表加一个逻辑主键id,再用关联字段把三张表串起来:宠物表记录发布人,领养申请表记录宠物和申请人,管理员审核时在申请表上写审核结果。角色上分ROLE_USER和ROLE_ADMIN两种,登录接口签发JWT时把用户id和角色放进去,后面的接口才能做权限判断。

宠物表是系统的主表:

CREATE TABLE pet ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '宠物ID', name VARCHAR(30) NOT NULL COMMENT '宠物名字', species VARCHAR(20) NOT NULL COMMENT '物种,cat或dog', breed VARCHAR(50) COMMENT '品种,可空', age_years TINYINT COMMENT '年龄,按年存,避免字符串排序问题', status TINYINT NOT NULL DEFAULT 0 COMMENT '0待领养 1申请中 2已领养 3已下架', publisher_id BIGINT NOT NULL COMMENT '发布人ID,关联sys_user', description TEXT COMMENT '描述,前端按富文本渲染', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) COMMENT '宠物信息表';

几个关键选择说明一下:status用TINYINT而不用字符串,是因为后面要跟状态机枚举做映射,数字在数据库里占空间小、比较快,而且不会出现“待领养”和“待领养 ”这种肉眼看不见的脏数据问题。description用TEXT,因为富文本编辑器的内容可能带HTML标签,VARCHAR(255)装不下。age_years用TINYINT存数字而不是“2岁”这种字符串,排序和筛选会更省事,这个细节答辩时可以提一句。

领养申请表是整个系统的业务核心,它决定了你能不能把“领养”讲清楚:

CREATE TABLE adoption_application ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '申请ID', pet_id BIGINT NOT NULL COMMENT '申请的宠物ID', applicant_id BIGINT NOT NULL COMMENT '申请人ID', status TINYINT NOT NULL DEFAULT 0 COMMENT '0待审核 1已通过 2已拒绝', apply_reason VARCHAR(500) COMMENT '申请理由,审核时参考', reviewer_id BIGINT COMMENT '审核人ID,通过或拒绝时写入', review_time DATETIME COMMENT '审核时间', follow_up_status TINYINT DEFAULT 0 COMMENT '0未回访 1已回访', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '提交时间' ) COMMENT '领养申请表';

这张表之所以关键,是因为它承载的是业务动作而不是商品交易。申请提交、管理员审核、回访登记,每一步都在这一张表上留下痕迹。apply_reason是审核判断的依据,reviewer_id和review_time记录是谁在什么时候做的决定,follow_up_status用来支撑“领养后的回访”这个场景。答辩时拿这张表讲业务,比讲一百行CRUD都有说服力。

提示:用户表在这里没有贴DDL,但别省。sys_user里至少要有id、username、password、role、phone五个字段,密码存BCrypt加密后的密文,不要存明文,这是答辩时经常被追问的点。

2.2 接口与权限:把登录、申请、审核三条链路定清楚

表建好了,接口设计就顺了。我把接口分成三类:公开接口、登录用户接口、管理员接口。公开接口只有登录和宠物列表、详情;提交领养申请需要登录,因为要拿当前用户身份;审核接口必须管理员才能调,前端隐藏按钮是不够的,后端接口要真正拦住。这个“前端控制显示、后端控制权限”的表述,基本是答辩必答。

核心接口清单如下:

接口方法路径权限作用
用户登录POST/api/auth/login公开校验账号密码,签发JWT
宠物列表GET/api/pets公开分页查询,按状态过滤
宠物详情GET/api/pets/{id}公开查询单只宠物
提交申请POST/api/adoption/apply登录创建领养申请
申请列表GET/api/admin/applications管理员查看待审核申请
审核申请POST/api/admin/applications/{id}/review管理员通过或拒绝,回写状态

统一前缀/api、统一返回Result对象,是前后端分离项目的基本纪律。Result里至少包含code、message、data三个字段,code为200表示成功,401是未登录,400是参数或业务校验失败。这个约定看起来琐碎,但答辩现场演示时如果接口报错,能根据code一眼定位问题,不会在老师面前翻车。

Controller层写一段最常见的模板:

@RestController @RequestMapping("/api/adoption") public class AdoptionController { private final AdoptionService adoptionService; public AdoptionController(AdoptionService adoptionService) { this.adoptionService = adoptionService; } @PostMapping("/apply") public Result submit(@RequestBody @Valid ApplyDTO dto) { // 当前登录用户的id从JWT解析后放进了ThreadLocal,这里直接取 Long userId = UserContext.getUserId(); if (userId == null) { return Result.error(401, "未登录或登录已过期"); } // 幂等校验:同一用户不能重复申请同一只宠物 if (adoptionService.alreadyApplied(userId, dto.getPetId())) { return Result.error(400, "你已经申请过这只宠物了"); } adoptionService.submit(dto, userId); return Result.ok(null); } }

几个参数要说明:ApplyDTO里放了petId和applyReason两个字段,都加了@NotNull和@NotBlank,这样请求体里缺字段时,Spring在进入方法前就返回参数校验错误,不用自己在业务代码里手动判空。UserContext是自己在拦截器里维护的一个ThreadLocal工具类,登录拦截器解析JWT后把userId放进去,请求结束后清掉。这样Controller里不需要每个方法都去解析token,代码干净很多。幂等校验放在Controller层是因为它属于接口约束,放在Service里逻辑也对,但职责上放这边更直观。

2.3 状态流转:为什么不能直接在数据库里改状态

这个系统最容易做歪的地方,是把宠物状态和申请状态当成普通字段随便update。常见做法是提供一个专门的Service方法,每次都校验“当前状态”和“目标状态”是否匹配,才执行更新。这样做的理由很实在:领养的每一步都有业务前提。宠物只有处于“待领养”才能被申请,申请只有处于“待审核”才能被通过或拒绝,没有前置校验直接改状态,系统就变成谁都能改的玩具。

public boolean reviewApplication(Long applicationId, Integer targetStatus, Long reviewerId) { AdoptionApplication app = applicationMapper.selectById(applicationId); // 状态不匹配直接拒绝更新 if (app.getStatus() != STATUS_PENDING) { throw new BusinessException("当前申请不是待审核状态,无法审核"); } AdoptionApplication update = new AdoptionApplication(); update.setId(applicationId); update.setStatus(targetStatus); update.setReviewerId(reviewerId); update.setReviewTime(new Date()); // 返回0说明影响行数为0,多半是并发导致状态已被改掉 return applicationMapper.updateById(update) == 1; }

这里最关键的是先查后改的判断逻辑:先selectById拿到当前状态,判断必须等于待审核,再执行updateById。并发场景下,两个管理员同时审核同一条申请时,可能都通过了第一次查询,再先后更新,造成最后结果与预期不符。若想更严谨,可以在UPDATE语句里带上状态条件,用affected rows是否为1来判断本次更新是否真正生效。这一点写完可以主动在答辩PPT里提,属于加分项。

申请通过后,宠物状态要同步从“待领养”变成“已领养”,这个联动动作放在同一个事务里执行,用@Transactional标注。一个事务里既更新申请表状态,又更新宠物表状态,任何一步失败都回滚,保证两张表不会出现“申请的宠物显示已通过但宠物还是待领养”这种前后矛盾。讲清楚这个事务,比罗列十个注解更能证明你理解业务一致性。

3. Vue前端:页面划分、接口联调与演示顺序

3.1 路由与页面划分:前台浏览、后台管理分开走

前端如果只按“能用”来写,页面会挤成一团。我的组织习惯是前台和后台拆成两套布局:前台面向普通用户,放宠物列表、宠物详情、领养申请页;后台面向管理员,放宠物管理、申请审核、回访记录。路由上通过嵌套路由把两套页面分开,再配合路由守卫做登录拦截。

const routes = [ { path: '/', component: LayoutFront, children: [ { path: '', name: 'Home', component: HomeView }, { path: 'pet/:id', name: 'PetDetail', component: PetDetail }, { path: 'apply/:petId', name: 'Apply', component: ApplyView, meta: { requiresAuth: true } } ] }, { path: '/admin', component: LayoutAdmin, meta: { requiresAuth: true, role: 'ADMIN' }, children: [ { path: '', name: 'AdminHome', component: AdminHome }, { path: 'applications', name: 'ApplicationList', component: ApplicationList } ] } ]

requiresAuth和role这两个meta字段是路由守卫的依据。requiresAuth为true的路由,用户未登录时会被守卫拦到登录页;role为ADMIN的路由,即使登录了但角色不对,也会被重定向回首页。这里有个容易忽略的点:路由守卫只控制页面能不能进,接口权限仍然要靠后端拦截器,两件事不要混为一谈。答辩UI页面时,可以顺口说一句“角色路由加了守卫,但真正的权限底线在后端”,老师会接住这个点。

3.2 axios封装与开发代理:一次配好,后面不折腾

前后端联调阶段最容易出现的重复劳动,是每个页面都写一遍axios请求、每个接口都手动拼token。我一律封装一个request实例,把baseURL、超时时间、token注入、错误提示统一处理掉。这样组件里只需要写业务代码,接口报错弹出统一提示,不用每个页面写try-catch。

import axios from 'axios' const service = axios.create({ baseURL: '/api', timeout: 10000 }) service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = 'Bearer ' + token } return config }) service.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { // 401统一跳登录,400只是业务校验失败,提示message即可 if (res.code === 401) { localStorage.removeItem('token') window.location.href = '/login' } return Promise.reject(new Error(res.message || '请求失败')) } return res }, error => { return Promise.reject(error) } ) export default service

这里几个参数值得说:timeout设成10000毫秒,答辩现场如果网络波动,请求最多转10秒就会超时并提示,不会一直卡在那里让老师等。baseURL统一为/api,开发时配合Vite的proxy把请求转发到后端8081端口,生产环境再由Nginx做反向代理,前端代码本身不需要感知后端地址。token注入了Authorization头,后端拦截器从请求头里解析,这套约定是前后端分离项目的标配,答辩PPT里放这段代码时配两句话解释,比整页代码截图效果好。

3.3 演示操作顺序:先给老师看什么、再看什么

答辩演示和平时自己点鼠标是两回事。系统要讲给老师听,顺序必须模拟一条完整的业务链:先以普通用户身份注册登录,浏览宠物列表,打开一只宠物的详情页,提交领养申请;然后退出登录,切换到管理员账号,进入后台看到这条待审核申请,点击通过;再回到前台刷新宠物详情,看到宠物状态变为已领养。这一套流程走完,老师对系统的完整性就有直观印象。

我一般会在演示前把数据准备到位:两只品种不同的猫、一只狗,照片用本地相对路径,避免现场没网时图片裂开;再准备一个测试账号和一个管理员账号,密码提前写在便签上。演示中途最怕出现的是“点了个按钮没反应”,所以演示用的那台机器不要开太多窗口,关闭无关软件,数据库、后端、前端按顺序启动后不要再去动配置文件。把演示流程在PPT的备注页里写一遍,上场前默念两遍,比背整个项目讲稿有用。

4. 答辩PPT的结构与话术:十分钟把技术讲成得分点

4.1 九页PPT的时间分配:每页只留一个记忆点

答辩PPT不是项目说明书,老师看的是你能不能抓住重点。我的建议是只做九页,每页一个记忆点,总时长控制在十分钟。页数多了会忍不住念稿子,页数太少又讲不清系统的完整性。

页码内容建议时长这一页要达成的目标
1封面:题目与技术栈20秒一句话说清做的是什么、用了什么
2项目背景与现有问题40秒说明为什么需要宠物领养系统
3功能模块划分60秒展示前后台各有哪些模块
4数据库设计60秒讲三张核心表和状态字段
5后端接口与权限60秒讲接口分类、JWT、事务
6前端页面与交互90秒演示主要流程,边点边讲
7核心代码片段60秒讲状态流转和并发处理
8测试与运行结果40秒展示接口测试通过情况
9收获与可改进方向40秒收束内容,留话头给老师提问

每页的时间分配决定了你能不能讲完。很多人在第3页功能结构上讲太久,到后面的核心代码反而被催着过一遍,这是本末倒置。功能模块拆开讲,每页一个模块,讲完就翻页。数据库、接口、代码这三页是技术分的主要来源,时间占比要保证。

4.2 高频提问怎么答:三个准备好就能稳住全场

根据往届答辩现场的情况,老师翻来覆去问的就那几类问题。第一类是业务理解的:“领养和购买有什么区别?”回答思路是:购买走订单结算,核心是库存和金额;领养走资格审核,核心是申请条件和责任约束。我的系统用申请单承载审核流程,用状态字段记录每个环节,审核通过后还要登记回访,这不是电商下单能覆盖的。

第二类是权限控制的:“你的权限是怎么做的?”回答要落到JWT和拦截器两层:登录接口签发带角色信息的token,后端拦截器统一解析鉴权,管理员接口额外校验角色,前端只是做了页面级的配合。把这两层说清楚,老师就不会再追问“前端不显示按钮但接口能直接调怎么办”这种问题了。

第三类是数据一致性的:“两个人同时申请同一只宠物怎么办?”回答要落到状态校验和事务上:提交申请时校验宠物状态必须为待领养,审核时校验申请状态必须为待审核,同时用更新行数判断是否被并发修改。这三个问题如果能自然答出,整个答辩的基本盘就稳了。

4.3 试讲与环境检查:上场前最后的确认项

答辩前的试讲不是把PPT翻一遍,而是要把演示流程完整走一遍。我习惯至少完整走三遍试讲:第一遍只讲不点,控制时间和语速;第二遍边讲边演示,把每个点击位置记录清楚;第三遍当作正式答辩,中途不暂停,看哪里卡壳就补哪里。试讲时最容易暴露的问题是语速偏快,尤其是讲到核心代码时,要刻意慢半拍。

环境检查同样重要。演示机上的后端别用IDE直接跑,改用打包后的jar包或已经配置好的启动脚本,双击启动,避免现场打开IDE耗时间。数据库用可视化工具提前确认服务已启动,表里有数据。前端如果已经打包部署,就直接用部署后的地址访问,不要依赖node的开发服务器。检查项记在手机备忘录里:后端启动了吗、数据库连接正常吗、测试账号能登录吗、演示图片能打开吗。全部打勾再上场,心里会踏实很多。

5. 答辩避坑:五个踩过才懂的现场问题

5.1 刷新页面就404:history模式的路由兜底

现象:系统演示时,从首页点进详情页再按F5刷新,页面直接白屏显示404,答辩氛围瞬间冷场。

原因:Vue Router用了history模式,地址栏里的路径是真实URL,刷新时浏览器会向服务器请求这个路径,而后端没有对应的路由处理,自然返回404。开发模式下Vite能兜住,部署到后端或Nginx后就暴露了。

解决:在Nginx配置里加一个try_files兜底,让所有找不到的路径都回到index.html,由前端路由接管。

location / { try_files $uri $uri/ /index.html; }

提示:如果后端用SpringBoot直接部署前端打包产物,也要加一个转发规则,把非/api的请求转发到index.html,这个坑属于“部署时必踩,踩完一次就会记住”的类型。

5.2 前后端联调时跨域被拦:代理与服务端配置的关系

现象:前端页面能打开,登录请求发出去后浏览器控制台报CORS错误,接口完全不通。第一次遇到的人会怀疑代码写错,实际上这是跨域问题。

原因:开发时前端跑在8080端口,后端跑在8081端口,端口不同就构成跨域,浏览器默认拦截非简单请求。前后端分离项目在开发阶段几乎必然遇到。

解决:开发阶段在前端工程里配置代理,让浏览器以为请求是同源的,生产阶段由Nginx反向代理统一转发。Vite的配置写在vite.config.js里:

server: { proxy: { '/api': { target: 'http://localhost:8081', changeOrigin: true } } }

如果不用代理,在后端加CORS配置也能解决,但只适合临时联调,答辩PPT里讲“我用代理解决开发环境跨域,生产环境用Nginx反向代理”是更完整的架构表述。我踩过的坑是只在前端配了代理但忘了重启dev server,配置不生效,检查时先确认服务重启没有。

5.3 中文乱码与图片打不开:两个基础却致命的细节

现象:数据库里查出来的宠物名字和申请理由全是问号,页面上的宠物图片裂开成一张空白图。这两个问题单独看都不难,但一起出现的概率很高。

原因:中文乱码八成是数据库连接串没指定字符集,或者建表时字符集不是utf8mb4;图片打不开通常是因为图片路径存了本机绝对路径,比如C盘下的某个地址,换机器后路径失效。

解决:数据库连接串里明确指定useUnicode=true&characterEncoding=utf8,建库时用utf8mb4字符集。图片路径统一存相对路径,上传的文件存到项目指定目录,数据库里只存 /images/pet/001.jpg 这种值,访问时通过后端静态资源映射或Nginx映射到磁盘目录。答辩前把所有图片路径检查一遍,确保在演示机上能打开。

5.4 两个用户同时申请同一只宠物:状态被重复写入

现象:A、B两个用户几乎同时提交对同一只宠物的领养申请,两个申请都显示成功,宠物状态一会儿是“申请中”,一会儿又是“待领养”,数据对不上。

原因:提交申请时只做了“查一下宠物状态是否待领养”的前置判断,两个请求同时通过查询,然后各自插入了申请记录。查询和更新不是一个原子操作,并发下就破了。

解决:给宠物状态更新加条件,让数据库帮我们保证原子性。提交申请前先执行一条条件更新:

UPDATE pet SET status = 1 WHERE id = ? AND status = 0

受影响行数是1才说明抢到了这只宠物的申请权,是0就说明别人已经申请了。这种“数据库条件更新”的思路比单纯查询判空可靠得多,答辩时主动讲出来,是能明显拉开差距的细节。

5.5 现场环境起不来:演示兜底与恢复次序

现象:答辩现场后端启动报端口被占用,或者数据库服务没起来,或者登录页一直转圈。这些事单独看都是小事,但在台上处理时每一秒都是煎熬。

原因:端口被占用是之前残留的进程没杀掉;数据库没起来是机器重启后服务没设开机自启;登录转圈多半是后端地址配置错了。

解决:演示机提前把启动流程固定成一个脚本,按顺序启动数据库、后端、前端,启动失败时能看到具体报错。另外准备一版录屏视频和一份核心截图放在同级目录里,万一现场环境始终修复不了,直接切换放录屏,边放边讲。这个习惯救过我一次,之后每次答辩演示都会备录屏,属于“后悔药”级别的准备。

6. 用状态机把领养流程讲成闭环:答辩收尾的高光技巧

最后要分享的,是一个能让答辩老师记住这个系统不是购物车的小设计:把领养流程显式建模成状态机。很多人的系统也能跑,但状态之间可以随意跳转,谁能看出这是个经过设计的业务系统呢?把状态迁移写清楚,既能让代码更严谨,也能在答辩时用一分钟画出整条业务链。

public enum AdoptionStatus { PENDING(0, "待审核"), APPROVED(1, "已通过"), REJECTED(2, "已拒绝"), FOLLOWING_UP(3, "待回访"), COMPLETED(4, "已完成"); public static boolean canTransit(int from, int to) { if (from == PENDING.value) { return to == APPROVED.value || to == REJECTED.value; } if (from == APPROVED.value) { return to == FOLLOWING_UP.value || to == COMPLETED.value; } if (from == FOLLOWING_UP.value) { return to == COMPLETED.value; } return false; } }

答辩现场如果时间够,可以把这个枚举的迁移规则打在PPT上,配合讲一句“状态机的每条边都对应一段业务校验,不允许从待审核直接跳到已完成”。这一句话,就把前面的简历从“你写了增删改查”提升到“你理解了业务状态”,效果很直观。

整套系统做完之后,我的习惯是花半小时把状态流转图画在白纸上:待审核出发,通往已通过和已拒绝;已通过之后进入待回访,回访登记完成后到已完成。每次答辩前看一遍这张图,确保每个状态都能自圆其说,然后才去准备其他内容。这半个小时换来的是台上被追问时的底气。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询