简介:这套宠物领养系统是一份面向2025届毕业设计/课程设计的完整项目资源,选题聚焦现实中的动物领养场景,适合计算机科学与技术、软件工程等专业学生参考。项目采用主流前后端分离模式:后端用SpringBoot3简化业务开发,前端以Vue.js3构建交互界面,MySQL8负责用户、宠物和领养申请等数据的持久化,流程覆盖宠物展示、注册登录、领养申请、后台审核与管理等环节,同时从用户隐私和SQL注入防护角度做了安全考量。资源包共6个文件、总大小103.3MB,包含三个zip压缩包(主源码、返修源码和返修文档)、一个sql数据库脚本、一张需求文档截图和一段mp4操作录屏,能够支撑从环境搭建到功能验证的完整学习过程。目前已有128人学习/下载。参考本资源时,可以重点学习前后端接口交互、数据表设计、状态流转实现等核心点;录屏教程能帮助快速启动项目,需求文档辅助理解整体功能边界。整体而言,这是一套结构清晰、资料齐全的实战型毕设方案,对想掌握SpringBoot3+Vue3开发流程的读者颇具价值。
1. 宠物领养系统 JAVA+SpringBoot3+Vue.js3:从选题到答辩这份毕设源码的完整拆解
做毕设最怕的不是不会写代码,而是选了一个自己撑不起来的题目。宠物领养系统听起来简单,但真动手才发现它牵扯的东西比预期多:领养流程有状态流转、宠物信息有多条件检索、用户有角色权限、后台有审核闭环,前端还得把地图、相册、表单校验这些体验做出来。这套 JAVA+SpringBoot3+Vue.js3 的毕业设计源码正好卡在一个微妙的平衡点上——功能量够写一篇像样的论文,技术栈又没冷门到答辩时被问倒。它的完整度接近一个能直接上线的商用模板:登录注册、宠物发布与浏览、领养申请、后台审核、管理员面板都是现成的,拿到手改改名字和细节就能跑。适合三类人:Java 后端基础不牢想攒实战经验的新手、论文已经开题只差可运行系统的学生、以及想用最短时间完成课程设计交付的在职党。这篇笔记按我实际拆项目的顺序来写:先看结构和环境,再跑通核心链路,最后讲我复现两遍才踩平的坑。
2. 工程全景与启动环境:先搞清楚这包代码怎么组织,再动手跑
2.1 前后端分离的双工程结构,目录上先做减法
解压这份资源后,你会看到两个独立工程目录,前端叫pet-frontend,后端叫pet-backend,这是 SpringBoot3 + Vue3 项目最常见的组织方式。前端是 Vite 脚手架生成的标准 Vue3 工程,核心代码集中在src下:api目录按模块拆 Axios 请求,views目录按角色拆页面(管理员、普通用户、领养审核页),router里做路由守卫。后端是 Maven 工程,包名通常是com.example.pet或个人学号,三层结构清晰:controller只做参数接收和结果包装,service写业务逻辑,mapper是 MyBatis-Plus 的接口层。如果你刚拿到源码,我建议先别急着启动,花 10 分钟把目录过一遍,认清资源给你的是「可运行系统」,不是「示例片段」。
环境方面有几个硬性要求,缺一个都会在启动时报出莫名其妙错误。JDK 必须是 17 及以上,SpringBoot3 底层是 Jakarta EE,JDK8 连依赖都解析不出来;Node.js 建议 18 LTS 或更高,Vue3 的生态工具链在高版本下兼容性更好;Maven 用 3.8+ 即可,IDE 用 IntelliJ IDEA 会比较顺手,Eclipse 对 Lombok 插件的支持不如 IDEA 稳定。建议先建一个干净的目录,把前后端源码放进去,再分别执行下面命令验证依赖环境。
# 检查 Java 版本,必须输出 17 或更高 java -version # 检查 Maven 版本,3.8 以上比较稳 mvn -v # 检查 Node 版本,18 及以上 node -v # 检查 npm 包管理器可用 npm -v这里每个命令都有一个实际意义。Java 版本决定了 SpringBoot3 能否加载jakarta.servlet相关类,版本低了启动直接报NoClassDefFoundError;Maven 版本影响依赖下载可靠性和spring-boot-maven-plugin的兼容性;Node 版本关系 Vite 的构建效率,旧版本可能不支持某些 ES 新语法。四项全过,再往后跑就有底了。
2.2 数据库初始化:脚本建库、初始数据、连接参数一次性配齐
这套系统的数据层用的是 MySQL 5.7 或 8.0,资源包里附带了pet_db.sql初始化脚本,通常在backend/sql或doc目录。脚本内容包含建库语句、十几张业务表(用户表、宠物表、领养申请表、宠物类型表等)以及必要的初始数据——比如管理员账号、宠物类别枚举。我惯用的做法是先手动建一个空库,再导入脚本,这样能避免脚本里CREATE DATABASE与本地已有库冲突。导入后检查两张关键表的行数,确认数据没丢,比如用户表应该有一条admin记录,宠物表至少有几条演示数据,这两个检查项能在后面前后端联调时帮你快速区分「代码 bug」还是「数据缺失」。
后端数据库连接配置集中在application.yml里,这是启动前必改的一处。如果不改,IDEA 里会看到 MyBatis 报一堆Communications link failure。配置本身简单,但要注意 MySQL8 的驱动类名是com.mysql.cj.jdbc.Driver,MySQL5.7 用com.mysql.jdbc.Driver也能跑但会有弃用警告。下面是一个我改完能直接用的最小配置块。
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/pet_adoption?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的密码 servlet: multipart: max-file-size: 10MB max-request-size: 50MB提示:serverTimezone=Asia/Shanghai这一项很关键,不配置的话 Java 8+ 时区处理会跟 MySQL 的 UTC 默认时区打架,时间字段存进去和查出来差 8 小时,在领养申请的「提交时间」这类字段上会直接出问题。max-file-size配置的是宠物图片上传上限,毕设答辩演示时传手机拍的照片,10MB 足够,不要设太大,否则前端等待上传的时间会很难看。
2.3 前端启动前的固定动作:装依赖、配代理、看默认端口
前端项目拿到手,第一步永远是在根目录执行npm install。由于 lock 文件通常已提交,这步理论上很稳,但国内网络源容易出现 node-sass 或 electron 这类二进制包下载卡住,可以用 npmmirror 镜像来兜底。装完依赖后,vite.config.js里有一个开发代理配置,它把前端地址/api开头的请求转发到后端 8080 端口,你在浏览器里看到的localhost:5173页面可以无缝请求localhost:8080的接口。这套代理是前后端分离项目的标配,不用 Nginx 也能联调。
// vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true, // 如果后端接口前缀不带 /api,就打开重写 // rewrite: (path) => path.replace(/^\/api/, '') } } } })把这段配置看完,你基本理解开发模式下前后端是怎么协作的。changeOrigin: true是为了让后端收到的请求头里的 Host 变成后端地址,避免一些后端校验 Origin 的逻辑误判;rewrite这行注释掉的代码是处理路径前缀的,如果你的后端Controller里的@RequestMapping本来就带/api,就需要把它打开,否则请求会变成/api/api/...。不管毕设论文里怎么写架构图,这个代理机制建议在答辩时提一句,能显示你是真跑通过联调的,而不是只贴了代码。
3. 后端核心链路拆解:SpringBoot3 + MyBatis-Plus 下的领养业务实现
3.1 实体设计与数据表映射:MyBatis-Plus 的注解用法
后端这块,作者用了 MyBatis-Plus 而不是原生 MyBatis,这是一个很适合毕设的选择——单表 CRUD 几乎不用写 SQL,内置的BaseMapper提供现成的selectById、insert、updateById等方法,代码量能少一半。实体类上用@TableName注解映射表名,用@TableId标记主键,用@TableField处理驼峰和下划线字段名的转换。下面这组代码是从宠物表实体里摘出来的典型写法,领养系统这类业务表都差不多的套路。
@Data @TableName("pet") public class Pet { @TableId(type = IdType.AUTO) private Long id; private String name; private String breed; private Integer age; private String gender; private String status; @TableField("cover_url") private String coverUrl; @TableField("create_time") private LocalDateTime createTime; }这段代码的逻辑是:@Data来自 Lombok,自动生成 getter/setter,一个实体类省几十行样板方法;@TableId(type = IdType.AUTO)声明主键自增,插入宠物记录时不用手动赋值 id;@TableField("cover_url")解决 Java 驼峰属性名与数据库下划线字段名的映射,MyBatis-Plus 默认开启驼峰转换,但显式声明更保险。参数方面要注意LocalDateTime对应的数据库类型应当为datetime,如果你在建表脚本里写的是timestamp,在跨时区环境下会有精度或偏移问题。
3.2 领养申请与状态机流转:事务注解的正确放置点
领养申请是整个系统的核心业务,它的状态通常分为待审核、已同意、已拒绝、已完成四档。用户的「提交申请」动作和后端管理员的「审核通过」动作,在代码里都涉及两个以上表的状态变更——申请记录表要插数据,宠物表状态要改成被领养或待审核。这类操作一定要加事务,否则用户在填写完申请、点击提交的一瞬间系统异常,会出现申请记录已写入但宠物状态未改变的数据错乱。ServiceImpl 里通常会这么写:
@Transactional(rollbackFor = Exception.class) public boolean applyAdopt(AdoptApplyDTO dto) { // 1. 校验用户是否重复申请同一宠物 Long count = applyMapper.selectCount( new LambdaQueryWrapper<AdoptApply>() .eq(AdoptApply::getUserId, dto.getUserId()) .eq(AdoptApply::getPetId, dto.getPetId()) ); if (count > 0) { throw new BizException("您已申请过该宠物,请勿重复提交"); } // 2. 写申请表 AdoptApply apply = new AdoptApply(); BeanUtils.copyProperties(dto, apply); apply.setStatus(AdoptStatusEnum.PENDING.getCode()); applyMapper.insert(apply); // 3. 同步修改宠物状态为申请中 Pet pet = petMapper.selectById(dto.getPetId()); pet.setStatus(PetStatusEnum.APPLYING.getCode()); petMapper.updateById(pet); return true; }这段代码做了三件事。第一,通过 LambdaQueryWrapper 查询当前用户对同一宠物的申请数,大于零直接抛业务异常,这是防止用户狂点提交按钮刷出多条申请的兜底逻辑。第二,插入一条申请记录,状态置为待审核(PENDING)。第三,把宠物的状态从「可领养」改到「申请中」,避免其他用户同时申请同一只宠物。注意rollbackFor = Exception.class,因为 Spring 默认只对运行时异常回滚,而BizException这类自定义异常通常继承自 RuntimeException,显式声明能让所有异常都触发回滚。实际项目里我还习惯在service层接口注释里写清楚这个方法的调用场景,不然别人接手时会跟前端的「收藏」功能搞混——收藏不改变宠物状态,申请才改变。
3.3 权限控制与接口安全:JWT 拦截器排除路径的设置逻辑
毕设系统一般用 JWT 做登录态管理,用户登录时后端签发一个带过期时间的 token,前端每次请求携带到 Authorization 头。资源里后端会有一个JwtInterceptor或AuthInterceptor,它拦截所有/api/**请求,但放行登录、注册、首页宠物列表等匿名接口。配置拦截器时最常踩坑的点就是放行路径的写法,用/**匹配多级路径,用/*只匹配一级。下面这段是资源里典型配置的精简版,我复现时调整过两处路径才让前端跑通。
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Resource private JwtInterceptor jwtInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) .addPathPatterns("/api/**") .excludePathPatterns( "/api/user/login", "/api/user/register", "/api/pet/list", "/api/pet/detail/**", "/error" ); } }参数说明:addPathPatterns("/api/**")表示拦截所有以/api/开头的接口;excludePathPatterns里列的是不需要 token 就能访问的端点。这里有两处细节容易出错。第一,/api/pet/detail/**的/**匹配后边的任意宠物 id,如果你漏掉了这条,前端浏览宠物详情页时不带 token 会直接被拦截器拒绝,返回 401。第二,/error也要放行,否则拦截器内部抛出异常后 Spring Boot 的默认错误转发路径会被再次拦截,导致前端收到的是空响应而不是 JSON 错误体。JWT 校验逻辑一般在拦截器的preHandle方法中:解析 header 里的 token,用密钥验签,若过期或签名非法则直接response.sendError(401),只要这步过关,后续 Controller 参数里用@RequestAttribute("userId")就能取到当前登录用户 id。
3.4 文件上传与静态资源映射:宠物图片的本地存储方案
宠物领养系统离不开图片展示,资源里后端上传接口采用了本地磁盘存储方案——文件保存到项目某目录,然后把 URL 返回给前端存入数据库。这个方案比云存储好部署,也够答辩展示。上传接口接收 MultipartFile,判断文件类型和后缀名是必要的体检,否则一个 .jsp 文件伪装成图片传上来会有安全隐患。核心代码如下:
@PostMapping("/upload") public Result<String> upload(@RequestParam("file") MultipartFile file) { if (file.isEmpty()) { return Result.error("文件不能为空"); } String originalFilename = file.getOriginalFilename(); String ext = originalFilename.substring(originalFilename.lastIndexOf(".")); List<String> allowExt = Arrays.asList(".jpg", ".jpeg", ".png", ".gif"); if (!allowExt.contains(ext.toLowerCase())) { return Result.error("仅支持 jpg/png/gif 格式的图片"); } String fileName = UUID.randomUUID().toString() + ext; String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")); File dir = new File(uploadPath + datePath); if (!dir.exists()) { dir.mkdirs(); } file.transferTo(new File(dir, fileName)); String url = "/upload/" + datePath + "/" + fileName; return Result.success(url); }代码里的关键守则:扩展名白名单校验是必须的,UUID.randomUUID()生成随机文件名是为了防重名覆盖;按日期分目录存放,既避免单目录文件过多,又方便后期排查。与此同时,SpringBoot 默认不映射/upload/**到磁盘真实路径,你必须在配置类里加一个WebMvcConfigurer的addResourceHandlers方法,把/upload/**映射到本地磁盘目录,不然前端<img src="/upload/xxx.jpg">会一直 404。这个资源和拦截器配置写在同一个类里即可,我习惯把路径写成配置文件里的变量,因为 Windows 和 Linux 的路径分隔符处理方式不同,硬编码会让搬运到服务器时多改好几个地方。
4. 前端 Vue3 交互体系:从路由守卫到领养申请全流程
4.1 路由与权限:动态标题、登录拦截、角色分流一体的 router 配置
前端项目用的是 Vue Router 4,路由表里定义了两个顶层容器:/布局组件(包含导航栏和底部)和/admin布局组件(管理员后台)。前端权限控制的思路是:所有需要登录的页面打上meta.requiresAuth: true,路由守卫读取本地 token 判断是否放行。admin路由下嵌套的所有页面用meta.role: 'admin'标记,守卫里再校验当前用户角色。下面这段是在permission.js或router/index.js里写的核心守卫逻辑:
// 全局前置守卫 router.beforeEach((to, from, next) => { document.title = (to.meta.title ? to.meta.title + ' - ' : '') + '宠物领养系统' const token = localStorage.getItem('token') if (to.meta.requiresAuth && !token) { next({ path: '/login', query: { redirect: to.fullPath } }) return } if (to.meta.role && token) { const role = localStorage.getItem('role') if (role !== to.meta.role) { next('/') return } } next() })这段逻辑很直白:第一段在每次路由跳转时把浏览器标签页标题改成「当前页面 - 宠物领养系统」,这个细节答辩演示时能加分实际使用也很顺手;第二段判断目标页面是否要求登录——如果要求且本地没 token,就跳去登录页并带上redirect参数,登录成功后前端可以用这个参数跳回原目标页;第三段是管理员后台的双重校验,token 存在但角色不是 admin 则踢回首页。注意这里的角色信息是在登录接口返回值里由后端下发的,前端拿到后存 localStorage。这种做法的缺陷是用户手动改 localStorage 里的 role 就能绕过前端限制,但后端的 JWT 和拦截器还在,真正敏感接口仍然安全,毕设层面完全够用。
4.2 Axios 封装:token 注入、响应拦截与错误提示的代码细节
资源前端的api/request.js是对 axios 实例的二次封装,几乎所有页面都通过它发起 HTTP 请求。封装的核心是拦截器:请求拦截器里把 localStorage 里的 token 塞进请求头;响应拦截器里统一处理错误码——比如 token 过期返回 401 时清除本地登录信息并跳到登录页。封装的好处是,如果后端接口返回格式统一为{ code, message, data },前端所有页面都能少写一堆错误判断。以下是这份资源里典型的封装方式:
import axios from 'axios' 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.data } if (res.code === 401) { localStorage.removeItem('token') localStorage.removeItem('userInfo') window.location.href = '/login' } ElMessage.error(res.message || '请求失败') return Promise.reject(new Error(res.message)) }, error => { ElMessage.error(error.message || '网络异常,请检查后端服务是否启动') return Promise.reject(error) } )这里的几个参数决定前端所有接口的体验。baseURL: '/api'配合 vite 代理,开发环境和后端发布的接口前缀保持一致;timeout: 15000是给慢接口的兜底,用户点击领养申请后如果后端 MyBatis 查询慢或网络卡,15 秒内必须报错而不是无限等待。响应拦截器把「HTTP 状态码」和「业务状态码」分开了处理——HTTP 层 2xx 都进第一个回调,业务层再细看res.code是否等于 200,这样后端即使是 500 错误,只要返回结构仍是{code: 500, message},前端也能拿到清晰提示而不是白屏。我个人习惯把ElMessage的提示时间设为 2 秒,登录失败的「用户名或密码错误」不至于遮住表单太久。
4.3 领养申请全交互链路:状态联动、表单校验、二次确认
前端「申请领养」是这套系统最完整的一条交互链路,涉及三个文件的配合:宠物详情页PetDetail.vue、领养申请表单ApplyForm.vue、个人中心申请记录页MyApply.vue。用户浏览宠物详情时,后端返回的宠物状态如果是「可领养」,页面上就渲染「申请领养」按钮;如果这宠物已经有别人申请、状态变成「申请中」,按钮就该置灰不可点。详情页拿到宠物数据后通常这么判断:
const canApply = computed(() => { return pet.value.status === 'AVAILABLE' && (role.value === 'USER' || !role.value) })computed在这里做响应式计算,宠物状态或角色一变,按钮状态立即刷新。角色判断的目的是防止管理员在后台详情页也看到「申请领养」按钮——管理员只负责审核,不应该申请。表单校验方面,领养申请通常要求填自我介绍、居住情况、养宠经验这些字段,validata规则里必填项和字数限制都有。用户点击提交后,前端组装 payload 调applyAdopt接口,成功之后要弹一个提示并跳转到个人中心的申请记录页,用户在列表里能看到自己的申请是「待审核」状态。这个链路看起来简单,但实际开发里翻车点往往在「提交后宠物状态立即变化」——如果接口请求成功后前端不重新拉宠物详情,页面上的宠物状态还是「可领养」,用户再点一次申请按钮就会拿到后端的「重复申请」报错。因此代码里这样写:
const handleApply = async () => { await formRef.value.validate() await applyApi.submit(form.value) ElMessage.success('申请提交成功,请等待审核') await getPetDetail() router.push('/my/apply') }这里的关键是await getPetDetail(),把宠物详情重新拉一遍,让pet.status响应式更新成「申请中」,这行代码决定了用户提交后按钮是否立即变灰。后续审核结果同步到个人中心,是通过「我的申请」列表接口实现的,列表每一项会根据后端返回的status字段渲染标签颜色和操作按钮——已拒绝的申请可以删除,已同意的申请引导用户去详情页查看联系方式,这样整个业务闭环就算完整了。
5. 部署与联调避坑指南:启动失败、数据错乱和图片 404 的完整排查
5.1 后端启动即报错:Failed to configure a DataSource
现象:在 IDEA 里直接运行PetApplication.java,控制台一两秒后出现Failed to configure a DataSource: 'url' attribute is not specified然后进程退出。原因:SpringBoot3 的自动配置启动时就要连数据库,而application.yml里没有正确加载数据源配置,常见情况是本地没有 MySQL 服务、密码不一致、或者配置文件放错目录。解决:先确认 MySQL 服务已启动,Linux 用systemctl status mysqld,Windows 任务管理器里看mysqld进程;再确认application.yml确实在src/main/resources下且配置键名无误;最后可以用下面命令快速测试数据库连通性,这样拿结果说话最高效。
# 用 Java 的 sql 客户端直接测试连接,超出 3 秒连不上先查 MySQL 端口和防火墙 mysql -h 127.0.0.1 -P 3306 -u root -p密码 -e "select 1"提示:如果你用的是 MySQL8,且 MySQL 服务的根用户允许的认证插件是
caching_sha2_password,SpringBoot 的驱动要配useSSL=false或allowPublicKeyRetrieval=true,否则即使密码正确也可能报认证失败。
5.2 前端打开就是白屏,控制台报Cannot GET /
现象:npm run dev跑起来后浏览器访问localhost:5173返回空白,控制台提示Cannot GET /。原因:路由模式用的是createWebHistory,Vite 开发服务器对刷新后的深层路径(如/login)没有 fallback,或者路由表里根路径/的重定向指向了一个不存在的组件。解决:先确认router/index.js的/路由有没有redirect到/home或component: Layout指向正确路径;再检查 Vite 配置里没有开historyApiFallback,Vite 的 dev server 默认支持,但如果你是对着旧 Vue2 webpack 配置改的容易漏掉。我实际遇到过把 Layout 组件的路径从@/layout/index.vue改成@/layout/Index.vue后 Linux 下大小写不匹配导致组件解析失败的,这种错误看控制台会直接指向路由文件。
5.3 图片上传成功但访问 404,控制台显示Failed to load resource
现象:上传接口返回{"url": "/upload/2025/04/01/uuid.jpg"},但浏览器打开这个地址 404。原因:后端没有配置静态资源映射。SpringBoot 默认只映射classpath:/static/下的静态文件,你自己定义的磁盘目录必须显式声明。解决:在config包里补一个WebMvcConfigurer,用registry.addResourceHandler("/upload/**").addResourceLocations("file:" + uploadPath)映射。同时要注意uploadPath是绝对路径还是相对路径——相对路径会让映射找不到目录。
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { String path = System.getProperty("user.dir") + "/upload/"; registry.addResourceHandler("/upload/**") .addResourceLocations("file:" + path); }5.4 Windows 能正常跑,部署到 Linux 服务器后接口通但图片不显示
现象:本机联调一切正常,打成 jar 包放服务器上,接口能查数据,但所有宠物图片裂图。原因:路径分隔符和当前工作目录。Windows 下File.separator是\,Linux 下是/,如果你在代码里把路径硬编码成了带盘符的形式,到 Linux 一定爆。解决:所有文件路径统一用System.getProperty("user.dir")拼接,或干脆在application.yml配置一个upload.path属性,启动时用@Value注入。文件存储不要放在 jar 包同级目录之外的随机路径,最好统一放在 jar 包目录的upload文件夹,方便备份和迁移。
5.5 领养审核通过后,管理端用户列表和宠物列表状态不同步
现象:管理员在后台把申请状态改为「已同意」后,去宠物列表页面刷新,宠物状态还是「申请中」。原因:审核接口在AdoptApplyServiceImpl里只更新了申请表的status字段,没有同步更新pet表的status。解决:在审核方法里加一段宠物状态联动更新——同意时将宠物状态置为「已领养」,拒绝时把宠物状态从「申请中」改回「可领养」。这个问题的关键不在代码有多难,而在于拆代码时很容易漏看状态机设计文档。建议把状态流转写清晰:
AVAILABLE(可领养) → APPLYING(申请中) → ADOPTED(已领养) APPLYING → REJECTED(申请拒绝) → AVAILABLE(可领养)补上这段业务后,建议写一个简单的单元测试或直接用 Postman 走一遍:申请→拒绝→再申请→同意,四个状态节点全部核对一遍再继续往下开发,能帮你省下答辩前夜改状态分支的时间。
6. 生产环境切换技巧:用 Spring Profiles 把开发环境与服务器配置分离
这份资源默认的开发配置直连本地 MySQL、文件存本地、接口直接暴露,适合课程设计演示。但如果你想把它放到真实服务器上跑几天给朋友体验,或者答辩老师现场要求「在服务器上演示」,这套默认配置就不太够用了——本地改配置、重新打包、再上传指纹文件的做法不仅繁琐,还容易把调试代码带到生产。我一般会这么做:引入application-dev.yml和application-prod.yml两套文件,把差异化配置抽出来,再用spring.profiles.active切换。
# application.yml 主配置里只放公共配置 spring: profiles: active: dev# application-prod.yml 生产配置 server: port: 8080 spring: datasource: url: jdbc:mysql://你的服务器IP:3306/pet_adoption?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: pet_admin password: 强密码 servlet: multipart: max-file-size: 10MB mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# application-prod.yml 生产环境可以关闭 SQL 日志 spring: datasource: url: jdbc:mysql://服务器IP:3306/pet_adoption?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl打包后启动命令变成一句:
java -jar pet-backend-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod个人习惯,两个配置文件的公共部分尽量收敛到主文件里,差异只用密码、地址、日志开关这种真正需要切换的项。这样在 IDEA 里开发用默认dev配置,上线时加一个参数切到prod,不需要动任何源码。验证配置是否生效很简单——看启动日志里The following 1 profile is active: "prod"这行输出,确认无误再开始下一步。从那以后我每次上服务器都会强制走一遍这条流程:本地跑通、切 prod 配置、打包、服务器启动、看日志确认 profile 生效、用浏览器走一遍核心链路,五步缺一不可。这也是拆这套宠物领养系统源码时最值得记住的一条习惯,希望帮到你。
本文还有配套的精品资源,点击获取