多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

SpringBoot+Vue+MySQL网上服装商城毕设项目从零到上线完整实战

SpringBoot+Vue+MySQL网上服装商城毕设项目从零到上线完整实战 又到了毕业设计扎堆的季节每年这时候都会有一批人对着“XXX管理系统”“XXX商城”之类的题目发愁。说实话SpringBoot Vue MySQL 的网上服装商城平台这个选题在毕设里属于老牌稳妥方向市面上资料多、技术栈经典、论文也相对好写但它绝不是“随便抄抄就能过”的项目——我见过太多人卡在环境配置、前后端联调、部署上线这几个环节最后答辩前一晚还在熬夜改Bug。所以这篇就把我做这类项目时从零到上线的完整思路、核心代码逻辑、数据库设计、避坑经验全捋一遍当成一份可以照着走的实操笔记。这篇内容适合三类人正在做类似商城选题的应届生、想快速上手前后端分离项目的自学者、以及帮人调试毕设代码的救火队员。你不需要一开始就把全部原理搞懂按下面这个顺序跟着操作先把项目跑起来再回头理解每一层的设计逻辑会比抱着书本啃效率高得多。1. 为什么这个选题是“安全牌”整体设计与技术选型思路1.1 技术栈选型的底层逻辑先说技术栈SpringBoot Vue MySQL 的组合在毕业设计里能成为主流是有原因的。SpringBoot 帮你把 Spring 那套繁琐的 XML 配置全部干掉内嵌 Tomcat 意味着本地跑后端不用单独装服务器Vue 负责前端页面和交互基于组件的开发方式对单人开发来说维护成本很低MySQL 则承担所有结构化数据的存储稳定、资料全、出问题随便一搜就有答案。我实际开发中更细化了一层基础框架用 SpringBoot 2.7.x JDK 8。你可能看到网上很多教程已经在推 SpringBoot 3但对毕设来说这是个坑——SpringBoot 3 强制要求 JDK 17且部分老版本依赖如 druid、mybatis-generator 的兼容性没跟上你搭环境的时间会白白多出一倍。选 SpringBoot 2.7 的原因很实际它处于成熟稳定期网上提问帖最多答辩被问到“为什么用这个版本”也答得上来。JDK 8 同理它是国内企业存量项目的主力版本面试和毕设两不误。前端框架 Vue 的版本选择也需要慎重思考。如果你拿到的是老模板大概率是 Vue 2 Element UI。如果是完全从零开始我会建议用 Vue 2 Element UI而不是直接上 Vue 3 Vite。不是 Vue 3 不好而是 Element UI 对 Vue 2 的组件生态太成熟了表格、表单、弹窗这些后台管理界面常用的东西拿来即用不需要自己造轮子。Vue 3 对应的 Element Plus 虽然也好但很多老教程的代码片段不兼容会对你自己排查问题增加难度。原则只有一条毕设追求的是稳定跑通不是技术尝鲜。ORM 层我用的是 MyBatis-Plus而不是原生 MyBatis。这也是一个很实在的决策点单表 CRUD 是商城后台最频繁的操作MyBatis-Plus 的 BaseMapper 直接帮你把增删改查写好了LambdaQueryWrapper 做条件查询也是一行代码的事省下的时间足够你多写一个模块。原生 MyBatis 固然更有“含金量”但毕业设计的核心是完成度是业务逻辑的完整性不是炫技。你把时间花在订单状态流转、库存扣减逻辑、权限拦截这些真正的业务难点上性价比会高得多。1.2 系统分层与模块划分思路有了技术选型下一步是设计系统分层。我做商城类项目的习惯是前端单独一个项目目录后端单独一个目录前后端只通过 JSON 数据交互。这种前后端分离架构最大的好处是前端开发和后端调试互不阻塞工作量一人分饰两角时心理压力也小。标准分层如下后端分层maven 单模块即可controller 层接收前端请求处理参数调用 serviceservice 层业务逻辑比如下单时的库存扣减、订单状态更新mapper 层与数据库交互只做增删改查不写业务entity 层实体类与数据库表字段一一对应common 层统一返回结果类 Result、异常处理器、工具类前端目录views页面组件每个路由对应一个 vue 文件router路由配置包括前端路由守护api封装 axios 请求统一管理接口地址storeVuex 状态管理保存用户 token、购物车数量等全局状态我见过很多同学的代码把 Controller 里塞满 SQL 操作service 层空着一旦业务变更就改不动。这里分享一个判断分层的经验在 service 层写业务逻辑时问自己一句话——“如果我要把 MySQL 换成 Oracle虽然现实中很少这样做我需要改动哪些文件”如果答案是只改 mapper 层的方言配置说明分层是合格的如果连 controller 都要动那就要反思了。商城功能模块的规划上用户端和管理端要分开这是电商系统的基本形态。用户端面向 C 端消费者包含注册登录、商品浏览、分类筛选、商品详情、购物车、订单确认与支付、个人中心这些模块管理端面向运营人员包含仪表盘统计、商品管理、分类管理、订单管理、用户管理。这个模块划分不是拍脑袋定的而是参考了主流电商平台的业务闭环用户产生购买行为运营需要管理商品和订单。你把这个闭环做通了系统就是完整的。2. 核心功能模块拆解与业务细节2.1 用户端从注册登录到购物车结算的完整闭环用户端是毕业设计里最容易被“做浅”的部分。很多人只是简单地做了几个页面商品列表能点、购物车能加、订单能下单然后就结束了。实际上用户端的核心在于流程的完整性——不是每一个按钮都有页面就叫完整而是每一步操作背后的数据流转是一致的。先说注册登录这个入口。登录我建议用 JWTJSON Web Token而不是传统的 Session。为什么前后端分离架构下前端部署在 Nginx后端跑在 Java 进程里Session 依赖服务器内存保存状态集群部署就失效了。JWT 把用户信息加密后放在 Token 里前端保存 Token每次请求放在请求头后端用拦截器解析天然适合前后端分离场景。答辩时这一条就能体现你对架构的理解深度。具体实现上有几个细节值得注意。密码绝对不能明文存储要在注册时用 BCrypt 加密——Spring Security 里自带的 BCryptPasswordEncoder 就可以它是一种加盐的哈希算法同样的密码每次生成的密文都不同能有效防止彩虹表攻击。登录接口校验通过后返回 Token同时把用户基本信息ID、用户名、角色放进 Token 的 payload 中。Vue 端拿到 Token 后存到 localStorageaxios 请求拦截器里统一加上Authorization: Bearer token头。后端写一个拦截器过滤所有/api/**请求放行登录注册接口解析 Token 失败就直接返回 401。商品浏览的体验优化也常被忽视。列表页不是简单的 SELECT * FROM product要做分页和条件筛选。MyBatis-Plus 的 Page 对象可以轻松实现分页条件筛选建议用 LambdaQueryWrapper 动态拼接——分类 ID、商品名称模糊查询、价格区间、上架状态这些条件在用户勾选的瞬间动态变化动态 SQL 的优势就在这里。前端对应封装一个获取商品列表的 api 方法参数包含 current(页码)、size(每页数量)、categoryId、keyword、priceMin、priceMax请求发出后拿到 {records, total, current, size} 四个字段渲染到表格的同时用 el-pagination 组件配合。还有商品详情页的“库存显示”要跟真实库存保持一致这里就关联到一个经典问题——详情页展示的是商品基本信息购物车操作和下单操作需要锁定的是 SKU 级别库存。对于毕设可以简化每个商品只有一个默认规格库存字段直接存在商品表里。但如果你做的是带尺码颜色的服装商城就建议建一个专门的 product_sku 表商品表存通用属性SKU 表存具体可售规格和各自库存。从表结构设计上这一步能区分你的系统是“课设水平”还是“毕设优秀水平”。2.2 管理端商品管理、订单处理和统计面板管理端开发中最重要的原则是不要把管理端当成 CRUD 集合。我见过很多人的管理端就是把数据库表逐个做成表格页面增删改查完事。这样的缺点是答辩时老师问一句“你的系统如何保证商品上下架状态一致性”就答不上来。商品管理模块要关注几个业务点。一是商品状态上架和下架不是一个 update 操作那么简单下架后的商品应该在前端不可见但购物车里已经加入但未下单的商品如何处理这里我采用的方案是每次从购物车结算时后端会重新校验商品状态和库存如果有问题就直接提示用户而不是在下架时去清理所有用户的购物车。二是图片处理商品图片不能直接存数据库 BLOB 字段应该上传到服务器本地目录或 OSS数据库只存访问路径。订单管理是管理端业务量最大的部分核心是订单状态机的设计。我常用的状态定义是状态码含义可触发操作触发方0待付款取消订单、模拟支付用户1待发货点击发货管理员2待收货确认收货用户3已完成申请售后/删除订单用户4已取消无系统/用户这套状态机的价值在于它定义了订单在整个生命周期中的合法流转路径。比如从 0 到 2 是不合法的还没发货就收货系统应该在代码层面阻止这种异常操作。具体实现时Update 语句里加上一个前置条件UPDATE orders SET status 2 WHERE id ? AND status 1这一步是并发场景下防止状态错乱的关键。如果更新影响行数为 0说明当前状态不对直接抛出异常即可。仪表盘统计模块则是很多人会漏掉但性价比很高的功能。用 ECharts 展示近 7 天订单量趋势图、商品分类占比图、热销商品 Top 10。统计 SQL 本身很简单——按天分组、按分类分组、按销量排序。但视觉效果和数据可视化能力在答辩现场非常加分老师看到图表会认为你具备数据展示意识。2.3 权限控制为什么用 JWT 拦截器而不是 Shiro商城系统天然分两类用户普通用户和管理员。权限控制是必须做的但实现方式的选择有讲究。Shiro 和 Spring Security 功能强大但对毕设项目太重了配置繁琐学习曲线陡峭而且这两个框架的文档对新手不友好遇到的问题查起来也费劲。我用 JWT 自定义拦截器的方案来解决权限问题。具体实现分三步登录时根据用户角色生成不同的 JWT管理员登录后 Token 的 payload 里带上role: admin写一个 HandlerInterceptor在 preHandle 方法里校验 Token 合法性解析出用户 ID 和角色后存入 ThreadLocal这样后续 Controller 里可以直接取用户信息管理端所有接口的请求路径统一为/api/admin/**拦截器里加一个判断——如果路径是 admin 开头但角色不是管理员直接返回 403这个方案的优点是代码完全可控所有逻辑自己写的答辩时能讲得头头是道。同时它规避了引入 Shiro 后可能遇到的各种配置困扰作为一个单体毕业设计项目够用了。实际项目里这么干可能会被架构师批但这是毕业设计架构的合理性永远要让位于可解释性和代码完成度。3. 数据库设计表结构背后的业务思考3.1 核心表结构与字段设计数据库设计是电商系统的地基地基不稳后面全崩。我的设计原则是宁可多建一张关联表也不要用逗号分隔的字符串存多值。下面这张表结构清单是我做服装商城沉淀下来的核心版本表名核心字段说明userid, username, password, nickname, phone, avatar, create_time用户表密码存 BCrypt 密文categoryid, name, parent_id, sort, icon分类表parent_id 支持二级分类productid, category_id, name, subtitle, main_image, detail, price, stock, status, sales, create_time商品表status 控制上下架product_skuid, product_id, name, stock, price规格表有颜色尺码需求时使用cartid, user_id, product_id, quantity, checked, create_time购物车表checked 标记是否选中结算addressid, user_id, receiver, phone, province, city, district, detail, is_default收货地址表ordersid, order_no, user_id, total_amount, pay_amount, freight_amount, status, receiver_address, create_time, pay_time, deliver_time, receive_time订单表冗余收货快照order_itemid, order_id, product_id, product_name, product_image, price, quantity订单项表冗余商品快照admin_userid, username, password, nickname, create_time管理员表与 user 分离这里有几个字段设计的逻辑需要展开讲讲因为论文里写系统设计章节时这些就是你“设计思路”的素材。订单表冗余收货地址快照为什么不直接关联地址表而要冗余一份因为用户下单后如果修改了地址历史订单的收货地址不应该跟着变。订单是交易凭证必须保存交易发生时的快照。同样订单项表冗余了商品名称和商品图片也是这个道理——商品可能被下架、改名但历史订单必须记住当时你买的是什么东西。这个细节在答辩时被老师问到的概率极高而且回答好了体现的是对业务的理解。金额字段用 DECIMAL 而不是 DOUBLE这是开发中的血泪教训。DOUBLE 是浮点数在计算机里用二进制存储会出现 0.1 0.2 ! 0.3 这样的精度问题。涉及钱的系统绝对不能用浮点数。DECIMAL(10, 2) 能精确表示两位小数MyBatis 里对应 BigDecimal 类型计算时也要用 BigDecimal 的 add、subtract 方法来确保精度。3.2 订单号的生成策略和库存扣减的并发控制订单号的生成是很多新手会忽略但几乎是必考的细节。方案其实很简单用时间戳 用户ID 四位随机数String orderNo ORD System.currentTimeMillis() userId (int)((Math.random()*91)*1000);。数据库层给 order_no 加上唯一索引兜底就算极小概率冲突了也会抛异常不会产生重复订单号。库存扣减的并发控制是商城系统的经典问题。最简单的毕设做法是下单时先检查库存 if (stock quantity)再执行 UPDATE product SET stock stock - #{quantity} WHERE id #{id} AND stock #{quantity}。核心在于 UPDATE 语句里的AND stock #{quantity}这个条件它保证了在并发场景下只有库存真正充足的请求才能更新成功如果影响行数为 0 就抛“库存不足”异常。这种方式叫乐观锁的思想不需要引入分布式锁那么重的机制在单机部署的毕设场景下完全够用。答辩时如果被问到“如何防止超卖”这就是一个漂亮的标准答案。3.3 初始化数据脚本让系统“看起来”很专业数据库初始化脚本的质量直接决定了演示效果。很多人的 init.sql 只插了三条商品数据和两个用户打开页面稀稀拉拉视觉上就输了。我会在脚本里准备至少 15~20 个服装商品分 3~4 个分类每件商品都有图片地址可以指向本地 static 目录或外链测试图片、合理的价格和描述。另外准备一个测试管理员账号admin / 123456和一个测试用户账号user / 123456并在文档里写清楚——这样不管是自己演示还是老师验收都不用临时注册体验顺畅很多。4. 前后端联调与关键接口实现4.1 统一返回结构与全局异常处理前端和后端联调时最大的痛点就是返回数据格式不统一有的接口返回{code: 200, data: [...]}有的直接返回数组有的报错返回字符串。这种混乱对调试效率是灾难。我从第一个项目开始就坚持全系统统一返回 Result 对象现在已经成为肌肉记忆了。Result 的定义很简单public class ResultT { private Integer code; // 200成功500失败401未登录 private String message; // 提示信息 private T data; // 返回数据 }对应的静态方法Result.success(data)、Result.success(message, data)、Result.error(message)。Controller 层的每个接口都返回 Result 类型这样前端 axios 的响应拦截器里只需要判断 code 是不是 200非 200 直接弹出 message 里的错误提示出错时定位问题的速度会快很多倍。全局异常处理用 RestControllerAdvice 注解实现这是 SpringBoot 里一个非常实用的注解。项目里最常出现的异常是业务异常比如库存不足、订单状态不对、参数校验异常比如用户名不能为空、运行时异常比如空指针。三种异常分别捕获统一封装成 Result.error 返回。这样做的好处是Controller 里不需要 try-catch 包裹业务代码代码干净异常又能被前端友好地感知到。代码逻辑出错时往前端抛“服务器开小差了”这种信息总比白屏一片好用得多。4.2 Vue 端的接口封装与动态路由Vue 前端这块我习惯在 src/api 目录下按照业务模块建文件user.js、product.js、cart.js、order.js。每个文件里导出一个函数函数内部用 axios 封装请求import request from /utils/request export function login(data) { return request({ url: /api/user/login, method: post, data }) }request是在 utils/request.js 里创建的一个 axios 实例配置了基础路径baseURL: /api和超时时间。请求拦截器里取出 localStorage 的 token 放到 header。响应拦截器里统一处理业务错误code 不是 200 时用 Element UI 的 Message 组件弹出错误信息code 是 401 时清除 token 并跳转到登录页。这层封装完成以后写具体页面时就彻底不用关心请求细节了。路由这块有一个在商城系统里非常实用的技巧——前端路由守卫控制页面访问权限。定义了三个路由层级白名单页面如登录页、注册页用户页面如商品列表、购物车、订单填写页管理页面如商品管理、订单管理。Vue Router 的 beforeEach 导航守卫里判断如果没有 token 且目标路由需要登录就跳转登录页如果有 token 但访问的是 admin 路由且当前用户不是管理员就跳转到首页并提示无权限。4.3 图片上传与文件存储的实现服装商城离不开商品图片。图片上传功能看似简单实际有坑。我在后端写一个upload接口接收 MultipartFile保存到服务器指定目录返回可访问的 URL// 保存路径示例/usr/local/upload/2023/05/20/uuid.jpg // 返回访问路径示例http://服务器IP:8080/upload/2023/05/20/uuid.jpg关键配置有二一是 spring.servlet.multipart.max-file-size 要设置大一点10MB否则默认 1MB 限制会让你传图失败二是需要做一个静态资源映射把本地的 upload 目录映射到 URL 的 /upload/** 路径否则图片虽然传上去了但浏览器访问 404。前端上传组件用 Element UI 的 el-uploadaction 指向后端上传接口on-success 回调里把返回的图片 URL 赋值给表单的 image 字段再做回显。这个流程走通之后整个商品管理模块就顺了。5. 部署上线从本地跑通到服务器可访问5.1 部署环境准备服务器、JDK、MySQL 安装要点部署是很多毕业设计项目最容易被卡住的地方但也是收获感最强的一环。当你把系统部署到一台公网服务器上手机打开浏览器输入 IP 就能看到页面那种感觉比本地跑通爽太多了。服务器方面建议用轻量应用服务器2核4G 的配置跑这个项目绰绰有余学生认证后会便宜很多。操作系统选 Ubuntu 或 CentOS二选一即可我没必要把两个系统的命令都罗列一遍这里按 Ubuntu 20.04 走一遍CentOS 的差异点我会标出来。JDK 8 安装走 openjdk-8-jdk 即可# Ubuntu sudo apt update sudo apt install openjdk-8-jdk -y java -versionMySQL 安装要注意版本选择。我推荐装 MySQL 5.7 或 8.0。需要提醒的是 Ubuntu 20.04 默认源里 MySQL 8.0安装完成后要执行安全初始化脚本sudo mysql_secure_installation来设置 root 密码。CentOS 的差异点是sudo yum install mysql-server -y sudo systemctl start mysqld # 初始密码在 /var/log/mysqld.log 里 grep temporary password5.2 后端打包部署Maven 构建与 jar 包运行后端打包前要改一个关键配置——数据库连接。本地开发时你用的是localhost:3306部署时改成服务器的内网 IP 或公网 IP。生产环境配置我写到 application-prod.yml 里用spring.profiles.activeprod来切换避免把本地配置冲掉。打包命令mvn clean package -DskipTests # 打包成功后 target 目录下生成 xxx.jar启动nohup java -jar xxx.jar --spring.profiles.activeprod app.log 21 nohup保证 ssh 断开后进程继续运行日志重定向到 app.log 方便排查问题。这里有一个毕设项目常见的部署错误端口默认是 8080但云服务器的安全组没放行 8080 端口导致外部访问不了。云服务器除了系统内防火墙还要去控制台安全组规则里放行端口两个地方都要配置差一个就白折腾。接着把 jar 包上传到服务器。推荐用scp或rsync如果你是从本地上传scp target/xxx.jar root服务器IP:/usr/local/app/5.3 前端构建与 Nginx 配置最容易被忽略的代理设置前端部署分两步本地构建上传文件。构建之前先在本地环境变量里确认好接口地址——由于前后端分离我们通常用 Nginx 做反向代理前端页面仍然请求/api路径Nginx 把/api转发到后端服务的 8080 端口。这样前端代码里不用写死服务器 IP本地和线上环境一致。本地构建npm install npm run build # 生成 dist 目录dist 目录就是你要部署的静态文件。上传到服务器的 /usr/local/nginx/html 目录下然后配置 Nginxserver { listen 80; server_name _; # 前端页面 location / { root /usr/local/nginx/html; index index.html; try_files $uri $uri/ /index.html; # 关键配置Vue 路由 history 模式需要 } # 后端接口代理 location /api/ { proxy_pass http://127.0.0.1:8080; 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 /upload/ { alias /usr/local/upload/; } }这里最关键的配置是try_files $uri $uri/ /index.html;这一行解决了 Vue Router 的 history 模式刷新页面 404 的问题。如果没有这个配置你点击路由跳转没问题但刷新当前页面就会报 404——因为 Nginx 没有找到对应的实际路径文件需要让它把所有找不到的路径都转发到 index.html由 Vue 路由接管。配置完成校验后重启 Nginxnginx -t sudo systemctl reload nginx5.4 数据库初始化与日常维护建议服务器上的 MySQL 建库建表可以直接用本地的 init.sql 迁移。推荐做法是先在本地用mysqldump导出完整数据文件再上传到服务器导入这样商品数据、分类数据都在系统打开就有内容可看# 本地导出 mysqldump -uroot -p shop shop.sql # 服务器导入 mysql -uroot -p shop shop.sql上线之后注意三件事一是定期备份数据库用 crontab 每天凌晨执行 mysqldump 并备份到另一台机器或云存储二是查看日志用tail -f app.log后端报错信息全在日志里三是服务器安全组尽量只放行需要的端口8080 端口也不要向公网开放——让 Nginx 代理转发到后端相当于把后端服务藏在了内网安全性高一个层级。6. 论文写作与答辩准备的实战建议6.1 论文结构怎么搭才像“自己做的”毕业论文的框架各学校要求大同小异一般包含摘要、绪论、需求分析、系统设计、系统实现、系统测试、总结。多数人写论文最大的问题是“抄模板痕迹太重”答辩老师一眼就看出来。我的建议是论文的核心图表和核心代码必须是你自己项目的截图和关键实现片段哪怕文字写得朴素一点也不要大段粘贴网上现成内容。需求分析章节要把功能需求画成用例图非功能需求从安全性、性能、易用性三个角度写。系统设计章节要把数据库表结构画成 ER 图这里有一个加分细节——除了表字段把表之间的关联关系用主外键和箭头标清楚。软件体系结构画成层次架构图前端展示层Vue、后端控制层Controller、业务层Service、数据访问层Mapper、数据存储层MySQL。这些图你用 ProcessOn 画好后截图放进去专业感立马上来。系统实现章节是最能拉开分数差距的部分。不用面面俱到选四五个核心功能的实现细节展开写比如商品查询的分页实现、购物车数量加一减一的边界处理、下单时的库存扣减和事务管理、JWT 拦截器实现思路。每部分配关键代码段 界面截图代码段截取核心逻辑部分不要贴一整个文件界面截图最好是带数据的真实运行图。6.2 答辩中高频问题的应答策略作为过来人我把答辩现场最常被问的问题整理一下提前准备不慌“你这个系统的主要功能有哪些”按照用户端和管理端两线介绍控制在一分钟内先说模块再补一句亮点比如订单状态机、JWT 权限控制。“数据库为什么设计这些字段”用订单表冗余地址快照举例说明这是为了保持历史订单的不可变性。“如何防止库存超卖”回答 UPDATE 语句的条件更新方式说明这是乐观锁的思想。“系统的安全性怎么考虑”从密码 BCrypt 加密、JWT 拦截器校验、管理端权限分离、配置文件敏感信息外部化四个点说。“如果用户量增大如何优化”建议答 Redis 做缓存把热点商品信息缓存到 Redis降低 MySQL 压力再说加一层 CDN 加速静态资源但记住这只是你的设计设想不用真去实现。答辩的核心策略是把问题引导到你真正做过、真正懂的地方。介绍系统时说“订单模块的实现我觉得挺有意思的”老师大概率顺着你的引子往那追问而你刚好对那块最熟。千万不要主动提自己不熟悉的领域那等于给评委递刀。6.3 部署文档不只是给老师看的说明书标题明确提到了部署文档千万不要忽略。一份好的部署文档应该让一个从没碰过这个项目的人按步骤操作也能把系统跑起来。我写部署文档的习惯是开头写明环境要求操作系统版本、JDK 版本、MySQL 版本、Node 版本正文按“数据库初始化 → 后端部署 → 前端部署”三大部分组织每部分先写概述再写详细步骤所有命令都是完整可直接复制执行的末尾加一个常见问题章节把数据库连接失败、端口被占用、Nginx 配置不生效这些问题列上排查方案。这份文档的额外价值在于项目能否复现是校企检查的核心指标之一有一个完善的部署文档整个项目的可信度会明显提升。它也能在答辩后保住你的项目——毕业设计不是答辩结束就完了后续抽查、评优甚至复试时都可能要求现场运行系统。7. 实操排坑记录那些文档里不会写的问题7.1 前端跨域问题为什么接口调不通本地开发时前端跑在localhost:8081后端跑在localhost:8080不同端口之间请求属于跨域浏览器会拦截。有两种解决方案一是在后端写一个 CORS 配置类允许所有来源跨域二是通过 Nginx 代理就是我们上线用的方案规避跨域。本地开发建议用 Nginx 方案因为它离生产环境更近前后端都不用改代码临时调试时也可以用 SpringBoot 的 CORS 配置快速解决。需要注意的是上线后不建议放开所有来源的跨域应该限定为你的前端域名或 IP。7.2 数据库连接失败经典的时区问题和密码问题刚部署到服务器时最容易遭遇的两个数据库报错The server time zone value Öйú±ê׼ʱ¼ä is unrecognized这是 MySQL 8.0 的时区问题连接 URL 上加?serverTimezoneAsia/Shanghai即可。Access denied for user rootlocalhost确认密码是否正确MySQL 8.0 默认使用 caching_sha2_password 认证插件如果你的 JDBC 驱动版本太低可能不兼容升级驱动到 mysql-connector-java 8.0.x 版本解决。7.3 服务器上内存不足闹出的幺蛾子1G 内存的小服务器同时跑后端 jar默认堆内存很大加 MySQL 就会很吃力表现为系统突然卡顿或 Java 进程被杀。解决方式是启动时手动限制堆内存java -Xmx256m -Xms128m -jar xxx.jar这个参数的意思是堆内存最大 256MB初始 128MB配合 small 服务器的内存完全够用。如果你用 2G 内存的服务器跑这个项目会轻松很多甚至可以给到 512MB。7.4 Vue 打包后页面白屏这大概是毕设届出现频率最高的前端事故。原因大多是构建时资源路径配置不对Vue CLI 默认的 publicPath 是/部署在服务器根路径时没问题但如果通过子路径访问就会白屏。解决方式是在 vue.config.js 里设置module.exports { publicPath: ./ // 打包后使用相对路径部署到任意子路径都不怕 }设置完重新npm run build再部署一次基本就能解决。7.5 购物车数量超出库存的边界问题用户在前端购物车里可以多次点击数量加号加到超过库存如果只做前端限制并不可靠——用户可以绕过页面直接提交请求。后端在下单接口里必须重新校验查询商品当前库存判断购物车每个商品的数量是否小于等于库存否则返回“商品库存不足”的友好提示。这种后端兜底的思路在电商项目里是必需的因为前端校验的目的一是提升体验二是减少无效请求真正的安全防线必须落在服务端。7.6 上传图片后页面无法显示图片上传成功但访问 404绝大多数情况都是静态资源映射没配。SpringBoot 里要在 WebMvcConfigurer 中配置Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceHandler(file: uploadPath); }这段配置的意思是URL 路径/upload/**映射到服务器的 uploadPath 目录。注意 Windows 和 Linux 下 file: 后面路径的书写格式不同Windows 是file:D:/upload/Linux 是file:/usr/local/upload/。写到这里我想起自己当年带毕设小组时说过最多的一句话这个项目没有多高级的“天庭技术”但你把每一层的逻辑想清楚、把每一步的边角补干净它就是完整的、能站得住脚的系统。平时同学之间的差距也是这样拉开的——同样的技术栈有人部署后三天两头出问题有人一次上线稳稳跑通。上面这个清单里的每一个步骤和坑都是我从本地开发到服务器部署反复折腾后沉淀出来的希望你能避开这些弯路把时间花在真正体现思考的业务逻辑和论文表达上。祝答辩顺利。
返回列表