每年毕设季,类似“XX商城”“XX销售平台”的选题能占掉很大比例,家电销售展示平台就是其中一个很典型的版本。表面上看,这类项目无非是把商品图片摆到首页,做个列表、详情、购物车,再配一个后台管理;实际上到答辩的时候,很多同学交上来的源码要么只有前端静态页面,要么数据库表结构经不起一句“为什么这样设计”的追问,要么论文和代码根本对不上。这篇文章围绕SpringBoot+Vue+MySQL三个技术栈,把家电销售展示平台从需求边界、表结构、后端接口、前端联调,一直聊到论文和部署文档的配合写法。目标是让准备拿这个选题做毕设的同学,不光下载一套能跑的源码,还能讲清楚每个模块为什么存在、每个关键字段为什么这样定。
1. 家电销售平台的功能边界:先想清楚什么该做什么该砍
1.1 前台用户能看到什么、后台管理员能管什么
做毕设最容易犯的毛病是一上来就写代码,结果做着做着发现范围越来越大,最后哪个模块都没做透。家电销售展示平台首先要想清楚“展示”和“销售”两个词的分量。展示指向用户端的信息呈现,销售指向购物车、订单这一套交易闭环。两者都要有,但不需要无限扩展。
以我比较认可的一套功能边界为例,前台用户端需要这些模块:
- 注册、登录、个人信息维护
- 首页轮播图、推荐家电商品位
- 商品列表页,支持按品牌、分类、价格区间筛选,也能按销量和价格排序
- 商品详情页,展示多张商品图、核心参数、图文详情
- 购物车增删改查、批量结算
- 提交订单、模拟支付、查看订单列表和订单状态
- 收货地址维护
后台管理端需要这些模块:
- 商品管理,包括上架、下架、库存调整、编辑详情
- 品牌管理和分类管理
- 轮播图管理,控制首页展示位
- 订单管理,查看订单明细、更新发货状态
- 管理员账号登录,和普通用户共用一张用户表,靠角色字段区分
这个功能集能覆盖一次完整的电商闭环,页面数量大概十几个,单人来写完全做得到。难点不在功能多,而在每一条数据链路能完整跑通,比如从商品详情加购、购物车勾选、提交订单、扣库存,到最后订单状态变化,每一步都得有对应的表和接口。
1.2 支付、物流、优惠券这些模块为什么不建议硬加
很多同学觉得电商平台没有真实支付就没有亮点,于是去研究某第三方支付接口。我的建议很直接:毕设阶段不要碰真实支付。原因是支付网关需要商户资质、回调地址、应用审核,本地开发也要考虑证书和调试成本,这些时间花在核心交易逻辑上更值得。
更稳妥的做法是做一个“模拟支付”:提交订单后显示订单金额和一个“确认支付”按钮,点击后把订单状态从“待支付”改成“已支付”。演示的时候不需要任何第三方依赖,也不会因为网络环境导致现场翻车,论文里把这一步描述成“为接入第三方支付预留了扩展接口”就完全合理。
物流模块同样做个状态流转就行,新增一个发货回调入口,让管理员手动更新订单状态为“已发货”。优惠券、秒杀、积分这些属于营销域,和核心交易逻辑耦合度不高,加了反而要在订单金额计算里处理各种边界条件。一句话:毕设评审判的是系统的完整性和逻辑正确性,不是业务的丰富度。
1.3 家电商品和卖服装图书的系统,差异到底在哪
家电销售平台让人感觉“就是商城换皮肤”,但如果把家电的品类特征忽略掉,答辩的时候很容易被一句话问住:你这个平台和通用商城有什么区别?
家电商品有几个明显特征要在表结构和页面里体现出来。第一是品类复杂度高,电视、冰箱、洗衣机、空调属于不同分类,每个分类的参数体系不一样,比如电视看屏幕尺寸和分辨率,空调看匹数和能效等级;第二是价格不低但毛利逻辑简单,下单时不需要像服装一样处理多规格多尺码,最多同一个商品有颜色或容量上的秒选变体;第三是交付信息重要,是否有货、是否包安装、保修期多长,这些直接影响购买决策。
所以商品表需要预留能效等级、功率、尺寸、保修期、是否包安装这类家电特征字段。前端详情页要用参数表格的形式把信息呈现出来,而不是只堆一段图文详情。能把这一点讲清楚,从需求分析到数据库设计都有了自己的东西,不会让整个项目沦为一个泛泛的商城。
2. SpringBoot+Vue+MySQL这套组合,为什么能经得起答辩追问
2.1 前后端分离在毕设场景下的收益
技术选型最忌讳的是“因为大家都用所以我也用”,答辩老师一旦追问选型理由就会露馅。前后端分离的好处体现在三个层面。
第一,开发职责清晰。前端ByVue负责页面渲染和用户交互,后端SpringBoot只出JSON接口,两边可以并行推进。毕设是一个人做,但按这种职责切分,代码写出来也是清晰的。第二,部署逻辑简单。前端打包成静态文件,可以放进Nginx,也可以由SpringBoot统一托管;后端是一个独立进程,只需要确保前端静态资源和接口地址能通就行。第三,调试方便。用浏览器开发者工具打开Network面板,能直接看到接口返回的请求和响应,定位问题比单体模板渲染时代要快得多。
用一句话回答“为什么选择前后端分离”:它让系统在开发期并行、在运行期解耦、在排查期可视化,三个维度的成本都更低。
2.2 SpringBoot和MyBatis-Plus的组合为什么省事
后端框架选SpringBoot,一是它把Spring的配置自动化了,内嵌Tomcat,不需要额外部署外置容器,写一个启动类就能运行;二是生态资料极其丰富,遇到问题搜一下基本都有现成答案。
ORM层我建议配合MyBatis-Plus而不是纯MyBatis。MyBatis-Plus对单表CRUD做了封装,简单查询不需要手写SQL,内置的分页插件也能直接解决列表页的分页问题。毕设项目里绝大多数操作都是单表加条件查询,比如按照分类查商品列表、按照用户查订单列表,用LambdaQueryWrapper就能很优雅地写出来。当然,涉及多表关联的统计查询还是要手写SQL,比如订单明细要join商品表拿商品名称和图片,这部分MyBatis-Plus也不会越权替你包办。
2.3 MySQL为什么够用,以及要不要Redis
MySQL是这个场景下的关系型数据存储核心,商品、订单、用户天然是二维表结构,订单明细和商品之间有关联关系,用MySQL来表达最直接。这种项目数据量级就是几千条商品、几百个用户,完全没必要引入Redis。如果想让系统有一点“亮点”,可以在轮播图接口或首页推荐位做一层简单缓存,但Redis会引入缓存与数据库的一致性问题,增加了装饰性的复杂度,不值得。
论文里有关“技术选型”的章节,建议按这个逻辑来写:需求是结构化的业务数据存储和多表关联查询,所以选择关系型数据库;后端要快速构建RESTful API且降低配置成本,所以选SpringBoot;前端需要组件化开发和良好的交互体验,所以选Vue。三步推导下来,选型逻辑完整闭环,答辩就很难被挑毛病。
2.4 版本匹配的坑,配置一个不对就启动失败
版本问题看起来小事一桩,但卡住的时候非常让人暴躁。常见组合是这样的:
| 组件 | 推荐版本 | 备注 |
|---|---|---|
| JDK | 1.8 | 与SpringBoot 2.x配套最稳 |
| SpringBoot | 2.7.x | 稳定且资料最多 |
| MySQL | 8.0 | 注意驱动和时区配置 |
| Node.js | 16.x或18.x | Vue相关工具链依赖 |
| Vue | Vue 3.x | 配合Vite或Vue CLI均可 |
| Element Plus | 2.x | Vue3对应的UI组件库 |
用JDK17跑SpringBoot 3.x也是可行的,但SpringBoot 3.x开始基于Jakarta命名空间,很多老教程里的代码不能直接复用,学生项目没必要在版本前沿冒险。MySQL 8.0要留意数据库连接串里的时区参数,以及驱动要用com.mysql.cj.jdbc.Driver。这些细节应该直接写进部署文档,而不是等别人踩坑之后再补。
3. 数据库设计才是重头戏:家电商品怎么建模、订单怎么流转
3.1 核心表一览,一张表格看清全貌
数据库设计是整个项目的地基,答辩中最容易被追问的就是外键关系和状态字段。我建议的核心表有八张,每张表干一件事:
| 表名 | 作用 | 关键字段 |
|---|---|---|
| user | 用户和管理员统一身份 | id, username, password, role, nick_name, phone |
| brand | 家电品牌 | id, name, logo |
| category | 商品分类 | id, name, parent_id, sort |
| product | 商品主表 | id, brand_id, category_id, name, price, stock, status |
| product_image | 商品图片 | id, product_id, url, sort |
| cart | 购物车 | id, user_id, product_id, quantity |
| orders | 订单主表 | id, order_no, user_id, total_amount, status, address, receiver |
| order_item | 订单明细快照 | id, order_id, product_id, product_name, product_image, price, quantity |
product_image单独拆一张表,是因为一个家电详情页往往需要多张轮播图,拆表之后字段不会冗余,排序也灵活。order_item里的product_name、product_image、price这三个字段是典型的“冗余快照”设计,不是简单的冗余,而是为了应对商品信息后续变化。这个细节写到论文里,是加分项。
3.2 商品主表字段怎么定,家电特征往哪里放
product表在通用商城模型的基础上,要补一组家电专属字段。我的推荐设计是:
CREATE TABLE `product` ( `id` bigint NOT NULL AUTO_INCREMENT, `brand_id` bigint DEFAULT NULL COMMENT '所属品牌', `category_id` bigint DEFAULT NULL COMMENT '所属分类', `name` varchar(200) NOT NULL COMMENT '商品名称', `subtitle` varchar(500) DEFAULT NULL COMMENT '商品副标题/卖点', `price` decimal(10,2) NOT NULL COMMENT '售价', `original_price` decimal(10,2) DEFAULT NULL COMMENT '原价,用于展示优惠', `stock` int NOT NULL DEFAULT 0 COMMENT '库存', `sales` int NOT NULL DEFAULT 0 COMMENT '累计销量', `energy_efficiency` varchar(20) DEFAULT NULL COMMENT '能效等级,如一级/二级', `power` varchar(50) DEFAULT NULL COMMENT '功率,如2200W', `specification` varchar(200) DEFAULT NULL COMMENT '主要参数,尺寸、容量等', `warranty_month` int DEFAULT NULL COMMENT '保修期,单位月', `install_service` tinyint DEFAULT 0 COMMENT '是否包安装,0否1是', `detail_html` text COMMENT '图文详情,存富文本内容', `status` tinyint NOT NULL DEFAULT 1 COMMENT '1上架 0下架', `created_at` datetime DEFAULT NULL, `updated_at` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_category` (`category_id`), KEY `idx_brand` (`brand_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家电商品表';价格用decimal(10,2)而不是float,这是财务类字段的基本要求,浮点数存在精度误差,一旦涉及金额就会被反复诟病。能效等级、功率、保修月数这些字段不用单独建表,因为它们本质上是商品静态属性,直接作为字段写进product表里最简单直观。上架状态用tinyint,1表示上架,0表示下架,查询列表时永远要带status=1条件。
3.3 购物车、订单、订单明细三张表的关联逻辑
购物车表的核心是(user_id, product_id)的唯一约束。同一用户把同一件商品放入购物车应该是数量累加,而不是插入多行,唯一约束能在数据库层面做兜底。
订单主表orders需要三个要点。
第一,order_no订单编号用时间戳加随机数生成,避免直接暴露订单数量给用户,也方便演示时口头描述。
第二,total_amount这个总金额必须从后端计算,不能信任前端传过来的价格。前端传的是商品快照和数量,后端重新查商品表计算金额,这个流程是交易系统的正确性底线。
第三,status字段设计成tinyint,0待支付、1已支付、2已发货、3已完成、4已取消。每次状态变化只允许按合法方向流转,比如从1到2,从2到3,0可以到4,但2不能回到1。这个状态机的说明直接画在论文里就很漂亮。
order_item是订单商品的快照表,字段里的product_name和product_image,是为了让订单列表接口不需要再join商品表就能展示商品信息;price字段保存的是下单那一刻的成交单价。订单生成之后商品改了价,用户的订单金额不会受影响,这就是快照的意义。
3.4 外键到底建不建,索引怎么加
很多教程会强调加外键约束,但在MyBatis-Plus这种ORM主导的项目里,我建议使用逻辑外键而不是物理外键。物理外键会让删除父表记录时背负额外检查,容易触发意外报错,也不利于分页和批量操作的性能。逻辑外键仍然是product表里存brand_id、category_id,但不加FOREIGN KEY约束,靠代码层面保证关联完整性。
索引方面,product表的category_id、brand_id要建索引,因为列表筛选常按这两个条件查;orders表的user_id要加索引,因为“我的订单”是高频查询。索引不是越多越好,每一张额外索引都会拖慢写入,适度最要紧。
4. 后端接口从查询到下单的完整实现顺序
4.1 工程结构和分层,不把代码全塞进Controller
后端开发建议按照标准的四层结构组织代码:Controller负责接收请求和参数校验,Service负责业务逻辑,Mapper负责数据库操作,Entity对应表结构。阿里规范里讲的Controller尽量薄,Service要厚,放到这个项目里就体现为一句话:商品列表接口的判断条件、下单时的库存检查、订单状态变更规则,全都应该写在Service里,Controller只做参数接收和结果封装。
工程目录大概长这样:
src/main/java/com/example/mall ├── controller │ ├── ProductController.java │ ├── UserController.java │ ├── CartController.java │ └── OrderController.java ├── service │ ├── impl ├── mapper ├── entity └── common ├── Result.java ├── PageResult.java └── GlobalExceptionHandler.javacommon包里的Result是统一返回结构,这是所有接口的出口。
4.2 统一返回结果,前端才不用到处判null
前后端联调能不能顺畅,很大程度取决于接口返回结构是否统一。我用的固定格式是:
public class Result<T> { private Integer code; private String msg; private T data; }code为200表示成功,500表示业务或系统错误,401表示未登录或权限不足。所有接口不管成功失败,返回的都是这个结构。前端axios拦截器拿到响应后先看code,不是200就统一弹出msg,不需要每个页面单独处理异常分支。
再配一个全局异常处理器,把Service层抛出的业务异常统一转成Result返回,这样代码里遇到库存不足就throw new BusinessException("库存不足"),前端自动就能拿到提示,不需要在Controller里一层层包try-catch。
4.3 商品分页查询:条件筛选怎么写才高效
商品列表页是整个系统被访问最多的接口,需要考虑分页和筛选的灵活组合。入参建议是page、pageSize、categoryId、brandId、keyword、minPrice、maxPrice、sortRule。用MyBatis-Plus实现时,核心是构建一个LambdaQueryWrapper,动态拼接条件:
LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(product -> product != null && product.getStatus() == 1, Product::getStatus, 1); wrapper.eq(categoryId != null, Product::getCategoryId, categoryId); wrapper.eq(brandId != null, Product::getBrandId, brandId); wrapper.like(StringUtils.hasText(keyword), Product::getName, keyword); wrapper.between(minPrice != null && maxPrice != null, Product::getPrice, minPrice, maxPrice); wrapper.orderByDesc("sales".equals(sortRule), Product::getSales); wrapper.orderByDesc("price".equals(sortRule), Product::getPrice);条件构造器里的Boolean条件参数,为null就不拼接,这一手法能避免大量if-else堆叠。配合MyBatis-Plus内置的分页插件,返回一个只含当前页数据的Page对象,字段再映射为VO返回给前端。注意,接口返回给前端的数据不要直接拿Entity暴露,商品表里有detail_html这种大字段,列表页用不到就不该传,这个细节能体现你有没有真正开发过项目。
4.4 下单扣库存:怎么保证不会超卖
下单是交易系统最核心的链路。整个流程放在同一个事务里,串起来是这样的:前端传来[商品id+数量]的列表,后端先查购物车或直接按提交的商品项处理,然后逐个查询商品当前库存,检查库存是否充足;充足则生成订单主记录,批量插入订单明细,更新商品表的库存字段和销量字段。任何一步失败就整体回滚。
库存扣减最容易出的问题是超卖。两个用户同时下单同一件商品,都读到库存剩1件,如果先校验再扣减,就可能两个人都扣成功。解决办法是不要把“查询校验”和“更新扣减”分成两步,而是让扣减动作本身就带上条件:
UPDATE product SET stock = stock - #{num}, sales = sales + #{num} WHERE id = #{productId} AND stock >= #{num}这条SQL执行后影响行数为1才算扣减成功,为0说明库存不足。这就是“原子扣减”,它保证并发下不会超卖。在答辩时把这个方案讲明白,基本能压住一场关于数据一致性的追问。
订单状态流转的Service方法可以设成仅内部调用,比如payOrder()、deliverOrder()、completeOrder(),每个方法先查当前状态是否合法,再做更新。不要把状态更新逻辑暴露成任意修改接口,否则前端调错状态,数据会乱。
4.5 图片上传和静态资源映射
商品图片、轮播图都需要上传功能。最省事的方案是上传到本地磁盘一个约定目录,再通过SpringBoot的静态资源映射暴露给前端。配置文件里加这样一段属性:
spring: web: resources: static-locations: file:D:/upload/上传接口返回图片URL相对路径,比如/upload/20250501/xxx.jpg,前端拿这个URL直接拼上后端地址就能显示图片。生产部署时如果用了Nginx,就用Nginx的alias指令指向这个磁盘目录,两种方式都行。要注意的是,本地路径写死会造成换机器图片全部丢失,所以部署文档里要把“修改上传路径”作为一个显式步骤列出来。
5. Vue页面怎么落地:组件拆分、路由权限与联调避坑
5.1 前端页面清单和组件划分
前端工程分为用户端和管理后台两大部分。用户端页面有首页、商品列表页、商品详情页、购物车页、订单确认页、订单列表页、登录注册页;管理后台有后台首页、商品管理页、分类管理页、品牌管理页、轮播图管理页、订单管理页。上规模的页面最好拆成多个组件,比如商品详情页拆成SwiperGallery、ParamsTable、DetailHtml三个组件,每个组件只负责一件事。
组件拆分的核心判断标准是“是否会被复用或是否值得独立维护”。多个页面都要用的商品卡片,比如首页推荐位和商品列表页都用,就抽成ProductCard组件。只有详情页才用的参数表格,也可以单独抽出来,因为里面包含字段映射逻辑,独立后代码量不会膨胀。
5.2 路由配置和登录守卫
前后端分离下,前端路由要做权限控制。管理后台的所有路由配置在一个children数组里,并为父路由加上meta信息:
{ path: '/admin', component: Layout, meta: { requiresAuth: true, role: 'admin' }, children: [ { path: 'product', component: ProductManage }, { path: 'order', component: OrderManage } ] }在全局前置守卫里做校验:
router.beforeEach((to, from, next) => { const token = localStorage.getItem('token'); if (to.meta.requiresAuth && !token) { next('/login'); } else { next(); } });登录成功后把token存到localStorage,后端接口通过Authorization请求头携带这个token。管理端路由再额外判断当前用户role是否等于admin,不让普通用户直接通过改URL进入后台页面。
5.3 Axios封装:拦截器统一处理Token和错误
Axios拦截器是前端项目里必备的公共逻辑。请求拦截器负责从localStorage取出token,塞到请求头里;响应拦截器负责统一处理code非200的错误提示,并针对401跳转登录页。
service.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers['Authorization'] = token; } return config; }); service.interceptors.response.use(res => { const data = res.data; if (data.code !== 200) { ElMessage.error(data.msg || '请求失败'); return Promise.reject(data); } return data; });这样每个页面调用接口时,只需要关心正常流程,不需要到处写错误处理。ElMessage错误提示统一由拦截器弹,页面代码能清爽很多。
5.4 跨域问题的三种解法
开发环境下,前端跑在8080,后端跑在8081,请求必然跨域。最常见也最推荐的方式是配前端开发服务器的代理,以Vite为例,在vite.config.js里这样写:
server: { proxy: { '/api': { target: 'http://localhost:8081', changeOrigin: true } } }前端所有请求都写相对路径/api/xxx,开发时由代理转发到后端,生产时由Nginx做同样的事。这样实际暴露给浏览器的永远是同源请求,不需要在前端代码里写死IP。
不推荐在Controller上直接加@CrossOrigin,简单省事但生产环境如果有多个域名调用就有隐患;也不推荐前端把后端完整地址写死在axios的baseURL里,这样换环境就要改代码重新打包。代理方案是越早定下来越省事。
5.5 管理后台功能开发顺序,怎么安排最合理
前后端联调容易“联死”,主要原因是两边对字段理解不一致。我的建议是从管理后台先入手,把商品管理做成“增删改查+图片上传”,因为这里会覆盖几乎所有字段的读写。管理后台跑通之后,前端已经明确知道了字段名和数据结构,再去做用户端的列表页和详情页就顺理成章。
用户端的开发顺序是:登录注册,然后首页,然后商品列表,然后商品详情,再然后购物车,最后订单闭环。把购物车和订单留在最后还有一个好处:前面几个页面积累了复用组件,购物车页和订单确认页写起来会快很多。
5.6 联调时最容易翻车的几个点
第一个是后端返回的时间字段,默认是带T的ISO格式,前端要是不做处理就直接显示,页面会很难看。统一在后台把日期格式化成yyyy-MM-dd HH:mm:ss再返回,前端就不必关心格式问题。
第二个是下拉框和复选框的值类型。前端经常出现字符串“1”和后端数字1不完全相等的情况,尤其是Element Plus的select组件,value设为Number类型后问题会明显减少。
第三个是图片地址拼接。图片来源可能是本地相对路径也可能是外部链接,前端最好封装一个工具函数,专门处理“相对路径自动拼上后端地址”的逻辑,不要在每个页面各拼各的。
6. 成套交付时最容易拉开差距的三样东西:数据库脚本、部署文档、论文提纲
6.1 数据库脚本不只是建表语句,还要有初始化数据
用户下载源码之后,第一步通常是导入数据库。如果只有建表语句没有初始化数据,前端页面打开都是空的,体验非常差。一个合格的db.sql脚本应该包含三部分:建库建表语句、初始分类数据和品牌数据、演示用商品数据和轮播图数据。
家电分类至少要覆盖电视、冰箱、洗衣机、空调、厨卫电器这几类,每类下放两三件商品,商品的图片URL要么是本地upload目录里的文件,要么是稳定的公开占位图。数据量不用大,但品类要全,让筛选功能在演示时有实际意义。初始化一个管理员账号和一个普通用户账号,并在脚本注释里写明密码是经过MD5加密还是BCrypt加密,避免部署的人登录不进去。
6.2 部署文档按什么顺序写,读者才能一次跑通
部署文档的使命是不折腾就能跑起来。常见的失败方式是文档里跳过了环境安装细节,导致读者在第一步就卡住。可靠的部署文档应该按这个顺序组织:环境版本表,说明JDK、MySQL、Node分别装哪个版本,以及为什么这样指定;数据库初始化步骤,包含启动MySQL、执行db.sql、验证表是否创建成功;后端启动步骤,改application.yml里的数据库用户名密码,用Maven启动SpringBoot项目;前端启动步骤,npm install之后如果出现版本冲突怎么处理,npm run serve之后访问哪个地址;生产构建说明,讲mvn package和npm run build的产物怎么配合Nginx部署。
写完之后,找一台干净环境的电脑,从头到尾按文档走一遍,任何缺失的步骤都会在这次测试中暴露。这个动作花不了半天时间,却能避开答辩现场的名场面——评委点开项目,页面白屏。
6.3 论文各章节和代码模块怎么一一对应
论文不是把代码贴一遍就完事,而是要体现“需求→设计→实现→测试”的完整链路。我建议对应关系这样建立:
- 绪论章节写给家电行业线上销售背景和本系统要解决的问题,这里不需要写代码。
- 相关技术章节重点写SpringBoot、Vue、MySQL的选型理由,正好呼应本文第二部分的技术逻辑。
- 需求分析章节画用例图,描述用户和管理员的两类角色,列出功能需求表和非功能需求表。
- 系统设计章节依次放系统架构图、功能模块图、数据库E-R图和核心表结构说明。E-R图直接根据第三章的八张表来画,多对多关系用关联表表达。
- 系统实现章节按用户端和管理后台来分小节,每节贴一个核心页面截图,配一段关键接口的实现说明,比如下单事务、库存原子扣减。
- 测试章节设计了功能测试用例表,覆盖用户注册、商品筛选、购物车、下单、状态流转等核心路径,再补一段压力或兼容性方面的简要说明。
论文图表不要从网上直接复制,自己用绘图工具画一遍,哪怕画得朴素一些,答辩老师更看重一致性。系统截图要保持环境干净清爽,别带着网页乱码和一堆调试信息。
6.3.1 答辩提问概率极高的三个问题怎么答
第一个问题:为什么用这套技术栈。回答就按第二章的逻辑来,先说需求特征对应关系型数据库,再说SpringBoot降低开发成本,最后说Vue组件化提升前端开发效率,三句话闭环。
第二个问题:订单和商品库存怎么保证一致性。回答从数据库事务和原子更新SQL入手,说明在同一个事务里扣减库存、创建订单,并且扣减语句本身带stock >= num的原子条件,所以不会超卖。
第三个问题:如果商品价格在用户下单前变化了怎么办。回答强调下单接口不信任前端传价,后端重新查询商品表获取当前价格计算总金额;如果管理员下架商品,提交订单时再检测一次status状态。这两个兜底逻辑已经覆盖了大部分场景。
备考期间,把这三个答案对着自己项目的代码再过一遍,尽量能指出具体类名和方法名,显得真的动手实现过。
6.4 源码交付时的目录结构说明和自查清单
最后交付的压缩包里,如果只是把整个工程文件夹丢进去,那也太缺乏整理意识了。建议按这个结构给最终交付包分层:
项目根目录 ├── src 后端代码 ├── frontend 前端工程 ├── docs │ ├── db.sql │ ├── 部署文档.md │ └── 论文.docx └── README.mdREADME里写清楚项目简介、技术栈、启动步骤、默认账号。这个文件不用长,但要把最重要的一步操作放在最前面。交付之前花半个小时完成一次完整的新环境部署测试,确认从导数据库到页面跳转全部正常,再决定是否提交。源码能跑通是底线,能讲清楚是上限,两者都准备了,这一场毕业设计才算真正过关。
从我带过的项目经验看,家电销售平台这类选题上限不低,核心就在“电商闭环是否完整、数据库设计是否经得起推敲”这两个点上。把本文提到的状态流转、原子扣库存、订单快照、前后端分离配合这些细节落进代码和论文里,答辩的时候自然会比那些只有页面效果的同学多一层底气和说服力。