ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Vue流浪宠物管理系统全栈开发实战与部署详解

SpringBoot+Vue流浪宠物管理系统全栈开发实战与部署详解 这标题看着眼熟吧“基于SpringBootVue的web流浪宠物管理系统设计与实现”虽然项目名里的“管理系统”重复了一遍但这类源码在毕业设计和实际小项目里出现频率极高。说白了一个流浪宠物管理系统就是把线下救助站里的宠物登记、领养申请、审核状态、公告发布这些事搬到Web上让普通用户能浏览待领养宠物、在线提交申请管理员能在后台审核、维护宠物信息。适合拿来学思路的同学、准备毕设的人以及刚接触前后端分离想找一份能跑通的项目做参考的开发者。这类系统最容易被误解成“CRUD堆砌”其实它的核心难点不在增删改查而在角色权限的划分、领养流程的状态管理、图片文件的上传访问以及前后端联调时的一堆隐形坑。接下来我按自己做这类项目的经验从需求拆解、数据库设计、前后端实现到部署排查把关键点挨个讲清楚。1. 项目整体设计与需求拆解1.1 做这类系统的第一件事先搞清楚“谁在用”很多同学拿到题目上来就建表写接口这是最容易翻车的。流浪宠物管理系统表面上只有一个“宠物管理”但实际使用场景至少有三类角色普通访客或领养申请人、管理员、以及可能有志愿者。访客希望看到宠物照片、健康状态提交领养申请管理员要负责宠物信息录入、审核申请、发布公告如果需要志愿者还可以登记暂时寄养信息。我见过不少源码把角色只做成“用户/管理员”两种这对单体项目够用。权限控制上管理员可以进入后台管理页面普通用户只能在前台操作。如果你要扩展可以加一个“审核员”角色但MVP阶段两个角色就足够。1.2 功能模块怎么切分一个完整的流浪宠物管理系统按用户端和管理端拆分大致是这样的模块用户端功能管理端功能宠物信息浏览列表、查看详情、按品种/状态筛选添加、编辑、下架宠物信息领养申请提交申请、查看申请进度审核通过/拒绝标记宠物已领养用户账号注册、登录、修改个人信息查看注册用户列表公告模块查看公告发布、删除公告基础数据收藏宠物统计领养数据这个切分方式的好处是每个模块围绕一条业务线走接口职责清晰前端页面也能一一对应。不用一开始就做权限到按钮级别的精细控制先把页面级权限做好再考虑更细的粒度。1.3 为什么是 SpringBoot Vue MyBatis这个技术组合在中小型项目里几乎是标准答案。SpringBoot 解决了 Spring 配置繁琐的问题内嵌 Tomcat 让应用能直接打包运行Vue 做前端页面比 JSPJQuery 舒服太多数据驱动视图列表渲染和表单双向绑定都很顺手MyBatis 则是半自动 ORMSQL 由自己掌控对喜欢写原生 SQL 的开发者特别友好遇到需要联表查询、动态条件过滤的情况也更灵活。如果拿 MyBatis 和 MyBatis-Plus 比Plus 确实能省掉大量单表 CRUD但很多老源码还是用原生 MyBatis XML因为它的动态SQL对复杂查询更透明。你想理解Mapper接口和XML是怎么绑定的读这种源码收获更大。2. 数据库设计与核心业务建模2.1 先画实体关系数据库设计决定后面代码好不好写。我习惯先从核心实体入手用户、宠物、领养申请、公告、收藏。用户和宠物之间通过“领养申请”关联一个用户可以有多个申请一个宠物如果被领养成功就不能再被其他人申请所以要在宠物表上维护status字段并且通过唯一业务约束防止重复申请。宠物和公告、收藏之间相对独立不需要刻意加外键逻辑上的关联用user_id、pet_id在业务层维护即可。实际项目里我基本不用物理外键避免插入和删除时的约束麻烦但逻辑外键必须保证索引。2.2 用户表和宠物表设计用户表要存账号、密码、手机号、角色。密码一定要存加密后的结果不要明文。宠物表核心字段包括宠物名称、类型、性别、年龄、是否绝育、是否打过疫苗、健康状态、描述、封面图、状态。下面是简化后的宠物表 SQL可以直接参考CREATE TABLE pet ( id int NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 宠物名称, type tinyint NOT NULL DEFAULT 0 COMMENT 类型0猫 1狗, gender tinyint DEFAULT NULL COMMENT 性别0公 1母, age varchar(20) DEFAULT NULL COMMENT 年龄描述, vaccine tinyint DEFAULT 0 COMMENT 是否已打疫苗, sterilized tinyint DEFAULT 0 COMMENT 是否已绝育, health varchar(255) DEFAULT NULL COMMENT 健康情况, description text COMMENT 详细介绍, cover_image varchar(255) DEFAULT NULL COMMENT 封面图URL, status tinyint NOT NULL DEFAULT 0 COMMENT 状态0待领养 1已领养 2暂停领养, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT流浪宠物表;注意几点表名和字段名尽量用有意义的英文status加索引是因为列表页经常按状态筛选age用 varchar 而不是 int因为实际场景下可能填“3个月”或“2岁左右”用字符串更灵活。图片路径存相对路径/upload/xxx.jpg不要存整个 http 地址否则换域名和迁移服务器时会很痛苦。2.3 领养申请与状态流转领养申请是整个系统里最容易出错的地方。我见过最典型的错误是用户反复提交申请或者宠物已经被领养了还能继续申请最终导致数据混乱。要解决这个问题需要一张申请表并在业务上做两套状态。申请表的字段除了用户和宠物外还要有申请理由、联系电话、申请状态、审核备注。状态通常定义成0待审核、1审核通过、2已拒绝。宠物状态单独维护0待领养、1已领养。业务规则是同一只宠物同一时间最多只有一条“待审核”或“通过”状态的记录申请通过后宠物状态同步改为已领养。可以在表结构里加一个联合唯一索引或者靠代码先查再插。更稳妥的方式是后者先查询该宠物是否存在状态为0或1的申请存在就提示不可重复申请。这样既避免了索引对状态组合的限制也方便给前端返回具体提示信息。3. 后端实现SpringBoot MyBatis 的核心逻辑3.1 项目结构和依赖一份比较规范的 SpringBoot 项目目录通常是 controller、service、mapper、entity、config、common、utils。入口类放在 root 包下面。依赖方面核心只需要dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdcom.auth0/groupId artifactIdjava-jwt/artifactId version4.4.0/version /dependency很多源码会再加 Lombok 减少 getter/setter。如果你为了保险不想用 Lombok也可以手动补齐不影响整体结构。3.2 统一返回体与异常处理前后端分离时最怕接口返回格式不统一前端拿到数据还要猜结构。我习惯定义一个通用返回类统一包含 code、message、data状态码约定200 成功400 业务错误401 未登录500 系统异常。Controller 里不要直接把 Map 或裸对象丢给前端统一包一层。异常处理建议用RestControllerAdvice把参数校验异常、业务异常、全局异常分别处理这样前端拿到的错误信息才是人能读懂的。比如用户不存在时抛一个BusinessException(用户名或密码错误)然后在全局异常处理器里返回 code400避免直接把堆栈打到前端。3.3 登录与 JWT 鉴权登录接口是用户模块的核心。流程是用户提交账号密码后端根据用户名查出用户用BCrypt或其他加密工具校验密码成功后生成 JWT返回给前端。前端后续请求在 Header 里带Authorization: Bearer token。JWT 的好处是服务端无状态不用存 session适合前后端分离。实现时我在 config 里定义一个拦截器拦截除登录、注册之外的接口从请求头解析 token如果解析失败返回 401成功则把用户 id 放入ThreadLocal或 request attribute后面接口直接取当前用户信息。这里有个容易被忽略的坑JWT 的 secret 不要太简单token 过期时间建议设成几小时前端拦截 401 后跳转登录页。如果做的是毕业设计不要为了“看起来简单”就不做拦截器没有鉴权的接口是不完整的。3.4 宠物列表的动态 SQL宠物列表查询是 MyBatis XML 里最典型的动态 SQL 场景因为前端可能有多个筛选条件比如按类型、按状态、按关键词。用where配合if可以拼接出合法 SQL而且能避免“where 后面直接跟 and”的语法错误。Mapper XML 大致如下select idselectPetList resultTypecom.example.straypet.entity.Pet select * from pet where if testtype ! null and type #{type} /if if teststatus ! null and status #{status} /if if testkeyword ! null and keyword ! and (name like concat(%, #{keyword}, %) or description like concat(%, #{keyword}, %)) /if /where order by create_time desc /select注意like的写法concat(%, #{keyword}, %)是最稳妥的不要写成%${keyword}%否则会触发 SQL 注入。#{}是预编译占位符${}是字符串拼接后者要非常谨慎。3.5 图片上传和访问宠物照片上传是刚需。本地开发我一般把图片存到磁盘目录然后通过 SpringBoot 配置虚拟路径映射来访问。在application.yml配置一个自定义路径比如upload.dir: ./upload/然后写一个配置类Configuration public class WebConfig implements WebMvcConfigurer { Value(${upload.dir}) private String uploadDir; Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file: uploadDir); } }上传接口用MultipartFile接收文件文件名用 UUID 重新生成避免中文名和重复名导致的问题。前端在el-upload里把 action 指向这个接口成功后把返回的/upload/xxx.jpg存入表单字段。生产环境可以考虑用 OSS但本地项目这个方案最省事也最容易讲清楚。4. 前端实现Vue 页面与交互4.1 目录和基础配置Vue 项目我习惯用 Vue CLI 创建使用 Vue 2 Element UI原因是组件生态成熟、学习资料多符合多数完整源码的实际搭配。项目内部结构src/api接口模块一个页面一个文件src/router路由配置src/storeVuex 状态管理src/views页面组件src/components公共组件基础配置里最关键是vue.config.js。开发环境要配置代理把/api开头的请求转发到后端http://localhost:8080解决跨域问题。示例devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } }这里路径重写要特别小心如果后端接口本来没有/api前缀就必须 rewrite否则请求会 404。这个坑我在前两台电脑上都踩过。4.2 路由守卫和状态管理路由配置要根据角色区分页面。普通用户访问/admin下的页面应该被拦截。Vue Router 的全局前置守卫是最合适的处理位置router.beforeEach((to, from, next) { const token store.state.token; if (to.meta.requireAuth !token) { next(/login); } else if (to.path.startsWith(/admin) store.state.role ! 0) { next(/403); } else { next(); } });Vuex 里只保存 token、用户信息、角色等少量数据页面刷新后从localStorage恢复。不要把所有接口数据都塞到 Vuex 里否则状态会变得很难维护。4.3 Axios 封装与接口对接使用 Axios 时最好封装一个统一请求模块。在src/api/request.js里创建实例设置超时时间、baseURL然后通过拦截器统一注入 token、处理响应状态码。响应拦截器里有几个常见分支code 是 200 时直接返回 data401 时清除本地登录状态并跳转登录页其他业务错误用Message.error提示。这样每个页面只关心业务数据不用重复处理错误逻辑。实际对接时我习惯先在浏览器 Network 里看请求是否发出、返回值是什么再去看前端代码。很多联调问题都是字段名不一致导致的比如后端返回coverImage前端写成了cover_img查看接口返回数据一眼就能定位。4.4 领养页面的交互细节领养申请页面看似简单但要做好状态控制。宠物详情页要显示“待领养”还是“已领养”如果是待领养才显示“申请领养”按钮用户点击后弹窗填写申请理由和联系电话提交后按钮置灰并显示“待审核中”防止重复提交。前端还要处理审核结果的展示。用户可以在“我的申请”页面查看每条申请的状态通过或拒绝时显示审核备注。这里我建议后端在列表接口里直接返回状态描述文本前端只需要渲染不用在每个页面重复写 if-else。另外宠物卡片图片的加载失败场景要处理给img加一个onerror默认图否则一裂就非常难看。这个小细节很加分。5. 从源码到本地跑起来部署与运行5.1 环境准备清单想把这个项目完整跑在本地需要先准备好这些环境工具建议版本说明JDK1.8 或 11大多数源码基于 1.8Maven3.6后端依赖管理MySQL5.7 或 8.0注意字符集设为 utf8mb4Node.js14 或 16Vue CLI 对版本比较挑剔IDEIDEA / VSCode后端推荐 IDEA不建议一上来就装最新的 JDK 21、Node 20很多老项目依赖没跟上会出现各种兼容性报错白白浪费时间。5.2 数据库初始化和后端配置拿到源码后先在 MySQL 里创建数据库导入项目提供的stray_pet.sql。导入完成后打开application.yml把数据库账号密码改成本机的确保 URL 后缀里带characterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai否则容易出现中文乱码和时区问题。接着在 IDEA 里以 Maven 项目方式导入后端等待依赖下载完毕。如果网速慢可以配置阿里云镜像。启动前需要检查target目录或编译是否正常直接用mvn spring-boot:run也可以。启动后访问http://localhost:8080能看到接口说明或 404 都不重要关键是控制台没有报错。5.3 启动前端以及联调前端进入vue-web目录执行npm install安装依赖。如果node_modules已经存在建议删掉重新安装避免残留依赖导致的诡异报错。然后npm run dev启动后访问http://localhost:8081。如果后端接口地址和代理配置不一致需要统一调整。最常见的情况是前端请求路径和后端 Controller 的 RequestMapping 不一致比如后端是/pet/list前端代理路径写成/api/pet/list通过 pathRewrite 就能解决。联调通过后再做页面功能测试表单提交、图片上传、审核流程一个都不能漏。5.4 打成单 JAR 包的小技巧前端开发完毕可以npm run build生成dist目录。很多毕设要求交付一个能直接运行的包最简单的做法是把dist里的index.html和static目录放到后端src/main/resources/static下然后重新打包后端。这样启动后端后直接访问http://localhost:8080就是完整系统了不用再单独启动前端服务。需要注意Vue 路由如果用了 history 模式刷新二级页面会 404解决办法是改用 hash 模式也就是路由里mode: hash。这是单包部署最常见的坑提前改好省得后面手忙脚乱。6. 实操中常踩的坑和排查思路6.1 接口报 404 但前端路由正常这种情况多半是代理没配对或者 Controller 路径没对上。先看浏览器 Network 里的请求 URL如果停留在localhost:8081/api/pet/list而后端实际接口是localhost:8080/pet/list那就是 pathRewrite 忘了写。直接修改vue.config.js然后重启 devServer 即可。还有一种是后端项目里新增的接口模块没有正确扫描到。解决办法是检查启动类的MapperScan注解路径有没有覆盖 Mapper 接口包以及是否漏掉RestController或RequestMapping。6.2 查出来的字段全是 null这个问题八成是驼峰和下划线命名没对上。数据库字段是create_timeJava 属性是createTime。MyBatis 默认关闭驼峰映射需要在application.yml中开启mybatis: configuration: map-underscore-to-camel-case: true如果还是 null就检查 resultType 对应的实体类字段名、getter/setter、以及 XML 里resultMap是否显式映射。另外用select *时字段顺序不影响但名称不一致一定出错。6.3 图片传上去显示不出来图片上传成功后返回/upload/2024/03/xxx.jpg前端能拿到这个字符串但浏览器打开图片 404。这时要看后端有没有配置资源映射。很多源码把图片存在E:/upload/你在 Linux 上跑自然访问不到所以要检查upload.dir路径和addResourceLocations是否匹配。还要注意路径拼接问题我之前遇到过反斜杠和正斜杠混用在 Windows 上没问题部署到服务器就挂统一用File.separator或者强制处理成/最稳妥。6.4 数据库连接或登录报错数据库连接报错最常见的是驱动版本和 MySQL 版本不匹配。MySQL 8 需要com.mysql.cj.jdbc.DriverMySQL 5.7 用com.mysql.jdbc.Driver同时 URL 里要带serverTimezone。如果登录接口报密码错误先不要怀疑代码直接在本地用客户端连一下数据库确认账号密码没问题再检查密码字段长度是否够存 BCrypt 加密结果。登录成功后前端如果请求别的接口还是 401看看拦截器是否把登录接口也拦截了或者 token 解析失败。把拦截器配置打印到日志里一眼就能找出问题。做完整套项目我最深的体会是这类管理系统真正值钱的不是代码有多花哨而是业务流程能不能闭环。比如领养申请从提交、审核到宠物状态的同步更新前端按钮状态和后端数据状态是否保持一致这些细节比结构本身更容易决定系统好不好用。你在改这套源码时可以优先把领养状态机和图片上传这两块吃透其他模块基本就是参照这个套路顺手推过去。之后再考虑加入 Redis 缓存热门宠物列表、用定时任务把长期未审核的申请提醒管理员都是很自然的扩展方向。
返回列表