☰
SpringBoot+Vue全栈宠物领养系统开发实战:从0到1完整指南
2026/10/17 11:38:12 网站建设 项目流程

宠物领养系统这类项目,在我接触过的全栈开发练手选题里,出现频率相当高。不管是课程设计、毕业设计,还是纯粹想系统梳理前后端分离开发流程的入门者,都会把"宠物领养"当成一个集业务完整度与技术覆盖度于一身的典型场景。它不像电商系统那样业务复杂,但也足够把用户注册登录、宠物信息管理、领养申请审批、后台数据维护这几条核心链路跑通;它也不像纯管理系统那样枯燥,涉及图片上传、状态流转、权限区分,正好能锻炼前后端分离项目里真正容易出问题的联动能力。

这套系统采用 SpringBoot + Vue + MyBatis + MySQL 的组合,属于目前 Java 全栈项目里非常主流、也特别适合学习的一套技术栈。SpringBoot 负责后端接口与业务逻辑,Vue 负责前端页面渲染与交互,MyBatis 作为持久层框架管理 SQL 映射,MySQL 存储所有业务数据。整篇文章我会把这个项目的完整设计思路、数据库建模、前后端核心代码、从零到一跑起来的全过程,以及我在实际开发与部署中踩过的坑,全部拆开讲清楚,照着一步步做,你就能把这个系统真正跑起来,并且理解每一行关键代码为什么这么写。

1. 项目核心需求与整体设计思路

1.1 宠物领养系统的业务闭环拆解

在做任何一个项目之前,先把业务链条理清楚,比急着写代码重要得多。宠物领养系统表面上看起来不复杂,但真正动手后你会发现,它涵盖了权限体系、文件上传、复杂状态流转和数据统计分析,每个点都需要单独设计。

站在用户视角,整个系统的核心流程是这样的:一个普通访客进入系统首页,可以浏览正在等待领养的宠物列表,点进详情页查看宠物的品种、年龄、健康状况、性格描述和图片。如果看中了某只宠物,需要先注册并登录账号,登录之后才能提交领养申请。提交申请时通常要填写申请理由、居住情况、养宠经验等信息,提交后这只宠物会进入"待审核"状态,不能再被别人重复申请。

站在管理员视角,流程就变成了:管理员登录后台,对用户提交的领养申请进行审核,审核通过后就进入"待交接"环节,线下完成宠物交接后,管理员再把状态更新为"已领养"。整个过程中管理员还要负责宠物信息的录入、编辑和下架,以及用户信息的管理和公告的发布。

这个闭环里最核心的一点是:领养申请与宠物状态之间必须做联动控制。一只宠物只要存在未完结的领养申请(待审核或待交接),就不能被其他人再次申请。这个逻辑如果只在页面上做限制,后端接口没加校验,别人绕过前端直接请求接口,就能重复提交,数据就乱了。这也是我在后端的领养申请接口里一定要做状态校验的原因。

1.2 为什么选 SpringBoot + Vue + MyBatis + MySQL 这套组合

技术选型这件事,我一直觉得要看项目目标是什么。如果目标是快速上线一个生产环境的小型系统,那可以选更轻量的方案;但如果目标是学习、毕设展示、或者作为简历上的项目经历,那这套组合几乎是当下最均衡的选择。

SpringBoot 的强大之处在于"约定大于配置"。以前用 Spring 写一个 Web 项目,光配置 XML 就要写一大堆,新手很容易卡在配置阶段。SpringBoot 通过自动配置把绝大多数繁琐的配置都省掉了,只需要在 pom.xml 里引入对应的 starter,框架会自动装配好内嵌的 Tomcat、数据源、事务管理等基础组件。这对教学和开发效率来说都是极大的帮助。

Vue 作为前端框架,核心优势是组件化和响应式数据绑定。宠物领养系统的页面数量不算多,但涉及数据列表、详情展示、表单提交、状态切换等多种交互,用 Vue 的组件化思路来组织代码,逻辑会清晰很多。配合 Vue Router 做前端路由,配合 Axios 做 HTTP 请求,一套标准的前后端分离前端工程就成型了。

