又到了一年一度的毕业设计季,每年这个时候后台咨询量最大的三件事就是:毕设选什么题、系统怎么做、论文怎么熬。如果你正在为选题发愁,或者手里已经拿了一套“SpringBoot+Vue+MySQL校园失物招领系统”,却不知道怎么把它讲清楚、写明白、交得出手,那这篇文章就是给你准备的。
这套东西说白了就是一套标准的前后端分离项目,后端用SpringBoot做接口服务,前端用Vue搭页面,数据库用MySQL存数据,三者合起来解决一个很接地气的问题——校园里丢了东西怎么找、捡了东西怎么还。别小看这个选题,它在毕业设计里的地位非常稳,为什么?因为业务逻辑清晰、技术栈主流、功能扩展空间大,而且做出来的东西是真能用的,不是那种纯为了答辩造出来的空中楼阁。
这篇文章我会从需求设计、核心功能、数据库建模、论文组织到部署上线,把整套项目按我自己做毕设时的思路,一五一十拆给你看。不管是打算照着做一个,还是已经拿到源码想彻底搞懂它,读完你都能心里有底。
1. 项目整体设计与思路拆解
1.1 为什么选失物招领做毕设
选毕业设计题目,最怕就是两种情况:一种是题太大,做着做着发现能力撑不住;另一种是题太虚,做完了也不知道解决了什么问题。失物招领恰好避开了这两头。
从业务角度说,每个学校都有失物招领的需求。表白墙、QQ群、贴吧里隔三差五就有人发“寻物启事”或者“捡到一张校园卡”,但这种信息散落在各个平台,没人统一管理,丢了东西的人不知道去哪找,捡了东西的人也不知道交给谁。一个集中的线上失物招领平台,解决的就是这个信息不对称的问题。
从技术角度说,这个系统的复杂度对本科生来说刚刚好。需要用户注册登录、失物和拾物的发布管理、类型分类、模糊搜索、认领流程、后台管理,这些功能覆盖了增删改查的所有基本形态,又不需要处理支付、实时通信、高并发这些难点。用SpringBoot+Vue把前端后端打通,再配上MySQL,整个链路完整且可控。
我当初选这个题还有一个原因——它有明确的核心矛盾。就是“怎么把一条失物信息和一条拾物信息高效地匹配起来”。这个矛盾点既是系统设计上的难点,也是论文里可以写出深度的地方,答辩的时候也有东西可讲。
1.2 技术栈选型的逻辑
技术选型上,主流组合就是SpringBoot + Vue + MySQL。这个组合在毕业设计中几乎成了“默认配置”,不是说它一定最好,而是它最稳妥。
后端用SpringBoot的理由很直接:内置Tomcat,不需要额外部署容器;Spring框架自带的依赖注入和面向切面编程能力,让业务代码组织起来很清晰;起步依赖机制极大地简化了Maven配置。换句话说,你写代码的时间可以更多花在业务逻辑上,而不是花在配环境、调配置上。
前端选Vue,核心理由是组件化的开发方式和渐进式的学习曲线。失物招领系统页面不少——首页、失物列表、拾物列表、详情页、发布页、个人中心、后台管理,如果全部用传统方式直接操作DOM,代码量会爆炸。Vue的组件化开发,把每个页面或页面的一部分抽象成组件,数据驱动视图更新,写起来非常舒服。
MySQL就不用多说了,关系型数据库,对校园这种规模的数据量(几千条失物信息撑死了),性能绰绰有余。更重要的是,它有完整的ACID事务支持和成熟的数据约束机制,对羊学生来说,SQL写起来直观,出了数据问题也好排查。
1.3 系统角色与核心流程
这套系统我设计了三个角色:普通用户、系统管理员、访客。
- 访客:可以浏览失物信息列表和拾物信息列表,查看公共信息,但不能发布信息。
- 注册用户:除了浏览,还能发布寻物启事和拾物招领、修改自己发布的信息、发起认领申请、确认认领结果。
- 管理员:负责审核用户发布的信息、管理用户账号、处理认领纠纷、查看系统数据统计。
核心的业务流程设计上,我重点处理了两个关键场景:
第一个是“我丢了东西怎么办”。用户登录后点击发布失物信息,填写物品名称、类别、丢失地点、丢失时间、详细描述、图片。发布成功后,这条信息进入失物列表,同时系统会尝试匹配库里的拾物记录,看有没有类别相同、地点相近的,有的话在“可能匹配”区域做提醒。
第二个是“我捡到了东西怎么办”。用户发布拾物信息,登记物品信息后,信息进入拾物列表。当失主在列表里发现疑似自己丢失的物品时,可以发起认领申请,提交证明材料。拾物发布者看到申请后,可以同意或拒绝。同意后,双方可以约定时间地点交接物品,最终由失主确认“已找回”,这条记录就被标记为已认领。
这里有个细节我踩过坑:认领流程不能设计得太复杂。最初我加了在线聊天、实时通知、信用积分一大堆设计,后来发现工作量扛不住,而且论文不好写。毕设的核心是把闭环跑通,不是把功能做得多花哨。
2. 核心功能模块与实操实现
2.1 用户模块:认证与权限设计
用户模块是整套系统的地基。没有用户体系,失物和拾物的发布、认领流程全都无从谈起。
注册登录这块,我用的是Spring Security + JWT的方案。JWT(JSON Web Token)这种东西,说白了就是个“一次性通行令牌”。用户登录成功后,后端签发一个包含用户ID和角色信息的加密令牌返回给前端。前端拿到后存起来,之后每次请求都带上这个令牌,后端校验令牌有效就放行,不需要在服务端存session了。
这样做的好处有两个:一是天然适合前后端分离架构,前端不管是Web还是小程序都能共用一套接口;二是后端无状态,横向扩展方便。缺点是令牌在有效期内没法主动吊销,但对毕设这个规模来说完全够用。
密码安全这里不得不提醒一句:千万别把明文密码往数据库里塞。用BCrypt加密,虽然会让密码变成60位长且无法逆向解密的字符串,但这是保护用户的基础底线。Spring Security的BCryptPasswordEncoder直接就能用:
@Configuration public class SecurityConfig { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }注册时加密存储,登录时用它进行校验。这个细节在论文里也很加分,属于安全性的体现。
权限控制上,我用注解的方式做接口拦截。比如发布失物信息必须要登录,就加上@PreAuthorize("hasRole('USER')"),管理后台的接口则要求hasRole('ADMIN')。这种声明式的权限控制比在代码里手动判断要干净得多。
2.2 失物与拾物信息管理模块
这是整个系统最核心的模块,业务量最大的部分。先梳理需要哪些功能:
- 发布失物信息(寻物启事)
- 发布拾物信息(拾物招领)
- 信息的列表查询与分页浏览
- 按物品类别、丢失/拾得地点、时间范围筛选
- 按关键词模糊搜索
- 信息详情页展示
- 本人信息的管理(编辑/删除/状态变更)
实体类设计上,失物信息和拾物信息有非常多的共性字段,我是用一个基础实体抽出来,然后两个子类分别扩展。核心字段包括:
| 字段 | 说明 | 示例 |
|---|---|---|
| 物品名称 | 必填,搜索关键词的主要来源 | 黑色钱包、学生卡 |
| 物品类别 | 分类筛选的依据 | 证件、电子产品、书籍、钥匙、其他 |
| 地点 | 丢失/拾到的位置 | 二食堂、图书馆三楼 |
| 时间 | 丢失/拾到的时间 | 2024-04-12 17:30 |
| 详细描述 | 补充特征,便于失主辨认 | 棕色皮质,印有卡通图案 |
| 图片 | 物品照片 | 上传的图片URL |
| 状态 | 待认领/已认领/已归还 | 初始默认为待认领 |
| 发布人 | 关联用户表 | 用户ID |
发布信息的接口设计上,我遵循了Restful风格。比如失物信息的接口就是:
POST /api/lost/create 发布失物 GET /api/lost/list 分页查询失物列表 GET /api/lost/{id} 失物详情 PUT /api/lost/update 编辑失物信息 DELETE /api/lost/{id} 删除失物信息这里有个关键点,也是Scrum里常见的坑——发布信息时要带上发布人的用户ID,但这个ID绝不能从前端传入。正确做法是从JWT令牌里解析出当前登录用户的ID,后端自己判断数据归属。否则用户把自己发布的信息改成别人的,系统的数据安全就崩了。
我的实现思路是这样的:
public Result addLost(@RequestBody LostDTO lostDTO) { // 从SecurityContext中获取当前登录用户 Long currentUserId = SecurityUtils.getCurrentUserId(); Lost lost = new Lost(); BeanUtils.copyProperties(lostDTO, lost); lost.setUserId(currentUserId); lost.setStatus("寻物中"); lostMapper.insert(lost); return Result.success(); }至于图片上传,我用的是本地存储的方式。在配置文件中指定一个上传目录,上传时把MultipartFile转存到这个目录,返回一个访问用的URL。毕设阶段完全可以不接云存储服务,但存储路径建议用相对路径,不要写绝对路径。否则部署环境一换,图片就全裂了。
2.3 认领流程模块
认领流程是失物招领系统的生命力所在。如果只能发信息不能认领,那这个系统就是一个公告板,支撑不起完整的业务闭环。
我设计的认领流程分两种场景:
第一种是失主找拾物。失主在拾物列表看到疑似自己丢失的物品后,发起认领申请,填写认领说明和证明自己身份的凭证信息。拾物发布者登录后会看到待处理的认领申请列表,核实后同意则进入交接阶段,拒绝则申请被打回。
第二种是拾主找失主。拾物发布者看到某条失物信息与自己捡到的东西高度匹配,可以主动联系失主,在失物信息下面发起“我有此物”的通知。失主看到通知后,可以通过系统与被联系方交换交接信息。
认领申请表的字段设计是:申请ID、物品ID、申请人ID、被申请人ID(即物品发布者)、申请说明、证明凭证URL、状态(待处理/已同意/已拒绝/已完成)、创建时间。
这块我踩过一个很实际的坑:状态流转必须用代码严格控制,不能想怎么改就怎么改。认领申请的状态机要明确好:待处理可以变成已同意或已拒绝,已同意才能变成已完成,已拒绝和已完成则是终态。状态机定清楚,论文好写,代码也好写。后期的逻辑判断不会出现“多个状态互相矛盾”的混乱场面。
2.4 后台管理模块
后台管理模块是给管理员用的。考虑到毕设的定位,后台功能不必做太庞大,把管理场景覆盖住就行。
我的后台功能做了四块:
- 用户管理:查看注册用户列表、禁用/启用账号、重置密码
- 信息审核:管理员可以下架违规的失物/拾物信息
- 认领管理:查看所有认领记录和处理状态,解决纠纷
- 数据统计:统计每天的新增发布量、认领成功率、分类占比
后台的界面和前台用的是同一个Vue项目,只是基于路由守卫和用户角色做了页面权限区分。登录用户如果是管理员,菜单里就会多出来“后台管理”的入口。管理员登录时后端返回的JWT令牌里就带有ADMIN角色信息,前端路由守卫根据角色控制访问权限。这个方案比单独再写一个后台系统省事得多,而且技术上也完全成立。
3. 数据库设计与核心流程实现
3.1 数据库表结构设计
数据库设计是撑起整个系统的基础,表结构设计得合理,后面写代码能省一半的力。我的库名就叫lost_found_db,核心表一共6张,外加一个用于刷数据的字典表。
用户表t_user:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键,自增 |
| username | varchar(50) | 用户名,唯一索引 |
| password | varchar(100) | BCrypt加密后的密码 |
| nickname | varchar(50) | 昵称 |
| phone | varchar(20) | 联系电话 |
| avatar | varchar(255) | 头像URL |
| role | varchar(20) | 角色,USER/ADMIN |
| status | int | 状态,1正常 0禁用 |
| create_time | datetime | 创建时间 |
失物信息表t_lost:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| user_id | bigint | 发布人ID,外键关联用户表 |
| title | varchar(100) | 标题 |
| item_name | varchar(100) | 物品名称 |
| category | varchar(50) | 物品类别 |
| lost_location | varchar(255) | 丢失地点 |
| lost_time | datetime | 丢失时间 |
| description | text | 描述 |
| image | varchar(255) | 图片URL |
| status | varchar(20) | 待认领/已认领 |
| views | int | 浏览量 |
| create_time | datetime | 发布时间 |
拾物信息表t_found的字段和失物表高度相似,不同在于地点字段是found_location、时间是found_time,状态多了一个“已归还”可选项。两个表的字段结构放在论文里做对照表格,一方面展示了抽象能力,另一方面也把这个系统“对称统一”的设计思想呈现出来了。
认领申请表t_claim:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| item_id | bigint | 物品ID(失物或拾物ID) |
| item_type | int | 物品类型,1失物 2拾物 |
| applicant_id | bigint | 申请人ID |
| receiver_id | bigint | 接收人ID |
| reason | varchar(500) | 认领说明 |
| proof | varchar(255) | 证明凭证 |
| status | varchar(20) | 待处理/已同意/已拒绝/已完成 |
| create_time | datetime | 申请时间 |
外键约束我没在表上强制建,为什么?因为MyBatis-Plus做关联查询不方便,而且外键约束在数据量上来后会拖累写入性能。逻辑外键配合手动校验就够了,这是实际开发中很常见的处理方式。但论文里写数据库设计时,会把ER图和关系标注清楚——毕竟毕设评审老师更看重的是你有没有能力设计出合理的关系模型。
3.2 核心查询实现
在查询功能的实现上,有几个细节值得好好说说。
搜索功能,我用的是MyBatis-Plus的LambdaQueryWrapper,不需要手写SQL就能完成动态条件拼接:
public IPage<LostVO> queryLostList(int pageNum, int pageSize, String keyword, String category) { Page<Lost> page = new Page<>(pageNum, pageSize); LambdaQueryWrapper<Lost> wrapper = new LambdaQueryWrapper<>(); // 关键词模糊匹配物品名称或描述 if (StringUtils.hasText(keyword)) { wrapper.and(w -> w.like(Lost::getItemName, keyword) .or().like(Lost::getDescription, keyword)); } // 分类筛选 if (StringUtils.hasText(category)) { wrapper.eq(Lost::getCategory, category); } // 按发布时间倒序排列 wrapper.orderByDesc(Lost::getCreateTime); IPage<Lost> result = lostMapper.selectPage(page, wrapper); // 转VO并补充发布人信息 return convertToVO(result); }这里要特别注意一个坑:如果同时要查询“物品名称”和“描述”两个字段,模糊条件必须用and(条件1 or 条件2)包起来,而不是直接like加like。否则加上别的筛选条件时,SQL拼接的优先级会出问题,查出来的数据就不对了。
列表查询和详情页查的数据不一样。列表页不需要完整描述和所有图片,详情页才需要。所以列表查询只取部分字段,详情再查全量数据。这个在数据量小的时候感觉不出来,但如果表里有text字段、图片字段,一次性全查出来对性能是有压力的。这也是把DO转成VO的一个重要意义——返回给前端的数据结构可以按需裁剪,而不是直接拿数据库映射的实体类往上怼。
关联信息的处理上,文章列表里要显示发布者的昵称和头像,但文章的表中只存了user_id。方案有两种:一种是一对一查出补充进去,另一种是写SQL用Join连表。我为了控制复杂度,采用的是在Service层做补全。虽然会产生N+1的查询问题,但对毕设的数据量来说毫无压力,而且代码结构清晰,可读性好。
3.3 认领流程的闭环实现
认领流程的实现,核心是把状态流转和权限校验做好。
我设计的认领流程图逻辑很清晰:
失主发起认领申请(携带物品ID和证明资料) → 拾物发布者查看申请 → 同意 → 双方线下交接 → 失主在系统确认已找回 → 记录状态变为已完成 → 物品状态更新为已认领后端控制的关键点是:只有物品的发布者才能处理这个物品的认领申请,申请人的ID必须从当前登录会话中获取,不能从请求参数中读取。这两个校验是把控认领流程安全性的底线。
交接确认功能也很简单,失主点击“确认已找回”,后端把认领记录状态改成“已完成”,同时把物品表的状态改成“已认领”。因为失物信息在认领成功后就不应该再出现在可认领列表里,状态字段是防重复认领的关键:
@Transactional public Result confirmClaimed(Long claimId) { Claim claim = claimMapper.selectById(claimId); // 校验状态机,只有已同意状态能走到已完成 if (!"已同意".equals(claim.getStatus())) { return Result.error("当前状态不可确认完成"); } claim.setStatus("已完成"); claimMapper.updateById(claim); // 同时更新物品状态 if (claim.getItemType() == 1) { lostMapper.updateStatus(claim.getItemId(), "已认领"); } else { foundMapper.updateStatus(claim.getItemId(), "已归还"); } return Result.success(); }加@Transactional注解是必须的,一旦后面的物品状态更新失败,前面的认领状态更新也要回滚,保证数据的一致性。这个注解的原理是Spring AOP,事务切面前置开启事务、后置提交或回滚。在论文里写清楚“为什么方法要加事务控制”,也能体现数据库层面思考问题的深度。
4. 论文写作组织与答辩要点
4.1 论文目录怎么搭
拿了一套毕设源码想写论文的人,最常问的问题就是“论文怎么组织”。失物招领这种信息管理类系统的论文结构非常固定,基本框架如下:
- 第一章 绪论:研究背景与意义、国内外研究现状、研究内容与目标、论文组织结构
- 第二章 相关技术介绍:SpringBoot、Vue、MySQL、MyBatis-Plus,以及JWT
- 第三章 系统分析:可行性分析、需求分析(功能性需求和非功能性需求)、用例分析
- 第四章 系统设计:总体架构设计、功能模块设计、数据库设计、接口设计
- 第五章 系统实现:对每个核心模块的实现过程和效果截图
- 第六章 系统测试:功能测试、性能测试、测试用例表、测试结论
- 第七章 总结与展望:总结完成的工作,指出不足和未来方向
第2章技术介绍最容易写空,但也是基础分所在。我的方法是每项技术都按“是什么—为什么选它—在本项目中承担什么角色”三段式来写,让老师看到你不是在背概念,而是真的把技术用进了项目。
需求分析是整个论文的灵魂。功能需求的每个条目都要能对应到后面的一处具体实现。比如“用户可以发布拾物信息”对应第四章的数据表设计、第五章的发布接口实现。如果写完系统代码再对照论文,发现某处功能在论文里没有体现,那是扣分点。
4.2 核心章节怎么写出深度
系统设计章是拉开论文档次的章节,建议花最多力气。我总结了几条写作心得。
第一,架构图画好。用一张前后端分离的系统架构图说明整个系统分几层,每层之间怎么通信。架构图能直接反映你的系统设计能力,别看它画起来简单,能画清楚的人往往代码也写得更结构化。
第二,数据库设计要画ER图并配文字说明。表之间的关系、每个字段的作用、字段类型的选取理由,这些都能体现出设计能力。比如status字段为什么用int不用boolean,因为系统可能不止两种状态——这种细节写进论文里非常加分。
第三,每个核心功能的实现描述都要包含“设计思路、实现代码、效果截图、要点分析”四件套。在实现章节中,我建议不要大段贴全部代码,而是选关键的十来行贴出来,围绕它讲清楚你这个设计的精妙之处或者踩过的坑就行。
答辩环节还有一个高频问题:“你这个系统有什么亮点?”提前准备好答案,比如JWT无状态认证、前端路由守卫权限控制、异常统一处理、数据状态机设计等,这些都是可以在代码里指出来并自圆其说的点。切忌说“没什么亮点,就是课程设计水平的综合运用”。
4.3 测试章节怎么写才不像凑字数
很多人的测试章节就是贴十几条测试用例表,然后说“经测试系统运行正常”。这种写法非常空洞。
我建议写测试时关注几个着眼点。功能测试要覆盖核心业务流的完整链路,而不是每条测试用例孤立存在。比如“发布拾物信息→失主搜索到该信息→发起认领→拾主同意→失主确认→拾物状态变更为已归还”,把这条链路测试过的过程完整记录下来,比十条割裂的“登录成功”测试有说服力得多。
性能测试方面,用性能测试工具对接口做简单的并发请求压测,记录响应时间。不用多专业,关键是体现你有性能意识,关注过系统在压力下的表现。安全测试可以写密码加密存储验证、未登录时越界访问接口被拦截的验证等。这些测试内容写进论文里,老师的观感会完全不一样。
5. 部署上线与环境配置实操
5.1 本地环境搭建
部署这块,我按照最省事的方式总结一套流程。不管用什么IDE,核心思路是一样的。
第一步,安装并配置基础环境。JDK 8或者JDK 11都行(Spring Boot 2.X建议JDK8,3.X就必须JDK17起)。安装好MySQL 5.7或8.0,Navicat数据库工具,VSCode,Maven。
第二步,初始化数据库。用Navicat连接MySQL后,创建一个名为lost_found_db的数据库,把提供的init.sql文件导入执行。导入完成后检查一下几个表的初始化数据是否都在。
第三步,配置后端。用开发工具打开后端工程,修改application.yml中的数据库连接信息,把用户名密码改成自己本地的:
spring: datasource: url: jdbc:mysql://localhost:3306/lost_found_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: yourpassword这里的serverTimezone=Asia/Shanghai一定要配置,否则连接MySQL 8.0时会报时区错误。然后启动后端服务,看到如下日志说明启动成功:
Tomcat started on port(s): 8080第四步,启动前端。用开发工具打开前端工程,在终端执行依赖安装目录后启动开发服务器。每次执行完后都要确认前端能通过代理正常请求到后端接口,如果前端报代理错误,需要检查Vue项目根目录下的配置文件里的代理端口是否和后端端口一致:
// vite 配置示例 server: { port: 3000, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }本地跑通这一套,意味着你已经在自己的机器上实现了一次前后端联调的完整项目部署。
5.2 服务器部署踩坑实录
有条件的话,建议把项目部署到一台云服务器上做线上演示,简历里也能写“已上线运行”,答辩观感加分不少。但服务器部署往往才是真正的翻车重灾区。
生产环境部署的思路和本地类似,但有几个额外的坑需要提前防踩。
前端跑的是构建后的静态文件,执行构建命令后,将生成的静态目录(比如dist)用Nginx托管。这里出现频率最高的问题是:刷新二级页面时404。原因是Vue Router用的History模式,刷新时Nginx不知道该把请求指向谁。解决方法是配置Nginx把所有非文件请求全部重写到index.html:
location / { try_files $uri $uri/ /index.html; }后端生产部署不要在开发环境跑,推荐打成jar包用java -jar命令跑。然后通过Nginx反代,把接口请求转发到后端端口。比如前端在线演示地址是8080端口,后端实际进程跑在8080,Nginx将/api前缀的所有请求转发到本机8080端口:
location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }图片上传存储路径在生产环境通常也需要调整。本地开发时的uploads/目录是相对路径,部署到服务器后要确认这个目录存在且有写入权限,否则上传图片接口会报IOException。
服务器部署时还容易忽略一个东西:数据库编码问题。如果导入SQL后在网页上看到乱码,大概率是导入时字符集不对。解决办法是在导入SQL前执行SET NAMES utf8mb4;,并且在创建数据库时就指定UTF-8编码。
5.3 部署文档怎么撰写
如果毕设要求提交部署文档,内容组织也越来越讲究。很多人觉得部署文档不重要,随手写个README就交,这其实是把送分题变成送命题。
一份合格的部署文档我建议包含五块内容:环境要求(操作系统、JDK版本、MySQL版本、Node版本)、软件安装步骤、数据库初始化的具体操作和导入脚本位置、后端部署与配置修改说明(重点是配置文件里需要改什么、为什么要改)、前端构建与部署说明。
写部署文档的时候有一个容易踩的坑,就是环境版本写得太含糊。比如“安装MySQL”这句话等于没说——应该写清楚是5.7还是8.0,因为不同主版本连接驱动和时区配置都有差异。如果用了文档里指定的版本,按步骤操作后一定能跑起来,这才是好文档。
6. 常见问题与排查技巧实录
做这套系统的过程中,我攒了不少调试经验,这里按“症状—原因—解法”的方式整理成速查表,照方抓药即可。
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 前端启动后接口全部返回404 | 前端代理端口与后端端口不一致 | 检查Vite或Webpack代理配置中的target地址 |
| 启动后端时报端口被占用 | 8080端口已被其他进程占用 | 找到占用进程并结束,或修改server.port |
| 数据库连接报Public Key Retrieval错误 | MySQL 8.0认证插件变化 | JDBC连接URL加上allowPublicKeyRetrieval=true |
| 查询结果中文乱码 | 数据库或连接字符串编码不对 | 确保库表用utf8mb4,URL带characterEncoding=utf-8 |
| 上传图片后无法访问 | 静态资源映射路径未配置 | 在SpringBoot中配置本地目录与URL的映射关系 |
| 刷新前端页面404 | History模式路由回退配置缺失 | Nginx配置try_files $uri $uri/ /index.html |
| 请求接口提示无权限 | JWT令牌缺失或过期 | 重新登录获取新令牌,检查拦截器排除路径是否配置全 |
| 跨域请求被浏览器拦截 | 前后端跨域配置缺失 | 后端配置CorsFilter或前端使用Vite代理解决 |
有几个问题值得一提。前端接口404是最常见的一个,新手根本想不到是代理服务器配置错了,而不是后端代码错了。排错思路要建立一套体系:先看浏览器开发者工具里请求的URL到底打到哪去,再看Nginx或Vite代理转发到哪,最后看后端有没有收到请求。逐段排查,三分五除二就能定位问题。
还有一个容易出问题的地方是Spring Security的拦截器配置。如果你配置了登录拦截,但没有放行注册接口、验证码接口、静态资源路径,那么会出现“明明没登录但页面可以打开,一旦调用某个接口立马报401”的情况。解决办法是单独检查拦截配置类,把不需要权限的接口列入白名单。
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) .addPathPatterns("/api/**") .excludePathPatterns( "/api/user/login", "/api/user/register", "/api/lost/list", "/api/lost/detail/**", "/api/found/list", "/api/found/detail/**", "/uploads/**" ); }最后补一句经验:排错时不要一上来就怀疑框架有Bug。90%的问题出在自己身上——配置写错、依赖没引、路径拼错。正确的自查顺序是先看本地的报错日志,再看前端控制台的报错信息,最后才去网上搜索。
7. 总结
整套系统做下来,我对一个道理体会非常深:毕业设计的核心不在于功能多炫、技术多新,而在于逻辑自洽。需求分析时讲清楚了要解决什么问题,系统设计时讲清楚了怎么解决,编码实现时按照设计一步步落地,测试时验证了每一步是正确有效的。这套方法论才是毕业设计真正要训练的东西。
如果你拿到的是别人写的源码,也不要慌,按这篇文章的思路从头到尾理一遍,跑通一次,改几个bug,把每一块都变成能说清来龙去脉的部分。然后论文里每个章节都能找到真实依据,答辩时每个技术选型都能给出理由,这套东西就是凭自己本事做出来的了。
最后分享一个我个人的小习惯:做完一个模块,我会在本地简单跑一遍核心流程,而不是全部写完才测。比如用户模块做完就测注册登录,物品模块做完就测发布查询,认领模块做完就测整个闭环。这样每个模块的问题都在当场解决,最后的综合联调会顺畅很多,体验过就知道这种方法有多省事了。