ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot+Vue流浪猫狗救助平台毕设完整实战:从表结构到前后端部署

SpringBoot+Vue流浪猫狗救助平台毕设完整实战:从表结构到前后端部署 这个标题我太有发言权了。去年我帮两个学弟改毕设前后接触过不下十套类似“SpringBootVue 流浪猫狗救助救援网站管理平台”的项目自己也完整从零写过一版。今天这篇就围绕JavaMySQL这套技术栈把这类项目的核心设计、表结构、前后端实现、踩坑点一次性讲透。不管你是正在选毕设题目还是想拿它当课设练手照着这个思路走省下的时间够你多刷两套真题。先说一个很多学生容易误解的地方流浪猫狗救助平台看起来是“公益项目”但它在功能上其实是一个标准的信息发布业务审核流程管理系统和二手交易平台、租房平台、校园失物招领平台在架构上没有本质区别。这也是为什么它特别适合做毕设——既有一个“有意义”的选题外壳又能把CRUD、权限、状态流转、文件上传、前后端联调这些基本功全部覆盖到。1. 项目整体设计思路与技术选型1.1 为什么选 SpringBoot Vue 这套组合先说选型逻辑。SpringBoot 在这类项目里的核心价值是“省配置”。你不需要像 SSM 时代那样写一堆 XML 配置文件一个spring-boot-starter-web就能把 Web 环境跑起来再加上spring-boot-starter-validation做参数校验、mybatis-plus做数据访问业务代码能集中在处理真实逻辑上而不是折腾环境。这对毕设时间紧、又需要稳定跑通的场景太重要了。前端选 Vue 的理由也很实在Vue 是渐进式框架从简单的new Vue({})到完整的 Vue CLI / Vite 工程化项目学习曲线是平滑的。而且 Vue 的组件化思维和 Element UI / Element Plus 组件库配合能很快搭出像样的后台管理界面。这年头不会点 Vue出去面试都不好意思说自己是做前端的。服务端用 Java 8数据库用 MySQL这两个是绝大多数高校课程里教过的技术答辩时老师问起来你不会无话可说这是选技术栈最大的隐性优势。1.2 前后端分离 vs 服务端渲染为什么推荐前者这里我多说两句因为很多同学在开题时会纠结用 JSP SpringBoot 不是更简单吗为什么非要用前后端分离我的观点是课设可以选 JSP但毕设强烈建议前后端分离。原因有三第一前后端分离是目前企业开发的主流形态写进简历和论文里说服力更强答辩时你可以说“前端通过 RESTful API 与后端交互使用 Axios 封装请求通过 Token 鉴权”这句话一出来老师就知道你学的是新东西而不是老古董。第二前后端分离天然划分了工作量。前端只需要关心页面渲染和数据展示后端只需要提供接口两个人协作时互不干扰。一个人做完整项目时也更容易理清思路——写前端时不惦记 SQL写后端时不惦记按钮样式。第三Vue 项目可以打包成静态文件扔进 SpringBoot 的src/main/resources/static目录下同一个端口访问部署的时候就是一个 Java 进程根本不存在跨域问题。这也是我后面会讲到的“单机部署最省事”方案。1.3 这类项目的通用功能地图不管你选什么业务场景只要是“平台管理类”项目底层功能地图基本是固定的用户端普通游客/注册用户信息浏览、注册登录、提交申请、发布内容、个人中心管理端管理员数据管理、审核、统计、用户管理对应到流浪猫狗救助平台我梳理的功能清单是这样的模块用户端管理端动物信息浏览猫狗列表、查看详情、搜索新增/编辑/上下架动物信息、处理领养申请领养流程提交领养申请、查看进度审核领养申请通过/拒绝、更新领养状态求助救援发布求助信息、查看救援动态接单救援任务、更新救援进度捐赠模块提交捐赠、查看捐赠记录查看捐赠明细、生成统计系统管理注册、登录、个人信息用户列表、权限控制、公告管理这套地图你往上一套换任何“XX平台管理系统”都能直接用。所以我说选这个题目不是选了一个孤立的项目而是选了一套可以迁移的骨架。2. 核心功能模块设计与业务细节拆解2.1 用户端核心流程找猫狗 → 提交申请 → 等审核用户端的核心链路是“找猫 → 看详情 → 提交领养申请 → 查看审核状态”。这个链路看着简单但每个节点都有细节坑。首先是动物信息的展示。猫狗列表不能只是干巴巴的表格你需要一张封面图、一段简介、一个状态标签比如“待领养”或“已被领养”。这里隐藏着一个高频设计错误把图片直接存数据库的 BLOB 字段里。我早期写过这样的代码结果数据库体积疯狂膨胀页面加载慢得像蜗牛。正确做法是把图片上传到本地磁盘或对象存储数据库里只存图片 URL 字符串。然后是领养申请的状态流转。我见过很多新手直接用一个布尔字段is_adopted来标记是否被领养这样根本撑不起流程。正确设计是维护一张领养申请表状态字段用整数表示0待审核 → 1已通过 → 2已拒绝同时还有变动时间、审核意见字段。只有这样用户才能看到“审核不通过的原因”——而“能看审核意见”正是这类项目和普通CRUD拉开差距的地方。2.2 后台审核为什么需要两张表配合后台审核是这类系统的业务核心。用户提交领养申请后管理员在后台看到申请列表点“通过”或“拒绝”。这里有个隐藏问题如果直接改动物表里的adopt_status一旦管理员点了拒绝这只猫狗又会回到“待领养”状态这没问题但如果点了通过你就必须同时把动物表的状态改为“已被领养”否则会出现“同一只猫被两个人同时申请”的脏数据。所以核心业务规则是领养申请通过 → 修改申请表状态为 1 → 修改动物表状态为“已领养”领养申请拒绝 → 只修改申请表状态为 2写入拒绝原因动物表不变领养状态为“已领养”的动物 → 前端列表不再展示或者展示但置灰这两步操作必须放在同一个事务里不然会出现“申请通过了但动物还是待领养”的数据不一致问题。SpringBoot 里只要在 service 方法上加一个Transactional注解就行理论课上讲过的东西在这个需求里终于体现出了价值。2.3 救援与求助模块别做成发帖子就完事很多同类项目的救助模块做成了“发帖灌水区”用户发布求助信息管理员看完了也没什么动作。这不叫救助平台这叫公告板。我当时是这样设计的用户发布求助信息时填写动物类型、位置、情况描述、图片系统生成一条求助记录管理员后台可以看到“待处理”的求助列表点击“受理”后这条记录会挂到某个管理员或志愿者名下并开始记录“救援进度”——比如“已到达现场”“已送医”“已安置”。用户端则可以用求助ID查询处理状态。这样设计的好处是整个系统的信息流是闭环的用户能追踪自己发起的求助管理员有明确的工作清单演示视频拍出来有头有尾。更重要的是它多了一张“救援任务表”让系统的数据关系更丰富——这写进论文里就是“多表关联”“状态模式”“业务流程设计”都是能加分的词。2.4 权限控制怎么做才能不被答辩老师问倒权限控制是答辩必问题目。这个项目里我只区分两种角色普通用户和管理员。技术上我用的是最基础的拦截器方案前端登录成功后后端返回一个随机生成的 Token前端把 Token 存在 localStorage每次请求通过请求头Authorization带上后端写一个拦截器拦截所有/api/**请求校验 Token 是否存在、是否有效管理员的接口在 Controller 上加注解或路径前缀只在 Token 对应的用户角色为管理员时才放行如果你引入 Spring Security JWT技术上更“高级”但学习成本和配置复杂度直线上升。对于毕业设计这个量级拦截器完全够用且更容易自己讲清楚。答辩老师的常见追问是“你这个 Token 有效期多长用户登出了怎么办”你只要能答出“Token 存在 Redis 或内存中登出时删除服务端记录”就已经超过九成的同学了。不过为了保持简单我只把 Token 存在了一个 HashMap 里项目跑通后再升级也来得及论文里写“基于内存Token机制实现”也算一种自圆其说。3. 数据库设计与核心表结构详解3.1 核心表单用户表、动物表、领养申请表数据库设计是这门课的大头我直接给你一套我用过、且踩过坑后修正过的核心表结构。用户表 t_userCREATE TABLE t_user ( id INT NOT NULL AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL COMMENT 用户名, password VARCHAR(100) NOT NULL COMMENT 密码(MD5或BCrypt加密), real_name VARCHAR(50) DEFAULT NULL COMMENT 真实姓名, phone VARCHAR(11) DEFAULT NULL COMMENT 联系电话, role TINYINT DEFAULT 0 COMMENT 角色 0-普通用户 1-管理员, avatar VARCHAR(255) DEFAULT NULL COMMENT 头像URL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意几个设计细节密码不能存明文至少用MD5加盐能上BCrypt更好用户名一定要加唯一索引否则注册接口会出重复数据手机号是将来“联系领养人”的关键字段不能省略role字段用 TINYINT 而不是字符串省空间且查询快。动物信息表 t_animalCREATE TABLE t_animal ( id INT NOT NULL AUTO_INCREMENT COMMENT 动物ID, name VARCHAR(50) NOT NULL COMMENT 动物昵称, type TINYINT NOT NULL COMMENT 类型 0-猫 1-狗, breed VARCHAR(50) DEFAULT NULL COMMENT 品种, age VARCHAR(20) DEFAULT NULL COMMENT 年龄描述如2个月, gender TINYINT DEFAULT 0 COMMENT 性别 0-未知 1-公 2-母, health_status VARCHAR(100) DEFAULT NULL COMMENT 健康状况描述, description TEXT COMMENT 详细介绍, cover_image VARCHAR(255) DEFAULT NULL COMMENT 封面图片URL, status TINYINT DEFAULT 0 COMMENT 状态 0-待领养 1-已被领养 2-暂不可领养, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 收录时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有个关键点age字段用VARCHAR。为什么不用DATETIME或INT因为流浪动物的年龄往往是个模糊描述——“大概2个月”“成年”存成字符串最灵活查询时也能直接 LIKE。这个取舍答辩时老师问到“为什么这么设计”时你可以理直气壮地解释。领养申请表 t_adopt_applyCREATE TABLE t_adopt_apply ( id INT NOT NULL AUTO_INCREMENT COMMENT 申请ID, animal_id INT NOT NULL COMMENT 动物ID, user_id INT NOT NULL COMMENT 申请人ID, apply_reason TEXT COMMENT 申请理由, status TINYINT DEFAULT 0 COMMENT 状态 0-待审核 1-已通过 2-已拒绝, audit_opinion VARCHAR(255) DEFAULT NULL COMMENT 审核意见, apply_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 申请时间, audit_time DATETIME DEFAULT NULL COMMENT 审核时间, PRIMARY KEY (id), KEY idx_animal_id (animal_id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;外键我故意没建物理外键只加了普通索引。说实话不少学校老师喜欢看外键但物理外键在做联表删除时非常容易出坑。我在实际项目里更倾向用逻辑外键——通过animal_id和user_id关联由代码保证数据一致性性能和灵活性都更好。如果你老师强制要求外键你可以补上但自己要能说清楚是“逻辑外键为主、物理索引为辅”的思路。3.2 扩展表求助表、捐赠表、公告表救助模块我加了一张求助表字段大概是id、user_id(发布人)、animal_type、location、description、image、status(0待受理 1处理中 2已完成)、handler(受理人)、progress。捐赠表则相对简单id、user_id、amount、message、donate_time。公告表就是常规的id、title、content、create_time。这三张表建议都放在第二次迭代再加第一版跑通核心链路后再扩展。我见过太多人一上来就把八张表的关系图画得无比复杂结果编码时一个都连不通。数据库设计一定要跟着功能走功能完成度 80% 时表就该稳定了不要为了“看起来完整”而设计一堆用不上的字段。3.3 状态字典为什么用整数不用字符串状态字段全用 TINYINT 注释这是我强烈推荐的规范。比如动物状态0待领养 1已领养 2暂不可领养前端的下拉框是写死的而后端返回的是数字由前端写出对应的中文标签。这样切数据时日志里看的是status1一眼就知道是“已领养”不用去翻那些冗长的字符串描述。实际开发中把所有状态字段集中写在一个枚举类或常量类里写代码时引用常量能避免魔法数字满天飞的囧境。4. 后端 SpringBoot 核心实现与接口设计4.1 后端项目结构按业务模块分包别按类型分包我见过很多新手把controller、service、mapper三种类型分成三个包然后所有业务的 Controller 全扔在一起。小项目能跑但代码一多就乱成一锅粥。推荐的做法是按业务模块分包com.campus.rescue ├── common // 通用类Result、常量、工具类 ├── config // 配置类CORS、拦截器 ├── controller // 控制层按模块分文件 ├── service // 业务层接口实现 ├── mapper // 数据访问层 ├── entity // 实体类 └── interceptor // 登录拦截器Controller 里只做参数接收和结果返回业务逻辑全部下沉到 Service。比如领养的“审核通过”操作Controller 里就一行adoptService.approve(id, opinion)具体的校验、改状态、更新动物状态都写在 Service 里。一是逻辑清晰二是答辩时老师问“核心业务规则放在哪里”时你能给出漂亮的答案。4.2 统一返回结果类一定不要跳过很多人写接口时直接返回Map或裸对象前端拿到数据后根本不知道是成功还是失败每个接口都要单独判断。正确做法是定义一个统一的ResultT泛型类结构如下public class ResultT { private Integer code; // 200成功500失败 private String message; private T data; public static T ResultT success(T data) { ... } public static T ResultT error(String message) { ... } }这样所有接口返回格式统一前端 Axios 拦截器里做一次判断后续每个页面都能少写几十行错误处理代码。后端代码长什么样不重要重要的是你要把“统一状态码”这个意识烙在脑子里出去工作也是这个套路。4.3 核心接口代码示例动物分页查询和领养申请我把两个核心接口直接贴出来这都是我总结的常用写法你可以直接改吧改吧用。动物分页查询接口用 MyBatis-Plus 实现GetMapping(/animal/page) public ResultIPageAnimal page(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, RequestParam(required false) Integer type, RequestParam(required false) String keyword) { LambdaQueryWrapperAnimal wrapper new LambdaQueryWrapper(); // 类型筛选猫/狗 wrapper.eq(type ! null, Animal::getType, type); // 关键词模糊搜索名称或描述 wrapper.and(StringUtils.hasText(keyword), w - w .like(Animal::getName, keyword).or().like(Animal::getDescription, keyword)); // 只看待领养 wrapper.eq(Animal::getStatus, 0); // 按收录时间倒序 wrapper.orderByDesc(Animal::getCreateTime); IPageAnimal result animalMapper.selectPage(new Page(page, size), wrapper); return Result.success(result); }这个方法里我用了wrapper.eq(条件, 字段, 值)的写法条件不满足时自动忽略该条件。这样前端传不传参数都能正常工作不会再出现“参数为空就报错”的问题。提交领养申请接口PostMapping(/adopt/apply) public Result? apply(RequestBody AdoptApplyRequest request) { // 1. 校验动物是否存在且待领养 Animal animal animalMapper.selectById(request.getAnimalId()); if (animal null || animal.getStatus() ! 0) { return Result.error(该动物不存在或已被领养); } // 2. 校验该用户是否已申请过这只动物 LambdaQueryWrapperAdoptApply wrapper new LambdaQueryWrapper(); wrapper.eq(AdoptApply::getAnimalId, request.getAnimalId()) .eq(AdoptApply::getUserId, request.getUserId()) .eq(AdoptApply::getStatus, 0); Long count adoptApplyMapper.selectCount(wrapper); if (count 0) { return Result.error(您已提交过申请请等待审核); } // 3. 插入申请记录 AdoptApply apply new AdoptApply(); apply.setAnimalId(request.getAnimalId()); apply.setUserId(request.getUserId()); apply.setApplyReason(request.getApplyReason()); apply.setStatus(0); adoptApplyMapper.insert(apply); return Result.success(null); }第 2 步的“重复申请校验”是我在真实使用中才补上的。第一版没加这个判断用户在页面疯狂点提交按钮后台瞬间多了十几条重复申请——这种细节才是高分项目的分水岭。4.4 登录与权限拦截器实现登录我用了最简单的方案用户名 密码查询密码用 BCrypt 校验登录成功生成一个 UUID 作为 Token存到一个内存 Map 中MapString, LoginUser tokenMap。每次请求时拦截器从请求头取出 Token查不到就返回 401。这里有个重要的细节管理员的接口和后端静态资源要放过拦截器。很多人把/api/admin/**拦了结果管理员自己也登不进去还有上传的图片存放在upload/目录下也被拦截器拦了页面图片全部加载失败。拦截器里要明确把登录接口、注册接口、图片访问路径放行。这算是这类项目里排名前三的隐形坑。4.5 事务与并发别忽略这个加分项管理员审核通过领养申请时要同时改领养申请表和动物表。我在 Service 方法上加了Transactional这两步要么都成功要么都回滚。这就是理论课上的“事务一致性”落地了。至于并发场景——两个管理员同时审核同一个申请——这个项目不需要过度设计但你要能在答辩时说清楚“如果有多个人同时操作可以用数据库行锁或乐观锁目前通过前置状态判断已经阻止了大部分冲突”。这句话就能体现你的工程素养。5. 前端 Vue 实现要点与页面结构5.1 前端项目结构与路由设计我用的是 Vue CLI 生成的标准工程目录大概长这样src ├── api // 封装好的接口请求模块 ├── assets // 静态资源 ├── components // 公共组件 ├── router // 路由配置 ├── store // Vuex/Pinia状态管理 ├── views // 页面组件 │ ├── home // 首页 │ ├── animal // 猫狗列表/详情 │ ├── adopt // 领养申请 │ ├── rescue // 救援求助 │ ├── donate // 捐赠 │ ├── user // 个人中心 │ └── admin // 后台管理 └── utils // 工具类axios封装等路由设计上常规页面用懒加载component: () import(../views/animal/AnimalList.vue)。懒加载的意义是首屏加载速度更快Vue 会按需拆分 JS 文件这个优化点在答辩时提出来很加分。5.2 Axios 封装与认证处理前端核心的公共代码就是 Axios 封装。我在utils/request.js里做了统一处理import axios from axios import { Message } from element-ui import router from /router const request axios.create({ baseURL: /api, timeout: 15000 }) // 请求拦截器自动携带Token request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) // 响应拦截器统一处理错误 request.interceptors.response.use( response { const res response.data if (res.code 200) { return res } else { Message.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } }, error { if (error.response error.response.status 401) { Message.error(登录已过期请重新登录) localStorage.removeItem(token) router.push(/login) } else { Message.error(网络异常请稍后重试) } return Promise.reject(error) } ) export default request这套封装做完之后后续写接口调用几乎没有重复的错误处理代码。比如请求动物列表就一行export const getAnimalPage (params) request.get(/animal/page, { params })前端任何一个组件里调用getAnimalPage({page: 1, size: 10, type: 1})直接拿到res.data.records就能渲染表格。这就是“基础设施前置”的威力。5.3 核心页面猫狗列表、详情与领养表单猫狗列表页我用 Element UI 的el-card和el-col做卡片网格布局每个卡片显示封面图、名称、类型标签、状态标签和“查看详情”按钮。搜索区用el-select选择类型、el-input输入关键词、el-pagination做分页。这些组件不用你自己写样式拖进来就能用几天时间就能把整套后台管理UI搭完。领养表单页则需要用好el-form的校验规则。申请理由设置为必填、最小长度 10 个字手机号如果需要申请人填就用validator校验 11 位手机号格式。提交成功后跳转到“我的申请”页面用el-timeline时间线展示审核进度——这个时间线组件做出来视觉效果特别好演示视频里是很出彩的一笔。5.4 前端跨域与生产环境代理配置本地开发时前端跑在http://localhost:8080后端跑在http://localhost:9090必然存在跨域问题。按老办法是在后端写 CORS 配置类但更省事的是利用 Vue CLI 的 devServer 代理// vue.config.js module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:9090, changeOrigin: true } } } }前端把请求路径设为/api/xxx开发时代理到后端的9090端口。后端接口路径统一以/api开头两边的界限非常清楚。生产部署时更省心的是把前端打包后的dist目录整个复制到 SpringBoot 的static目录下。前端请求/api后端在同一个端口不需要反向代理也不需要 Nginx一个 SpringBoot 进程全搞定。这是我最推荐的单人毕设部署方案省掉了大量服务器配置时间。6. 数据库初始化与环境搭建6.1 建库建表与初始数据在 MySQL 里建库CREATE DATABASE IF NOT EXISTS rescue_platform DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;用 Navicat 或命令行执行建表 SQL。注意不要忘了插入初始管理员账号INSERT INTO t_user (username, password, role) VALUES (admin, 这里填BCrypt加密后的密文, 1);密码必须用后端工具类生成的 BCrypt 密文不要直接写123456进去否则你登录时 BCrypt 比对永远不通过。我踩过这个坑后来学乖了在项目里写了一个GeneratePasswordUtil测试类用来专门生成密文。初始数据也要准备几条猫狗记录最好插三只待领养的猫、两只待领养的狗公母、年龄、品种尽量有区分度。有数据才能测试搜索、筛选、分页这些功能——空表跑起来所有列表页面都是白板调试时连有没有 bug 都看不出来。6.2 前后端启动步骤整个项目运行分四步我按顺序写清楚第一步启动 MySQL确保rescue_platform数据库存在检查编码是不是 utf8mb4第二步启动后端IDEA 打开后端工程修改application.yml中的数据库密码运行RescueApplication.java主类第三步启动前端命令行进入前端目录npm install安装依赖npm run serve启动开发服务器第四步浏览器访问http://localhost:3000分别测试未登录状态、普通用户登录、管理员登录三种场景如果你碰到npm install特别慢的情况就用淘宝镜像源npm config set registry https://registry.npmmirror.com6.3 Maven 配置与版本兼容后端依赖用 Maven 管理pom.xml里核心依赖就这几个spring-boot-starter-webmybatis-plus-boot-starter我用的是 3.5.xmysql-connector-javalombokspring-boot-starter-validationspring-boot-starter-test版本上有个容易踩的坑SpringBoot 2.x 配 Java 8 是官方稳妥组合如果你本地装的是 JDK 17就要注意 SpringBoot 版本别太老。单纯做毕设我建议直接上SpringBoot 2.7.x JDK 8 MySQL 5.7 或 8.0这个组合网上资料最多踩到坑也最容易搜到答案。没必要追求最新版本方案稳定比什么都强。7. 常见问题与排查技巧实录7.1 启动类报错与数据库异常这类项目最容易出的问题我在下面按“踩坑频率”列成速查表症状原因解决办法后端启动报Access denied for userapplication.yml 里数据库密码错误检查配置文件使用本机 MySQL 正确用户名密码后端启动报Unknown database数据库没建或名称不一致连 MySQL 执行建库语句核对库名Failed to configure a DataSourceSpringBoot 启动时找不到数据源配置确认引入了 JDBC/MySQL 依赖且配置正确前端请求报404代理目标端口或路径前缀不对核对 Vue 代理的 target 是否指向后端实际端口前端请求报CORS没走代理直接跨域访问使用 devServer 代理或后端配置 CORS 过滤器上传图片无法访问拦截器拦截了静态资源放行/upload/**路径或配置资源映射登录一直失败BCrypt 密文不是对应明文或数据库存了明文用后端工具类重新生成密文确保比对逻辑正确前端npm run serve端口被占用8888/8080 端口被系统应用占用改vue.config.js里的devServer.port7.2 我真实经历过的三个疑难坑第一个坑是图片上传后的路径问题。本地开发时图片上传到项目根目录/upload/用绝对路径访问没问题但打成 jar 包部署后相对路径会随 JVM 启动目录变化导致所有图片 404。解决方法是把上传路径配置成外置绝对路径比如D:/upload/并在配置类里注册虚拟路径映射代码里统一用接口返回的 URL 而不是绝对路径。第二个坑是 MyBatis-Plus 的selectPage返回总数不对。排查后发现是数据库表没有设置id主键自增导致分页插件统计COUNT时走了复杂查询。解决方案很粗暴检查所有表的id字段是否为NOT NULL AUTO_INCREMENT并把主键加上分页立刻正常。第三个坑是前端打包放进 SpringBoot 后刷新页面 404。因为 Vue 是单页应用路由用 history 模式时刷新/animal/1这个地址后端没有对应的静态资源映射就直接返回 404。解决办法有两个要么把 Vue 路由改成 hash 模式要么在后端写一个ErrorController把非/api开头的未知路径全部转发到index.html。我图省事直接用了 hash 模式地址栏带个#虽然丑了点但胜在绝对可靠。7.3 避坑原则与代码质量自查清单做到最后我给你一份项目自检清单提交之前逐条过一遍后端所有接口统一返回ResultT不允许裸返回 Map密码字段在任何日志和前端响应中都不会出现领养审核通过时动物状态同步修改且在同一事务里列表接口支持分页和条件查询默认按时间倒序前端所有请求都走封装后的 Axios 实例统一带 Token 和错误处理管理员接口有权限校验普通用户无法直接调用数据库字符集统一 utf8mb4没有字段乱码风险图片使用 URL 访问不直接存数据库大字段项目有README.md写明启动步骤和测试账号这九条全部打勾你的项目完成度就已经超出大多数同届学生了。哪怕功能少一点工程规范性和数据设计的条理性在答辩时往往比“为了显摆写了个炫酷但没人用的功能”更能获得老师的认可。毕竟评审老师看的核心还是你有没有把一个需求完整地落地、跑通、并解释清楚——这个目标这套 SpringBootVue 的流浪猫狗救助平台一定能帮你实现。
返回列表