MyBatis 是持久层框架里比较特殊的一个。它不像 JPA 那样完全屏蔽 SQL,而是把 SQL 写在 XML 映射文件或注解里,由开发者精确控制每一条语句。对于宠物领养系统这种需要写多表联查、动态查询、状态条件筛选的业务场景,MyBatis 的灵活性是实打实的优势。比如宠物列表页的筛选功能,可能需要根据品种、年龄区间、状态等多个可选条件拼接查询,MyBatis 的动态 SQL 就能很优雅地解决。

MySQL 的选择就更不需要多说了,稳定、开源、资料多,几乎所有 Java 项目的第一落点都是它。这套组合合在一起,恰好覆盖了一个后端开发者日常工作中最常用到的核心技术面。

1.3 技术栈之外的几个关键设计决策

技术栈确定了,还有一些设计决策会影响整个项目的开发体验和数据安全性,我在这里重点说三个。

第一个是登录认证方案。宠物领养系统里,普通用户和管理员是两类不同的身份,接口权限必须区分。我采用的方案是基于 JWT 的 Token 认证:用户登录成功后,后端签发一个包含用户 ID、角色信息的 Token,前端存在 localStorage 里,之后每次请求时在请求头带上 Token,后端通过拦截器统一校验。这样既天然适配前后端分离的架构,又不需要维护复杂的 Session 同步。

第二个是接口响应格式的统一。所有后端接口都返回统一结构体,包含状态码、消息提示和业务数据三部分。这样前端不管请求哪个接口,都能用同一套逻辑处理成功和失败的情况,代码会干净很多,排查问题也方便。后文我会给出这个统一响应类的具体写法。

第三个是文件存储方案。宠物图片上传是刚需,考虑到项目体量不大,我没有引入独立的对象存储服务,而是把图片上传到后端服务器本地的 uploads 目录,并通过一个映射路径对外提供访问。这样做的好处是部署简单、不依赖外部服务,适合学习和项目展示。当然后面也可以很容易地换成真正的对象存储。

2. 数据库设计与核心表结构

2.1 整体表关系梳理

数据库设计是整个项目的地基。地基没打好,后面写接口的时候就会到处别扭。这个系统我最终设计了五张核心表:用户表、宠物信息表、领养申请表、公告表、以及用于存储宠物图片的多张图片关联表。

表之间的关系其实比较直观:一个用户可以有多个领养申请记录;一只宠物可以对应多张预览图片;领养申请关联了用户和宠物。用 ER 模型画出来是一个很典型的"用户-宠物-申请"三方模型,中间表就是领养申请表,它同时持有了用户 ID 和宠物 ID 的外键。

在字段类型选择上,有几个地方需要特别留个心眼。比如宠物年龄,我建议大家不要只存整数"岁",而是存一个"年龄类型"字段,用来区分"月龄"和"岁龄",因为幼猫幼犬的年龄很多都在一岁以下,只用整数很容易丢失信息。再比如宠物状态字段,我用的是 tinyint 类型,分别约定:0 代表待领养、1 代表已预约(有未完结的申请)、2 代表已领养、3 代表已下架。状态值在前后端都要有对应的枚举说明,避免魔法数字满天飞。

2.2 核心表结构详解

先看用户表。这张表保存的字段包括用户名、密码、手机号、邮箱、角色、头像、注册时间等。密码不是明文存储,而是经过 MD5(建议实际项目用 BCrypt)加盐加密后再入库。角色字段用 1 和 2 区分普通用户与管理员,这个字段在 JWT 签发和接口拦截时都会用到。

然后是宠物信息表,这是业务的核心表。字段包括宠物名称、品种、类别(猫/狗/其他)、性别、年龄数值、年龄类型、体重、疫苗状态、绝育状态、健康描述、性格描述、所在城市、状态、创建时间和更新时间。其中疫苗状态和绝育状态都是布尔类型,在页面上用"已打疫苗 / 未打疫苗"这样的标签展示。为了让列表页的筛选功能更好做,品种、类别、城市、状态这几个字段都建议加上索引,它们是被高频查询的条件。

