像很多计算机专业的同学一样,我第一次接触“家具商城系统”这个题目时,脑子里冒出的第一个念头是:这不就是一个普通的增删改查项目吗?SpringBoot + Vue,前端一张页面,后端几张表,搞定收工。但真正动手做到一半我才发现,家具这个品类和做服装、图书、数码类商城完全不一样。它有大件商品特有的属性——规格多、运输成本高、图片展示要求高、用户决策周期长,这些都会直接影响到数据库设计、接口拆分和前端页面交互。
这篇文章就围绕“Java毕业设计基于SpringBoot+Vue的家具商城系统设计与实现”来展开,把我从需求分析到部署上线的完整过程、踩过的坑、优化过的细节都整理出来。不管你是正在做类似选题的应届生,还是想接私活快速交付的开发者,都可以参考这套设计思路。
1. 整体需求拆解与核心功能规划
1.1 先想清楚:家具商城和普通商城到底差在哪
接到题目第一步不是写代码,而是想清楚业务逻辑。普通的服装商城核心是“sku(库存量单位)+ 尺码 + 颜色”,一个商品详情页能覆盖所有规格。但家具不一样,一套实木沙发可能有三人位、四人位、贵妃榻三种主规格,每种规格还有不同的面料颜色、是否带脚墩、是否包含茶几。
所以在设计数据模型的时候,我顺着逆向查询的思路来做:用户在前台看到的是一个“商品SPU(标准化产品单元)”,点击进去选择具体的规格后,才对应到精确的“商品SKU”。这种两级模型是家具商城系统设计中最关键的一点。如果一开始就把每个规格当成独立商品,后台管理会累死人,前台用户也会被大量重复的卡片刷屏。
1.2 功能模块划分:前后台两套逻辑并行
我这个系统总体分成前台展示和后台管理两条线。前台面向普通用户,核心是商品浏览、商品搜索、购物车、订单结算和个人中心;后台面向运营人员,核心是商品管理、分类管理、库存管理、订单处理和轮播图配置。
功能模块用表格梳理会更清楚:
| 区域 | 模块 | 核心功能点 |
|---|---|---|
| 前台 | 用户模块 | 注册、登录、个人信息维护、收货地址管理 |
| 前台 | 商品模块 | 分类展示、关键词搜索、商品详情、规格选择 |
| 前台 | 购物车模块 | 加入购物车、修改数量、删除、批量结算 |
| 前台 | 订单模块 | 创建订单、订单列表、订单详情、取消/确认收货 |
| 后台 | 管理员模块 | 管理员登录、权限校验 |
| 后台 | 商品管理 | 商品CRUD、规格管理、上下架、库存调整 |
| 后台 | 订单管理 | 订单列表、发货处理、状态流转 |
| 后台 | 内容管理 | 轮播图、公告管理、分类维护 |
有些同学会纠结要不要做支付功能。我的建议是:毕业设计可以接一个模拟支付按钮,或者直接对接一个沙箱环境,别真的把企业级支付证书、微信商户号这些流程写进系统里。一方面商户资质申请周期长,另一方面演示环境经常网络受限。我用的是模拟支付逻辑——用户点击“立即支付”后,系统直接把订单状态从“待支付”改成“待发货”,这个对我们验证核心流程完全足够。
1.3 角色权限设计的三层思路
权限这块我采用了最简单的三表模型:用户表、管理员表、角色表。实际开发中不需要引入Spring Security那套厚重的体系,因为绝大多数毕设后台只有一种角色——管理员。为了方便以后演示的时候让评审老师快速上手,我在后端写了一个拦截器,对 /admin/ 路径做登录状态校验,非登录状态统一返回 JSON 提示信息。
拦截器的实现思路很简单:用户登录成功后,后端生成一个 Token 返回给前端,前端存在 localStorage 里;每次请求时在 Header 里带上 Token;后端拦截器拿到 Token 解析出管理员 ID 后放行。这里有一个我踩过的小坑:如果用 SpringBoot 自带的拦截器注册方式,一定要记得把静态资源路径也排除掉,否则 Element UI 的字体文件和图片加载会被拦下来。
2. 技术选型背后的取舍与版本踩坑
2.1 SpringBoot 与 Vue 不是随便搭在一起的
选题既然固定了 SpringBoot + Vue,那整个项目就是经典的前后端分离结构。SpringBoot 负责提供 RESTful API,Vue 负责渲染页面和交互。这套组合最大的好处是开发边界清晰:前端同学可以 Mock 数据并行开发,后端同学只需要保证接口返回结构稳定。
我用的具体版本是:SpringBoot 2.7.x,MyBatis-Plus 3.5.x,MySQL 8.0,Redis 5.x(只用它做验证码缓存和 Token 过期管理,没有做复杂缓存),前端 Vue 2.7 + Element UI 2.15 + Axios + ECharts(用于后台数据统计)。
这里特别说一下为什么不用 Vue 3。Vue 3 的组合式 API 确实更现代,但 Element UI 对 Vue 2 的组件库最成熟,Element Plus 虽然支持 Vue 3,但是部分组件在表格编辑、弹窗表单这类高频场景下还是会遇到兼容性问题。对于毕业设计而言,稳定交付比技术新潮更重要。如果你时间比较充裕,以后想往这个方向继续深入,把 Vue 3 + TypeScript 作为加分项写在论文“未来展望”章节里会更好看。
2.2 版本不一致带来的连环坑
SpringBoot 2.7 和 MyBatis-Plus 3.5 组合时有个常见的坑:MyBatis-Plus 的分页插件在旧版是 PaginationInterceptor,3.5 版本改名为 MybatisPlusInterceptor。很多教程还是老写法,复制过来直接报“无效的拦截器”错误。
解决方式是在配置类里重新声明插件 Bean:
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }再强调一点,MySQL 8.0 的驱动类名是com.mysql.cj.jdbc.Driver,不是老的com.mysql.jdbc.Driver。如果你用数据库连接池连接失败,八成是驱动没更新。同时连接串里面要加上serverTimezone=Asia/Shanghai,否则会报时区错误。这些小坑单独看都不难,但叠在一起会消耗大量排查时间。
2.3 前后端分离项目的前置准备
开发前后端分离项目之前,先把环境统一好。后端用 IDEA 打开 Maven 工程,确保 Maven 仓库能够拉到 SpringBoot 2.7 的依赖;前端用 VS Code 或者 WebStorm,Node 版本建议 14 以上,npm 源如果下载太慢就切到国内镜像。
我习惯先写一个最简单的“版本健康检查接口”,例如/api/ping返回{code:200, msg:"ok"},前端能调通之后再去写业务模块。这样能第一时间区分问题出在前端、后端还是网络代理层。
3. 数据库设计:从E-R图到表结构的落地过程
3.1 核心数据表拆分思路
家具商城系统的表结构相比通用商城多了一层“规格组”的概念。我先列举我在项目中实际建的表,再解释每张表为什么这么设计:
furniture_category:商品分类表,包含分类ID、名称、父级ID、排序号。为了以后扩展无限级分类,我用了“父级ID关联自身”的写法。furniture_product:商品SPU表,一条记录代表一个家具商品,包括商品名称、主图URL、详情富文本内容、品牌、是否上架、创建时间等。furniture_sku:商品SKU表,一条记录代表一个具体规格,包括商品ID、规格描述、价格、库存、划线价、规格图URL。furniture_cart:购物车表,用户ID + SKU ID + 数量 + 选中状态。furniture_order:订单主表,订单号、用户ID、总金额、状态、收货信息快照。furniture_order_item:订单明细表,订单ID、SKU ID、商品名称快照、商品图快照、价格快照、数量。furniture_user:用户表,用户名、密码(BCrypt加密)、手机号、头像。furniture_admin:管理员表,账号、密码。furniture_address:收货地址表,用户ID、收货人、手机号、省市区、详细地址、是否默认。furniture_banner:轮播图表。
我当时写了一个小工具类,专门负责根据SKU的规格值数组生成规格描述字符串,比如“三人位 + 科技布 + 深灰色”。这个字符串不只是给用户看的,后端在处理订单明细时也会把它作为一种冗余存储,避免因为SKU后续修改导致历史订单信息错乱。
3.2 订单状态字段要设计成可扩展的
订单状态这个字段看起来简单,实际上最容易踩坑。我采用的是整数状态码:
0 待支付 1 待发货 2 已发货 3 已签收 4 已取消为什么不用字符串?因为状态流转在后端代码里更依赖数值比较,而且数据库查询用state < 3这类范围检索时,整数效率远高于字符串模糊匹配。
在写订单状态流转的Service方法时,我额外做了一个小的状态机校验。比如“待发货”状态只能从“待支付”流转过来,“已取消”只能从“待支付”或“待发货”流转。这样虽然多花了一点代码量,但演示的时候不会出现用户疯狂点按钮把订单状态点乱的情况。
3.3 价格字段设计:别用Double,用Decimal
家具单价动辄几千上万,金额的精度问题在毕设里经常被忽略。数据库里价格字段请用DECIMAL(10, 2),在 Java 实体类里面对应BigDecimal。用 Float 或者 Double 做价格计算,总金额会出现 0.001 之类的误差,一旦订单金额生成后与前端回显不一致,排查成本很高。
我这里写了一个统一的计算工具方法:
public BigDecimal calculateTotalAmount(List<OrderItem> items) { BigDecimal total = BigDecimal.ZERO; for (OrderItem item : items) { BigDecimal itemAmount = item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())); total = total.add(itemAmount); } return total.setScale(2, RoundingMode.HALF_UP); }这里有个设计细节:订单表里存“总金额”,订单明细表里也存每个商品的“价格快照”。下单那一刻会把商品当前价格复制到订单明细里,之后无论商品价格怎么改动,历史订单都不受影响。这个快照思路在实际的商业系统里是标准做法,写在论文里也是一个不错的亮点。
4. 后端接口设计与核心模块实现
4.1 统一返回格式:让前端少写一半判断逻辑
后端接口设计最重要的不是单个接口怎么实现,而是所有接口的返回结构要一致。我自己封装了一个Result<T>类,结构如下:
{ "code": 200, "message": "操作成功", "data": {} }凡是出现业务错误,比如库存不足、未登录、参数校验失败,返回的 code 不会是 200,前端 Axios 拦截器会根据 code 统一弹提示。这样前端不用每个请求都写一遍错误处理逻辑。
同时我在后端写了一个@RestControllerAdvice全局异常处理器,把所有异常都兜住,返回统一结构。特别要提醒的是:不要把系统内部的 SQL 异常直接抛给前端,用日志记录详细错误,反馈给用户的信息只要一句话就够了。
4.2 商品列表接口的查询优化
商品列表页是访问量最大的接口。如果用最简单的写法——“查分类下所有商品,再查出每个商品的所有SKU”,会出现严重的 N+1 查询问题:一次商品查询带来 N 次SKU查询,数据库压力大,响应时间跟着涨。
我的处理方案是分两步查询。第一步查出当前分类下的商品列表,第二步根据商品ID集合一次性查出所有关联的SKU,然后在内存中完成分组聚合。
// 第一步:查商品列表(带条件分页) Page<Product> page = productMapper.selectPage( new Page<>(pageNum, pageSize), new LambdaQueryWrapper<Product>() .eq(Product::getCategoryId, categoryId) .eq(Product::getStatus, 1) .orderByDesc(Product::getCreateTime) ); // 第二步:根据ID集合批量查SKU List<Long> productIds = page.getRecords().stream().map(Product::getId).toList(); List<Sku> skus = skuMapper.selectList( new LambdaQueryWrapper<Sku>().in(Sku::getProductId, productIds) ); // 内存分组 Map<Long, List<Sku>> skuMap = skus.stream().collect(Collectors.groupingBy(Sku::getProductId));商品列表展示SKU的时候,我只取每个商品库存最大、价格最低的一个SKU作为“起始价”展示,这样用户第一眼看到的就是“某某牌沙发 低至 2199 元”,比较符合电商展示习惯。
4.3 下单流程:事务和库存扣减的顺序要严谨
下单流程是系统里逻辑最密集、最容易出错的模块。很多人会在一张订单里同时做“扣库存”和“写订单”两步操作,然后随便加一个@Transactional就完事。但这里有一个细节:库存扣减一定要用乐观锁,否则并发环境下会超卖。
我设计的库存扣减 SQL 长这样:
UPDATE furniture_sku SET stock = stock - 1 WHERE id = #{skuId} AND stock >= 1通过stock >= 1这个条件,可以确保即使两个用户同时发起购买,也只有一个请求能更新成功。另一个请求受影响行数为0,此时再抛出“库存不足”的业务异常。
下单流程大体如下:
- 校验用户是否登录,未登录直接返回“请先登录”。
- 根据购物车选中的 SKU ID 列表查询商品信息,校验商品上下架状态。
- 执行库存预扣减,失败则返回对应商品库存不足。
- 创建订单主表和订单明细表,计算总金额。
- 清空购物车中对应的条目。
- 整个方法加
@Transactional(rollbackFor = Exception.class)。
有一个容易忽略的逻辑:订单表里要生成一个唯一订单号。我用了自定义编号策略:“时间戳 + 用户ID后四位 + 随机数”,这样既保证查询可读性,也降低并发重复概率。
4.4 轮播图和文件上传的处理
家具商城对图片的要求比较高,很多真实项目会使用OSS等云存储。但毕业设计环境一般不具备这个条件,所以我采用了本地静态资源映射方案:上传的图片保存到服务器某个磁盘目录下,同时在后端配置一个映射,让/upload/**路径直接映射到该目录。
file.upload-path=/data/furniture/upload/ spring.web.resources.static-locations=classpath:/static/,file:${file.upload-path}上传接口返回相对路径,前端拿到路径后拼上后端地址就可以访问。需要注意一个问题:如果将来你把这个项目放到线上或者演示环境,一定要把上传目录单独持久化,不要放在项目临时目录里面,否则项目重启后图片会丢。
4.5 登录鉴权与Token管理
登录逻辑我设计得尽量简单实用。管理员通过账号密码登录后,后端用UUID生成一个 Token,然后把它存进 Redis,过期时间为2小时;前端每次请求在 Header 里带Authorization。拦截器从 Redis 里查找 Token,存在就放行,不存在就返回“登录已过期”。
用户端登录逻辑和管理端类似,只是 Token 会存在另一个 Redis key 前缀下,避免和管理员混淆。这里我建议把密码字段的加密放到注册接口和登录校验中统一处理,用 BCrypt 加密存储。
注意:Token 模式在并发明文传输时有一定安全隐患,但这个复杂度对毕设已经足够。如果真的想提升安全等级,可以再加一层 RefreshToken 机制,但这块建议写到论文“系统不足与改进”部分,别在核心代码里无限膨胀。
5. 前端页面构建与核心交互实现
5.1 Vue项目搭建与路由划分
前端我用的 Vue 2 工程化方式,通过 Vue CLI 创建项目。路由划分遵循前后台分离的思路:
const routes = [ { path: '/', component: Home, meta: { title: '首页' } }, { path: '/category/:id', component: Category, meta: { title: '分类' } }, { path: '/product/:id', component: ProductDetail, meta: { title: '商品详情' } }, { path: '/cart', component: Cart, meta: { title: '购物车', requiresAuth: true } }, { path: '/login', component: Login, meta: { title: '登录' } }, { path: '/register', component: Register, meta: { title: '注册' } }, { path: '/order', component: OrderList, meta: { title: '我的订单', requiresAuth: true } }, // 后台相关 { path: '/admin/login', component: AdminLogin }, { path: '/admin/dashboard', component: Dashboard, meta: { requiresAdmin: true } }, { path: '/admin/product/list', component: ProductManage }, { path: '/admin/order/list', component: OrderManage } ]前端路由守卫要配合登录状态做跳转。如果访问requiresAuth: true的页面且本地没有 Token,直接跳转到登录页。后台同理,如果是管理员路由,需要额外校验当前 Token 对应的身份类型。
5.2 商品详情的规格选择交互
家具商品的规格选择是前端交互设计里比较头疼的地方。用户进入详情页之后,页面要展示商品主图文案、可选规格列表、当前选中的规格、对应的价格和库存。
我的实现方式是:页面初始化时,请求后端获取商品的全部SKU,前端把这些SKU按规格维度组织成一个笛卡尔积选择器。例如沙发有“尺寸”和“颜色”两个维度,每个维度各有若干选项,用户先点“三人位”,再看“深灰色”按钮是否可点,如果这个组合没有SKU,按钮置灰。
状态控制其实不复杂,关键在于弄清楚每个SKU的规格属性值如何存储。我在SKU表里加了一个specsJSON字段,比如[{"name":"尺寸","value":"三人位"},{"name":"颜色","value":"深灰色"}],前端拿到后解析成选择器需要的结构。
这个方案有一个额外好处:后端不需要为每种家具定制单独的规格列表,数据库结构保持通用。
5.3 购物车和订单结算的状态联动
购物车页面的交互核心就是“选中状态”与总价的联动。前端用一个数组来记录每个购物车项的选中状态,每次勾选或者修改数量时,重新计算合计金额。
结算按钮点击后,把选中的购物车 ID 列表传给后端,后端一次性生成订单。这里前端一定要做一次“空选拦截”,不要等请求发出去了再提示用户。
还有一个小细节:购物车里的商品如果已经下架或者库存变为0,前端列表要呈现出不可结算的状态。我在查询购物车列表接口里,会把商品的当前状态一起返回,前端根据状态禁用对应行的结算复选框。
5.4 后台管理页面的表格和弹窗组合
后台管理页面我是用el-table+el-dialog的组合来实现的。产品列表页用表格展示所有商品,操作列提供“编辑”“上架/下架”“删除”按钮;新增和编辑共用一个弹窗表单,表单里除了基本信息之外,还支持动态维护SKU规格列表。
这里有一个实操技巧:动态SKU表单在 Vue 里用数组渲染,每行包含规格值、价格、库存、规格图。新增一行就往数组里 push 一个空对象,删除就 splice 掉对应行。提交的时候把整个数组作为 JSON 传给后端,后端再把数组转成 List 批量插入。
动态行表单的校验稍微有点麻烦,我的方案是:不依赖 Element UI 的复杂规则校验,而是在弹窗点击“确定”时,先遍历数组做基础空值检查,发现空值就把对应的输入框边框标红,并提示用户。这样实现成本低,交互也足够友好。
5.5 ECharts数据统计图表的接入
后台首页我放了一个简单的销售概览页面,用 ECharts 绘制了“近七日订单量折线图”和“分类销售额占比饼图”。这个模块虽然业务复杂度不高,但视觉效果好,答辩演示的时候非常加分。
后端接口需要提供聚合数据。我的实现方式是写了一条 SQL 查询近七天每天的订单数:
SELECT DATE(create_time) AS date, COUNT(*) AS count FROM furniture_order WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 6 DAY) GROUP BY DATE(create_time)前端拿到数据后直接 setOption 即可。需要注意的是,如果某天没有订单,SQL 结果里就没有那一天的数据,前端绘图前要做一个“补零”处理,把缺失的日期填成0,否则折线图会少点。
6. 联调过程中最常见的几个问题与排查手段
6.1 跨域问题:前端调不通接口的第一大坑
前后端分离项目调试时,跨域问题是必然遇到的。我推荐的开发期解决方案是在 Vue 的vue.config.js中配置代理:
module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }这样前端所有/api开头的请求都会被转发到后端8080端口,浏览器不会产生跨域报错。生产环境部署时,用 Nginx 配置同一个代理规则,前后端就通过 Nginx 统一对外服务。
我见过很多同学直接把后端的@CrossOrigin注解加到所有控制器上,这不推荐。原因有两点:第一是太散,每加一个控制器都要手动维护;第二是@CrossOrigin配置相对固定,换成生产环境以后容易漏改。
6.2 Axios拦截器统一处理登录过期
前端每个请求都要判断返回状态是否正常,不能每个页面写一遍。我在 Axios 封装里加了一个响应拦截器:
service.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { if (res.code === 401) { router.push('/login') } Message.error(res.message || '请求失败') return Promise.reject(new Error(res.message)) } return res }, error => { Message.error('网络异常,请稍后重试') return Promise.reject(error) } )这样统一处理后,所有页面只需要关心正常数据,不用重复写错误提示。
6.3 后端接口调试的几个高效技巧
我在开发过程中采用了一个非常实用的日志方案:在 Service 层每个关键方法入口打印入参,在方法出口打印耗时。SpringBoot 自带的 logback 日志配置一下即可,不需要引入额外的链路追踪组件。
排查问题时,优先看日志里有没有feign之外的SQL日志。可以在配置文件里开启MyBatis-Plus的SQL日志打印,这样每条 SQL 和参数都能在控制台看到。开启方式:
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl这个日志调试手段在定位“接口返回慢、数据不对、更新影响行数为0”这类问题时非常高效。
6.4 商品图片加载404的排查思路
家具商城如果图片加载不出来,演示效果会大打折扣。这类问题常见的排查路径是这样的:
- 先用浏览器直接访问图片完整URL,确认后端是否能返回图片。
- 确认后端静态资源映射是否生效,看配置文件的路径是否和实际保存路径一致。
- 确认数据库存的是“相对路径”还是“绝对路径”,如果存的是绝对路径,那么前端配置的后端地址变了就会失效。
我自己把图片路径统一存成相对路径,比如/upload/20250315/xxx.jpg,前端组装时统一加上VUE_APP_BASE_URL前缀,这样环境变更时只需要改一个环境变量。
7. 项目打包部署与答辩准备的细节提醒
7.1 后端打包:本地运行没问题不代表服务器也能跑
后端打包时,我先把本地application.yml中的数据库地址、Redis地址都改成了服务器环境对应的配置。然后在 IDEA 中执行mvn clean package -DskipTests,在 target 目录中得到一个 jar 包。执行时用下面的命令启动:
java -jar furniture-admin-1.0.0.jar --spring.profiles.active=prod如果你在服务器上用的是 MySQL 8.x,确认一下数据库里已经执行过项目提供的init.sql脚本。脚本里除了建表语句,还应该包含至少一个初始管理员账号和几条测试商品数据,否则评审老师打开后台,看到空荡荡的界面会很难留下好印象。
7.2 前端打包:路由模式要和Nginx匹配
前端执行npm run build之后,dist 目录会生成静态文件。我把这些文件放到 Nginx 的 html 目录下,然后配置反向代理:
server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }如果你的前端路由用history模式,还要额外配置一个try_files $uri $uri/ /index.html;,否则刷新二级页面时会报404。这里要注意:前端路由刷新报404、接口请求502、图片请求404,这三个问题的排查方向完全不一样,动手之前先分清。
7.3 答辩前需要准备的演示数据
答辩演示最难堪的场景是:打开网站首页,结果商品图片裂了、购物车里空荡荡、后台订单列表也没有数据。我的建议是提前准备一组“看起来真实”的数据:
- 5个父分类,10个左右商品,每个商品配3~6条SKU。
- 提前在后台创建一个测试订单并完成发货流程,这样评审老师点进订单管理,能看到明确的状态流转记录。
- 首页轮播图放两到三张大尺寸、清晰度高的家具图片,视觉上专业很多。
数据质量比数量更重要。家具商品图片尽量从图库站点寻找,不要用带明显水印的截图。我当时自己利用一个下午的时间把图片统一处理成了1:1比例、压缩到200KB以内,页面加载速度明显提升。
8. 写在最后的几条经验
整个项目从零开始到全部完成,我大概用了三周时间。第一周做需求分析和数据库设计,第二周完成后端主要接口和前台页面,第三周做后台管理、联调优化和部署。如果你之前没有完整做过前后端分离项目,时间规划上建议再往前多留一周。
中间最耗费时间的地方不是写代码,而是调整各种“差一点就能跑通”的部分:跨域配置、Token失效跳转、图片上传后回显、商品规格参数传递格式。后来我总结出一个经验:开发前后端分离项目时,一定要把接口数据结构先定死。先把每个接口的请求参数和返回结果定义好,再分别开工,前后端配合会顺畅很多。
这个家具商城系统的代码量不算大,但麻雀虽小五脏俱全。你要能把“SPU/SKU拆解”“订单状态机”“库存乐观锁”“统一返回结构”这几个词讲清楚,答辩的时候基本就稳了。哪怕以后工作中遇到更复杂的电商系统,这套设计思路依然能复用。希望这篇内容对你做毕业设计项目有点启发,少走几步弯路。