这个基于SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0的宠物领养系统,是我在2022年为一个动物保护机构开发的实际项目。系统上线后日均处理300+领养申请,显著提升了机构的工作效率。不同于简单的CRUD示例,这个项目完整实现了宠物展示、在线申请、审核流程、领养人管理等业务闭环。
技术栈选择上,前端采用Vue3组合式API+Pinia状态管理,后端使用SpringBoot2.7.x配合MyBatis-Plus动态SQL,数据库选用MySQL8.0利用其JSON字段特性存储宠物特征。整套系统采用RESTful风格接口设计,前后端完全分离。
提示:系统源码包含完整的权限控制模块,支持管理员、审核员、普通用户三级角色,文档中详细说明了如何根据实际需求调整权限颗粒度。
SpringBoot2.7.3作为基础框架,主要配置项包括:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/pet_adoption?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 加密处理
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
MyBatis-Plus 3.5.2的亮点应用:
java复制// 典型Mapper接口示例
public interface PetMapper extends BaseMapper<Pet> {
@Select("SELECT * FROM pet WHERE status = #{status}")
List<Pet> selectByStatus(@Param("status") Integer status);
}
Vue3组合式API的典型应用场景:
javascript复制// 宠物列表页逻辑
const { proxy } = getCurrentInstance()
const state = reactive({
petList: [],
loading: true
})
onMounted(async () => {
const res = await proxy.$api.getPetList()
state.petList = res.data
state.loading = false
})
Pinia状态管理核心配置:
javascript复制// stores/user.js
export const useUserStore = defineStore('user', {
state: () => ({
token: localStorage.getItem('token') || '',
userInfo: null
}),
actions: {
async login(params) {
const res = await api.login(params)
this.token = res.token
localStorage.setItem('token', res.token)
}
}
})
主要表结构设计:
sql复制CREATE TABLE `pet` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
`category` enum('DOG','CAT','OTHER') COLLATE utf8mb4_general_ci NOT NULL,
`attributes` json DEFAULT NULL COMMENT '特征JSON',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '0-待领养 1-审核中 2-已领养',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_category_status` (`category`,`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
JSON字段查询优化:
sql复制-- 查询所有具有"vaccinated"特征的宠物
SELECT * FROM pet
WHERE JSON_CONTAINS(attributes->'$.health', '{"vaccinated": true}')
索引策略:
缓存方案:
连接池配置:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
状态机设计:
java复制public enum AdoptionStatus {
PENDING(0, "待审核"),
INTERVIEWING(1, "面试中"),
APPROVED(2, "已通过"),
REJECTED(3, "已拒绝"),
COMPLETED(4, "已完成");
// 省略构造函数和get方法
}
审核逻辑示例:
java复制@Transactional
public void processApplication(Long applicationId, Boolean approved, String comments) {
AdoptionApplication application = applicationMapper.selectById(applicationId);
if (application.getStatus() != 0) {
throw new BusinessException("申请已处理");
}
application.setStatus(approved ? 1 : 3);
application.setAuditTime(LocalDateTime.now());
application.setAuditor(SecurityUtils.getCurrentUserId());
application.setAuditComment(comments);
applicationMapper.updateById(application);
sendNotification(application.getUserId(), approved ? "进入面试" : "未通过初审");
}
多存储方案支持:
java复制public interface StorageService {
String upload(InputStream inputStream, String contentType, String filename);
}
@Service
@ConditionalOnProperty(prefix = "storage", name = "type", havingValue = "local")
public class LocalStorageService implements StorageService {
@Value("${storage.local.path}")
private String basePath;
@Override
public String upload(InputStream inputStream, String contentType, String filename) {
String relativePath = DateUtils.format(new Date(), "yyyy/MM/dd") + "/" + UUID.randomUUID() + getExtension(filename);
File dest = new File(basePath + "/" + relativePath);
dest.getParentFile().mkdirs();
FileUtils.copyInputStreamToFile(inputStream, dest);
return "/uploads/" + relativePath;
}
}
Docker Compose配置示例:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: pet@1234
MYSQL_DATABASE: pet_adoption
ports:
- "3306:3306"
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/conf:/etc/mysql/conf.d
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/pet_adoption
SpringBoot Actuator配置:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
metrics:
enabled: true
ELK日志收集方案:
java复制@Configuration
public class LogbackConfig {
@Bean
public LoggerContext loggerContext() {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
context.reset();
try {
configurator.doConfigure(getClass().getResourceAsStream("/logback-spring.xml"));
} catch (Exception e) {
e.printStackTrace();
}
return context;
}
}
完整文档包含以下核心部分:
重要:文档中特别标注了需要根据实际部署环境修改的配置项,包括:
- 数据库连接参数
- 文件存储路径
- 短信/邮件服务配置
- 第三方API密钥
移动端适配:
智能推荐:
java复制// 基于用户特征的宠物推荐算法
public List<Pet> recommendPets(Long userId) {
User user = userMapper.selectById(userId);
return petMapper.selectList(new LambdaQueryWrapper<Pet>()
.eq(Pet::getStatus, 0)
.apply("JSON_CONTAINS(attributes->'$.suitable_for', {0})",
user.getLivingCondition()));
}
这个项目最让我有成就感的是审核流程的状态机设计,通过清晰的枚举定义和事务处理,保证了业务数据的一致性。在实际运行中,建议根据机构规模适当调整HikariCP的连接池参数,我们最终设置为maximumPoolSize=50时性能达到最佳平衡点。