ARTICLE DETAIL

资讯详情

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

SpringBoot3+Vue3失物招领系统开发指南:从数据库设计到部署答辩

SpringBoot3+Vue3失物招领系统开发指南:从数据库设计到部署答辩 1. 这题目为什么值得做失物招领系统的价值与技术选型逻辑1.1 题目从哪来、能解决什么问题每年毕业设计季我都收到大量同学的私信“老师我基础很差能做一个什么系统”“SpringBoot3 Vue3 选什么题目好写又不容易翻车”“有没有现成源码能参考”问得多了我发现失物招领系统是一个被低估的好题目——它不复杂但五脏俱全覆盖了增删改查、分类查询、状态流转、权限控制、文件上传这些毕设常考的点而且业务场景非常生活化答辩时哪怕被问到“你为什么设计这个功能”你也能很自然地讲出真实需求。举个实际场景高校里每天都有学生丢校园卡、丢耳机、丢伞捡到的人不知道交给谁失主不知道去哪里找。很多学校靠QQ群、朋友圈手工转发信息碎片化严重。失物招领系统的核心价值就是把“捡到的人”和“丢东西的人”拉到同一个信息池里拾取者登记失物信息失主按类型、地点、时间筛选一旦匹配上再走认领流程确认身份。这是一个真正被需要、有逻辑闭环、不依赖任何外部接口就能独立运行的项目。对于零基础的同学它的规模也刚刚好。不像电商系统要折腾支付、库存、秒杀也不像管理系统动辄几十张表。失物招领系统核心业务就围绕“发布—查找—认领—统计”四条线前后端加起来四五个核心页面就能跑通。重点是这套业务逻辑你能真正吃透而不只是复制粘贴代码。答辩时老师问“这张表为什么有这个字段”“这个状态是怎么变的”你能答上来那这个题目就是稳的。1.2 SpringBoot3 Vue3 这套组合为什么是当前最优解技术栈选择这件事很多同学有个误区觉得越老越稳选了SpringBoot2 Vue2。说实话如果只是为了混过答辩老技术栈确实能跑。但如果你的目标是“验收时让老师觉得你关注了当前主流技术”那SpringBoot3 Vue3 就是性价比最高的选择。SpringBoot3 最核心的变化是强制要求JDK17。JDK17 相比老掉牙的JDK8带来的不仅是语法上的提升比如 record、switch 表达式、文本块更是整个生态向前走的一个信号。Spring官方已经明确SpringBoot2.x 停在老版本维护阶段新特性、新安全补丁都集中在3.x。你拿SpringBoot2做项目可能毕业两年后回头看这套代码就已经完全跟不上环境了。Vue3 这边同样如此。Vue3 的 Composition API 解决了过去 Options API 里逻辑分散的问题同一个功能的代码可以聚在一起维护起来舒服得多。而且Vite 的开发服务器启动速度比 Webpack 快得不是一个量级改代码热更新几乎是秒级刷屏这对开发体验的提升非常明显。你想象一下用Vue2写页面保存一次等三秒才刷新一天下来光等待浪费的时间就够写好几个组件了。从答辩的角度看这套组合也有天然的说服力JDK17 是LTS版本、SpringBoot3 是当前主流、Vue3 Vite 是前端新工具链你每一个选型都能讲出理由而不是像很多同学一样张嘴就是“大家都这么用”。这套技术栈的“势能”是向上走的你站在这个势能上做项目至少不会一上来就输在印象分上。2. 开工前先把地基打好环境版本、工程结构与数据库表设计2.1 环境版本清单与版本匹配陷阱先说最容易翻车的环境部分。SpringBoot3 的第一个坑就是JDK版本。SpringBoot3 底层基于 Spring 6要求JDK17 起步。如果你电脑上还只有JDK8启动项目时会直接报UnsupportedClassVersionError或者Spring模块找不到的错很多人第一反应以为是依赖没下对其实是编译字节码版本和运行环境不匹配。我建议的环境组合如下组件版本备注JDK17 及以上推荐 17 LTSSpringBoot3 强制要求Maven3.6.3 以上建议用 IDEA 内置的MavenMySQL8.0 及以上5.7 也能用但8.0更主流Node.js18 以上Vite 5 对 Node 版本有要求开发工具IntelliJ IDEA 2023.x社区版也够用这里有个很容易被忽略的细节Maven 编译器插件版本。默认IDEA新建的SpringBoot项目如果maven-compiler-plugin版本低于 3.10可能不会正确识别 JDK17 的 release 参数。所以我在 pom.xml 里通常显式声明properties java.version17/java.version maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target /properties另外初次拉取SpringBoot3 依赖时如果你用阿里云镜像请确认镜像仓库支持org.springframework.boot:spring-boot-starter-parent:3.x.x有些老镜像同步不及时会导致下载不到新版本。建议直接使用 Maven 中央仓库或者阿里云最新的公共仓库。然后是 MySQL 驱动依赖的变化。SpringBoot3 里mysql-connector-j的坐标改了使用方式和老版本不太一样。以前写mysql-connector-java现在更推荐dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency这个坐标默认跟着SpringBoot3 父工程管理版本不需要写version。如果你在网上一搜2023年之前的教程很可能复制到旧的mysql-connector-java在SpringBoot3 下也能跑但总归不够规范而且版本冲突时排查起来很麻烦。2.2 数据库四张核心表的设计思路失物招领系统的数据库设计我建议最小化但完整用户表、失物信息表、招领信息表、认领记录表。这四张表覆盖了业务主链路四张以外你最多加一张公告表用于后台发布通知别的先不要加。用户表user的设计关键不是字段多而是逻辑清晰CREATE TABLE user ( id bigint NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT 密码MD5或BCrypt, real_name varchar(50) DEFAULT NULL COMMENT 真实姓名认领时核对, phone varchar(20) DEFAULT NULL COMMENT 联系电话, role tinyint NOT NULL DEFAULT 0 COMMENT 角色0-普通用户 1-管理员, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;我建议密码一律用 BCrypt 加密存储Spring Security 或者 SpringBoot 自带的BCryptPasswordEncoder都能做。哪怕你的项目没有引入完整的Spring Security单独加一个工具类做加密也花不了几分钟答辩时这是一个加分点——安全设计是老师爱问的地方。失物信息表lost_item和招领信息表found_item的设计几乎对称这就是这个项目的特性之一写代码时你可以抽象出一个公共结构来减少重复。字段至少包含标题、物品类别、拾取/丢失地点、时间、图片、描述、发布人、状态。CREATE TABLE found_item ( id bigint NOT NULL AUTO_INCREMENT, title varchar(100) NOT NULL COMMENT 物品名称, category varchar(50) DEFAULT NULL COMMENT 类别证件/电子/服饰/其他, location varchar(100) DEFAULT NULL COMMENT 拾取地点, found_time datetime DEFAULT NULL COMMENT 拾取时间, image_url varchar(255) DEFAULT NULL COMMENT 图片URL, description text COMMENT 详细描述, status tinyint NOT NULL DEFAULT 0 COMMENT 状态0-待认领 1-已认领, publisher_id bigint NOT NULL COMMENT 发布者ID, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category), KEY idx_location (location), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT招领信息表;注意看索引设计。很多毕设项目只建主键查询一慢老师就会追问。category和location是查询频率最高的过滤条件加普通索引就够了。status状态字段也是筛选维度同样建索引。别加太多索引否则写操作会变慢插入测试数据时自己也能感觉到。认领记录表claim_record是这个项目的“关系表”它记录“谁认领了哪条招领/失物信息管理员是否审核通过”。这里要设计一个状态流转0待审核用户提交认领申请等待管理员处理1已通过管理员确认同意双方可以线下交接2已拒绝管理员驳回东西还在待认领状态这个表逻辑上是连接用户和物品的桥梁也是答辩时值得展开讲的设计亮点。毕设很多同学的代码只会“增删改查”但一旦涉及“审核流程”这种状态机模型项目的档次立刻就上来了。2.3 后端工程结构分包别乱来很多零基础同学看完教程一上来就建一个包把所有的类堆进去看起来像一个大杂烩。SpringBoot 项目的分包没有绝对标准但有一个原则包名要和职责边界对应。我习惯的分包方式如下com.example.lostfound ├── controller // 接收前端请求返回JSON ├── service // 业务逻辑层 │ └── impl ├── mapper // MyBatis-Plus的Mapper接口 ├── entity // 数据库实体类 ├── dto // 接收参数用的数据传输对象 ├── vo // 返回给前端的结果对象 ├── config // 配置类跨域、拦截器、WebMvc配置 └── common // 通用类统一返回结果、异常处理为什么要拆 dto 和 vo直接拿 entity 返回给前端不是更简单吗这里有一个很重要的原因实体类直接对应数据库字段会暴露你不想让前端看到的内容比如密码。更关键的是数据库字段和下发的 JSON 字段往往不完全一致你需要在控制器层做一次组装。用 vo 包装返回数据接口变化时改动只在 controller 层不会污染实体类。3. 后端核心接口的实现从配置到业务逻辑的完整链路3.1 配置类写好了吗数据源、连接池、日志与统一返回SpringBoot3 的application.yml配置最基础的是数据源。我用的是 HikariCP 连接池SpringBoot3 默认就是它连接池大小默认是10开发环境够用。连接池参数不是越多越好如果并发量不大保持默认就挺好。server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/lost_found?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 hikari: minimum-idle: 5 maximum-pool-size: 15 connection-timeout: 30000这里有个小坑serverTimezoneAsia/Shanghai一定要加。MySQL8 的默认时区和系统时区不一致时插入datetime类型的数据会差8小时你白天发布的信息数据库里显示的是凌晨。这种bug你单独看表怎么都看不出来但前端页面上时间就是不对。加了这个参数就能解决。然后是日志。SpringBoot3 的日志默认用的是 Logback很多同学不在意日志配置但开发调试时没有日志输出出了问题只能干瞪眼。你需要一个logback-spring.xml把com.example.lostfound这个包设置成 DEBUG 级别专门输出业务日志框架本身的日志保持 INFO 级别这样既能看清自己代码的运行流程又不会被 Spring 启动的一大堆 INFO 刷屏。最后是统一返回封装。另外我更推荐在返回前端的结果里加一个简单的统一格式比如public class ResultT { private Integer code; // 200成功其他失败 private String message; private T data; // getter/setter }这样前端 Axios 拿到响应后只需要判断res.data.code 200就能统一处理成功和失败而不是每次请求都写一遍if (code)。这个小设计写起来十分钟但对前后端协作体验的提升是决定性的。3.2 MyBatis-Plus 如何让 CRUD 代码少写一半失物招领系统的 CRUD 操作非常多新增失物、新增招领、修改状态、分页查询、按条件筛选。如果全部手写 SQL光 Mapper XML 就能写上百行而且极易出错。这里强烈推荐 MyBatis-Plus它的核心价值是单表 CRUD 不需要写 SQLBaseMapper 已经把selectById、selectPage、insert、updateById全都内置了你只要继承接口这些方法直接用。实体类示例Data TableName(found_item) public class FoundItem { TableId(type IdType.AUTO) private Long id; private String title; private String category; private String location; private Date foundTime; private String imageUrl; private String description; private Integer status; private Long publisherId; private Date createTime; }注意三个注解Data是 Lombok 自动生成 getter/setterTableName(found_item)指定表名因为类名FoundItem和表名found_item不一致不指定的话 MyBatis-Plus 会默认匹配found_item按下划线驼峰规则实际上也匹配得上但显示写上更稳妥TableId(type IdType.AUTO)声明主键数据库里是自增主键就要配 AUTO否则插入时会报主键冲突。写一个分页查询MyBatis-Plus 自带分页插件你需要一个配置类Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }然后在 Service 里直接用 LambdaQueryWrapper 写条件public PageFoundItem pageFoundItems(int page, int size, String category, String location) { PageFoundItem p new Page(page, size); LambdaQueryWrapperFoundItem wrapper new LambdaQueryWrapper(); wrapper.eq(StringUtils.hasText(category), FoundItem::getCategory, category) .like(StringUtils.hasText(location), FoundItem::getLocation, location) .orderByDesc(FoundItem::getCreateTime); return foundItemMapper.selectPage(p, wrapper); }看到.eq(boolean condition, ...)这种写法了吗这是 MyBatis-Plus 的精髓第一个参数是布尔条件只有条件成立时才拼接这个 SQL 片段。category为空就不按类别筛选location为空就不按地点筛选。这样一套代码动态查询就全出来了不需要像写 if 一样在 XML 里拼接。3.3 分页查询与条件筛选前端列表页的数据来源分页查询这个功能基本所有毕设必考。除了用插件你还需要考虑查询结果里不能直接返回 用户ID应该把发布者的用户名、联系方式一起带出来因为失主看到了匹配的物品需要电话联系拾取者。这就是我前面说的 vo 的作用。实现方式很简单分页查出来列表后根据publisherId批量查出用户名再组装成一个 vo。这一步你也可以在前端通过登录用户信息拼接但聚合信息放在后端做更合理前端拿到的直接是完整渲染数据。public PageFoundItemVO pageFoundItemVO(int page, int size, String category, String location) { PageFoundItem p pageFoundItems(page, size, category, location); PageFoundItemVO result new Page(p.getCurrent(), p.getSize(), p.getTotal()); ListFoundItemVO voList p.getRecords().stream().map(item - { FoundItemVO vo new FoundItemVO(); BeanUtils.copyProperties(item, vo); User user userMapper.selectById(item.getPublisherId()); vo.setPublisherName(user.getUsername()); vo.setPublisherPhone(user.getPhone()); return vo; }).collect(Collectors.toList()); result.setRecords(voList); return result; }注意BeanUtils.copyProperties有个坑JDK17 的模块化限制导致某些场景下反射拷贝会报错尤其是属性名相同但类型不同的字段。如果报错建议手动 set或者用 MapStruct 生成转换器。失物招领系统的字段不多手动 set 反而最稳代码多一点但绝不会出隐藏问题。3.4 认领流程的状态流转从提交申请到审核通过认领流程是整个系统最有“逻辑感”的部分。用户看到一条招领信息觉得是自己丢的东西点击“认领申请”此时系统应该做什么注意这里不应直接把这条招领信息的状态改成“已认领”而是先创建一条认领记录状态为“待审核”。为什么因为用户只是“自称”这是他的东西管理员必须核实描述是否吻合比如出示校园卡、描述物品细节。所以流程是用户发起认领 → 创建记录状态待审核管理员进入后台审核查看申请描述选择通过或拒绝通过后 → 招领信息状态变为已认领为防止他人重复认领该招领信息不再出现在“可认领”列表拒绝后 → 招领信息状态保持待认领认领记录标记为已拒绝这个状态机用普通 if/else 也能写但更优雅的方式是定义一个状态枚举把每一步的合法性校验集中起来。public enum ClaimStatus { PENDING(0, 待审核), APPROVED(1, 已通过), REJECTED(2, 已拒绝); private final int code; private final String desc; }认证状态转换时我在 Service 层写了一个校验方法public void approve(Long claimId) { ClaimRecord record claimRecordMapper.selectById(claimId); if (record null || record.getStatus() ! ClaimStatus.PENDING.getCode()) { throw new RuntimeException(该申请已处理请勿重复操作); } record.setStatus(ClaimStatus.APPROVED.getCode()); claimRecordMapper.updateById(record); // 联动修改招领信息状态 FoundItem item foundItemMapper.selectById(record.getFoundItemId()); item.setStatus(1); foundItemMapper.updateById(item); }这段代码虽然简单但体现了一个核心思想一个业务操作往往涉及多张表如果只更新一张表数据就处于不一致状态。这也是面试/答辩时老师最爱问的“事务问题”——多表更新必须加Transactional注解保证认领记录状态和招领信息状态同时变更要么一起成功要么一起回滚。3.5 统一异常处理别让错误信息直接糊到前端脸上零基础同学最容易忽略的就是异常处理。默认情况下SpringBoot 抛出的异常会返回一个默认错误页或者堆栈信息前端收到后根本不知道发生了什么用户体验极差。更致命的堆栈信息可能暴露内部代码结构这在答辩演示时非常尴尬。我建议写一个全局异常处理器RestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(RuntimeException.class) public ResultVoid handleRuntimeException(RuntimeException e) { return Result.error(500, e.getMessage()); } ExceptionHandler(Exception.class) public ResultVoid handleException(Exception e) { return Result.error(500, 系统繁忙请稍后重试); } }这样前端拿到的永远是统一结构的 JSON解析逻辑简单而且不会泄露 Java 异常类名。你也可以在错误信息里故意写得人性化一点比如“该物品已被认领请选择其他物品”而不是NullPointerException: null这种让用户一头雾水的内容。4. Vue3 前端的搭建与页面开发4.1 用 Vite 创建项目Vue3 开发的第一步前端部分我直接推荐用 Vite而不是 Vue CLI。Vite 最大的优势是开发服务器启动速度极快底层利用 ESModule 原生特性不需要先把整个项目打包再启动。你用 Vue CLI 新建一个项目可能要等30秒用 Vite 几乎是秒开。npm create vitelatest lost-found-web -- --template vue cd lost-found-web npm install创建完项目第一件事是安装路由和 HTTP 库npm install vue-router4 axios element-plusElement Plus 是 Vue3 的组件库后台管理页面用它的表格、表单、按钮、弹窗可以省掉大量手工样式代码。在前端工程里我建议全量引入 Element Plus 就好。个人开发者的项目全量引入比按需引入更省心虽然打包体积大一倍但开发效率高不必为了体积优化耽误时间。4.2 页面路由规划一个后端管理系统该有的骨架失物招领系统的前端页面我规划为两个区前台展示和后台管理。一开始千万别贪多先把核心页面建好路由页面说明/首页失物与招领信息汇总入口/lost失物列表丢了东西的人发布和浏览/found招领列表捡到东西的人发布和浏览/publish发布信息选择发布失物还是招领/admin管理后台审核认领申请、管理公告/login登录注册用户鉴权路由配置里要注意懒加载。项目页面数量少全量加载也没问题但养成() import()的习惯是好的const routes [ { path: /, component: () import(/views/Home.vue) }, { path: /lost, component: () import(/views/LostList.vue) }, { path: /admin, component: () import(/views/Admin.vue) } ]路由懒加载意味着首屏只加载首页代码进入其他页面时才按需加载首次打开速度更快答辩演示时也更体面。4.3 Axios 封装与跨域处理很多同学直接在组件里axios.get(url)每写一次请求就重复一遍完整地址、超时时间、Token。这非常不利于后期维护。我建议封装一个统一的 request 工具。参考import axios from axios import { ElMessage } from element-plus const request axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器自动携带token 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 ! 200) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res.data }, error { ElMessage.error(网络异常请稍后重试) return Promise.reject(error) } ) export default request为什么 baseURL 是/api而不是http://localhost:8080这涉及前后端分离开发时的跨域问题。跨域的本质是浏览器同源策略网站页面地址是http://localhost:5173后端接口地址是http://localhost:8080端口不同就触发了跨域。解决跨域有几种方案比如在后端加CrossOrigin注解、全局 CORS 配置但对开发环境来说最舒服的是用 Vite 的代理// vite.config.js export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) } } } })这样前端代码里请求/api/found/pageVite 开发服务器会自动转发到http://localhost:8080/found/page浏览器看到的所有请求都是同源的跨域问题从根上消失。打包后部署时再用 Nginx 做一层location /api的代理转发前后端就能无缝衔接。这个“开发环境Vite代理生产环境Nginx代理”的思路是前后端分离项目里最标准的姿势。4.4 核心页面组件的开发思路列表页与发布表单招领列表页我建议做成“卡片 搜索栏”的布局卡片展示实物照片、物品名称、类别、地点、状态。搜索栏提供类别下拉选择、地点输入、关键字搜索调用后端分页接口。这里用 Element Plus 组件写起来非常快template div classcontainer el-form :inlinetrue el-form-item label类别 el-select v-modelquery.category placeholder全部类别 clearable el-option label证件 value证件 / el-option label电子产品 value电子 / el-option label服饰 value服饰 / el-option label其他 value其他 / /el-select /el-form-item el-form-item label地点 el-input v-modelquery.location placeholder输入地点 clearable / /el-form-item el-form-item el-button typeprimary clickloadData查询/el-button /el-form-item /el-form el-row :gutter16 el-col v-foritem in list :keyitem.id :span6 el-card classitem-card img :srcitem.imageUrl classitem-img / h4{{ item.title }}/h4 p地点{{ item.location }}/p p时间{{ formatTime(item.createTime) }}/p el-button typeprimary sizesmall clickopenClaim(item.id)我要认领/el-button /el-card /el-col /el-row el-pagination v-model:current-pagequery.page :page-sizequery.size :totaltotal current-changeloadData / /div /template发布表单页的核心是表单校验。失物信息至少需要标题、类别、地点、时间、详细描述图片建议做成单选上传限制格式和大小。Element Plus 的el-upload组件支持自定义上传行为把图片传回后端后端返回图片 URL再把 URL 提交到表单数据里。这里有个容易忽略的点图片上传接口应该放在/api/file/upload接收MultipartFile保存到服务器的uploads目录返回绝对路径给前端。如果直接把图片 base64 编码塞进数据库数据库会迅速膨胀后期查询性能很糟糕。4.5 权限控制与登录状态管理有了登录就涉及权限控制。前端现在登录成功后后端返回一个 JWT Token 和用户角色信息。我的处理方案登录成功后把 Token 和 user 信息存到 localStorage请求拦截器自动带上 Token后台管理页面前端判断当前用户角色如果不是管理员直接跳转首页并提示无权限前端路由守卫router.beforeEach((to, from, next) { const user JSON.parse(localStorage.getItem(user) || {}) if (to.path.startsWith(/admin) user.role ! 1) { ElMessage.error(无权限访问) next(/) } else { next() } })注意前端的权限控制只是用来改善用户体验真正的安全边界必须放在后端。后端接口要校验管理员身份比如加一个拦截器拦截所有/admin/**的请求验明 Token 和角色不合法直接返回 401。这块哪怕不用 Spring Security 全量功能只写个 HandlerInterceptor 也足够。否则懂行的人直接调你的后台接口前端那点拦截根本拦不住。5. 部署与交付从源码到毕业设计的最后一公里5.1 后端打包前端构建系统做完接下来是打包部署。很多同学开发时一切正常一打包就懵这里我把流程拆开后端先确认application.yml里的数据库连接地址是部署机的地址不是本地 localhost然后把 SpringBoot 项目用 Maven 打包mvn clean package -DskipTests也可以直接用 IDEA 右侧 Maven 面板双击package。打包完成后target目录下会生成一个 jar 包文件名通常是lost-found-0.0.1-SNAPSHOT.jar。运行指令java -jar target/lost-found-0.0.1-SNAPSHOT.jar前端的构建更简单npm run build构建产物在dist文件夹中里面是纯静态文件HTML、CSS、JS部署时交给 Nginx 托管即可。这里有个关键配置因为前端路由用了 history 模式Nginx 需要把所有非静态文件请求重定向到 index.html否则刷新页面会 404。配置如下location / { root /www/lost-found-web/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8080/; }这里面的逻辑是/api开头的请求反向代理到后端 8080其余请求优先匹配静态文件匹配不到就回退到 index.html由前端路由接管页面分发。5.2 交付物清单源码、数据库脚本、文档的服务作为一个面向“毕业设计”需求的教程项目交付物一定不只是一堆代码。我给自己定的交付清单非常明确完整源码前端 后端带 .git 目录保留提交历史数据库初始化脚本create_table.sql data.sql包含测试数据毕业设计文档开题到结题的全套材料README 部署说明从环境安装到启动的每一步数据库脚本里一定要准备测试数据。很多同学建好表就交差表格空空如也页面打开一片空白演示效果极其拉胯。我建议至少为 lost_item 表插入 15 条数据、found_item 表插入 15 条、user 表 3 个用户含1个管理员并覆盖不同类型的物品和不同的状态。这样你第一次打开系统就有内容可看真实性、完成度马上不一样。5.3 项目答辩的准备思路毕业设计不只是做系统还有论文和答辩。我每次回答学员的问题时都不忘提醒论文不要变成“代码说明书”也不要变成“功能介绍流水账”而是要讲清楚你做系统时针对特定业务场景做的思考和设计。比如失物招领系统最常见的论文切入点有失物与招领信息的双向匹配策略如何通过物品类别 地点 时间范围做初步匹配再靠人工审核兜底认领流程的状态机设计多表事务的一致性保证前端基于 Vue3 的组合式函数复用逻辑如认证状态管理、搜索条件封装每一个切入点都是你这个项目相比于“随便一个增删改查系统”的独特增量。有这些内容论文就立住了答辩老师问起来你也有话可说。6. 零基础最容易翻车的几个坑提前帮你排掉6.1 JDK版本、Maven仓库、Node版本三座大山结合我带过的学员反复踩坑的经验最容易卡住三天以上的就是环境问题。SpringBoot3 必须 JDK17不是 8也不是 11。装好 JDK17 后在 IDEA 里要把项目 SDK、模块 SDK、以及 Maven 的 Java 编译器版本全部改成 17三处缺一不可。很多人只在 Project Structure 改了Maven 的 Java Compiler 还指向 8启动照样报错。Node 版本影响的是 Vite 的启动。Vite 5 要求 Node 18如果你用老版本 Node执行npm run dev会提示版本过低。检测命令是node -v不满意的直接用 nvm 切版本。6.2 前后端联调接口路径要关注的两类错误联调阶段最经典的报错是 404 和 405。404 多数是路径对不上前端请求/api/found/list后端接口定义的是/found/list写上面加了 Vite 代理的话实际转发时前缀处理不当就会对不上。405 则是请求方式错误后端只写了PostMapping前端用了axios.get自然报错。联调前先理清接口文档或者直接用 IDEA 的 HTTP Client 把每个接口先自测一遍再让前端对接。6.3 文件上传与静态资源映射项目里涉及图片上传但SpringBoot 默认不帮你映射本地磁盘目录。你在application.yml里配置一个上传目录然后写一个 WebMvc 配置类Configuration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/uploads/**) .addResourceHandler(file:D:/lost-found/uploads/); } }注意file:后面必须是绝对路径而且 Windows 和 Linux 的写法不同。没有这一步你会看到图片上传成功、数据库里有路径但页面上图片就是裂的。这个坑非常经典我几乎每个带过的学员都会碰上至少一次。6.4 数据库连接参数、时区与字符集保持 utf8mb4 字符集非常重要。很多同学数据库建表时默认用 utf8存中文没问题但一旦描述里夹杂了 emoji 字符比如“丢了一把”插入时就会报Incorrect string value错误。utf8mb4 是 utf8 的超集能存四字节字符建表语句里顺手把CHARSETutf8mb4加上能省很多麻烦。7. 我对这个项目的最终看法做了这么多次毕业设计指导我对失物招领系统这个题目最大的评价是入门友好上限不低。如果你只做基础的四张表和增删改查它足够简单你甚至能在一周之内把整个系统跑通。但如果你想拿高分可以在失物与招领的智能匹配度、审核流程的严谨性、数据统计图表化这些方向继续深挖每一次添加功能都是给论文加一块明显的内容增量。最后分享一个我平时最爱给学员的建议不要等到系统写完了再写文档。开发过程中每完成一个模块就在项目的 docs 目录里用几句话记录下你当时的设计理由比如“为什么认领状态是三个而不是两个”“为什么地点查询用的 like 而不是精确匹配”。这些零散记录最后整理成论文就是最真实、最扎实的素材远比你想着一切做完后再凭空回忆要轻松得多。做技术项目不难难的是把它讲清楚。而这套系统正好给了你一个足够简单、又足够完整的舞台去练习这件事。
返回列表