尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Spring Boot+Vue3实现AI宠物领养管理系统全栈实战

Spring Boot+Vue3实现AI宠物领养管理系统全栈实战 你正在做毕业设计或项目实战打算用前后端分离的方式写一个管理系统但最怕的不是不会写接口而是写完不知道下一步干什么。如果你刚完成一个Spring Boot项目接着要写前端页面然后还要把AI能力接进去这篇文章应该能帮你把整条线串起来。这篇文章围绕一个完整的业务系统展开AI宠物领养管理系统。它不是一个普通的CRUD练习而是一个包含用户端、管理端、宠物档案、领养流程、图片识别等完整业务闭环的全栈项目。核心判断是真正让你从会写接口进步到能交付项目的不是某个框架的新特性而是你把前端、后端、数据库、AI服务、前后端联调这五件事串起来的工程能力。读完你会有三个收获第一理解这类前后端分离项目的模块划分方式第二拿到一套可以直接抄作业的宠物领养业务表结构和核心接口第三弄明白AI能力宠物品种识别、相似宠物推荐应该放在哪个环节、怎么和业务代码对接。1. 宠物领养管理系统难点从来不是CRUD很多人拿到XX管理系统第一反应是不就是用户表、商品表、订单表然后写增删改查吗这话对了一半。增删改查确实是基础但一个能上台演示、能写进简历、能在答辩时讲出亮点的系统真正难的是三层东西。第一层是业务状态流转。宠物领养不是用户点一下我要领养就结束的。一只宠物要经历待领养 - 申请中 - 审核通过 - 已领养 - 已下架这些状态。每个状态由谁触发、在哪个接口触发、触发后其他模块要做什么这些规则才是业务系统的灵魂。很多新手项目死在状态是字符串到处随便改最后代码里到处都是if else逻辑根本没法看。第二层是角色权限。领养系统里至少有三类人游客可以浏览宠物、注册用户可以提交领养申请、管理员可以审核申请并维护宠物档案。如果你把所有接口都公开或者所有页面都不做权限控制项目演示时面试官追问一句怎么防止普通用户直接调用管理员接口你就很难回答。Spring Boot里的拦截器、JWT、Vue里的路由守卫这些不是八股文而是真实系统必须有的东西。第三层是AI怎么落地。既然叫AI宠物领养管理系统AI不能只是标题里的装饰词。比较自然的落地方式是用户上传宠物照片时系统自动识别宠物品种辅助用户填写档案或者根据用户填写的居住条件、养宠经验推荐匹配度高的宠物。这不是炫技而是真实业务里能提效的功能点。所以这篇文章不是给你堆一堆代码而是用宠物领养这个场景把业务设计 - 接口开发 - 前端对接 - AI接入 - 项目验证全部走一遍。你把这个项目做完比背十套模板项目都有用。2. 系统功能设计与技术选型2.1 你要实现的完整模块清单先看整体功能边界做项目最忌讳上来就写代码先把模块画清楚。模块子功能面向角色用户认证注册、登录、个人信息游客、用户、管理员宠物管理宠物列表、宠物详情、发布宠物、上下架用户、管理员领养流程提交申请、审核、状态查询用户、管理员AI 能力品种识别、宠物相似推荐用户、系统后台管理用户管理、宠物审核、领养审核管理员消息通知申请结果通知、宠物上新提醒系统这里需要说明一个设计取舍宠物发布和领养申请现实中可能是管理员统一录入但在教学项目里我们可以把发布宠物和提交领养申请都开放给用户管理员只做审核。这样做的好处是项目的所有角色都会深度参与整个流程演示起来也更完整。2.2 技术栈选型这套系统的技术栈选择原则是前端主流、后端主流、AI能力最低成本接入。你不用追求最新版本但每项选型都要知道为什么选它。层次技术选型说明前端Vue 3 Vite Element Plus Pinia中后台项目的成熟组合组件生态丰富后端Spring Boot MyBatis Plus MySQLJava项目实战的主力组合资料多、排错容易认证JWT 拦截器无状态认证适合前后端分离AI图像识别API 相似度推荐不在本地训练模型调用现成能力门槛低部署Docker可选后阶段打包部署项目实战加分项如果你是Python方向后端也可以换成FastAPI或Django但本文代码示例按Spring Boot来写。前后端分离的思想是通用的业务设计更是可以照搬的。3. 数据库设计领养系统的表结构怎么规划数据库设计是项目实战的地基。很多翻车的项目不是代码写错了是表结构设计有缺陷导致后面每个接口都写得很难受。宠物领养系统的核心表我建议分为五张。3.1 核心表结构清单第一张是用户表user字段包括id、username、password加密存储、phone、roleUSER/ADMIN、create_time。第二张是宠物表pet这是系统的核心业务表字段包括宠物名称、品种、年龄、性别、健康状况、描述、图片地址、状态、发布人ID等。第三张是领养申请表adoption_apply记录谁在什么时间申请了哪只宠物审核状态、审核意见、申请时的居住情况、养宠经验等。第四张是宠物图片表pet_image。为什么要单独建因为一只宠物通常有多张照片而且AI识别功能需要在图片上传后做异步处理单独建表方便记录每张图的识别状态和识别结果。第五张可以是消息表notification记录系统给用户发送的申请结果通知。3.2 宠物表与领养申请表的DDL参考下面是宠物表的核心DDL字段类型和注释写清楚这是基本功CREATE TABLE pet ( id bigint(20) NOT NULL AUTO_INCREMENT, pet_name varchar(50) NOT NULL COMMENT 宠物名称, species varchar(50) NOT NULL COMMENT 物种如猫/狗, breed varchar(50) DEFAULT NULL COMMENT 品种如英短/金毛, age int(11) DEFAULT NULL COMMENT 年龄单位月, gender tinyint(1) DEFAULT NULL COMMENT 性别0-母 1-公, health_status varchar(255) DEFAULT NULL COMMENT 健康状况描述, description text COMMENT 宠物介绍, main_image varchar(255) DEFAULT NULL COMMENT 主图地址, status tinyint(1) NOT NULL DEFAULT 0 COMMENT 状态0-待审核 1-待领养 2-申请中 3-已领养 4-已下架, user_id bigint(20) NOT NULL COMMENT 发布人ID, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT宠物信息表;领养申请表同样要重视CREATE TABLE adoption_apply ( id bigint(20) NOT NULL AUTO_INCREMENT, pet_id bigint(20) NOT NULL COMMENT 宠物ID, user_id bigint(20) NOT NULL COMMENT 申请人ID, reason varchar(500) DEFAULT NULL COMMENT 领养理由, has_experience tinyint(1) DEFAULT 0 COMMENT 是否有养宠经验, home_type varchar(50) DEFAULT NULL COMMENT 居住类型租房/自有住房, status tinyint(1) NOT NULL DEFAULT 0 COMMENT 状态0-待审核 1-已通过 2-已拒绝, audit_comment varchar(255) DEFAULT NULL COMMENT 审核意见, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT领养申请表;这里要特别解释一下pet.status和adoption_apply.status的关系。当领养申请提交成功时pet.status要同步从待领养改成申请中当审核拒绝时pet.status要恢复成待领养审核通过则改成已领养。这两个状态不能靠前端传值随意修改一定要在后端Service层通过事务保证一致性。4. 后端接口开发从登录认证到领养流程4.1 项目结构与认证设计后端项目按模块分包推荐结构如下src/main/java/com/example/petadopt/ ├── controller/ # 接口层 ├── service/ # 业务逻辑层 ├── mapper/ # 数据访问层 ├── entity/ # 实体类 ├── dto/ # 接口入参出参对象 ├── config/ # 配置类 ├── interceptor/ # 登录拦截器 ├── util/ # 工具类 └── common/ # 通用返回结果、异常处理认证用的是JWT。用户登录成功后后端生成token返回给前端。前端把token存在localStorage里每次请求在请求头携带Authorization字段。后端写一个拦截器统一校验校验通过才放行。这里是JWT工具类的核心逻辑重点看token的生成和解析方式// 文件路径src/main/java/com/example/petadopt/util/JwtUtil.java public class JwtUtil { private static final String SECRET your-secret-key-change-in-production; private static final long EXPIRE_TIME 7 * 24 * 60 * 60 * 1000L; // 生成token public static String generateToken(Long userId, String role) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setExpiration(new Date(System.currentTimeMillis() EXPIRE_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } // 解析token public static Claims parseToken(String token) { return Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); } }注意加密密钥在生产环境必须放到配置中心或环境变量里不能写死在代码中。这是项目实战里一个非常重要的安全习惯。4.2 领养申请的状态流转逻辑这部分的代码是整个后端业务的核心比单纯的CRUD要值得写得多。提交领养申请的Service方法逻辑是这样的校验宠物存在且状态是待领养。校验该用户没有正在审核中的申请避免重复申请。插入一条领养申请记录。把宠物状态改为申请中。所有操作在一个事务里任何一步失败都要回滚。// 文件路径src/main/java/com/example/petadopt/service/impl/AdoptionApplyServiceImpl.java Transactional(rollbackFor Exception.class) public Result submitApply(AdoptionApplyDTO dto, Long userId) { // 1. 查宠物 Pet pet petMapper.selectById(dto.getPetId()); if (pet null) { return Result.error(宠物不存在); } if (pet.getStatus() ! PetStatus.WAIT_ADOPT.getCode()) { return Result.error(该宠物当前不可领养); } // 2. 查重复申请 Integer count adoptionApplyMapper.countByUserAndPet(userId, dto.getPetId(), AdoptionStatus.WAIT_AUDIT.getCode()); if (count 0) { return Result.error(你已提交过申请请等待审核); } // 3. 插入申请记录 AdoptionApply apply new AdoptionApply(); apply.setPetId(dto.getPetId()); apply.setUserId(userId); apply.setReason(dto.getReason()); apply.setHasExperience(dto.getHasExperience()); apply.setHomeType(dto.getHomeType()); apply.setStatus(AdoptionStatus.WAIT_AUDIT.getCode()); adoptionApplyMapper.insert(apply); // 4. 同步宠物状态 pet.setStatus(PetStatus.APPLYING.getCode()); petMapper.updateById(pet); return Result.success(申请提交成功); }这里特别强调一下状态值为什么要用常量类或枚举而不是散落的魔法数字。因为前后端对接、AI状态同步、将来加状态记录表都依赖一个统一的约定。写代码的时候每个数字都写注释不然三天之后你自己都分不清1到底代表已通过还是申请中。4.3 管理员审核接口管理员的审核接口同样要考虑状态一致性。审核拒绝时除了更新申请状态还要把宠物的状态恢复为待领养审核通过时宠物状态改为已领养。这样才能保证用户看到的宠物状态永远是准确、一致的。Transactional(rollbackFor Exception.class) public Result auditApply(Long applyId, Integer status, String comment) { AdoptionApply apply adoptionApplyMapper.selectById(applyId); if (apply null) { return Result.error(申请记录不存在); } if (apply.getStatus() ! AdoptionStatus.WAIT_AUDIT.getCode()) { return Result.error(该申请已处理过); } Pet pet petMapper.selectById(apply.getPetId()); apply.setStatus(status); apply.setAuditComment(comment); adoptionApplyMapper.updateById(apply); if (status AdoptionStatus.APPROVED.getCode()) { pet.setStatus(PetStatus.ADOPTED.getCode()); } else { pet.setStatus(PetStatus.WAIT_ADOPT.getCode()); } petMapper.updateById(pet); // 给用户发送站内信 sendNotification(apply.getUserId(), 你的领养申请已 (status AdoptionStatus.APPROVED.getCode() ? 通过 : 被拒绝)); return Result.success(审核完成); }这段代码直接体现了事务 状态机的编程思想。你写完这两个接口整个领养业务的主链路就已经通了。5. 前端页面开发Vue3 Element Plus实战5.1 前端项目结构与路由前端用Vue3 Vite Element Plus。项目结构如下src/ ├── api/ # 接口请求封装 ├── views/ │ ├── home/ # 首页和宠物列表 │ ├── pet/ # 宠物详情、发布宠物 │ ├── apply/ # 我的申请 │ ├── admin/ # 管理后台 │ └── login/ # 登录注册 ├── router/ # 路由配置 ├── store/ # Pinia状态管理 ├── components/ # 公共组件 └── utils/ # 请求封装、token处理路由配置里要做两件关键事给需要登录的页面加meta.requiresAuth给管理员页面加meta.requiresAdmin然后在路由守卫里做跳转判断。// 文件路径src/router/index.js const routes [ { path: /login, component: Login }, { path: /, component: Home }, { path: /pet/:id, component: PetDetail }, { path: /publish, component: PublishPet, meta: { requiresAuth: true } }, { path: /my-apply, component: MyApply, meta: { requiresAuth: true } }, { path: /admin, component: AdminDashboard, meta: { requiresAuth: true, requiresAdmin: true } } ]; router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.meta.requiresAuth !token) { next(/login); return; } const userInfo JSON.parse(localStorage.getItem(userInfo) || {}); if (to.meta.requiresAdmin userInfo.role ! ADMIN) { next(/); return; } next(); });5.2 宠物卡片列表页宠物列表页是门面功能设计得好看直接影响项目整体印象。用Element Plus的el-card组件展示宠物卡片每个卡片展示主图、品种、年龄、状态标签。这个页面的核心在于把后端的status字段映射成用户能看懂的状态标签。!-- 文件路径src/views/home/Home.vue -- template div classpet-grid el-card v-forpet in petList :keypet.id classpet-card img :srcpet.mainImage classpet-image alt宠物图片 / div classpet-info div classpet-name{{ pet.petName }}/div div classpet-breed{{ pet.species }} · {{ pet.breed || 未知品种 }}/div el-tag :typestatusType(pet.status){{ statusText(pet.status) }}/el-tag el-button typeprimary sizesmall clickgoDetail(pet.id) 查看详情 /el-button /div /el-card /div /template script setup import { ref, onMounted } from vue; import { useRouter } from vue-router; import { getPetList } from /api/pet; const router useRouter(); const petList ref([]); const statusText (status) { const map { 0: 待审核, 1: 待领养, 2: 申请中, 3: 已领养, 4: 已下架 }; return map[status] || 未知状态; }; const statusType (status) { const map { 0: warning, 1: success, 2: primary, 3: info, 4: danger }; return map[status] || info; }; const goDetail (id) router.push(/pet/${id}); onMounted(async () { const res await getPetList(); petList.value res.data; }); /script这段代码已经把列表页需要掌握的知识点都覆盖了接口调用、状态映射、路由跳转。真正到你的项目里可以在这个基础上加分页、搜索筛选、图片懒加载。5.3 领养申请表单与提交申请表单要注意的不是表单控件本身而是提交成功后如何处理。因为一个用户只能对同一只宠物申请一次所以提交成功后按钮应该变成已申请并且不能再重复提交。el-form :modelapplyForm label-width100px el-form-item label领养理由 el-input v-modelapplyForm.reason typetextarea :rows4 placeholder请简单介绍你的领养计划和条件 / /el-form-item el-form-item label养宠经验 el-switch v-modelapplyForm.hasExperience active-text有 inactive-text无 / /el-form-item el-form-item label居住情况 el-radio-group v-modelapplyForm.homeType el-radio valueself_owned自有住房/el-radio el-radio valuerent租房/el-radio /el-radio-group /el-form-item el-form-item el-button typeprimary :loadingsubmitting clicksubmitApply 提交申请 /el-button /el-form-item /el-form前端提交逻辑要注意异常处理。后端返回的错误信息要原样展示给用户比如该宠物当前不可领养你已提交过申请。这些提示直接决定用户体验不要在前端写死错误文案。6. AI能力接入宠物品种识别与相似推荐6.1 AI能力在整个系统中的位置传统的宠物领养系统宠物品种是用户或管理员手动填写的漏填、错填非常常见。AI接入之后发布宠物时用户上传照片系统自动识别出品种用户确认或修正后保存。这个流程既真实又不过度复杂是项目实战里最合适的AI落地点。同时可以做一个相似宠物推荐用户看中一只金毛后系统根据品种、年龄、健康状况计算相似度在详情页底部推荐其他宠物。这个功能不一定要用深度学习模型基于标签匹配和简单的相似度算法就能实现但演示效果很好。6.2 接入方式的推荐与代码示例AI能力不建议自己在本地训练模型真实项目中也是调用现成的视觉识别API或者调用自己团队部署的模型服务。实践中通常的做法是后端把宠物图片地址传给AI服务AI服务返回识别结果后端再更新宠物记录。// 文件路径src/main/java/com/example/petadopt/service/AiRecognizeService.java Service public class AiRecognizeService { private RestTemplate restTemplate; public AiRecognizeService(RestTemplate restTemplate) { this.restTemplate restTemplate; } /** * 调图像识别服务识别宠物品种 * imageUrl 是上传后的图片地址由文件服务返回 */ public RecognizeResult recognizeBreed(String imageUrl) { String url http://ai-service:8081/api/recognize; MapString, String requestBody new HashMap(); requestBody.put(imageUrl, imageUrl); ResponseEntityRecognizeResult response restTemplate.postForEntity( url, requestBody, RecognizeResult.class); if (response.getStatusCode().is2xxSuccessful()) { return response.getBody(); } // 降级处理AI服务不可用时返回空结果不影响主流程 return new RecognizeResult(null, 0.0, false); } }这里要特别强调降级处理的重要性。真实项目里AI服务可能超时、可能限流。如果AI挂了整个发布流程就走不下去这是不能接受的。所以代码里当AI服务不可用时返回空结果前端提示自动识别失败请手动填写品种而不是报错。AI识别结果更新宠物字段的业务代码public void recognizeAndUpdate(Long petId) { Pet pet petMapper.selectById(petId); if (pet null || pet.getMainImage() null) { return; } try { RecognizeResult result aiRecognizeService.recognizeBreed(pet.getMainImage()); if (result.isSuccess() result.getBreed() ! null) { if (StringUtils.isBlank(pet.getBreed())) { pet.setBreed(result.getBreed()); petMapper.updateById(pet); } } } catch (Exception e) { // AI识别失败不能影响主流程记录日志后人工处理 log.error(AI recognize failed for petId: {}, petId, e); } }AI服务是否可用的一个最简单的判断方式发布宠物成功后前端调开始识别按钮后端调用这个识别方法然后返回识别结果。识别失败时用户手动填写流程始终能走通。6.3 相似宠物推荐的后端实现相似推荐的实现逻辑比较简单可以按品种和年龄区间筛选。这里不追求算法复杂度重点在于你理解了推荐也是一种查询而不是玄学。public ListPet recommendSimilarPets(Long petId, int limit) { Pet currentPet petMapper.selectById(petId); if (currentPet null) { return Collections.emptyList(); } LambdaQueryWrapperPet wrapper new LambdaQueryWrapper(); wrapper.ne(Pet::getId, petId) .eq(Pet::getStatus, PetStatus.WAIT_ADOPT.getCode()) .eq(StringUtils.isNotBlank(currentPet.getBreed()), Pet::getBreed, currentPet.getBreed()) .last(LIMIT limit); return petMapper.selectList(wrapper); }这段代码的逻辑是先排除当前宠物自己只推荐待领养状态的宠物优先匹配同品种。如果同品种不够再放宽条件按同物种查询。这样实现简单但勉强能支撑演示和文档说明后续要优化也能有扩展方向。7. 前后端联调从跨域配置到如何区分前后端Bug7.1 环境配置层面前后端分离项目的第一个拦路虎就是跨域。开发环境里Vite启动在5173端口Spring Boot启动在8080端口两者端口不同直接请求必然跨域。解决方案有两种前端Vite代理或者后端配置CORS。生产环境建议后端统一配置CORS。后端CORS配置// 文件路径src/main/java/com/example/petadopt/config/CorsConfig.java Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }注意allowCredentials(true)时如果允许了具体的域名要小心安全边界用allowedOriginPatterns(*)是演示环境方便。生产环境应该写成明确的域名列表。7.2 联调时最容易被问的怎么区分是前端Bug还是后端Bug这是让很多新手头疼的问题。这里给你一套排查思路。现象排查位置判断方法页面报404后端看后端接口路径和前端的API请求路径是否一致优先用Postman直接调接口能通就是路径问题页面报500后端看后端控制台异常日志一般能直接定位到代码位置页面数据为空后端先用Postman请求接口确认返回JSON是否正常再确认前端字段名是否和后端实体字段名一致页面有报错但网络Status 200前端大概率是响应格式解析问题比如后端返回{code: 1, data: [...]}前端却取了res.data.data你会发现核心思路是用接口调试工具和前端Network面板把问题卡到某一端。后端能直接连数据库验证前端能直接改代码调试只有左右隔离才能快速定位。7.3 联调最佳实践统一响应格式联调时最痛苦的事情是字段名不一致。比如后端叫petName前端写成了name页面就是显示不出来。这个问题的根治方案是后端定义统一返回体Result接口对齐后前端直接用res.data.petName不做过多的额外转换。// 文件路径src/main/java/com/example/petadopt/common/Result.java Data public class ResultT { private Integer code; // 0-成功 非0-失败 private String message; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.setCode(0); r.setMessage(success); r.setData(data); return r; } public static T ResultT error(String message) { ResultT r new Result(); r.setCode(500); r.setMessage(message); return r; } }前端的请求封装也要统一处理这个结构避免每个页面重复写错误判断// 文件路径src/utils/request.js import axios from axios; import { ElMessage } from element-plus; const request axios.create({ baseURL: /api, timeout: 10000 }); request.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.Authorization Bearer ${token}; } return config; }); request.interceptors.response.use( response { const res response.data; if (res.code ! 0) { ElMessage.error(res.message || 请求失败); return Promise.reject(new Error(res.message)); } return res; }, error { if (error.response error.response.status 401) { localStorage.removeItem(token); window.location.href /login; } ElMessage.error(网络异常请稍后重试); return Promise.reject(error); } );前后端统一了响应格式联调效率会高很多。8. 项目本地运行与全功能验证路径项目写完之后要能从头到尾跑通并演示。下面给出完整的运行步骤和验证路径。8.1 后端启动# 1. 准备MySQL创建数据库 mysql -uroot -p CREATE DATABASE pet_adopt DEFAULT CHARSET utf8mb4; # 2. 执行src/main/resources/sql/init.sql初始化表结构 # 3. 修改application.yml中的数据库连接信息 spring: datasource: url: jdbc:mysql://localhost:3306/pet_adopt?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your-password # 4. 启动Spring Boot应用 mvn spring-boot:run后端启动成功的标志是控制台出现Started PetAdoptApplication in xxx seconds同时访问http://localhost:8080/api/pet/list能看到JSON数据。8.2 前端启动# 1. 进入前端目录 cd pet-adopt-frontend # 2. 安装依赖 npm install # 3. 启动开发服务器 npm run dev前端启动成功后访问http://localhost:5173能看到宠物列表首页。8.3 全功能验证清单建议按下面的路径完整走一遍这也是演示时的脚本步骤操作预期结果1. 注册点击注册填写信息注册成功自动登录2. 发布宠物上传宠物照片填写信息点击AI识别能识别出品种或允许手动填写宠物进入待审核状态3. 管理员审核用管理员账号登录后台审核宠物宠物状态变成待领养首页可见4. 提交领养申请用另一个用户账号申请领养宠物状态变成申请中管理员后台出现待审核申请5. 管理员审核申请通过或拒绝申请宠物状态变为已领养或恢复待领养用户收到通知6. 相似推荐进入宠物详情页页面底部展示相似宠物推荐这六步跑通整个系统的核心业务闭环就完成了。9. 常见问题与排查方法项目实战中最让人焦虑的就是程序跑不起来也不报错。这里整理几个高频问题问题现象可能原因排查方式解决方案前端请求接口报跨域错误后端CORS未配置或配置错误打开浏览器Network面板确认跨域报错信息检查CorsConfig确认allowedOriginPatterns正确登录成功但刷新页面后登录态丢失token未存或路由守卫未正确读取F12看localStorage是否有token登录成功后localStorage.setItem(token, res.data.token)刷新后重新读取启动报数据库连接失败MySQL未启动或账号密码错误用Navicat/命令行测试连接检查application.yml的连接URL、用户名、密码MyBatis Plus查询结果字段全部为null实体类字段与数据库字段驼峰映射关闭查看MyBatis Plus配置开启map-underscore-to-camel-case: true发布宠物后列表页看不到宠物状态是待审核列表页只查待领养用SQL直接查询pet表确认status值确认列表查询接口的status过滤条件正确管理员审核时提示申请已处理过同一申请被重复提交查看adoption_apply表status是否已被更新审核接口做好幂等判断前端按钮同时禁用10. 工程化建议与部署提醒项目写完后如果要往简历写、往生产部署这里有几点建议。第一不要使用明文密码。用户的注册密码必须经过BCrypt加密存储数据库中不能出现明文密码。这是安全底线面试官一定会看。第二图片上传必须做类型和大小校验。只允许jpg、png等图片格式限制文件大小上限防止恶意上传。上传路径最好使用对象存储本地磁盘只适合开发环境。第三日志要分级。业务操作记录info日志异常记录error日志并输出完整堆栈。生产环境排查问题全靠日志。第四部署要自动化。前后端分离项目可以用Docker Compose编排一个命令启动MySQL、后端、前端。这一步是项目实战的加分项。第五删除操作要谨慎。真实系统里的宠物列表和领养申请尽量用状态字段做逻辑删除不要物理删除。逻辑删除的好处是随时能恢复数据也方便审计。11. 总结回到文章开头的问题完成一个AI宠物领养管理系统真正要掌握的不是某一个框架的语法而是从业务设计到前后端联调再到AI接入的完整闭环能力。你需要能把领养状态机的流转讲清楚能把JWT认证和前端路由守卫配对使用能在AI服务不可用时做降级处理能在跨域报错时五分钟定位到问题。这些能力光看文章练不出来。建议你拿到这篇文章后先照着一张表把模块设计画出来再建库、写后端、写前端最后接AI。跑通全流程后再把代码重构一遍注意异常处理和代码风格。如果你的下一步是深入学习重点可以放在几个方向一是将推荐模块升级为基于用户行为数据的真实推荐算法二是把AI识别替换成自己微调的模型三是加入消息队列处理领养申请的通知异步发送四是用Docker Compose完成整套系统的容器化部署。建议把本文收藏备用。祝你的宠物领养系统项目一次跑通。
返回列表