领养申请表承载的是业务流转逻辑。字段包括申请用户 ID、宠物 ID、申请理由、居住情况、养宠经验、联系地址、申请状态、申请时间、审核时间和审核备注。申请状态同样用数字约定:0 待审核、1 审核通过待交接、2 已领养、3 已拒绝。这里有一个容易被忽略的点:申请状态和宠物状态是两个维度,不能混为一谈。宠物状态是"全局唯一"的,而申请状态是针对某一条申请记录的。只有当前存在状态为 0 或 1 的申请时,宠物才被锁定为"已预约"。

公告表就相对简单了,字段包括标题、内容、创建时间,主要用于前台首页展示平台公告和领养须知。

图片关联表我用的是 pet_images 表,一张宠物对应多行图片记录,通过宠物 ID 和图片路径来关联。这样宠物详情页的轮播图、列表页的封面图都有数据来源。SQL 初始化脚本里,我会把测试数据一起写好,避免项目跑起来后页面上空荡荡的,什么效果都看不到。

2.3 几条关键 SQL 与 MyBatis 映射心得

数据库建好了,最考验功底的地方就落在 SQL 写法上。宠物领养系统的几个核心查询里,最简单的当然是按 ID 查宠物详情,真正有难度的是带筛选条件的宠物列表查询。

列表页的筛选条件是可选的:可能用户只选了城市,也可能同时选了品种和状态,还可能选了类别和年龄区间。这种"条件可有可无"的查询,最适合用 MyBatis 的动态 SQL 来处理。我推荐把这段逻辑写在 XML 映射文件里,而不是 Java 代码里用 if 拼接字符串,因为 XML 里的动态 SQL 可读性更高,后期维护也方便。写完动态 SQL 之后,有条件的多个 where 片段的拼接要特别注意 and 和 where 标签的搭配,MyBatis 等于符号的写法也有讲究。我习惯把所有比较操作符统一用 CDATA 包一层,避免小于号之类的特殊符号引起 XML 解析问题。

领养申请列表这个查询要关联三张表:申请记录、用户信息、宠物信息,因为列表页面要同时展示申请人的用户名和宠物的基本信息。这里我建议先分页查申请记录,再通过关联查询填充用户和宠物的信息,尽量避免使用嵌套子查询,性能和可读性都更好。

3. 后端核心功能实现

3.1 项目基础结构与启动配置

后端工程我用 Maven 做依赖管理,标准的 SpringBoot 项目结构。controller 层只负责接收请求、调用 service 层并返回结果,不写业务逻辑;service 层处理核心业务规则;mapper 层通过 MyBatis 与数据库交互。这种分层约束刚开始会觉得繁琐,但项目一大了就知道清爽的好处。

pom.xml 里的依赖选择比较常规,核心的几个是 spring-boot-starter-web、mybatis-spring-boot-starter、mysql-connector-java、lombok 和 JWT 相关的工具库。Lombok 推荐加上,可以用 @Data 注解省掉实体类里一堆 getter/setter,代码一下子精简很多。JWT 库我用的 jjwt 系列,体积小、文档全。

application.yml 配置文件里要关心的主要是端口号、数据库连接和文件上传相关配置。我这里梳理一下关键配置项的含义:数据源配置里的 URL 要把数据库名、时区、字符编码都写对,字符编码用 UTF-8 避免中文乱码;文件上传大小限制需要单独配置,默认 1MB 的上传限制在实际使用中很容易触顶,宠物图片通常都在 1-5MB 之间,可以放宽到 10MB。配置完之后不要忘了检查数据库连接账号是否有足够的权限。

3.2 用户注册登录与 JWT 认证

用户模块是系统的基础模块,核心接口有三个:注册、登录、获取当前登录用户信息。

注册接口的逻辑并不复杂,但有两个点必须处理好。第一是唯一性校验,注册前要查一次用户名和手机号是否已存在,不能等数据库抛唯一索引异常才处理,那样前端拿到的错误提示会很粗鲁。第二是密码加密,我要求所有密码经过加密后入库,用 MD5 加盐的方式。加盐的意思是截取用户名的一段字符拼接到密码后面再计算 MD5,这样即使用户密码简单,也不至于直接被反查出来。

登录接口的逻辑是:根据用户名查出用户记录,比对密码,比对成功后生成 JWT Token。Token 的 payload 里我只放了用户 ID、用户名和角色三个字段,不把密码之类的敏感信息放进去。生成 Token 时设置过期时间,我用的 24 小时,过期后前端再请求接口时会被拦截器统一拦截并提示重新登录。

