
先说个题外话。每年到了毕设季总有人问我“Java后端到底做什么题目好”我的答案一直没变过管理系统类题目是下限最低、也最容易做出完整度的方向而宠物领养系统又是这类题目里少有的、能从业务逻辑上撑起一个完整项目的存在。它不像纯电商那样堆模块也不像纯CRUD那样没营养用户、宠物、领养申请、审核流程、公告、留言这几条线串起来之后既有权限控制的考点也有业务状态流转的难点还很适合在答辩时讲出“我考虑过什么、我踩过什么坑”。这篇就来完整拆一套基于SpringBoot的宠物领养系统从技术选型、数据库设计到前端联调、服务器部署再到答辩考点和面试关联题按我实际做项目的顺序捋一遍源码和部署文档之外的思路也都尽量写透。1. 项目整体设计与技术栈选型1.1 为什么是SpringBoot MyBatis Vue这套组合先把结论放在前面这套组合在2024年依然是Java后端毕设和中小型项目的“标准答案”而且短时间内不太会变。原因不复杂——SpringBoot把Spring生态的配置成本压到了最低MyBatis半自动化的SQL控制让开发者能精确控制每一条查询Vue则能在不引入重型前端框架的前提下做出足够流畅的页面交互。很多人纠结要不要换成Spring Cloud或者微服务我的建议很直接单机项目不要为了“看起来高级”而上微服务那是给自己挖坑。SpringBoot单体应用在这个规模下启动快、调试方便、部署简单把业务逻辑做扎实比堆技术栈有价值得多。真要体现能力不如在代码规范、接口设计、异常处理、状态机流转这些地方下功夫。1.2 系统角色与核心功能模块划分宠物领养系统最核心的价值是它天然带有“多角色 审批流”的业务基因这让它比起普通的增删改查项目多了一条可以深挖的逻辑主线。管理员端登录后进入后台负责宠物信息的审核、领养申请的审批、公告的发布、用户的禁用与解禁、留言评论的管理。普通用户端注册登录后可以浏览宠物列表、查看宠物详情、在线提交领养申请、收藏宠物、查看申请进度、发表留言、维护个人中心。宠物主人侧可选扩展发布宠物信息、查看自己宠物收到的领养申请、协商并确认领养人。这个角色如果做进去系统的商业闭环和故事线就完整了。三个角色对应三条权限线各自有独立的操作边界。我的建议是不要在第一版就贪多先把管理员和普通用户两条线做扎实宠物主人可以直接复用自己的用户表加一个“发布者”字段来区分不需要单独建表。1.3 项目的适用人群与学习路径如果你是为了毕设这套系统的体量刚好是“花一个多月能写透”的程度如果你是在校学生想通过实际项目提升SpringBoot实战能力这同样是个合适的第一完整项目。我的建议路径是这样的三到五天跑通源码阅读核心模块理解表结构之间的关系一周左右自己动手从零搭建数据库按模块重写一遍核心代码不要复制粘贴最后一周补充前端交互细节、写部署文档、准备答辩PPT和演示脚本。2. 数据库设计与核心表结构2.1 第一版我设计了多少张表为什么是这几张很多人拿到题目先写代码这是本末倒置。数据库表结构就是整个项目的骨架骨架歪了后面全歪。这个系统我第一版设计了8张表后续按需微调到了10张左右每一张表都不是随便加的。用户表user主键、用户名、密码、昵称、头像、电话、邮箱、地址、角色标识、状态、注册时间。宠物表pet主键、宠物名称、品种、年龄、性别、健康情况、是否绝育、是否疫苗、所在城市、详细描述、封面图URL、宠物类型、发布者ID、审核状态、领养状态、浏览量、创建时间。宠物类型表pet_type类型ID、类型名称猫、狗、兔、鸟等。领养申请表adopt_apply主键、用户ID、宠物ID、申请人姓名、联系电话、家庭住址、是否自有住房、家庭成员情况、养宠经验、申请理由、申请状态、申请时间、处理时间。公告表notice公告ID、标题、内容、发布人ID、发布时间。留言反馈表feedback留言ID、用户ID、宠物ID可空、留言内容、回复内容、留言时间、回复时间。收藏表favorite收藏ID、用户ID、宠物ID、收藏时间。领养记录表adopt_record记录ID、宠物ID、宠物主人ID、领养人ID、所属申请ID、领养时间。为什么要有宠物类型表而不直接存字符串因为类型是可枚举的、需要被查询和统计独立成表既保证了数据一致性也方便后期扩展筛选功能多花一张表的成本很低但设计上的规范度是答辩加分项。2.2 宠物表关键字段设计状态字段如何避免数据混乱宠物表里有两类状态必须提前想清楚否则到后期会出大乱子。第一类是审核状态。用户发布的宠物不能直接上架管理员要先审核内容是否合规、照片是否清晰、信息是否完整所以需要 audit_status 字段取值0待审核、1已通过、2已拒绝。这个设计模仿的是真实平台的内容风控机制写进答辩文档里很有说服力。第二类是领养状态。宠物可能处于“待领养”“已被申请”“已成功领养”“已下架”几个阶段用 adopt_status 字段控制取值0待领养、1申请中、2已领养、3已下架。这里最容易踩的坑是审核状态和领养状态混在一个字段里。我见过有人用一个状态字段既管审核又管领养结果“一个已通过审核且已被领养的宠物”表达不出来只能靠硬编码字符串硬凑非常痛苦。两个字段各管一摊互不干扰查询时用多条件组合逻辑非常干净。2.3 领养申请流程:申请状态是一个小状态机领养申请的状态流转是整个项目业务逻辑的核心也是体现你设计能力的地方。我在 adopt_apply 表里设计了 status 字段对应四类状态0待审核用户提交申请后宠物主人或管理员还未处理1已通过审核通过待确认交接2已拒绝申请未被通过3已完成宠物成功交到领养人手上流程闭环。这个状态机看着简单但它有一个衍生逻辑——领养状态和申请状态要联动。具体来说当一个申请被“审核通过”时宠物表的 adopt_status 要同步变为“申请中”并且同一时间只能有一条“已通过”的申请其他待审核的申请应自动标记为“已拒绝”。我当时在事务里处理了这个联动用 synchronized 关键字加数据库乐观锁双重保险防止并发场景下同一个宠物同时被多人通过审核。虽然毕设场景下并发量很低但这个设计写出来很能体现业务思考的深度。3. 后端核心功能实现要点3.1 项目结构分层controller只管收参数和写返回值SpringBoot项目的包结构我建议一个约定俗成的分层方式不要搞花活controller接收前端请求参数校验调用service返回统一结果。service承载核心业务逻辑事务控制在这里做接口实现的写法便于维护。mapper定义数据访问方法配合resources下对应位置的XML文件写SQL。entity数据库表对应的实体类。dto / vo部分复杂场景下用于前端交互的数据模型和实体类解耦。多层之间不要互相越权这个习惯如果养成了后期接新功能会非常顺。我见过不少代码把SQL直接写在controller里或者把业务逻辑堆在service接口里初次跑通没问题但一旦要加需求就会非常痛。关于返回结构统一用一个 Result 类包裹包含 code、message、data 三个字段。这个设计不只是为了美观更关键的是前端axios拦截器可以统一处理错误码不用每个请求单独写错误弹窗逻辑。3.2 登录鉴权为什么推荐JWT而不是Session在这个系统里我推荐使用JWT做登录鉴权。原因有三点前后端分离架构下后端不持有会话状态天然支持横向扩展Vue端把token存到localStorage或sessionStorage请求时统一放到header里实现简单答辩时你能清楚地解释无状态认证和Session方式的区别这是一个高频面试点。JWT实现要点如下用户登录成功后用用户的ID、用户名、角色生成token设置过期时间一般两小时返回给前端。前端每次请求在拦截器中携带token后端写一个拦截器或过滤器统一解析token并验证合法性。拦截器要特别注意两个问题一个是要在配置类中注册并添加或排除拦截路径登录接口、注册接口、宠物列表这类公开接口要放行另一个是解析token失败时返回401前端axios接收到401后统一跳转回登录页。把这两个点处理好前后端联调过程中的大部分鉴权问题都能提前规避。3.3 宠物发布与图片上传一个常被忽略的配置点用户发布宠物信息时前端通过表单提交宠物基本信息和图片文件。后端接收时用 MultipartFile 接收图片然后进行文件存储。图片上传有两种方案。我推荐本地存储方案配置文件里设置一个文件上传路径如 upload.path用 UUID 重新生成文件名防止文件名冲突和路径穿越问题按月份新建子目录方便后期管理将文件的相对访问路径返回给前端如 /upload/2024/05/xxx.jpg。这里有个特别容易被忽略的问题SpringBoot默认静态资源路径并没有把本地磁盘目录映射到URL上直接访问会404。需要在配置类里继承 WebMvcConfigurer 并重写 addResourceHandlers 方法把本地磁盘路径映射到 /upload/** 访问前缀。这个配置我在部署文档里单列了一节因为十个部署的人里有三四个会栽在这里。另一种方案是存为Base64字符串直接写进数据库优点是不用处理文件存储缺点是数据库体积飙升、查询性能受影响只适合临时演示正规项目不要这么干。3.4 领养申请的后端业务实现:事务与并发控制提交领养申请的接口是整个系统最值得展开讲的地方。它在一次请求里至少要完成四步操作查询宠物当前状态必须是“待领养”才能继续写入一条领养申请记录状态为“待审核”更新宠物的领养状态为“申请中”返回给前端操作结果。四步操作必须放在同一个事务里否则会出现申请提交成功但宠物状态没更新、或者宠物状态已锁定但申请没写进去的脏数据问题。在service方法上加上 Transactional 注解简单实用。需要注意的是事务超时时间和回滚条件默认对RuntimeException回滚如果业务里抛的是checked exception记得指定 rollbackFor Exception.class。另外一个小点pet表我加了一个 version 字段做乐观锁更新宠物状态时set adopt_status #{新状态} where pet_id #{id} and adopt_status #{旧状态}更新受影响行数为0说明宠物状态已变化直接抛出业务异常提示“该宠物已被其他用户申请请刷新重试”。这个设计对一个毕设项目来说是明显高于平均水平的产物而且SpringBoot MyBatis的乐观锁实现成本非常低值得写进去。4. 前端Vue实现与前后端联调4.1 Vue项目与组件拆解前端我用的Vue 2.6 Element UI这套组合在中文社区的资料量最大遇到问题几乎都能搜到答案。技术上完全够用选它不是为了追求新而是为了求稳。Vue项目的结构按页面拆组件首页宠物列表页搜索栏、筛选条件类型、城市、状态、宠物卡片网格。宠物详情页图片轮播、基本信息卡片、领养按钮、留言区域。领养申请页表单弹窗包含家庭情况、养宠经验、联系方式等信息。个人中心申请记录、收藏列表、我发布的宠物。后台管理宠物审核、申请审批、公告管理、用户管理。组件拆分的原则是每个页面内的业务模块独立成一个子组件页面之间通过vue-router跳转组件之间用props和$emit通信跨页面共享数据用Vuex或简单的event bus。4.2 Axios请求封装与跨域代理前后端联调时第一个砸到脸上的问题就是跨域。前端跑在8080端口后端跑在8081端口浏览器的同源策略会直接拦截请求。最推荐的方案是利用Vue脚手架自带的webpack devServer代理。在 vue.config.js 里配置module.exports { devServer: { proxy: { /api: { target: http://localhost:8081, changeOrigin: true, pathRewrite: { ^/api: } } } } }这样做的好处是浏览器视角下所有请求都发到自身同源的 /api 前缀没有跨域问题同时后端接口不需要额外处理CORS。如果后端也要开启跨域配置可以用 CrossOrigin 注解但更统一的方案是写一个CorsFilter注册为Bean。两种方案选取一种即可同时配置容易出一些隐蔽的问题。Axios封装我不建议每个人重新造轮子网上有一套成熟的实践直接套用创建实例时设置 baseURL 为 /api设置超时时间请求拦截器里从localStorage取token并放到header响应拦截器里统一处理code不为200的情况和401未授权跳转。4.3 图片显示与文件访问的联动前端在页面上展示宠物图片时src地址要和后端返回的图片访问路径配合好。后端存储的相对路径是 /upload/2024/05/xxx.jpg图片上传时直接返回给前端完整可访问的URL需要把存储的路径和本地访问前缀拼接好。开发环境下前端页面显示的图片地址来自后端接口返回的JSON因此后端返回时要把存储路径封装成前端可访问的完整地址。部署到服务器后这个配置也要改服务器的IP和端口不同所以部署文档里我专门写了“上传后的图片地址如何配置”这一节防止大家部署完发现图片全裂。5. 部署流程与常见问题排查5.1 本地部署步骤十分钟跑通本地跑通这套项目核心就三步。先说数据库用Navicat或命令行工具创建一个名为 pet_adopt 的数据库字符集用 utf8mb4然后把项目中的 pet_adopt.sql 导入这一步就完成了数据初始化。再说后端修改 application.yml 中的数据库连接信息用户名、密码、数据库名这三个必须改对如果MySQL版本是8.0以上驱动要使用 com.mysql.cj.jdbc.Driver时区建议设置为 Asia/Shanghai。最后前端进入vue目录执行 npm install 安装依赖然后 npm run serve 启动开发服务器。只要你环境里的JDK不低于1.8、Maven是3.6以上这个过程通常不会遇到大问题踩坑主要集中在数据库连接信息和端口占用上下面单独说。5.2 使用部署文档打包并发布到服务器部署文档的意义就是避免你上线时反复试错。我个人的标准操作流程如下后端在项目根目录执行 mvn clean package -DskipTests打包生成 jar 包确保target目录下出现可执行文件把 jar 包上传到服务器 /opt/pet 目录springboot项目用内嵌Tomcat本地用java -jar 命令即可启动。如果要后台常驻运行用 nohup java -jar pet-adopt-1.0.0.jar app.log 21 日志输出到app.log方便排查。前端在vue目录执行 npm run build生成dist目录把dist里的静态文件放到Nginx的html目录下然后配置Nginx反代后端接口。Nginx配置里有几个关键点前端静态资源的 root 路径要和上传位置一致接口代理 /api 反代到后端服务器注意处理 history 路由模式的 fallback避免刷新页面404。服务器上跑项目还有一个必须注意的点如果用的是云服务器记得在安全组或防火墙里放行前后端端口很多人部署成功后访问不了就是端口被安全组拦截了。5.3 常见报错与排查方案速查端口被占用启动报错端口被占用最简单的处理是改配置端口或查看占用端口的进程并结束进程。Windows下用 netstat -ano | findstr 8081 找到PID然后 taskkill /PID xxx /F 杀掉。数据库连接失败报 Communications link failure 时重点检查MySQL服务是否启动、URL中的IP端口是否正确、账号权限是否允许远程访问、密码是否含有需要转义的特殊字符。前端请求跨域失败在浏览器F12看Network面板关注请求是否真正发出、响应的状态和CORS报错信息。重点检查代理配置是否生效、后端是否返回了正确的响应头。图片上传后访问404绝大多数是因为静态资源映射没有生效去后端配置类里检查addResourceHandlers那段代码同时确认存储的路径以及数据库里存的URL与访问路径是否一致。中文字符乱码确认MySQL数据库、表、字段都是 utf8mb4连接URL加上 characterEncodingutf8 参数前端页面meta也确保utf-8。6. 从源码到答辩:代码讲解与面试关联6.1 如何在答辩时把项目讲出深度这套系统做出来以后答辩能不能漂亮地收尾拼的就是对细节的理解。我见过太多人代码是拷的、文档是拼的一到提问环节就露馅。真正能体现水平的其实集中在几个点上。第一个是讲清楚状态设计和对业务联动的理解比如宠物的审核状态和领养状态为什么分开、申请通过时如何同步宠物状态、并发下如何保证不出现“一猫多主”。第二个是讲清楚文件上传的处理方式为什么用UUID重命名、本地存储和对象存储各自优劣。第三个是讲清楚接口的校验逻辑比如后端参数校验除了前端必填之外还做了哪些边界防护。这三个点讲明白老师基本会认为这是你自己从头做出来的。如果你能再顺口补充一两个测试场景比如用过Postman构造什么极端输入、如何验证事务的回滚答辩效果会更好。6.2 这些关键点和SpringBoot面试题如何串联如果是为了就业做这个项目那它在面试环节还能发挥余热。SpringBoot相关的热门面试题里有相当一部分可以直接从这个项目里引出。比如 SpringBootApplication 这个组合注解包含哪些注解为什么一个类能被自动扫描和配置SpringBoot的自动配置原理是什么为什么引入starter之后相关Bean就自动装配了那是spring.factories和ConditionalOnClass等条件注解在起作用。这些都能在项目中转开去聊。再比如SpringBoot项目打包为什么能做成一个可执行的jar包内嵌Tomcat在spring-boot-maven-plugin里的工作原理。MyBatis相关的话题也可以聊#{}和${}的区别是什么为什么用Param注解传多个参数数据库字段名和实体类属性名的下划线与驼峰怎么映射。这些你在这个项目里都真实用到了面试官问起来就不是背书而是聊自己踩过的坑。6.3 配置类里值得放进亮点的小技巧这套系统源码里有几个配置类很多拿到源码的人会直接跳过没看但我建议你花十分钟去读一读。MyBatis的驼峰映射配置在 application.yml 里就一行map-underscore-to-camel-case: true。但有些人数据库里字段是 pet_name实体类是petName没有开这个配置就会导致查询结果全是null这是很常见的新手坑。分页查询使用的PageHelper插件在pom.xml里引入starter后需要在MyBatis配置里注册分页拦截器或通过starter自动配置使用使用时在查询前调用 PageHelper.startPage(pageNum, pageSize)多查一条统计总数。这个平时不显眼但在答辩时简单提一句“分页我用了拦截器实现它会自动改写原SQL”就能让人感受到你是懂原理的。7. 最后的几点心得如何把这套项目变出自己的味道在我实际做了几轮这种类型项目之后最大的感受是宠物领养系统的下限很容易达到但要做出有别于模板代码的质感功夫都在细节里。首先不要把心思花在华丽的页面原画上而是把每个表单的校验和反馈做扎实。我用Element UI之后发现用户进入领养申请页面时的填写引导、字段校验错误提示、提交成功后的反馈跳转这三样体验做好了就比很多系统的“能用”高一个档次。其次多设计几个“小异常”场景。比如宠物在被人申请的过程中另一个用户又来点击领养后端要怎么提示用户登录过期后停留在页面上操作点击提交时前端要如何跳转登录并保留原页面路由这些你想到了、做了处理代码自然就显得完善。最后一定要自己从零走一遍部署流程。源码能跑和能部署到服务器上是两码事你在部署过程中遇到的每个报错、每段Nginx配置调整都会成为答辩时最真实的素材。拿自己亲手踩过的坑去回答“项目遇到过什么问题吗”这个问题的答案质量会完全不一样。宠物领养系统的核心价值在于它覆盖了一个Web项目从后端业务建模、权限控制、文件处理到前端交互、部署上线的完整链路而且业务本身有人情味做的时候不至于太枯燥。希望这篇拆解能帮你把它做明白也做出自己的亮点。