SpringBoot+Vue二手手机商城系统:从数据库设计到前后端联调实战解析
做毕设选题目这件事每年都能卡住一批人。选题太简单怕过不了太难又怕做不完最后变成给自己挖坑。如果你正好需要一个既有完整业务闭环、又能在答辩论证技术含量的Java Web方向项目那“SpringBootVue 华强北商城二手手机管理系统平台”这套组合拳是我见过性价比很高的选择。这套源码我断断续续调了一周多从数据库脚本到前端页面再到接口文档把整条链路完整跑通了。今天这篇就围绕这套项目把核心设计思路、数据库表结构、接口怎么写、前后端怎么联调、答辩会被问什么一次性讲透。先交代一下定位。这不是一个普通的增删改查学生管理系统它模拟的是华强北线下二手手机档口向线上商城转型的真实场景。核心业务是二手手机的展示、检索、下单和交易管理并且因为二手商品特殊业务里还牵扯到“成色分级”“质检描述”“价格浮动”这些在一手电商里见不到的细节。整体采用前后端分离架构后端用SpringBoot提供RESTful API前端用Vue 2 Element UI搭建页面数据库用MySQL配合MyBatis-Plus做ORM再通过JWT做登录鉴权。可以说这套技术栈覆盖了当前Java Web企业级开发最主流的组合。如果你是准备拿它当毕业设计那么这个项目能展示的点其实相当集中数据库设计能力、业务逻辑建模能力、接口设计规范、前后端联调能力以及排查问题的动手能力。视野再大一点这个项目完全可以从二手手机商城延伸出去换成二手相机、二手游戏卡带、二手书籍交易前台展示、用户下单、后台管理的骨架不用大改属于一个业务模型可以复制到多个场景的典型例子。这也正是老师喜欢看到的点在。1. 为什么选“华强北商城”这个题目1.1 二手手机交易场景比你想的更有料写毕设最忌讳一点做一个“哪里都能见到的管理系统”。普通的管理系统本质就是一张表对着五个页面做到了第三周你就会觉得无聊写到后面连自己都不想看。但二手手机商城不一样它的业务场景天然逼你先去思考真实的交易逻辑。二手手机每一台都是独一无二的虽然型号一样比如两台 iPhone 13 Pro但因为成色、内存、电池健康度、是否过保、有无维修记录不一样价格完全不同。所以商品模型不能直接套“SKU库存”那一套标准化逻辑你得给每台机器单独建档成色要分级99新、95新、9成新、靓机、小花、大花价格要能动态调整上架之前还要写一段质检报告。这些细节就是论文和答辩里能讲出东西的地方。另一层是交易流程。用户挑中一台手机下单前要不要锁库存订单超时未支付要不要自动取消库存什么时候回滚这些都是订单业务建模里值得反复推敲的点。还有售后场景二手手机用户签收后可能以“成色不符”为由退货后台要不要支持退款可能需求文档里没有提但我自己在扩展项目的时候就加了一个退款状态字段答辩时主动提出新增理由老师追问了两三个问题都答上来了这个加分项是实打实的。1.2 为什么选 SpringBoot Vue 这套组合如果放到十年前Java Web毕设还是 JSP Servlet Tomcat 一条龙前端后端写在一个 WAR 包里。现在再去学那一套项目经验和就业市场已经严重脱节。SpringBoot Vue 之所以成为当前毕设标配核心原因有两条。第一SpringBoot 把配置简化到了极致。以前 SpringMVC 要写一堆 XML用 Maven 管理依赖还要手动处理版本冲突。SpringBoot 用 starter 机制往 pom 里塞一个spring-boot-starter-web内嵌 Tomcatmvn spring-boot:run就能起服务开发体验直接上了一个台阶。对于毕设来说你不需要研究微服务那套复杂架构只需要把 SpringBoot、SpringMVC、MyBatis-Plus 吃透已经足够支撑一个完整的中小型管理系统。第二Vue 的核心优势是组件化和响应式。前端不再是一堆零散的 HTML 页面而是把轮播图、商品卡片、订单状态标签这些都封装成了一个个可复用组件数据变了页面自动更新。配合 Vue Router 管理路由、Vuex 管理全局登录状态再加上 Element UI 提供了现成的表格、表单、弹窗、分页组件前端开发效率比传统 jQuery 时代高出一大截一个前端功能几十行代码能搞定的事完全不必写几百行对毕设时间很友好。1.3 这套项目我看到的亮点在设计细节上我之所以对这套源码有印象是因为它有一条比较清晰的“场景驱动设计”思路不是硬堆 CRUD。比如前台商城的搜索页面二手手机最基础的需求是“按品牌筛选、按成色筛选、按价格区间检索、按销量和价格排序”。前端通过 Vue 的 data 里维护一组筛选条件后端用 MyBatis-Plus 的QueryWrapper动态拼接 SQL传入非空字段就追加条件不需要手写一堆 if 嵌套的 XML 动态 SQL。这个设计在毕设答辩里是可以展开讲五分钟的如何用条件构造器减少重复代码如何通过 SQL 层面的索引提升筛选效率。再比如订单号的设计。用户下单之后前端要展示订单号后台要对账接口要对订单做状态流转。我看到源码里订单号生成策略是时间戳精确到秒 用户ID 随机四位数字直接用字符串拼接不依赖数据库自增ID。这样做的优势是订单号可读性强能反推下单时间劣势是并发场景下可能重复但在毕设这种低并发场景里完全够用。这个细节体现的是“根据实际场景权衡技术方案”的思路答辩的时候完全可以拿出来说。还有一个细节是图片存储。二手手机商品详情往往要上传多张实拍图源码里并没有引入 OSS 或者 FastDFS 这种重量级组件而是把图片文件保存在本地磁盘目录然后通过一个/images/**静态资源映射的配置对外暴露访问地址。数据库里存图片的相对路径前端直接拼接访问。这对于毕设场景是比较合理的取舍不会因为外部云服务过期或者网络不通导致项目演示时图片全部挂掉。2. 数据库设计与 SQL 脚本整个项目的地基2.1 核心表结构与字段设计拿到源码的第一件事不是启动项目而是先把 SQL 脚本好好读一遍。这套项目我数了一下一共是 9 张核心表用户表、Banner轮播图表、手机分类表、手机商品表、购物车表、订单表、订单明细表、公告表再加一个用于后台管理的管理员表。每一张表都对应一条业务主链路没有为了凑数而设计的冗余表这个点我觉得对毕设题目的“工作量展示”来说很讨巧。重点说手机商品表二手手机业务的复杂度都集中在这张表上。字段大致是主键ID、商品标题、品牌单独一个字段、型号单独一个字段、成色等级、内存容量、颜色、屏幕尺寸、电池健康度、是否原装枚举0/1、购入价格、售价、库存数量、商品主图、商品详情图片组、质检报告文本、上架状态、销量、创建时间、更新时间。其中“品牌”和“型号”虽然也可以用一对多分类表管理的关联设计但考虑到二手手机的单机属性利用普通字段存储反而让前端筛选更直接属于能用简单方案就不要复杂化的设计思路。另一点值得说商品详情图片组这个字段。有人会用一张映射表去存商品的多个图片也有人用逗号分隔的字符串。源码里的方案是存 JSON 数组字符串例如[/images/phone_01.jpg,/images/phone_02.jpg]前端拿到后用JSON.parse转成数组渲染成图片轮播。这个做法在“图片是查询侧低频修改、展示侧高频读取”的场景下很实用少一次关联查询实现也直观。业务体量不大的系统用 JSON 字段存结构化数组完全可行这也是当前不少中小型项目里的常规操作。2.2 订单状态的设计是业务逻辑的精华订单表是全系统涉及状态最复杂的表。我的建议是先把状态机画清楚再去看代码。用状态枚举存订单当前状态待付款0、已付款待发货1、已发货2、已完成3、已取消4、申请退款5、已退款6。其中用户操作和后台操作分别触发不同的状态流转路径。举个例子用户看中一台手机加入购物车去提交订单页面确认收货地址、联系方式和留言点击立即购买此时创建订单状态为待付款。用户点击“模拟支付”后端调用支付成功后把状态改为已付款待发货。管理员在后台看到新订单点击发货状态变为已发货。用户可以点“确认收货”状态变为已完成。这里涉及的关键设计是订单状态不是前端直接改的而是通过不同的后端接口配合状态判断来实现。每次状态转换都要校验当前状态是否符合预期比如已取消的订单不能直接改成已完成防止“步进式状态机”被乱跳。订单明细表的作用是记录下单那一刻的订单快照。为什么需要快照因为用户下单之后管理员完全有可能把商品价格改了。如果不做快照订单详情页显示的金额就和结算页对不上。订单明细表里的商品名称、主图、单价、数量都冗余一份订单生成后不再依赖商品表的数据。这个设计在传统电商项目里非常基础但在毕设项目中能考虑进去说明对业务的理解已经超过“会写增删改查”的水平写在论文的创新点里也不心虚。2.3 SQL 脚本的初始化要点与测试数据整个项目的初始化数据脚本我建议用 Navicat 直接打开执行前提是 MySQL 版本在 5.7 以上。脚本开头要检查字符集统一问题数据库的默认字符集尽量设置成 utf8mb4别用 utf8。原因很简单utf8mb4 是 utf8 的超集能正常存 emoji 表情二手手机的商品描述里经常会有符号和特殊字符如果字符集不对一旦入库前端展示就会出现乱码。下面这句是我在导入脚本时每次都必用的SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;执行之后再跑建表语句和数据 INSERT 语句。脚本里如果包含外键比如订单明细表关联订单表主键建议先把外键检查关掉数据导完再用SET FOREIGN_KEY_CHECKS 1;恢复避免因为插入顺序问题导致外键冲突这个技巧在导入任何 SQL 项目时都非常实用。随机广告数据量也很重要。商品表建议至少插 20 条以上的模拟数据覆盖苹果、华为、小米、OPPO、vivo 这些主流品牌成色最好也能覆盖 99新、95新、9成新、靓机几个等级。数据太少前端商城页面空荡荡的答辩演示基本没法看测试数据准备得充分演示时商品列表的分页、筛选、排序都能一眼看出效果。还有一点admin 管理员账号和普通用户账号SQL 脚本里默认用 MD5 加密密码。MyBatis-Plus 查用户时会把数据库中加密后的密码和用户输入的密码加密后比对。如果你导入后发现登录不进去优先检查你输入的密码加密后是否和库里存的一样最简单的验证办法是先写个测试类用DigestUtils.md5DigestAsHex()打印一下加密结果再和管理员表里的密文比对。2.4 数据库设计中的常见问题速查现象原因解决方式中文乱码数据库连接URL没有指定字符集在 JDBC URL 上增加characterEncodingutf8导入SQL报 1451 外键错误外键约束导致插入顺序冲突导入前执行SET FOREIGN_KEY_CHECKS0手机号/昵称为空仍能注册后端没有做参数校验在实体字段上增加NotBlank使用Validated注解删除分类时报外键约束分类下还有商品引用后台先移除该分类下的商品再执行删除日期返回格式不对后端日期序列化为时间戳在application.yml里设置spring.jackson.date-format和time-zone前后端统一格式3. 后端接口设计与接口文档的写法3.1 接口文档为什么值得认真写很多同学习惯把接口写完、前端拿到数据一展示就觉得完事了接口文档能省则省。但这个项目里附带接口文档的价值在答辩时才会完全体现出来。接口文档本质上是你对整个系统接口约束的书面沉淀写清楚它就等于把后端的业务流程又梳理了一遍。接口文档应该包含以下信息接口名称、请求方式GET/POST/PUT/DELETE、请求路径、是否需要登录Token、请求参数表参数名、类型、是否必填、描述、响应参数表响应码、消息、数据体、以及一个典型的 JSON 响应示例。响应数据的格式要全局统一项目里统一使用{ code: 200, message: success, data: {} }这种结构。只要嵌套层级固定前端 axios 拦截器就能统一处理返回值。首页接口我用文档里现成的GET /api/home/info举例它返回的数据结构和接口文档里会写得很明确{ code: 200, message: success, data: { bannerList: [ { id: 1, image: /images/banner_01.png, productId: 1 } ], newProductList: [ { id: 1, title: iPhone 13 Pro 256G 国行 95新, price: 3999.0, coverImage: /images/phone_01.png } ], hotProductList: [] } }前端拿到后依次渲染轮播图和商品列表data 里每个字段都有明确的语义前端根本不需要问后端“这个字段什么意思”这就是好文档的效果。3.2 核心接口清单与典型调用流程把整个项目跑通后我按模块整理了一份接口清单你可以对照自己的模块做复查模块接口路径方法说明是否需要Token用户登录/api/auth/loginPOST用户/管理员登录返回JWT否用户注册/api/auth/registerPOST注册商城用户否首页聚合/api/home/infoGET返回轮播图、推荐商品否商品分页/api/product/listGET关键词、品牌、成色、价格筛选否商品详情/api/product/{id}GET查询单品全部信息否加入购物车/api/cart/addPOST添加商品到购物车是购物车列表/api/cart/listGET当前用户购物车集合是提交订单/api/order/createPOST从购物车或立即购买生成订单是模拟支付/api/order/pay/{orderId}PUT模拟支付更新状态是用户订单列表/api/order/myGET按状态筛选订单是后台商品管理/api/admin/product/*POST/PUT/DELETE商品增删改查是后台订单管理/api/admin/order/listGET全部订单支持状态筛选是接口调用中比较典型的链路是“用户下单”。前后端交互是这样的前端先从购物车列表取出商品提交订单时把cartIds购物车行ID列表传给后端后端算出总价、生成订单和订单明细同时把购物车中对应的商品行删除再更新商品表的库存。这里有几个容易出错的位置库存扣减和订单生成必须放在同一个事务里否则会出现订单生成了但库存没扣的脏数据同时需要校验商品状态下架商品不允许下单。如果事务没加并发测试的时候问题就会被放大日常调不出来一演示就出事所以我建议在Transactional注解里把订单生成、明细写入、库存扣减、购物车清理集中在一个方法中。3.3 接口文档排错实用技巧自己写接口文档时建议用 Apifox 或者 ApiPost 这类工具类似 Postman可以一边调试一边生成在线文档导出 JSON 格式的接口文档也方便。 写接口文档最怕的是文档和代码不同步代码改了文档忘了更新。处理好这个问题最好是先写完接口文档再去实现代码写文档的过程其实就是理清输入输出的过程这样反而能减少返工。还有一个重要技巧在后端写一个全局异常处理器用RestControllerAdvice统一捕获业务异常和参数校验异常返回统一的 JSON 错误格式比如{code: 500, message: 库存不足}。这样前端不管遇到哪种错误都能在 axios 拦截器里统一弹提示接口文档里也可以只约定一种错误返回结构。否则每个接口自己抛异常前端就不得不针对不同情况写不同的处理逻辑这是毕设里常见又让人头疼的问题。4. Vue 前端实现与前后端联调4.1 前端项目初始化与环境配置前端部分是标准的 Vue 2 Element UI 工程通过 Vue CLI 4 创建。拿到项目后先别急着看代码先把依赖装好。在项目根目录执行npm install如果网络比较慢可以先配置镜像源再安装npm config set registry https://registry.npmmirror.com然后执行npm run serve启动开发服务器默认端口是 8080但后端一般也默认 8080所以前端开发环境要把端口改一下在项目根目录vue.config.js里配置module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }changeOrigin: true很重要后端获取请求来源 Host 时如果不对某些场景可能导致会话失效。配置代理的好处是前端代码里所有请求都写成/api/xxx开发环境请求会通过 devServer 转发到后端绕开了跨域问题生产环境再把前端打包出来的静态文件交给 Nginx用 Nginx 反代后端接口。4.2 前端路由与核心页面拆解前端技术栈配合 Vue Router 做路由管理。这里建议把所有页面路由写在src/router/index.js里并区分“不需要登录”和“需要登录”的路由。不需要登录的包括首页、商品列表页、商品详情页、登录注册页。需要登录的是购物车页面、订单确认页、我的订单页面、个人中心页面。后台管理相关页面也需要登录而且要求角色必须是管理员。Vue Router 提供了一个很好用的能力叫路由守卫在beforeEach钩子里做登录拦截router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } })这样就可以实现“用户没登录时强制跳转到登录页”的核心需求不用在每一个页面的created里写判断。页面层面重点是这几块。首页是一个聚合页由轮播图组件、新品首发组件、热卖单品组件组成全部数据来自聚合接口/api/home/info。商品列表页是重头戏要实现顶部分类筛选品牌、成色、价格区间和排序价格升序、价格降序、销量优先、最新上架。搜索关键词通过this.$route.query.keyword获取筛选条件通过this.$route.query中的参数传给后端每次点击筛选条件就调用一次后端接口。这里可以直接使用 Element UI 的el-select组件绑定筛选值变更事件里统一调用加载列表方法。商品详情页包含图片轮播el-carousel、核心参数表格、质检报告面板、购买数量选择器按钮有“加入购物车”和“立即下单”。4.3 前后端联调登录鉴权与数据对接联调时最重要的一步是处理登录鉴权。项目使用 JWTJSON Web Token方案用户登录成功后端返回一个 token 字符串。前端把 token 存到localStorage里之后每次请求都需要在请求头带上这个 token。这是通过 axios 拦截器来统一完成的service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config })注意项目里后端写的是把 token 放在Authorization头里有些项目是token前后端要保持一致。如果后端 JWT 校验不通过通常返回 401前端在响应拦截器里判断 401 后跳转到登录页并清掉本地 token。联调中日经常出现的问题包括后端返回的日期格式、金额单位不一致、字段名对不上。前端需要JSON.parse的图片数组在后端返回的却是字符串忘了转后端返回的价格是BigDecimal前端直接展示没问题但如果要计算小计就要用 Number 转成数字。调试的时候我建议先打开浏览器 F12在 Network 面板看请求和响应逐字段对照接口文档一般能很快定位问题。5. 从“能够跑通”到“可以答辩”常见问题与实战排查5.1 环境与工程启动类问题这个项目我最初在环境上浪费了不少时间典型能遇到的问题有四个。第一个是 JDK 版本不匹配。SpringBoot 2.6.13 环境比较适合 JDK 8 或 JDK 11如果你电脑上装的是 JDK 17启动时经常会报 UnsupportedClassVersionError 或者 Caused by java.lang.reflect.InaccessibleObjectException。解决办法是在 IDEA 里把 Project Structure 中的 SDK 和 Project language level 降到 8 或 11同时确认 Maven 用的 JRE 也是同一个版本。ideal 里 File - Project Structure - Project 改 SDK将 Build Tools - Maven - Runner 里的 JRE 也同步调整。第二个是 maven 依赖下载慢、首次启动报错。解决方式是配置阿里云 Maven 镜像在settings.xml的mirrors节点里加上阿里云的 mirror。下载完依赖后如果还报千奇百怪的错直接在 IDEA 终端执行mvn clean package -DskipTests重新编译打包能看到更清晰的报错日志。第三个是 MySQL 8.x 驱动问题。项目默认使用 com.mysql.cj.jdbc.Driver如果你的 MySQL 是 5.7 版本可能需要换驱动名并在 URL 中指定 serverTimezoneAsia/Shanghai。如果是 MySQL 8.0 以上则必须确认 pom 中 mysql-connector-java 的版本在 8.0.11 以上否则连接会直接失败。第四个是端口占用。后端 8080 端口被占用启动报 Port already in use。在application.yml中改端口即可也可以写死一个不常用的端口比如 8081。前端代理配置要和端口保持一致改一个忘了另一个就会出现“前端404”的诡异现象。5.2 数据库与数据问题数据库类的坑往往比环境问题更隐蔽。启动项目时提示“Unknown database”说明 SQL 脚本里建库语句没执行或者你连接的数据库名和application.yml中配置的jdbc:mysql://localhost:3306/huacheng不一致。这个不一致是最常见的问题我建议拿到项目后先打开 SQL 文件第一行看看它 CREATE DATABASE 的项目名再把 yml 中的 url 改成完全一致的。另一个比较隐蔽的是数据库表中明明有数据但接口返回空列表。这种情况十有八九是后端查询时加了逻辑删除条件。项目用了 MyBatis-Plus如果实体字段标了TableLogic查询时自动拼上deleted0条件。你如果直接往表里 INSERT 数据时没设置 deleted 字段默认值恰恰是 1那这条数据就被“隐藏”了。解决办法是 INSERT 时显式给 deleted 赋 0或者把表结构默认值改为 0。还有 MyBatis-Plus 的分页插件。如果只引入分页依赖但没配置 PaginationInnerInterceptor调用 selectPage 时会出现查出来的数据是全部而不是分页或者直接 SQL 报错。检查配置类里有没有加上Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }这个 Bean 是分页工作的核心它的重要性值得在答辩说明里专门提一句。5.3 接口与业务逻辑问题接口层面的高频问题主要集中在几个方向。一是接口能通但返回 500看后端日志基本上能找到NullPointerException或SQLSyntaxErrorException。前者大多数是前端传参缺失后台没做判空导致的后者大概率是表名或字段名写错。建议从接口文档入手核对请求参数确认每个字段名和数据库字段一致。二是登录接口返回成功但前端没有跳转。这种情况经常是前端在响应拦截器里处理返回值时取字段层次出了问题。我记得这套项目的返回体是{code, message, data}其中 data 里才是{token, userInfo}刚开始没注意嵌套层级的值直接从根上取 token取出来是 undefined后面按层级取就恢复正常了。三是跨域问题。前端启动在 3000 端口后端在 8080 端口从浏览器直接发请求会出现跨域解决方法我更倾向于在后端写一个 CORS 配置类用addCorsMappings允许所有源跨域访问Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET,POST,PUT,DELETE,OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }开发上用 devServer 代理也行但后端一旦开了 CORS前端不管用什么环境联调都比较自由。5.4 答辩前的功能性检查我特意预留了一节来提醒答辩准备。技术能跑通只是底线答辩要能说清楚自己的设计思路。建议按下表逐项自查维度你要能回答的问题数据库设计为什么订单明细要冗余商品快照后端架构JWT 相比 Session 的优势在哪里业务逻辑下单时库存怎么扣超时订单怎么处理前端实现路由守卫解决了什么问题项目部署前端打包后怎么部署Nginx反代怎么配从我个人的经验来看只要准备好处这几个问题答辩时让老师觉得“这个学生真把项目做透了”是完全没问题的。6. 本地部署与后续扩展建议6.1 从零跑通项目的完整步骤总结我按自己实操的顺序整理了一份跑通步骤按这个顺序操作可以省掉很多不必要的折腾安装 JDK 8/11、Maven 3.6、MySQL 5.7/8.0、Node.js 14。用 Navicat 创建数据库并导入 SQL 脚本确认字符集是 utf8mb4。修改application.yml中的数据库用户名和密码。启动后端看到 Tomcat started 字样后用浏览器访问http://localhost:8080/api/home/info如果能返回 JSON说明后端就绪。进入前端目录执行npm install装依赖然后改代理端口执行npm run serve。浏览器访问前端地址用管理员账号登录后台测试商品上下架再用普通用户登录前台测试搜索、加购、下单、支付全流程。如果需要部署前端执行npm run build把 dist 目录交给 Nginx后端执行mvn package打成 jar运行java -jar xxx.jar。6.2 这个项目还能怎么扩展如果时间充裕或者想冲一下优秀毕设可以在现有基础上做几个低成本但高展示度的扩展。第一个是增加实时验机视频播放。二手手机的交易信任问题永远是个话题可以给每台手机配备一段验机短视频M3U8 格式的流媒体前端用 Vue 结合播放器组件实现视频播放后端提供视频访问接口。这个能明显拉升用户体验和二手手机业务结合度非常自然。第二个是增加订单超时自动取消。可以引入定时任务框架比如 Spring Task 自带的Scheduled注解每分钟扫描一次待付款订单超过 15 分钟未支付就自动取消并回滚库存。这是一个完全独立于现有业务的加分功能实现成本大概就小半天可讲的内容却很充足。第三个是把销售数据做成前端可视化大屏用 ECharts 展示手机品牌销量占比、价格区间分布、近七日订单趋势。表格页面变成图表页面评委一眼就能看出你在数据展示上有想法。最后再分享一点个人感受。真正做完一套项目你会发现最难的不是敲代码而是把一个模糊的“我要做一个商城”的念头一步步拆解成数据库表、接口定义、页面组件、状态流转最后拼成一个完整系统。这套“华强北商城二手手机管理系统”最让我满意的地方是它的业务场景足够真实二手手机的每台单品属性、成色分级、动态定价逼着你去理解业务建模而不只是机械地写层调层。如果你也打算拿这个方向做毕设建议别只停留在把代码跑通把每张表为什么这么设计、每个状态为什么这么流转、每个接口为什么这么定义都过一遍这些才是你答辩时最硬气的底牌。