拦截器是后端权限控制的关键。我写了一个 JwtInterceptor,在拦截逻辑里尝试从请求头取出 Token,校验签名与有效期,解析出用户信息后放进当前线程的上下文中。这里值得提醒一个细节:拦截器的注册顺序和拦截路径的白名单配置很关键。登录、注册、浏览宠物列表和详情这些接口必须放行,其他接口都要过校验。放行列表如果写错,会出现前端白屏或一直跳转登录的情况。另外我还区分了管理员的接口权限,普通用户的 Token 无法访问管理员接口,这个判断是在拦截器校验角色时完成的。

3.3 宠物信息管理与图片上传

宠物信息的管理逻辑集中在管理员一侧。新增宠物时,管理员填写基本信息,然后上传图片。图片上传这个功能,我单独写了一个 FileUploadController,接收 MultipartFile 类型的文件参数,校验格式和后缀,把文件保存到服务器的 uploads 目录。为了让图片隔离得干净,我是按日期文件夹存放的,比如 uploads/20240615/xxxxx.jpg。文件名我用时间戳加随机数重新生成,防止用户上传的原始文件名带来乱码和覆盖问题。

图片上传完成后,接口返回图片的相对访问路径,前端拿到路径拼接服务器地址后就能直接显示。这里有一个要特别注意的地方:后端要配置一个静态资源映射,把 uploads 目录映射成一个 URL 路径,否则浏览器无法直接访问上传的图片。很多同学项目跑起来后,宠物列表文字正常但图片全裂,90% 都是漏了这一步。

宠物上下架其实就是在更新状态字段。下架操作在页面上就是一个状态切换,但实际影响很大:下架后宠物不能再被申请领养,已有的待审核申请也要被自动标记为失效。这个联动我放在 service 层处理,一旦宠物状态改为下架,就同步把该宠物所有状态为 0 的申请更新为已拒绝,保证业务数据不会出现"宠物已下架但申请还在审核中"的矛盾状态。

3.4 领养申请与状态流转

领养申请是系统里业务规则最密集的地方,也是我在开发时格外谨慎的部分。

提交申请接口的正确流程应该是:从 Token 里取当前登录用户 ID,校验用户身份是普通用户;查询宠物信息,判断宠物状态是否为 0(待领养),如果不是就直接返回提示"该宠物已被预约或已领养";再查一下当前用户对这个宠物是否已存在待审核的申请,防止重复申请;校验都通过后,插入一条申请记录,同时把宠物状态更新为 1(已预约)。这两处数据变更必须放在同一个事务里,我用 @Transactional 注解声明事务,任何一个步骤失败都要整体回滚,绝对不能出现"申请记录插入成功但宠物状态没变"的情况。

审核接口是管理员侧的镜像逻辑。管理员通过后,申请状态更新为 1,宠物状态保持不变(还是已预约);等线下交接完成,管理员执行确认领养操作,申请状态更新为 2,宠物状态更新为 2(已领养)。如果是拒绝,申请状态更新为 3,同时宠物状态要立刻恢复为 0,以便其他人可以继续申请。这里的回滚点在于"拒绝时宠物状态恢复"这一步很容易遗忘,我的建议是把这种状态联动写成枚举加状态的统一转换方法,减少散落编码带来的漏改。

3.5 管理后台数据统计

管理后台除了基础的增删改查,我加了三个简单的统计接口:总用户数、待领养宠物数、待审核申请数。这三个数字对管理员快速了解平台运行情况非常有用。统计实现很简单,就是三个 count 查询,不需要复杂的聚合函数。但我把它们统一放在一个 StatisticController 里,前端后台首页直接调用,一次性展示,体验很好。这个设计成本极低,但会让整个系统看起来完整很多。

4. 前端核心功能实现

4.1 前端工程结构与页面规划

后端接口定了,前端就是把这些接口变成可操作的页面。我用的 Vue 2 配合 Vue CLI 创建工程,目录结构做了功能划分:views 目录放页面级组件,components 目录放可复用的子组件,router 目录配置路由,api 目录统一封装后端请求,utils 目录放工具函数。这样的划分在项目规模不大的情况下刚刚好,既不会过度设计,又留有扩展空间。

