温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片!
1. 项目背景与意义
随着城市养宠人群的快速增长,流浪动物数量也在持续增加。传统的宠物领养信息多依赖线下公告栏、社交群组或口头传播,存在信息分散、更新不及时、领养流程不透明等问题。搭建一个基于 Spring Boot 的宠物领养管理系统,可以将宠物信息发布、领养申请、审核管理、用户互动等环节集中到线上平台,帮助救助机构与潜在领养人高效对接,提升领养成功率,同时让领养流程更加规范、可追溯。
从技术学习角度看,该项目覆盖了 Spring Boot 后端开发、MyBatis 数据持久化、前端页面交互、文件上传、权限控制等常见企业级开发技能,非常适合作为毕业设计或个人项目练手。
2. 系统需求分析
2.1 用户角色
- 普通用户(领养人):浏览宠物信息、提交领养申请、查看申请进度、收藏宠物、发布送养信息。
- 管理员:审核宠物发布信息、审核领养申请、管理用户账号、管理公告与分类。
2.2 核心功能模块
- 宠物信息管理:宠物种类、年龄、性别、健康状况、照片、送养人联系方式等信息的发布与维护。
- 领养申请管理:用户提交领养意向,管理员审核并更新申请状态(待审核、已通过、已拒绝)。
- 用户管理:注册、登录、个人信息维护、密码修改。
- 公告管理:管理员发布平台公告与领养须知。
- 数据统计:统计宠物数量、领养成功率、每日新增用户等。
3. 技术栈选型
| 层次 | 技术选型 | 说明 |
|---|---|---|
| 后端框架 | Spring Boot 2.7.x | 快速构建独立运行的 Web 应用,简化配置 |
| 持久层框架 | MyBatis-Plus | 简化 CRUD 操作,内置分页插件 |
| 数据库 | MySQL 8.0 | 存储用户、宠物、申请等业务数据 |
| 安全认证 | Spring Security + JWT | 实现登录认证与接口权限控制 |
| 前端技术 | Thymeleaf + Bootstrap 5 | 服务端渲染页面,配合 Bootstrap 快速搭建界面 |
| 文件存储 | 本地文件上传 / 阿里云 OSS | 保存宠物照片等图片资源 |
| 构建工具 | Maven | 依赖管理与项目构建 |
4. 数据库设计
系统核心数据表包括用户表、宠物信息表、领养申请表、公告表。下面给出主要表结构。
4.1 用户表 t_user
CREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID', username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名', password VARCHAR(100) NOT NULL COMMENT '密码(BCrypt加密)', nickname VARCHAR(50) COMMENT '昵称', phone VARCHAR(20) COMMENT '手机号', role TINYINT DEFAULT 0 COMMENT '角色:0-普通用户,1-管理员', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) COMMENT '用户表';4.2 宠物信息表 t_pet
CREATE TABLE t_pet ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '宠物ID', name VARCHAR(50) NOT NULL COMMENT '宠物名称', species VARCHAR(20) COMMENT '种类:猫/狗/其他', breed VARCHAR(50) COMMENT '品种', age INT COMMENT '年龄(月)', gender TINYINT COMMENT '性别:0-公,1-母', health_status VARCHAR(200) COMMENT '健康状况描述', photo_url VARCHAR(255) COMMENT '照片地址', status TINYINT DEFAULT 0 COMMENT '状态:0-待领养,1-已领养', publisher_id BIGINT COMMENT '发布人ID', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '发布时间' ) COMMENT '宠物信息表';4.3 领养申请表 t_adoption
CREATE TABLE t_adoption ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '申请ID', pet_id BIGINT NOT NULL COMMENT '宠物ID', user_id BIGINT NOT NULL COMMENT '申请人ID', reason VARCHAR(500) COMMENT '领养理由', status TINYINT DEFAULT 0 COMMENT '状态:0-待审核,1-通过,2-拒绝', apply_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '申请时间' ) COMMENT '领养申请表';5. 项目结构设计
pet-adoption-system/ ├── src/main/java/com/example/petadoption/ │ ├── controller/ # 控制层 │ │ ├── PetController.java │ │ ├── AdoptionController.java │ │ └── UserController.java │ ├── service/ # 业务层 │ │ ├── PetService.java │ │ └── AdoptionService.java │ ├── mapper/ # 数据访问层 │ │ ├── PetMapper.java │ │ └── AdoptionMapper.java │ ├── entity/ # 实体类 │ │ ├── Pet.java │ │ └── Adoption.java │ ├── config/ # 配置类 │ │ └── SecurityConfig.java │ └── PetAdoptionApplication.java ├── src/main/resources/ │ ├── mapper/ # MyBatis XML 映射文件 │ ├── static/ # 静态资源 │ └── application.yml └── pom.xml6. 核心代码实现
6.1 启动类
package com.example.petadoption; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("com.example.petadoption.mapper") public class PetAdoptionApplication { public static void main(String[] args) { SpringApplication.run(PetAdoptionApplication.class, args); } }6.2 宠物实体类
package com.example.petadoption.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.time.LocalDateTime; @Data @TableName("t_pet") public class Pet { @TableId(type = IdType.AUTO) private Long id; private String name; private String species; private String breed; private Integer age; private Integer gender; private String healthStatus; private String photoUrl; private Integer status; private Long publisherId; private LocalDateTime createTime; }6.3 宠物 Mapper 接口
package com.example.petadoption.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.petadoption.entity.Pet; import org.apache.ibatis.annotations.Mapper; @Mapper public interface PetMapper extends BaseMapper<Pet> { }6.4 宠物业务层
package com.example.petadoption.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.example.petadoption.entity.Pet; import com.example.petadoption.mapper.PetMapper; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class PetService { @Resource private PetMapper petMapper; /** 分页查询宠物列表 */ public Page<Pet> pagePets(int pageNum, int pageSize, String keyword) { Page<Pet> page = new Page<>(pageNum, pageSize); LambdaQueryWrapper<Pet> wrapper = new LambdaQueryWrapper<>(); if (keyword != null && !keyword.isEmpty()) { wrapper.like(Pet::getName, keyword).or().like(Pet::getBreed, keyword); } wrapper.orderByDesc(Pet::getCreateTime); return petMapper.selectPage(page, wrapper); } /** 发布宠物信息 */ public boolean publishPet(Pet pet) { pet.setStatus(0); return petMapper.insert(pet) > 0; } /** 更新宠物信息 */ public boolean updatePet(Pet pet) { return petMapper.updateById(pet) > 0; } /** 删除宠物信息 */ public boolean deletePet(Long id) { return petMapper.deleteById(id) > 0; } }6.5 宠物控制层
package com.example.petadoption.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.example.petadoption.entity.Pet; import com.example.petadoption.service.PetService; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @RestController @RequestMapping("/api/pet") public class PetController { @Resource private PetService petService; @GetMapping("/page") public Page<Pet> page(@RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "10") int pageSize, @RequestParam(required = false) String keyword) { return petService.pagePets(pageNum, pageSize, keyword); } @PostMapping("/publish") public String publish(@RequestBody Pet pet) { return petService.publishPet(pet) ? "发布成功" : "发布失败"; } @PutMapping("/update") public String update(@RequestBody Pet pet) { return petService.updatePet(pet) ? "更新成功" : "更新失败"; } @DeleteMapping("/delete/{id}") public String delete(@PathVariable Long id) { return petService.deletePet(id) ? "删除成功" : "删除失败"; } }6.6 领养申请业务层
package com.example.petadoption.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.example.petadoption.entity.Adoption; import com.example.petadoption.entity.Pet; import com.example.petadoption.mapper.AdoptionMapper; import com.example.petadoption.mapper.PetMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; @Service public class AdoptionService { @Resource private AdoptionMapper adoptionMapper; @Resource private PetMapper petMapper; /** 提交领养申请 */ public boolean apply(Adoption adoption) { // 校验宠物是否已被领养 Pet pet = petMapper.selectById(adoption.getPetId()); if (pet == null || pet.getStatus() == 1) { return false; } adoption.setStatus(0); return adoptionMapper.insert(adoption) > 0; } /** 审核领养申请 */ @Transactional(rollbackFor = Exception.class) public boolean review(Long adoptionId, Integer status) { Adoption adoption = adoptionMapper.selectById(adoptionId); if (adoption == null) { return false; } adoption.setStatus(status); int rows = adoptionMapper.updateById(adoption); // 审核通过后,将宠物标记为已领养 if (rows > 0 && status == 1) { Pet pet = new Pet(); pet.setId(adoption.getPetId()); pet.setStatus(1); petMapper.updateById(pet); } return rows > 0; } /** 查询用户的历史申请记录 */ public java.util.List<Adoption> listByUser(Long userId) { LambdaQueryWrapper<Adoption> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Adoption::getUserId, userId).orderByDesc(Adoption::getApplyTime); return adoptionMapper.selectList(wrapper); } }6.7 文件上传工具类
package com.example.petadoption.util; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.util.UUID; public class FileUploadUtil { /** * 保存上传的图片,返回可访问的 URL 路径 */ public static String saveImage(MultipartFile file, String uploadDir) throws IOException { if (file == null || file.isEmpty()) { return null; } // 生成唯一文件名,避免重名覆盖 String originalName = file.getOriginalFilename(); String ext = originalName != null && originalName.contains(".") ? originalName.substring(originalName.lastIndexOf(".")) : ".jpg"; String fileName = UUID.randomUUID().toString().replace("-", "") + ext; File dir = new File(uploadDir); if (!dir.exists()) { dir.mkdirs(); } file.transferTo(new File(dir, fileName)); return "/upload/" + fileName; } }7. 系统测试与运行效果
系统开发完成后,可通过以下步骤在本地运行验证:
- 创建数据库 pet_adoption,执行上述建表 SQL。
- 在 application.yml 中配置数据库连接信息。
- 启动 PetAdoptionApplication 主类。
- 访问 http://localhost:8080 查看系统首页,注册账号后即可浏览宠物并提交领养申请。
测试重点包括:宠物信息发布与图片上传是否正常、领养申请流程是否完整、管理员审核后宠物状态是否正确更新、不同角色的权限控制是否生效。
8. 总结与展望
本文设计并实现了一个基于 Spring Boot 的宠物领养管理系统,覆盖了宠物信息管理、领养申请审核、用户管理等核心业务,技术栈选型主流、结构清晰,具备较好的可扩展性。后续可以在此基础上增加在线聊天沟通、领养回访记录、地图定位附近宠物店等功能,进一步提升系统的实用性和用户体验。