1. 项目概述与技术选型
这个宠物领养系统采用了当前企业级开发中最流行的前后端分离架构,后端基于SpringBoot 2.7.x构建,前端使用Vue3组合式API开发,数据持久层采用MyBatis-Plus增强,数据库选择了MySQL 8.0。整套系统代码结构清晰,包含完整的用户认证、宠物管理、领养申请和后台审核流程。
为什么选择这样的技术栈?SpringBoot的自动配置特性让后端服务可以快速搭建,内置Tomcat容器也省去了传统Java Web项目的部署复杂度。Vue3相比Vue2在性能上有显著提升,组合式API让代码组织更灵活。MyBatis-Plus在基础CRUD操作上提供了大量开箱即用的方法,而MySQL作为最成熟的关系型数据库之一,在事务处理和复杂查询方面表现稳定。
提示:实际开发中建议锁定具体版本号,比如SpringBoot 2.7.15避免自动升级带来的兼容性问题。我曾在一个项目中因为没锁定版本导致SpringBoot从2.7.0自动升级到2.7.1后,MyBatis的枚举类型处理出现了不兼容变更。
2. 系统架构设计
2.1 前后端分离实现方案
系统采用完全解耦的架构设计,前端Vue3项目通过axios与后端SpringBoot服务通信。开发环境下通过Vue CLI的proxyTable解决跨域问题,生产环境则通过Nginx配置反向代理。一个典型的接口交互流程如下:
- 前端Vue组件触发API请求
- axios实例携带JWT token访问SpringBoot接口
- SpringSecurity进行权限校验
- Controller层处理参数并调用Service
- MyBatis执行SQL并返回结果
- 结果通过统一响应封装返回前端
这种架构的最大优势是前后端可以并行开发。我在实际项目中会使用Swagger或Knife4j自动生成API文档,前端同学不需要等待后端接口完成就能开始联调。
2.2 数据库设计要点
宠物领养系统的核心表包括:
| 表名 | 主要字段 | 说明 |
|---|---|---|
| user | id, username, password, phone, avatar | 用户表,密码需加密存储 |
| pet | id, name, type, age, gender, health_status | 宠物基本信息表 |
| adoption | id, pet_id, user_id, status, apply_time | 领养申请记录表 |
| adoption_audit | id, adoption_id, admin_id, audit_result | 审核记录表 |
特别注意几点:
- 密码字段使用BCryptPasswordEncoder加密
- 状态字段使用枚举类型(如领养状态:APPLYING, APPROVED, REJECTED)
- 建立合理的索引(如pet表的type字段)
- 外键关联要设置ON DELETE规则
3. 核心功能实现
3.1 用户认证模块
采用JWT+SpringSecurity实现无状态认证。关键配置如下:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()));
return http.build();
}
}
前端需要在axios拦截器中处理token:
javascript复制// 请求拦截器
service.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// 响应拦截器
service.interceptors.response.use(response => {
if (response.data.code === 401) {
router.push('/login')
}
return response
})
3.2 宠物列表与筛选
后端采用MyBatis-Plus的动态SQL构建查询:
java复制@GetMapping("/pets")
public Result listPets(
@RequestParam(required = false) String type,
@RequestParam(required = false) Integer minAge,
@RequestParam(required = false) Integer maxAge) {
QueryWrapper<Pet> wrapper = new QueryWrapper<>();
if (StringUtils.isNotBlank(type)) {
wrapper.eq("type", type);
}
if (minAge != null) {
wrapper.ge("age", minAge);
}
if (maxAge != null) {
wrapper.le("age", maxAge);
}
return Result.success(petService.list(wrapper));
}
前端Vue3中使用组合式API实现响应式筛选:
javascript复制const filterPets = computed(() => {
return pets.value.filter(pet => {
return (
(!filters.type || pet.type === filters.type) &&
(!filters.minAge || pet.age >= filters.minAge) &&
(!filters.maxAge || pet.age <= filters.maxAge)
)
})
})
3.3 领养申请流程
领养申请涉及多个状态变更和事务处理:
java复制@Transactional
public boolean applyAdoption(Long petId, Long userId) {
// 检查宠物是否可领养
Pet pet = petMapper.selectById(petId);
if (pet == null || !pet.getAdoptable()) {
throw new BusinessException("该宠物不可领养");
}
// 创建申请记录
Adoption adoption = new Adoption();
adoption.setPetId(petId);
adoption.setUserId(userId);
adoption.setStatus(AdoptionStatus.APPLYING);
adoption.setApplyTime(LocalDateTime.now());
adoptionMapper.insert(adoption);
// 更新宠物状态
pet.setAdoptable(false);
petMapper.updateById(pet);
return true;
}
4. 开发中的典型问题与解决方案
4.1 MyBatis枚举类型处理
在早期版本中,MyBatis对枚举类型的处理不够友好。推荐两种解决方案:
- 使用MyBatis-Plus的枚举转换器:
java复制@Bean
public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() {
return properties -> {
GlobalConfig globalConfig = properties.getGlobalConfig();
globalConfig.setDbConfig(new GlobalConfig.DbConfig()
.setLogicDeleteField("deleted")
.setEnumHandler(MybatisEnumTypeHandler.class));
};
}
- 自定义TypeHandler:
java复制public class AdoptionStatusTypeHandler extends BaseTypeHandler<AdoptionStatus> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
AdoptionStatus parameter, JdbcType jdbcType) {
ps.setInt(i, parameter.getCode());
}
// 其他方法实现...
}
4.2 Vue3组件通信问题
在大型项目中,组件间通信容易变得混乱。推荐以下实践:
- 使用provide/inject跨层级传递数据
- 复杂状态使用Pinia管理
- 事件总线只用于全局通知(如登录状态变化)
- 父子组件通信优先使用props/emit
一个典型的Pinia store示例:
javascript复制export const usePetStore = defineStore('pet', {
state: () => ({
currentPet: null,
favorites: []
}),
actions: {
async fetchPet(id) {
this.currentPet = await getPetById(id)
},
toggleFavorite(pet) {
const index = this.favorites.findIndex(f => f.id === pet.id)
if (index >= 0) {
this.favorites.splice(index, 1)
} else {
this.favorites.push(pet)
}
}
}
})
4.3 性能优化实践
- 后端优化:
- 启用MyBatis二级缓存(注意分布式环境要用Redis实现)
- 复杂查询添加@Transactional(readOnly = true)
- 使用SpringBoot Actuator监控接口性能
- 前端优化:
- 路由懒加载
- 图片使用WebP格式
- 列表数据分页+虚拟滚动
- 使用keep-alive缓存组件状态
5. 部署与运维建议
5.1 生产环境部署
推荐使用Docker Compose编排服务:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: pet@1234
MYSQL_DATABASE: pet_adoption
volumes:
- mysql_data:/var/lib/mysql
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
frontend:
build: ./frontend
ports:
- "80:80"
volumes:
mysql_data:
5.2 常见运维问题
- MySQL连接池耗尽:
- 调整spring.datasource.hikari.maximum-pool-size
- 添加连接泄露检测
- 使用连接池监控工具
- Vue项目首屏加载慢:
- 配置gzip压缩
- 使用CDN加载第三方库
- 开启HTTP/2
- 文件上传问题:
- SpringBoot配置multipart.max-file-size
- 使用OSS存储替代本地存储
- 前端做好文件类型和大小校验
这个项目完整展示了现代Java全栈开发的典型技术组合。在实际开发中,我建议采用Git分支策略管理代码,比如main分支用于生产环境,develop分支用于集成测试,feature分支开发新功能。对于团队协作,可以引入SonarQube进行代码质量检查,使用Jenkins或GitHub Actions实现CI/CD流水线。