页面规划上,前台部分包括首页、宠物列表页、宠物详情页、登录注册页、个人中心页;后台部分包括登录页、工作台、用户管理页、宠物管理页、申请审核页、公告管理页。总页面数量十几个,每个页面的功能定位都很清晰。路由配置采用了懒加载方式,组件用 import 按需引入,这样首屏加载不会因为一次性加载所有页面而变慢。

4.2 Axios 封装与请求拦截

前端与后端交互的入口是 Axios,我强烈建议统一封装一个请求模块,不要每个页面直接调用 axios。封装的核心价值在于:统一设置请求头、统一处理 Token、统一拦截错误提示。

我的 request.js 里做了三件事。第一,从 localStorage 取出 Token,放到请求头的 Authorization 字段,这样每个接口都会自动携带认证信息。第二,响应拦截器里统一处理后端的统一响应结构,如果业务状态码是 200 就直接返回数据,否则弹出提示信息并拒绝 Promise。第三,单独处理两个特殊状态码:401 表示 Token 失效或未登录,这时清除本地存储的登录信息并跳转登录页;403 表示权限不足,直接提示无权限访问。前端把这三件套做好,后面的业务代码能省掉大量重复的判断逻辑。

4.3 核心页面组件开发

几个核心页面的实现思路值得详细说一下。

首页是最先做的页面,结构比较简单:顶部导航栏、轮播 Banner、宠物推荐列表、底部公告区域。宠物推荐列表直接调用后端接口取前几只待领养的宠物,展示封面图、昵称、品种、城市这几个关键信息。这个页面最重要的是视觉上要给人温暖的感觉,图片比例、卡片间距、文字颜色这些细节需要反复调整。

宠物列表页是系统里交互最复杂的页面,因为筛选条件较多。我在页面上方放了类别、品种、城市、状态四个筛选下拉框,筛选条件变化时就重新请求后端接口。搜索组件我用了防抖处理,避免用户连续切换条件时短时间内发出大量请求。列表本质上是复用同一个宠物卡片组件,所以我把卡片做成了独立的 PetCard 组件,首页和列表页共用。

宠物详情页的关键是展示完整信息和发起领养申请。图片区用轮播图展示多张图片,基本信息区用描述列表展示品种、性别、年龄、疫苗状态等字段,中间穿插宠物介绍文字。领养申请按钮根据登录状态和宠物状态做条件展示:未登录时点击跳转到登录页,已登录但宠物不可领养时按钮置灰并提示原因。申请弹窗里放一个申请理由的多行文本,提交后刷新页面状态。

个人中心页展示当前用户的资料和我的领养申请列表,用户可以查看自己提交的每一条申请记录的状态和审核进度。管理员端的宠物管理页、申请审核页则是对应后端管理接口的表格页面,表格操作列里有编辑、删除、审核按钮,状态通过标签颜色区分,一眼就能看明白当前处理阶段。

4.4 路由权限控制

前端路由的权限控制也是不可缺少的一环。虽然后端接口有拦截器兜底,但前端如果不做限制,普通用户还是能通过修改路由地址直接访问管理后台页面,虽然接口会拒绝,但体验很糟糕,路径也更冗长。

我在路由配置里给每个路由加了一个 meta 标记,标记页面所需的角色。然后在 Vue Router 的全局前置守卫里做校验:判断要跳转的页面是否需要特殊角色,如果需要就检查本地存储的用户信息是否具备对应角色,不具备就强制跳转到首页并提示无权限。这套前端路由守卫配合后端接口拦截,形成了双保险。

5. 本地部署与前后端联调完整流程

5.1 环境准备与版本选择

要把这个系统在本地完整跑起来,需要准备的环境包括 JDK 1.8 及以上、Maven 3.6 及以上、MySQL 5.7 或 8.0、Node.js 14 及以上。版本选择上我提醒一句:如果本地已经装了新版本的 JDK 和 Node,要注意兼容性,JDK 17 跑 SpringBoot 2.x 有时会有反射相关的兼容问题,Node 18 以上跑 Vue CLI 4 也可能有 OpenSSL 相关的报错。最稳妥的组合是 JDK 8 + SpringBoot 2.7 + Node 14 + Vue CLI 4,这个组合我已经验证过多次,非常稳定。

