SpringBoot2+Vue3宠物领养系统全栈开发实践
发布时间:2026/9/12 22:07:49 作者:尧图编辑部 阅读量:1,286

1. 项目概述与技术栈选型这个基于SpringBoot2Vue3MyBatis-PlusMySQL8.0的宠物领养系统是一个典型的全栈Web应用开发案例。作为从业多年的Java全栈开发者我认为这个技术组合在当前企业级应用开发中具有很高的参考价值。技术栈选择背后的考量SpringBoot 2.x版本在企业中仍占据主流地位相比最新的3.x版本它的社区支持更成熟遇到问题更容易找到解决方案Vue3作为前端框架其Composition API提供了更好的代码组织方式特别适合复杂交互的领养流程实现MyBatis-Plus在传统MyBatis基础上做了大量增强可以节省约60%的常规CRUD代码量MySQL 8.0在JSON支持、窗口函数等方面的改进非常适合存储宠物特征这类半结构化数据提示新手开发者常犯的错误是盲目追求最新版本。实际上SpringBoot 2.7.x Vue 3.2.x MyBatis-Plus 3.5.x的组合在稳定性和功能完整性上已经足够优秀。2. 系统架构设计与模块划分2.1 后端工程结构标准的Maven多模块结构如下pet-adoption ├── pet-common // 公共模块 ├── pet-system // 系统模块 ├── pet-admin // 管理后台API ├── pet-api // 用户端API └── pet-mobile // 移动端适配层关键配置点在application.yml中需要特别配置MySQL 8.0的连接参数spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/pet_adoption?useSSLfalseserverTimezoneAsia/Shanghai username: root password: 你的密码MyBatis-Plus的分页插件配置Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }2.2 前端工程结构Vue3项目采用典型的Vite工程结构src/ ├── api/ // 接口定义 ├── assets/ // 静态资源 ├── components/ // 公共组件 ├── router/ // 路由配置 ├── stores/ // Pinia状态管理 ├── utils/ // 工具函数 └── views/ // 页面组件关键配置点在vite.config.js中需要配置代理解决跨域server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: path path.replace(/^\/api/, ) } } }3. 核心功能实现细节3.1 宠物信息管理模块后端实体类设计示例Data TableName(pet_info) public class PetInfo { TableId(type IdType.AUTO) private Long id; private String name; private Integer age; private String gender; private String type; // 猫/狗等 private String healthStatus; private String description; TableField(typeHandler JacksonTypeHandler.class) private ListString images; // 使用JSON存储图片列表 private LocalDateTime createTime; }前端使用Element Plus实现表单template el-form :modelform label-width120px el-form-item label宠物名称 el-input v-modelform.name / /el-form-item el-form-item label年龄 el-input-number v-modelform.age :min0 / /el-form-item el-form-item label性别 el-radio-group v-modelform.gender el-radio-button labelmale雄性/el-radio-button el-radio-button labelfemale雌性/el-radio-button /el-radio-group /el-form-item !-- 其他表单项 -- /el-form /template3.2 领养申请流程状态机设计public enum AdoptionStatus { PENDING_REVIEW, // 待审核 APPROVED, // 审核通过 REJECTED, // 审核拒绝 COMPLETED // 领养完成 }业务逻辑实现Service RequiredArgsConstructor public class AdoptionServiceImpl implements AdoptionService { private final AdoptionMapper adoptionMapper; Transactional public void processAdoption(Long adoptionId, AdoptionStatus status) { Adoption adoption adoptionMapper.selectById(adoptionId); if (adoption null) { throw new BusinessException(领养记录不存在); } // 状态校验 if (adoption.getStatus() ! AdoptionStatus.PENDING_REVIEW) { throw new BusinessException(当前状态不允许此操作); } adoption.setStatus(status); adoption.setReviewTime(LocalDateTime.now()); adoptionMapper.updateById(adoption); // 如果审核通过更新宠物状态 if (status AdoptionStatus.APPROVED) { petService.updatePetStatus(adoption.getPetId(), PetStatus.ADOPTED); } } }4. 开发中的典型问题与解决方案4.1 MyBatis-Plus版本兼容性问题常见报错java: You arent using a compiler supported by lombok...解决方案确保Lombok版本与JDK版本匹配在IDEA中启用注解处理Settings → Build → Compiler → Annotation Processors勾选Enable annotation processing4.2 Vue3组件通信问题在宠物详情页和领养表单组件间共享数据的最佳实践// 使用Pinia状态管理 import { defineStore } from pinia export const usePetStore defineStore(pet, { state: () ({ currentPet: null }), actions: { setCurrentPet(pet) { this.currentPet pet } } }) // 在组件中使用 const petStore usePetStore() petStore.setCurrentPet(selectedPet)4.3 文件上传实现后端接收多文件上传PostMapping(/upload) public ResultListString uploadFiles(RequestParam(files) MultipartFile[] files) { ListString urls new ArrayList(); for (MultipartFile file : files) { String url ossService.upload(file); urls.add(url); } return Result.success(urls); }前端实现拖拽上传template el-upload action/api/upload multiple drag :on-successhandleSuccess i classel-icon-upload/i div classel-upload__text将文件拖到此处或em点击上传/em/div /el-upload /template5. 性能优化实践5.1 数据库优化针对宠物列表查询的优化方案-- 创建合适的索引 CREATE INDEX idx_pet_type ON pet_info(type); CREATE INDEX idx_pet_status ON pet_info(status); -- 使用覆盖索引优化查询 EXPLAIN SELECT id, name, age FROM pet_info WHERE type DOG AND status AVAILABLE;5.2 缓存策略使用Spring Cache实现缓存Cacheable(value pet, key #id) public PetInfo getPetById(Long id) { return petMapper.selectById(id); } CacheEvict(value pet, key #pet.id) public void updatePet(PetInfo pet) { petMapper.updateById(pet); }5.3 前端性能优化组件懒加载const PetDetail () import(./views/PetDetail.vue)API请求防抖import { debounce } from lodash-es const searchPets debounce(async (keyword) { const res await api.searchPets(keyword) petList.value res.data }, 500)6. 安全防护措施6.1 认证与授权JWT实现方案Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers(/api/auth/**).permitAll() .anyRequest().authenticated() .and() .addFilter(new JwtAuthenticationFilter(authenticationManager())) .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); } }6.2 SQL注入防护MyBatis-Plus已经内置了SQL注入防护但需要注意永远不要直接拼接SQL语句使用QueryWrapper时也要注意// 错误做法 wrapper.apply(date_format(create_time,%Y-%m-%d) date ); // 正确做法 wrapper.apply(date_format(create_time,%Y-%m-%d) {0}, date);6.3 XSS防护前端使用DOMPurify净化输入import DOMPurify from dompurify const clean DOMPurify.sanitize(userInput)后端同时做校验NotBlank Size(max 500) Pattern(regexp ^[a-zA-Z0-9\\u4e00-\\u9fa5。、【】「」“”‘’…—\-—·]$) private String description;7. 部署与监控7.1 生产环境部署推荐使用Docker Compose部署version: 3 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: pet1234 MYSQL_DATABASE: pet_adoption ports: - 3306:3306 volumes: - mysql_data:/var/lib/mysql backend: build: ./backend ports: - 8080:8080 depends_on: - mysql frontend: build: ./frontend ports: - 80:80 depends_on: - backend volumes: mysql_data:7.2 监控配置SpringBoot Actuator配置management: endpoints: web: exposure: include: health,info,metrics endpoint: health: show-details: always前端错误监控Sentry示例import * as Sentry from sentry/vue Sentry.init({ app, dsn: your-dsn, integrations: [ new Sentry.BrowserTracing({ routingInstrumentation: Sentry.vueRouterInstrumentation(router) }) ], tracesSampleRate: 0.2 })8. 项目文档编写要点完善的文档应该包含本地开发环境搭建指南数据库初始化脚本API接口文档推荐使用Swagger或Knife4j前端组件说明部署手册常见问题解答Swagger配置示例Configuration EnableSwagger2 public class SwaggerConfig { Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage(com.pet.adoption.controller)) .paths(PathSelectors.any()) .build() .apiInfo(apiInfo()); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title(宠物领养系统API文档) .description(前后端交互接口说明) .version(1.0) .build(); } }在实际开发中我发现很多团队忽视了文档的重要性。一个好的文档应该像代码一样维护建议将文档纳入版本控制系统与代码同步更新。对于宠物领养系统这类涉及多方协作的项目清晰的文档可以减少80%以上的沟通成本。