SpringBoot+Vue+MyBatis校园失物招领系统:前后端分离实战
发布时间:2026/9/10 3:12:01 锦皓数字建站

大学里丢过校园卡的人应该都有这种体会失物招领处永远在一楼值班室角落的纸箱子里你跑去问的时候值班阿姨只能让你自己翻。更难受的是捡到东西的人一张学生卡被扔在桌上想找失主只能等对方来找。后来我做了这个前后端分离的校园失物招领系统用SpringBootVueMyBatisMySQL这套组合把“丢失”和“拾获”两条信息流真正打通了。这篇文章会从需求拆解、技术选型、数据库设计、前后端实现到部署上线把整个项目的完整思路和实操步骤讲清楚。无论你是想完成毕业设计、课设项目还是第一次接触前后端分离项目想找一份拿来就能跑的源码这篇都能帮你省掉大量踩坑时间。1. 从纸箱子到信息流校园失物招领到底要解决什么问题1.1 传统失物招领的痛点在哪里很多校园项目一上来就追求功能多但做失物招领系统之前先得想明白一件事这个系统的真正痛点是什么。传统模式最大的问题是信息不对称。失主丢了东西只能靠自己去食堂、图书馆、保安室挨个问捡到东西的人也不知道该把东西交给谁。两边都在找却互相看不见。所以这个系统第一个要做的事情就是搭一个公共的信息池——丢失信息和拾获信息都往里放让双方可以搜索、比对、联系。第二个痛点是认证和信任问题。校园场景里谁也不能随便把别人捡到的物品拿走。系统里必须有一个身份标识让用户知道对面是本校学生同时留联系方式。我设计了最简单的学生认证方式注册时填写学号和手机号配合后台管理员的审核操作既保证门槛不高又确保信息可追查。第三个痛点是状态管理。一个物品从丢失/捡拾到最终归还中间的状态必须清晰待认领、申请中、已归还、已关闭。没有状态机列表里全是陈年旧信息系统就废了。1.2 系统角色与核心流程梳理这个系统涉及的参与者比较清晰普通学生用户、后台管理员。普通用户的核心操作是四件事发布失物信息我丢了什么发布招领信息我捡到了什么浏览和搜索能不能找到匹配提交认领申请我觉得这条信息对应的就是我的东西管理员的核心操作是审核用户、管理异常信息维护物品分类查看平台整体数据认领流程需要重点设计。整个闭环是失主发布失物 → 捡到者发布招领 → 一方发现匹配信息 → 提交认领申请 → 对方确认并联系 → 线下核实身份 → 状态更新。这里面最关键的是让“提交申请”和“确认状态”两个动作在系统里形成闭环而不是让双方下载手机号后在线下解决最后连物品状态都没人更新。1.3 功能模块拆解不堆大而全只做够用我把系统拆成六个模块不多不少模块功能点用户模块注册、登录、个人中心、学号认证失物模块失物发布、列表浏览、关键字搜索、分类筛选、状态更新招领模块招领发布、列表浏览、关键字搜索、分类筛选、状态更新认领模块认领申请、申请审核、我的申请列表分类管理物品分类维护卡类、电子设备、证件等后台管理用户管理、信息审核、数据统计这里有一个设计取舍很多类似项目会做“失物匹配推荐”“相似物品智能识别”但对一个校园失物招领系统来说这些功能投入产出比并不高。我更倾向于做扎实的搜索和分类筛选让用户自己判断是否匹配。系统本身的定位是信息撮合平台而不是自动判案系统。2. 技术栈选型为什么这套组合是校园项目的最优解2.1 SpringBoot一个人也能撑起后端后端选型是SpringBoot这个选择基本没有争议。SpringBoot把Spring繁琐的XML配置全部干掉了内嵌Tomcat一个java -jar就能把服务跑起来。对校园项目来说这意味着你可以把精力集中在业务逻辑上而不是折腾SSH框架的配置地狱。这个项目我用的版本是2.7.18对应JDK8这两个版本组合在兼容性和稳定性上经过了非常充分的验证不太会出现网上说的各种奇怪问题。项目里用到的SpringBoot核心能力包括Spring MVC处理HTTP请求、Spring Security的核心概念虽然我用JWT做轻量鉴权、Transactional事务管理、以及自动配置带来的便捷性。2.2 Vue页面变化的愉悦感前端选Vue 2.6配合ElementUI组件库。Vue的响应式数据绑定让“页面数据变成真”的过程非常自然用户发一条失物信息列表页马上就能看到搜索时输入关键词列表实时刷新。Vue的组件化开发在失物招领系统里收益很明显。失物卡片、招领卡片、筛选栏、分页器这些UI模块在多个页面中反复复用。我把它们抽成公共组件每次加功能改样式都只动一个文件。组件化还有一个特别实际的好处我一个人维护整个项目时不需要记住所有页面的细节打开组件文件就能快速定位问题。后来项目扩展到后台管理页面我只写了一小部分新代码大部分组件都是直接从用户端复用的。2.3 MyBatis与MySQLSQL在手数据我有MyBatis在这个项目里的价值可以概括成四个字SQL可控。Spring Data JPA虽然也很流行但它自动生成的SQL在复杂多表查询时往往不够直观出了问题排查很累。MyBatis让你每条SQL都清清楚楚写在XML文件里性能问题一眼就能看出来。MySQL作为数据库对校园项目来说是性价比最高的选择免费、稳定、生态成熟。我用的是MySQL 8.0Java生态对它支持非常友好。2.4 前后端分离与单体开发的分界线有一次跟同学聊技术他说“前后端分离是不是有点过度设计我一个人开发为什么不用一个Thymeleaf模板一把梭”这个问题问得很好。我的回答是前后端分离的核心优势不是“让前后端的人可以并行开发”而是职责边界清晰。后端只负责返回JSON数据前端只负责展示和交互。调试时后端可以用Postman测接口前端可以用Mock数据部署时前端挂Nginx后端走Java进程互不干扰。对校园项目来说分离架构的调试体验好、可维护性强虽然前期要处理跨域问题但收益远大于成本。这个项目里前后端完全通过RESTful API通信后端统一返回{code, message, data}结构前端根据code判断业务是否成功。这是一个非常标准、也极其好维护的约定。3. 数据库设计失物、招领、认领三张表如何互相咬合3.1 用户表与鉴权字段数据库设计是整个系统最重要的环节表结构设计得不好后面所有功能都要返工。我从用户表开始想办法。用户表user我定义了这些核心字段字段名类型说明idBIGINT主键自增usernameVARCHAR(50)登录账号学号passwordVARCHAR(100)BCrypt加密后的密码nicknameVARCHAR(50)昵称phoneVARCHAR(20)联系电话head_imgVARCHAR(255)头像图片URLroleTINYINT0普通用户1管理员statusTINYINT0禁用1正常create_timeDATETIME注册时间这里有个容易踩坑的点密码字段别用明文别用MD5直接用BCryptPasswordEncoder。MD5在彩虹表面前形同虚设BCrypt自带盐值是目前最合适的选择。用户表为什么用学号作为登录账号因为这个系统本身就是封闭的校园场景学号天然唯一身份可信度也更高。后面管理员审核资料时主要也是核对学号和姓名是否匹配。3.2 失物表和招领表的设计理念失物表和招领表是系统里最核心的两张业务表它们的字段设计有很强的对称性——因为本质上是同一种信息的不同方向。失物表lost_item核心字段字段名类型说明idBIGINT主键user_idBIGINT发布者用户IDcategory_idBIGINT物品分类IDtitleVARCHAR(100)物品名称descriptionTEXT详细描述物品特征、丢失时间等locationVARCHAR(255)丢失地点imagesVARCHAR(1000)图片URL列表逗号分隔contactVARCHAR(50)联系方式statusTINYINT0待认领1已找到2已关闭create_timeDATETIME发布时间update_timeDATETIME更新时间招领表found_item结构几乎一模一样只有语义不同location字段记录的是“拾获地点”status状态从“待认领”变为“待认领/已归还/已关闭”。有一个字段设计上我纠结过很久为什么用images逗号分隔的方式存多张图片而不是新建一张图片表后来想法是这个系统的单条记录图片数量很少最多3张用逗号分隔存一个字段可以省掉一次关联查询前端拿到后split(,)就能直接用。如果哪天图片量大了再拆表对这个系统来说基本不会发生。3.3 认领申请记录表一个表打通认领闭环认领申请记录表claim_record是整个系统业务闭环的关键。字段名类型说明idBIGINT主键item_typeTINYINT0失物申请认领1招领申请认领item_idBIGINT关联的失物/招领IDapply_user_idBIGINT申请者用户IDowner_user_idBIGINT发布者用户IDmessageVARCHAR(500)申请留言说明物品特征statusTINYINT0待审核1通过2拒绝create_timeDATETIME申请时间handle_timeDATETIME处理时间这里用item_type item_id做多态关联这是很有争议的一个设计决策。严格的关系型数据库设计会用两张表分别记录“失物认领申请”和“招领认领申请”。但我在实际开发中发现它们的字段几乎一模一样硬拆成两张表反而让查询逻辑重复。用item_type字段区分一张表就能支撑两种业务场景。owner_user_id为什么要冗余存一个因为查询“我收到的申请”时可以直接按owner_user_id过滤不用先根据item_type去查失物表或招领表再反查发布者。这个冗余换取的是性能便利而且数据一致性在业务逻辑层面就控制了——发布者信息本来就是静态的不会频繁变化。3.4 索引设计与SQL注意事项索引设计是数据库设计中最容易被忽略的部分。很多人在表建完之后就把索引忘了等到数据量上来才发现查询慢得离谱。这里我做了三个索引lost_item.create_time列表页按时间排序没有索引会全表排序found_item.create_time同上claim_record.item_type, item_id联合索引支撑认领申请查询另外有一件特别值得注意的事MySQL 8.0里utf8mb4才是真正的UTF-8完整实现utf8字符集在存特殊字符时可能出问题。建库时统一用utf8mb4排序规则用utf8mb4_general_ci就够用了。4. 后端实现从接口规划到文件上传落地4.1 项目结构与统一返回体后端项目结构我按功能分包而不是按技术分层分包。这样做的好处是改动一个功能时相关的Controller、Service、Mapper都在一起找代码很快。com.example.lostfound ├── config # 跨域配置、WebMvc配置 ├── controller # 接口层 ├── service # 业务逻辑层 ├── mapper # MyBatis Mapper接口 ├── model # 实体类、DTO、VO ├── common # 统一返回、异常、工具类 └── interceptor # JWT拦截器ResultT统一返回体是前后端协作的基础。每个接口都返回{code: 200, message: success, data: ...}前端拿到code ! 200就弹错误提示逻辑非常简单统一。这里给出一个比较简洁的写法Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }同时配置全局异常处理器Controller里就不需要到处try-catch了。业务异常直接throw new BusinessException(xxx)全局处理器统一转成Result返回。这个设计能让业务代码干净非常多。4.2 登录鉴权JWT的配置与拦截器白名单登录鉴权是前后端分离项目里绕不开的话题。Session方案在分离场景下要处理跨域Cookie太麻烦我直接用JWT方式服务端不存状态每次请求带上Token后端解析校验身份。JWT分为三部分Header、Payload、Signature。Header声明加密算法Payload放用户ID和过期时间Signature用服务端密钥签名。我封装了一个简单的JwtUtil工具类核心方法就三个生成Token、解析Token、校验是否过期。Component public class JwtUtil { Value(${jwt.secret}) private String secret; Value(${jwt.expire}) private Long expire; public String generateToken(Integer userId, String role) { return Jwts.builder() .claim(userId, userId) .claim(role, role) .setExpiration(new Date(System.currentTimeMillis() expire)) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } public Claims parseToken(String token) { return Jwts.parser().setSigningKey(secret) .parseClaimsJws(token).getBody(); } }这里有个特别关键的配置环节拦截器白名单。注册、登录、列表浏览、搜索这些接口必须放行否则没登录的用户什么都看不了。我在拦截器里维护一个白名单数组用AntPathMatcher做路径匹配。这是个很细微但非常重要的逻辑。针对跨域问题后端配置了CORSConfiguration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowCredentials(true) .maxAge(3600); } }注意allowCredentials(true)会让浏览器在跨域请求时携带Cookie如果前端用Token方式认证其实可以不设这个。但保留它有一个好处后续如果要扩展记住登录状态的Cookie功能不用再改。4.3 核心接口设计与MyBatis映射后端接口按资源维度划分下面是核心接口清单接口路径方法说明/api/user/registerPOST用户注册/api/user/loginPOST用户登录/api/user/infoGET获取当前用户信息/api/lost/listGET分页查询失物列表支持关键字、分类筛选/api/lost/addPOST发布失物/api/lost/updatePUT修改失物信息/api/lost/statusPUT更新失物状态/api/found/listGET分页查询招领列表/api/found/addPOST发布招领/api/found/updatePUT修改招领信息/api/found/statusPUT更新招领状态/api/claim/addPOST提交认领申请/api/claim/myGET我提交的申请/api/claim/receivedGET我收到的申请/api/claim/handlePUT处理认领申请/api/category/listGET获取分类列表/api/statistics/summaryGET后台统计汇总以失物列表接口为例它的MyBatis XML映射值得重点讲。因为这里的动态SQL逻辑很典型位置有记录、数字类型区分了相关参数能够表达搜索、过滤与排序组合的场景select idselectLostList resultTypecom.example.lostfound.model.vo.LostItemVO SELECT li.id, li.title, li.description, li.location, li.images, li.contact, li.status, li.create_time, u.nickname AS publisherName, u.head_img AS publisherAvatar, c.name AS categoryName FROM lost_item li LEFT JOIN user u ON li.user_id u.id LEFT JOIN category c ON li.category_id c.id where if testkeyword ! null and keyword ! AND (li.title LIKE CONCAT(%, #{keyword}, %) OR li.description LIKE CONCAT(%, #{keyword}, %)) /if if testcategoryId ! null AND li.category_id #{categoryId} /if /where ORDER BY li.create_time DESC /select用LEFT JOIN而不是INNER JOIN是为了防止用户信息或分类被删除后导致列表查不出来。外键关联字段宁可显示NULL也不能让主数据消失。分页参数我通过PageHelper插件处理一行PageHelper.startPage(pageNum, pageSize)就能完成物理分页返回PageInfo直接带总条数、总页数等信息前端分页组件可以直接用。4.4 图片上传本地存储与访问路径配置图片上传是一个前后端分离项目里非常容易出问题的环节。上传本身不难难的是让前端能用URL访问到上传后的图片。我在后端实现了一个简单的图片上传接口接收MultipartFile校验类型和后缀名生成UUID文件名原后缀保存到本地磁盘目录如/data/lostfound/uploads/返回给前端一个相对路径/uploads/20250601/xxxxx.jpg。同时需要配置静态资源映射让SpringBoot能直接对外提供这个目录的访问Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/uploads/**) .addResourceLocations(file: uploadPath /); } }这里有个容易踩坑的细节uploadPath结尾一定要带斜杠否则路径拼接会出错。我一开始把路径配成了/data/lostfound/uploads结果映射死活不生效日志排查了半天才发现是少了最后那个/。关于上传另一个重要经验是前端必须做限制。我配合ElementUI的el-upload组件设置acceptimage/*并且在before-upload里检查文件类型和大小。前后端双重校验是安全底线绝不能只依赖前端。5. 前端实现Vue页面从路由到组件落地5.1 Vue项目结构与环境前端用Vue CLI创建项目Node版本建议16.xnpm包管理。需要注意如果直接装了最新版Node可能和Vue CLI 4.x的webpack版本冲突最后会弹出各种奇怪的编译错误。项目结构保持标准方式但按模块划分了视图组件src/ ├── api/ # 接口请求封装 ├── assets/ # 静态资源 ├── components/ # 公共组件失物卡片、筛选栏等 ├── router/ # 路由配置 ├── store/ # 状态管理简单用户信息 ├── views/ # 页面组件 │ ├── home/ # 首页 │ ├── lost/ # 失物相关页面 │ ├── found/ # 招领相关页面 │ ├── user/ # 个人中心、登录注册 │ └── admin/ # 后台管理页面 └── utils/ # 工具函数5.2 路由设计信息流页面怎么组织路由设计对用户体验影响很大。这个系统的路由我设计成这样路径页面是否需要登录/首页最新失物招领信息流否/lost失物大厅可搜索筛选否/found招领大厅可搜索筛选否/detail/lost/:id失物详情否/detail/found/:id招领详情否/publish/lost发布失物是/publish/found发布招领是/mine个人中心是/mine/claims我的认领申请是/admin后台管理是管理员路由守卫是前端安全的比较重要的组成部分。虽然后端接口有JWT鉴权但前端依然要做路由守卫否则未登录用户访问/publish/lost时会直接看到空白页或报错。用router.beforeEach拦截未登录访问router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next({ path: /login, query: { redirect: to.fullPath } }) } else if (to.meta.requiresAdmin token localStorage.getItem(role) ! 1) { next({ path: / }) } else { next() } })登录后跳转回原页面的redirect参数是个很值得做的小功能。用户没登录时点击“发布失物”被引导到登录页登录完成后自动回到发布页面体验流畅很多。5.3 axios封装与API对接前端的API请求封装所有请求都走同一个axios实例统一拦截器处理Token注入和响应码判断。这是前端项目中必须做好的基础设施。import axios from axios import { Message } from element-ui import router from /router const service axios.create({ baseURL: process.env.VUE_APP_BASE_URL, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { Message.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res.data }, error { if (error.response) { if (error.response.status 401) { localStorage.removeItem(token) localStorage.removeItem(userInfo) Message.error(登录已过期请重新登录) router.push(/login) } else { Message.error(error.response.data.message || 请求异常) } } else { Message.error(网络异常请稍后重试) } return Promise.reject(error) } ) export default service重点说一下401处理。JWT过期后后端返回401前端如果什么都不做用户会一直停留在旧页面点任何按钮都报错。我在拦截器里统一判断状态码过期后清掉本地登录信息跳转到登录页同时弹一个“登录已过期”的提示。这个细节看似不起眼实际体验差异非常大。API层每个接口单独封装成一个函数比如失物模块的请求都放在src/api/lost.jsimport request from /utils/request export function getLostList(params) { return request({ url: /api/lost/list, method: get, params }) } export function addLost(data) { return request({ url: /api/lost/add, method: post, data }) } export function updateLostStatus(id, status) { return request({ url: /api/lost/status, method: put, data: { id, status } }) }5.4 页面组件卡片列表、筛选栏与发布表单前端页面我做得最用心的是首页信息流。它同时展示最新失物和招领信息采用上下两块Tab切换每块都是卡片列表。失物卡片是复用率最高的组件展示标题、分类标签、丢失地点、发布时间点击进入详情。卡片右上角有个状态标签不同状态显示不同颜色红色“待认领”、绿色“已找到”、灰色“已关闭”。这个设计让用户扫一眼就能知道信息的时效性。筛选栏组件也是高度复用的。失物大厅和招领大厅顶部都有搜索框和分类下拉选择搜索框支持按物品名称、地点关键字搜索分类下拉从后端加载分类表数据。筛选条件变化后重新请求接口同时更新URL的query参数这样用户刷新页面后筛选条件还在。发布表单做过三个版本最后确定的是表单拆分步骤比单独一个大表单体验更好。第一步选择发布类型失物还是招领第二步填写关键信息物品名称、分类、地点、详细描述、联系方式第三步上传图片最多三张可实时预览6. 本地跑通与部署上线关键步骤与踩坑实录6.1 环境准备JDK、MySQL、Node、Nginx部署整套系统前要准备环境我假设目标服务器是CentOS 7或Ubuntu 20.04整个过程主要依赖命令行操作。后端运行环境JDK 1.8我强烈建议直接用1.8SpringBoot 2.7系列对它的支持非常成熟Maven 3.6打包用MySQL 8.0数据库前端构建环境Node 16.x npm打包用Nginx作为静态文件服务器启动后端前先确认MySQL已启动并发起建库操作mysql -u root -p CREATE DATABASE lostfound DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后导入项目自带的SQL脚本脚本里包含了建表语句和初始化分类数据。后端application.yml里面需要改成自己环境的配置server: port: 9090 servlet: context-path: / spring: datasource: url: jdbc:mysql://127.0.0.1:3306/lostfound?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: yourpassword mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.lostfound.model configuration: map-underscore-to-camel-case: true jwt: secret: your-secret-key expire: 6048000006.2 后端打包与启动jar包的一站式运行环境准备好后后端打包非常简单mvn clean package -DskipTests打包完成后在target目录下会生成一个lostfound-0.0.1-SNAPSHOT.jar。直接启动java -jar lostfound-0.0.1-SNAPSHOT.jar本地调试时用上面这种方式就够了。部署到服务器后台运行我建议用nohupnohup java -jar -Xms256m -Xmx512m /opt/lostfound/lostfound-0.0.1-SNAPSHOT.jar /opt/lostfound/log/log.out 21 -Xms和-Xmx设置JVM初始和最大堆内存。校园项目512MB完全够了不需要给太多服务器资源也是钱。启动后验证接口是否正常可以执行curl -X POST http://127.0.0.1:9090/api/user/login \ -H Content-Type: application/json \ -d {username:test,password:123456}能返回code: 200就说明后端已经跑通了。6.3 前端打包与Nginx配置刷新404的坑前端打包同样简单npm install npm run build生成的dist目录就是需要部署的静态文件。把它拷贝到服务器我放在/opt/lostfound/dist目录。Nginx配置是整个部署过程中最常见的“翻车点”特别是“前端路由刷新404”的问题。Vue Router如果使用默认的hash模式URL带#号不会遇到刷新404但URL很丑。用history模式的话URL是干净的/lost路径直接访问http://域名/lost时Nginx会去服务器上找/lost这个文件找不到就返回404。解决办法是在location /里配置try_files回退到index.htmlserver { listen 80; server_name yourdomain.com; root /opt/lostfound/dist; index index.html; # 前端路由history模式必须配置 location / { try_files $uri $uri/ /index.html; } # 后端API反向代理 location /api/ { proxy_pass http://127.0.0.1:9090; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 图片等上传文件访问 location /uploads/ { alias /data/lostfound/uploads/; } }6.4 部署后最容易翻车的三个细节部署完成后前端能访问、后端接口也通了但很多人在测试过程中会遇到几个隐蔽的问题我可以分享一下经验。第一个是数据库时区问题。如果你在数据库的DATETIME字段里存的时间比北京时间少了8个小时多半是数据库连接URL里缺了serverTimezoneAsia/Shanghai。加上之后重启服务就能解决。这是一个非常典型的时序问题每次重新配置环境都可能遇到。第二个是图片显示不出来。前端页面上图片的URL是后端返回的相对路径比如/uploads/xxx.jpg。开发环境下前端通过Vue CLI构建的代理服务器访问但生产环境必须保证Nginx配置了location /uploads/指向实际存储目录。这里最容易犯的错误是忘记启动后检查上传目录的用户权限导致Nginx进程没有权限读取该目录。用chmod -R 755或者将上传目录归属改为Nginx运行用户都能解决。第三个是跨域配置与Nginx代理叠加的逻辑。当你已经配置了Nginx反向代理后前端请求/api已经走了Nginx转发所以浏览器里看到的请求是“同源”的——此时后端CORS配置不是必须的。但本地开发时前端跑在8080端口、后端跑在9090端口就会出现跨域所以后端CORS配置仍然要保留。这个“开发时跨域、生产时同源”的双重逻辑很多人一开始会绕晕。还有一个小经验Nginx配置修改后一定要记得nginx -s reload而不是直接重启整个Nginx服务否则可能出现短暂的服务中断。配置完直接访问测试接口确认所有功能正常再收工。这套系统的完整实现到此就全部梳理完了。我个人在做了这个项目之后最大的感受是前后端分离不是银弹但它确实把一个人的项目开发效率提升了不少。后端接口不好用了直接Postman调前端样式崩了打开浏览器DevTools就行两边不用互相扯皮。如果你打算找一个靠谱的课程设计或毕业设计项目这套SpringBootVueMyBatisMySQL的失物招领系统源码和部署流程可以直接拿去做二次开发数据库表结构、接口设计、前端组件这些基础都铺好了你要做的就是往里面填充自己的校园特色功能。如果部署过程中遇到什么问题可以按这篇文章的排查思路一步步过大部分问题都出在环境配置和路径映射这两个环节。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。