MySQL 的安装和账号创建我就不展开了,直接说关键:创建数据库时要指定 utf8mb4 字符集,排序规则用 utf8mb4_general_ci,这样中文显示不会出现乱码。然后导入 init.sql 脚本,脚本里会建表并插入测试数据。导入完成后可以用几条简单的查询确认表和数据都正常存在。

5.2 后端启动与连接配置

后端启动的核心在于把 application.yml 里的数据库连接信息改成你自己的实际配置。这里需要修改三个值:数据库地址、用户名、密码。很多人兴冲冲地跑起来,结果启动就报错,报错的 80% 原因是数据库连接超时或密码不对,还有 20% 是因为数据库名写错,连到了不存在的库。

配置改好后,在项目根目录执行 mvn spring-boot:run 或者直接运行启动类。看到 "Started Application" 日志出现就说明启动成功了。我不建议用 IDE 的 Run 按钮替代命令行,因为命令行能看到更完整的日志,排查问题时信息量更大。

验证后端接口是否正常,最直接的办法是用浏览器访问 Swagger 地址(如果集成了)或者直接请求一个测试接口。我习惯先请求宠物列表接口,如果返回 JSON 数据而不再是错误页面,就说明后端和数据库的连接已经通了。

5.3 前端启动与联调配置

前端工程在 vue.config.js 里配置了一个开发代理,这个配置相当关键。因为前端开发服务器默认跑在 8080 端口,后端接口跑在 8081 端口,如果前端直接请求后端的绝对地址,必然遇到跨域问题。我采取的方式是:前端所有请求都写成 axios 配置里的相对路径 /api,通过 devServer.proxy 将 /api 前缀代理到后端地址。这样开发环境不会跨域,也不需要后端开启跨域支持。

执行 npm install 安装依赖时要有点耐心,国内网络环境下如果速度太慢,可以把 npm 源切换为国内镜像源。依赖装完后执行 npm run serve,看到编译成功日志后在浏览器打开本地地址,就能看到系统首页了。此时的前端页面应该能调通后端接口,宠物列表、登录注册这些功能都能正常使用。

5.4 生产环境的打包部署

开发环境跑通后,如果要把系统展示给老师或同事看,就涉及打包部署。前端执行 npm run build 会生成 dist 目录,里面是纯静态文件,可以放到任意 Web 服务器下,或者更简单的方式是直接放到后端的静态资源目录里,由 SpringBoot 统一托管。后端打 jar 包用 mvn package,生成的 jar 包可以直接用 java -jar 命令启动。

把前端 dist 文件复制到后端的 static 目录后,访问后端端口就能直接打开整个系统,不需要再启动 Node 开发服务器,这是演示时最省事的方式。不过要注意,这种方式下前端请求的路径要和打包时配置的一致,否则会出现资源 404 或接口 404 的诡异问题。

6. 常见问题与排查技巧实录

6.1 高频报错速查表

项目开发与部署过程中,我遇到过的典型问题可以整理成一张速查表,按问题现象、原因、解决方案三列列出,非常实用。

问题现象常见原因解决方案
后端启动报数据库连接失败数据库地址、用户名、密码配置错误核对 application.yml 中的连接配置,确认数据库服务已启动
中文乱码数据库字符集不是 utf8mb4,或数据库连接 URL 缺少编码参数建库时指定 utf8mb4,连接 URL 添加 useUnicode=true&characterEncoding=utf8
前端图片全裂后端 static 资源映射未配置增加资源映射器配置,将 uploads 目录映射为可访问的 URL 路径
接口返回 401 但仍能访问页面前端路由守卫未校验权限在路由全局前置守卫中增加角色判断逻辑
跨域报错前后端端口不一致,且代理配置不正确开发环境配置 devServer.proxy;或后端统一开启 CORS
上传图片超过大小限制SpringBoot 默认文件上传上限过小配置 multipart 相关参数,增大 max-file-size
时间字段查询范围不准数据库时间与本地时区偏移连接 URL 配置 serverTimezone,并与前端约定时间格式

这张表里的问题大多是项目开发过程中一定会遇到的,我建议你把这张表保存下来,遇到对应问题直接对照排查。

6.2 几个印象深刻的实际问题

有一个问题我印象特别深,是宠物列表页的筛选功能。界面上一共四个筛选条件,我最初直接用了字符串拼接的方式写动态 SQL,结果其中有一个条件为空时,拼接出来的 SQL 语句里多了一个多余的 AND 关键字,接口直接报 SQL 语法错误。排查了很久才发现是某个筛选条件的空判断写漏了。换成 MyBatis 的 where 标签后,这个场景被框架自动处理得干干净净,我再也没有遇到过这类问题。所以我的建议是能交给框架处理的地方,就不要自己手拼 SQL。

还有一个问题是关于图片上传后无法立即显示。我上传图片成功后,数据库里存的是相对路径,前端拿到后直接访问,一直报 404。后来我注意到前端页面基础路径和后端资源映射前缀不一致,导致拼接出来的地址差了一层目录。解决思路是把路径拼接逻辑统一集中在后端的返回结构中,前端直接使用返回的完整访问路径,不自己拼接,排查时也容易定位。

6.3 部署前必做的几项安全检查

项目要演示或上线前,我会养成一个习惯,按固定清单做一轮自检。第一,检查数据库测试数据是否太随意,比如密码是否都是明文、图片路径是否真实存在,演示效果会直接受影响。第二,检查敏感配置文件有没有被提交到代码仓库,application.yml 里的数据库密码最好改成环境变量方式读取。第三,检查管理员账号的初始密码是否过于简单,演示前最好改为一个合理强度的密码。第四,检查上传目录是否有写入权限,很多部署环境因为目录权限问题导致图片上传失败,这一类问题在本地开发环境通常不会暴露,一换环境就现形。这套自检清单虽然简单,但每次都能帮我提前排除一些看似低级、实际很影响演示效果的问题。

7. 项目复盘与可扩展方向

7.1 实际开发中的几点体会

做完这个项目,我最大的体会是:真正花时间的不是写代码,而是梳理业务规则和数据之间的关系。宠物领养系统的功能模块看起来不多,但领养申请与宠物状态的联动、申请状态的流转、权限的分层,这些业务逻辑如果一开始不设计清楚,后面写代码时一定反复改。我把大部分设计时间花在画表结构和状态流转图上,事实证明这笔时间花得非常值。

另一个体会是前后端分离项目的调试方式。前后端分离后,接口是前后端之间的契约,这个契约必须清晰稳定。我在开发过程中会给每个接口写一份简短的接口说明,包括请求方式、路径、参数和返回结构,前端照着文档联调,误差会小很多。不要依赖口头沟通记接口,项目一小也可能遗忘,文档写出来的才是真正可以交付的。

还有一点是关于代码的可读性。我自己在写这个项目时,特意要求每个类只负责一件事,每个方法长度控制在一定范围内,复杂的逻辑加注释说明原因而不只是说明做了什么。代码是写给自己看的,也是写给审阅项目的人看的,整洁的代码本身就是在降低沟通成本。

7.2 后续可以怎么扩展

项目做完不是终点,后面的扩展方向其实很多。从功能角度,可以加入收藏功能,让用户能收藏心仪的宠物;增加站内消息通知,申请审核结果推送给用户;引入 Spring Security 做更完整的权限体系;把图片上传替换为对象存储服务,让系统更接近生产环境。从技术角度,可以把前端升级到 Vue 3 加 Vite,后端引入 Redis 做缓存和验证码存储,再把代码拆分成更细的微服务模块。不过我对扩展的建议始终是保持克制,先跑通主链路、保证代码质量,再按需求逐步扩展,不要为了追求技术栈的丰富而把项目越做越复杂。我自己在后续迭代这个项目时,会优先补上接口压测和代码测试相关的部分,这些对工程能力的提升其实比多加一个功能更有价值。

这篇文章里涉及的核心代码和完整部署步骤,如果你在复现过程中遇到问题,欢迎随时交流。我个人在踩过这么多坑之后最大的感受是:这类全栈项目,真正让你成长的不是最终跑通的那一刻,而是过程中反复定位问题、补全认知的每一个瞬间。

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

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

立即咨询