前端时间好几个学生来问我同一个问题:毕设想做一个微信小程序的电商系统,问我这个选题行不行、好不好做、有没有坑。说实话,“基于微信小程序实现电商管理系统”这类题目确实是计算机毕业设计里最稳的选题之一,知识点覆盖面广,前端、后端、数据库、接口设计、部署上线全都能涉及,难度又不像大型分布式系统那样不可控。但我发现很多同学拿到题目后,其实根本不知道从哪儿下手,项目结构怎么搭、数据库表怎么建、登录认证怎么做、论文怎么写,全是一头雾水。
这篇文章我就用“优购电商管理系统”这个实际项目把整条链路讲清楚,从需求拆解、技术选型、数据库设计,到核心功能实现、高频踩坑排查,再到论文结构和答辩准备,一次性讲透。无论你是正在为毕设选题发愁,还是已经选了类似题目但进度卡住,这篇文章应该都能帮上忙。项目本身是基于微信小程序原生框架,配合Spring Boot后端和MySQL实现,属于典型的单体应用架构,整个项目代码量和复杂度都非常适合作为本科毕业设计。
1. 项目整体设计与技术选型
1.1 这套系统到底在解决什么问题
先把这个项目定位说清楚。优购电商管理系统,核心用户角色分成两类:普通消费者和管理员。消费者端是微信小程序,完成登录、浏览商品、查看分类、加入购物车、下单、支付(演示环境下用模拟支付)、查看订单、管理收货地址这些常规电商操作。管理端则可以处理商品的增删改查、上下架、库存调整、订单状态流转、销售数据统计等后台功能。
为什么这个定位很适合做毕设?因为它完整覆盖了一个信息管理系统从数据建模到前后端交互的几乎所有环节,而且在答辩时能讲的东西很多。比如商品模块涉及图片上传、分类关联、搜索排序;订单模块涉及状态机流转、库存事务处理;登录模块涉及微信开放能力、JWT鉴权。任何一个点拿出来,都能深入聊上几分钟,这比做一个纯前台展示型页面要值钱得多。
对比来说,有的同学想直接做一个类似淘宝那么完整的大平台,或者反过来只做一个静态展示页面,这两种都容易出问题。前者工作量失控,后者技术含量太低。优购这个规模刚刚好卡在“能撑起一篇毕业论文、代码量合理、一个学期能完成”的位置上。
1.2 技术栈选型:为什么是原生小程序加Spring Boot
这一节应该是很多人最纠结的地方,我先说结论:前端用微信小程序原生框架,后端用Spring Boot,数据库用MySQL,管理端直接在小程序里实现。这个组合在毕设场景里是最稳的,没有之一。
先说前端。微信小程序原生框架和uniapp、Taro这类跨端框架相比,好处在于没有额外编译层,出问题好排查。跨端框架确实能一套代码跑多端,听起来很香,但它的坑在于:如果遇到一个只在微信端出现的渲染问题,你需要先判断是自己代码的问题还是框架编译的问题,这对新手来说很痛苦。而原生小程序框架,任何报错基本都能在官方文档或者社区里找到答案,资料数量完全不是一个量级。
再说后端。Spring Boot在Java技术栈里已经是事实标准,为什么不用Node.js、Django、Flask?不是说那些不行,而是从毕设答辩的角度考虑:大部分高校的软件工程类课程体系是围绕Java展开的,Spring Boot的SSM架构、依赖注入、ORM映射这些概念,老师听得懂,也认可其工作量,答辩时的沟通成本低很多。Plus,Spring Boot的资料实在太多了,随便搜一个报错都能找到详细解答。
数据库选MySQL基本不需要讨论,开源免费、资料多、JDBC连接池方案成熟。需要提醒的一点是,操作系统中MySQL版本最好装8.0以上,虽然5.7也完全够用,但8.0是当前主流配置,网上查到的很多新语法和驱动配置默认按8.0来,免得版本不一致导致各种莫名其妙的问题。
1.3 功能模块划分与前后端交互结构
整个系统的功能模块可以拆成四个部分:用户端小程序、管理端小程序页面、后端接口服务、数据库。其中用户端和管理端在物理上属于同一个小程序工程,只是根据登录用户角色显示不同的入口。这样做可以省掉一个独立的Web管理后台项目,对毕设的工作量控制很友好。
用户端的核心页面包括:首页(搜索、轮播图、商品分类导航、商品列表)、商品详情页(大图、价格、规格选择、加入购物车、立即购买)、购物车页(勾选、修改数量、删除)、下单确认页(选择收货地址、优惠券、支付金额结算)、订单列表页(订单状态分类、取消、确认收货)、个人中心页(头像昵称、订单入口、地址管理、意见反馈)。
管理端采用tabBar或侧边菜单控制,整个后端管理模块分为几大块:商品管理、分类管理、订单管理、用户管理、数据统计。其中商品管理负责新增和编辑商品信息,订单管理负责查看和修改订单状态,数据统计用简单的柱状图或列表展示销售趋势。
前后端交互统一采用RESTful接口风格,数据格式用JSON。每个接口在执行前统一走拦截器校验token,返回结构统一封装成{ code, message, data }三层结构,这个设计在后面开发时会省很多事。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据库设计与接口规划
2.1 核心数据表到底怎么建
数据库设计是整个系统最重要的一环,不少学生上来就建表,建着建着发现字段对不上,后面返工特别痛苦。优购系统的数据表我建议至少设计以下这些:
user用户表:id、openid(微信唯一标识)、nickname、avatar_url、phone、role、create_time。openid字段必须加唯一索引,这个字段是用户身份的核心。category商品分类表:id、name、sort、create_time。goods商品表:id、category_id、name、description、price、original_price、stock、sales、image_url、detail_images、status(0下架,1上架)、create_time。banner轮播图表:id、image_url、link_goods_id、sort。cart购物车表:id、user_id、goods_id、goods_name、goods_image、price、count、selected(是否勾选)、create_time。address收货地址表:id、user_id、receiver_name、receiver_phone、province、city、district、detail_address、is_default。orders订单主表:id、order_no、user_id、total_amount、pay_amount、status、receiver_name、receiver_phone、receiver_address、pay_time、delivery_time、finish_time、create_time。order_item订单明细表:id、order_id、goods_id、goods_name、goods_image、price、count、total_price。coupon优惠券表:id、name、type、amount、threshold、total_count、received_count、start_time、end_time。user_coupon用户优惠券表:id、user_id、coupon_id、status(0未使用,1已使用)、receive_time、use_time、order_id。
订单为什么要拆主表和明细表两张?因为一个订单可能包含多个商品,主表只需要保存一次收货人信息和总金额,明细表记录订单里每个商品的快照信息。注意这里商品快照字段(goods_name、goods_image、price)不能只存一个goods_id了事,因为订单生成之后商品改价或下架,历史订单的数据不能被影响。这种设计在答辩时也是可以讲的点。
2.2 接口风格与返回数据结构
后端接口统一以/api开头,按模块划分路径,比如:
| 模块 | 请求方式 | 接口路径 | 说明 |
|---|---|---|---|
| 登录认证 | POST | /api/auth/login | 用code换取openid和token |
| 商品 | GET | /api/goods/page?page=&size=&keyword=&categoryId= | 分页查询商品 |
| 商品 | GET | /api/goods/detail?id= | 查询商品详情 |
| 分类 | GET | /api/category/list | 获取商品分类 |
| 购物车 | GET | /api/cart/list | 获取购物车列表 |
| 购物车 | POST | /api/cart/add | 加入购物车 |
| 购物车 | PUT | /api/cart/update | 修改数量/勾选状态 |
| 购物车 | DELETE | /api/cart/delete | 删除购物车项 |
| 订单 | POST | /api/order/create | 创建订单 |
| 订单 | GET | /api/order/list?status= | 按状态查询订单 |
| 订单 | POST | /api/order/cancel | 取消订单 |
| 订单 | POST | /api/order/pay | 模拟支付 |
| 订单 | POST | /api/order/confirm | 确认收货 |
| 地址 | GET/POST/PUT/DELETE | /api/address/... | 地址增删改查 |
| 后台商品 | POST | /api/admin/goods/save | 新增/编辑商品 |
| 后台商品 | PUT | /api/admin/goods/status | 上架/下架 |
| 后台订单 | GET | /api/admin/order/list | 后台订单列表 |
| 数据统计 | GET | /api/admin/stats | 销售统计 |
返回结构统一用Result<T>泛型封装:
java复制public class Result<T> {
private Integer code;
private String message;
private T data;
}
前后端约定:code=200是正常,其他code都是异常或业务错误。这样前端页面只需要判断res.code === 200,再决定是继续渲染还是弹出错误提示,维护起来非常舒服。
2.3 登录鉴权:为什么用JWT而不是Session
整个系统里,用户每次打开小程序就是一次全新的网络请求,HTTP本身是无状态的,那后端怎么知道“你是谁”?常见的方案有两种:Session和JWT。我推荐毕设里用JWT。
流程是这样:小程序端调用wx.login()拿到一个临时code,后端拿着这个code去微信服务器请求jscode2session接口,交换得到用户的openid。这个openid是用户在小程序里的唯一身份标识,后端根据openid查user表,如果不存在就自动注册一条新用户记录。然后后端用用户信息生成一个JWT令牌返回给前端。前端把token存到wx.setStorageSync('token', token),之后每次请求都放在header的Authorization字段里。后端拦截器统一校验token,解析出用户身份后再放行具体接口。
JWT相比Session的优势就是无状态,后端不用在内存或Redis里保存会话信息,用户下次访问时只需要验签token是否有效。而且JWT本身就携带了用户ID等基本信息,后端不用每次都查数据库确认登录状态。这对Spring Boot项目来说是及格线以上的方案,答辩时老师如果问“session和jwt有什么区别”,你能把上面这段话答出来,基本就拿到分了。
一个关键注意点:JWT的secret密钥不要硬编码在代码里,要放到配置文件里,并且不同环境用不同的密钥。这在项目展示的时候是个细节加分项。
3. 核心功能实现与关键代码
3.1 登录流程:code换openid的全链路
这一节直接贴核心代码,项目里这段逻辑是复用率最高的,我按自己的实现习惯写了一个参考版本。后端接收小程序传过来的code后,调用微信接口:
java复制public String code2Session(String code) {
String url = "https://api.weixin.qq.com/sns/jscode2session?appid="
+ appId + "&secret=" + appSecret
+ "&js_code=" + code + "&grant_type=authorization_code";
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
JSONObject json = JSON.parseObject(response);
String openid = json.getString("openid");
return openid;
}
拿到openid之后,查询用户表。如果不存在,就插入一条新记录,角色默认是普通用户。如果openid对应的是管理员账号,那就返回管理员标识,前端根据这个标识动态决定是否展示管理后台的入口。
前端登录的调用代码:
javascript复制wx.login({
success: async (res) => {
const { code } = res;
const result = await request.post('/api/auth/login', { code });
if (result.code === 200) {
wx.setStorageSync('token', result.data.token);
wx.setStorageSync('userInfo', result.data.userInfo);
}
}
});
这个流程里最容易踩的坑是:开发者工具里wx.login正常,但真机上后端拿不到openid,或者拿到的openid跟预期不一致。绝大多数情况是appid和secret配置错误,或者没把后端请求地址从小程序的request合法域名里加进去。后面第4节我会集中排查这些坑。
3.2 首页商品列表与分类联动
首页是用户第一眼看到的东西,也是答辩演示时最先展示的页面。设计上建议用官方自带的swiper组件做轮播图,下面放一张用scroll-view横向滑动的一级分类导航条,再下面就是商品瀑布流列表。
分类和商品列表的联动逻辑是:点击分类导航时,切换分类ID,重新请求商品分页接口。数据获取用onReachBottom触底加载,这里有个常见的分页参数设计方案:请求参数里带page和size,后端返回时除了数据列表,还要返回total总数和hasMore布尔值,前端根据hasMore决定是否显示“加载中”和“没有更多了”。
商品列表页的wxml核心结构大概是:
xml复制<view class="goods-grid">
<view class="goods-item" wx:for="{{goodsList}}" wx:key="id" bindtap="goDetail">
<image src="{{item.imageUrl}}" mode="aspectFill"></image>
<view class="goods-name">{{item.name}}</view>
<view class="goods-price">¥{{item.price}}</view>
</view>
</view>
首页数据量的控制也要注意,第一次加载建议8到10条就足够了,不要一次性返回几十条,既影响用户体验,也让代码显得不够精细。分页查询用MyBatis Plus的Page对象即可,不需要手写复杂SQL。
3.3 购物车设计:存本地缓存还是存后端
购物车这个模块有两种设计思路:一种是完全存放在小程序本地缓存,一种是同步到后端数据库。我建议毕设项目直接用后端存储,因为这样在换设备登录时购物车数据不丢失,而且订单模块要读取购物车数据,涉及服务端逻辑,纯前端本地缓存反而会绕复杂。
购物车表核心字段就是user_id + goods_id + count + selected,查询时联表查出商品当前的最新价格。注意购物车里的数据在展示时要判断商品是否已下架,如果下架了要置灰或标注“失效商品”,这个细节做好了在答辩演示时很加分。
加入购物车的核心接口逻辑:
java复制public void addCart(Long userId, Long goodsId, Integer count) {
Cart cart = cartMapper.selectByUserIdAndGoodsId(userId, goodsId);
if (cart != null) {
cart.setCount(cart.getCount() + count);
cartMapper.updateById(cart);
} else {
Goods goods = goodsMapper.selectById(goodsId);
Cart newCart = new Cart();
newCart.setUserId(userId);
newCart.setGoodsId(goodsId);
newCart.setCount(count);
newCart.setSelected(true);
newCart.setPrice(goods.getPrice());
cartMapper.insert(newCart);
}
}
很多新手在购物车模块写成“每次都删除旧记录再插入新记录”的方式,逻辑上虽然能用,但会产生大量冗余的id自增和数据碎片,而且无法维护创建时间。复用一个已有记录来累加数量,才是更常规的做法。
3.4 商品库存与防超卖设计
下单是电商系统里最核心也最容易被问细的环节。一个最简单的下单流程是:用户从前端提交商品id数量和收货地址,后端创建订单记录,同时扣减商品库存。如果商品库存不够,就返回“库存不足”。
但这里有个经典的并发问题:两个用户同时买同一个商品,库存只剩1件,两个人同时下单,如果都用“先查库存再扣库存”的方式,就会导致两个订单都创建成功,库存变成负数,这就是超卖。防超卖是电商系统里非常经典的话题,也是答辩时老师最喜欢追问的点。
常用做法是在数据库层面做原子扣减,用一条SQL同时完成判断和扣减:
sql复制UPDATE goods SET stock = stock - #{count}
WHERE id = #{goodsId} AND stock >= #{count}
这条SQL执行后,判断受影响的行数,如果等于1说明扣减成功,可以继续创建订单;如果等于0说明库存不足,直接回滚。整个操作放在@Transactional事务里,保证扣减库存和创建订单要么同时成功要么同时失败。
事务里还要把订单主表和订单明细表一起写入。创建订单时要生成唯一订单号,我建议用时间戳加随机数的格式,比如yyyyMMddHHmmss + 6位随机数,这样既保证可读性,也基本不会重复。订单初始状态设置为待付款。
3.5 模拟支付:毕设里最现实的处理方案
老实说,个人主体的小程序是开通不了微信支付的,必须是企业主体、个体工商户等商户资质才能申请。学生做毕设时,绝大多数人没有公司资质,所以最合理的方案是做一套“模拟支付”,然后在论文里明确说明这一点,并在系统改进方向里写“后续可接入微信支付官方API”。
模拟支付的实现思路很简单:创建订单后,前端弹一个支付确认框,显示应付金额,用户点击“确认支付”后调用后端/api/order/pay接口,后端直接把订单状态从“待付款”改成“待发货”,记录支付时间,并减少商品销量增加。如果要点更有深度,可以在pay接口里接入微信支付沙箱环境,但这对大多数毕设来说并不必要,模拟支付已经完全能讲清楚业务流程了。
不过要注意,模拟支付不能做得太假。至少要做到:支付前校验订单是否属于当前用户、订单状态必须等于待付款、支付金额必须与订单金额一致。这些校验的存在,才能在答辩时说清楚“这个支付流程是安全的”。
3.6 后台管理:商品管理与订单状态流转
管理端我建议直接做在小程序里,入口放在个人中心页:如果当前用户role=admin,就显示“管理后台”入口,点击后进入后台管理页面。这种做法的好处是省去了再单独开发一套Web管理后台的成本,但工作量仍然足够撑起论文里的“系统实现”章节。
商品管理这里有一个容易犯的错误:新增商品时只填了基本信息,没有处理图片上传。图片上传方案我建议用wx.uploadFile将图片上传到后端指定目录,后端用MultipartFile接收后保存到本地,再用配置好的访问前缀拼接一个完整的图片URL返回给前端。如果申请了阿里云OSS或者腾讯云COS,也可以直接上传到云存储,然后返回CDN地址。毕设环境用本地存储就够,但要注意保存目录的读写权限和路径配置。
订单管理主要就是状态机流转:待付款、待发货、已发货、已完成、已取消。管理员在后台可以看到所有用户的订单,对待发货订单点击“发货”时填入物流单号,订单状态变为已发货。用户收到货后点确认收货,状态变为已完成。订单状态的变更应该在状态字段更新时记录时间,方便后续查看整个订单的生命周期。
4. 实操过程中的高频踩坑与排查实录
4.1 用户信息获取失败:wx1cb4398e1413dce7
很多人在做用户头像昵称的时候会遇到类似“小程序获取登录后的微信用户失败”的报错,报错信息里带着一串AppID字符,比如wx1cb4398e1413dce7。这个问题的根源基本可以锁定为:新版微信小程序已经关闭了wx.getUserInfo和open-type="getUserInfo"直接弹出授权框的能力,开发者必须要用“头像昵称填写能力”来替代。也就是说,用户需要点击一个button,然后button的open-type="chooseAvatar"来领取头像,昵称则通过一个input绑定type="nickname"来让用户填写。
如果你在毕设代码里还是老式的wx.getUserInfo写法,在开发者工具的老版本模拟器下可能还能跑通,一上真机就会失败。解决方案是改成新版写法:
xml复制<button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image class="avatar" src="{{avatarUrl}}"></image>
</button>
<input type="nickname" class="nickname-input" placeholder="请输入昵称" bindinput="onNicknameInput"/>
后端需要新增一个更新用户资料的接口,前端拿到头像临时路径和昵称后,先上传头像拿到永久URL,再调用接口保存。注意头像临时路径wxfile://开头的是临时空间,第二次打开可能就失效了,所以一定要先上传再保存。
4.2 真机调试时报错:net::ERR_CONNECTION_RESET
这个错误在真机测试时特别常见。开发者工具里接口能通,一到真机就提示failed net::ERR_CONNECTION_RESET。原因通常是这几个之一:请求地址写的是localhost或局域网IP但真机连不上后端所在电脑的端口;小程序后台没有配置request合法域名,同时真机没有开启“不校验合法域名”的调试选项;后端服务所在的机器防火墙挡住了请求。
解决思路很明确,按顺序检查:
- 确认请求地址能通过浏览器或Postman直接访问。后端地址不要写localhost,应该写电脑的局域网IP,比如
http://192.168.1.100:8080。 - 在微信开发者工具的“详情-本地设置”里勾选“不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书”。这个选项只对开发者工具里预览有效,真机预览时需要在微信中打开调试模式(右上角菜单,打开调试开关)。
- 检查电脑防火墙,将Java进程或指定端口加入允许访问列表。
另外提醒一下,微信小程序正式上线时,request的域名必须是HTTPS并且完成ICP备案,所以如果做的是毕设演示,用开发者工具配合“不校验合法域名”的方式是最省事的,论文里说明“本系统开发调试阶段采用本地接口联调”即可。
4.3 开发者工具的一些环境性问题
有同学在Windows环境下会碰到maximum setlocal recursion level reached这个报错,这个跟项目代码其实没有直接关系,通常是开发者工具运行环境或系统环境变量出现了递归问题,尤其是Windows PowerShell或系统变量异常时更容易触发。解决方法是重启开发者工具或重启电脑,一般可以恢复。如果再不行,卸载重装开发者工具的最新稳定版本就能解决。
另一个高频问题是开发者工具里报“间隔名未定义”“setData函数报错”之类,这类绝大多数是自己代码作用域写错了,例如在wx.request的回调里直接使用this.setData,而这里的this已经不是Page实例了。需要在wx.request外面先把const that = this存下来,或者使用箭头函数。这个问题在短视频上被反复提及,但每年都有同学踩,写在这里帮大家提前避坑。
4.4 图片上传与图片回显问题
商品图片、用户头像都要经历“本地上传-服务端存储-回显URL”的过程。我在开发过程中发现,比较容易踩的是wx.uploadFile里name字段和后端@RequestParam("file")的参数名不一致,导致后端一直收不到文件。
前端上传示例:
javascript复制wx.uploadFile({
url: baseUrl + '/api/upload/image',
filePath: filePath,
name: 'file',
success: (res) => {
const data = JSON.parse(res.data);
if (data.code === 200) {
console.log(data.data.url);
}
}
});
后端接收示例:
java复制@PostMapping("/image")
public Result<String> uploadImage(@RequestParam("file") MultipartFile file) {
String originalFilename = file.getOriginalFilename();
String ext = originalFilename.substring(originalFilename.lastIndexOf("."));
String fileName = UUID.randomUUID() + ext;
File dest = new File(uploadDir + fileName);
file.transferTo(dest);
return Result.success("/upload/" + fileName);
}
后端保存图片后,需要配置一个静态资源映射目录,让浏览器或小程序可以通过http://ip:8080/upload/xxx.jpg直接访问到图片。如果忘了配置静态资源映射,图片上传成功后却无法回显,这是新手常犯的错误。Spring Boot可以写一个WebMvcConfigurer实现:
java复制@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/upload/**")
.addResourceHandler("file:" + uploadDir);
}
4.5 常见问题速查表
| 现象 | 常见原因 | 解决思路 |
|---|---|---|
| 登录后获取用户信息失败 | 新版小程序取消getUserInfo授权弹窗 | 改用chooseAvatar和nickname输入方案 |
| 真机请求后端报ERR_CONNECTION_RESET | 域名未配置、调试模式未开、端口被防火墙拦截 | 勾选不校验域名/开真机调试/配防火墙 |
| maximum setlocal recursion level reached | 开发者工具环境异常 | 重启开发者工具或系统 |
| 上传图片后URL无法访问 | 未配置静态资源映射 | Spring Boot中实现addResourceHandlers |
| MySQL连接报错 | 驱动版本或时区配置问题 | 使用mysql-connector-j 8.0.x,添加serverTimezone=Asia/Shanghai |
| 令牌过期后接口返回401 | token有效期过短 | 前端统一拦截401跳转登录页 |
| 购物车商品价格与商品页不一致 | 购物车保存了快照价格 | 查询购物车时联表查最新价格 |
| 订单创建超卖 | 先查库存再扣库存 | 用UPDATE原子扣减并判断影响行数 |
5. 论文写作与答辩准备
5.1 毕业论文的结构安排
毕设论文结构一般要走标准套路,优购电商管理系统的论文章节建议这样安排:摘要(中文摘要加英文摘要)、绪论(项目背景、国内外研究现状,重点谈微信电商的发展和消费习惯变化)、需求分析(功能需求、用例图、非功能需求)、总体设计(系统架构、技术选型、功能模块图)、详细设计(数据库表设计、关键类设计、接口设计)、系统实现(按模块展示核心界面和关键代码)、系统测试(功能测试用例表、性能测试结果)、总结与展望。这个结构每个学校的模版可能略有差异,但大体不变。
写论文时最容易犯的毛病是空谈概念。比如绪论里花三页介绍电商的发展史,第一段“随着移动互联网的发展”,第二段“微信已成为人们日常生活中不可缺少的一部分”,这些套话不仅浪费时间,还容易被导师批评没有实际内容。正确做法是每部分都和“优购系统”本身挂钩,比如写背景,就直接讲校园二手交易、小范围内电商购物这个具体场景的痛点,这样才有说服力。
5.2 图表怎么画更规范
论文里至少要包含以下图表:系统功能结构图、系统架构图、用户端业务流程图、管理员订单处理流程图、数据库ER图、核心表结构说明表、功能测试用例表。这些图不需要用多高深的工具画,Visio、ProcessOn、draw.io都可以,关键是要规范、统一、层级清晰。
数据库ER图建议用实体关系图的方式呈现,把user、goods、orders、order_item、cart这些表的主外键关系画出来。功能流程图要能一眼看出订单的状态流转。答辩的时候老师通常是先翻图再提问,图好说明逻辑清晰,这是第一印象。
5.3 答辩高频问题与回答思路
答辩时老师问的问题基本都集中在系统设计和技术细节两个维度。我整理几个高频问题,你们可以直接备好答案:
- 为什么用JWT而不是Session?因为小程序端与后端是前后端分离架构,JWT无状态、适合移动端,服务端不需要维护会话数据,扩展性好。
- 防超卖怎么做的?用数据库乐观锁思路,在UPDATE语句中加库存判断条件,通过受影响行数判断是否扣减成功,并用事务保证数据一致性。
- 为什么订单表要拆主表和明细表?因为一个订单对应多个商品,主表存储订单公共信息,明细表存储每个商品的快照,降低数据冗余,同时保证商品表变化不影响历史订单。
- 支付是怎么实现的?个人开发者无法使用微信支付,所以设计为模拟支付流程,后端同样做了金额校验和状态校验,将来可以替换为微信支付V3接口。
- 如果用户量变大,系统怎么扩展?可以从数据库读写分离、Redis缓存、图片存OSS、部署升级成HTTPS等多方面讲,这也是论文“展望”部分该写的内容。
回答问题时不需要背稿子,但一定要说出设计时的考量。老师最怕听到的回答是“这个功能我是照着网上敲的,我也不太懂”,哪怕项目功能简单一点,只要你能把为什么这么设计的逻辑讲清楚,分数就不会低。
我在实际做这个项目时的一些体会
每年带学生做这类毕设项目,我最大的感受是,很多人的问题不是不会写代码,而是不会拆解任务。拿到“微信小程序电商系统”这个题目,第一反应是找源码、找模板,结果下载了一堆却不知道怎么改。正确的路径应该是:先画出功能图,再把功能图翻译成数据库表,再写接口文档,最后才是动手写代码。代码只是最后一个环节。
如果你们是两个人组队,建议一个人负责前端小程序页面和交互,一个人负责后端接口和数据库,分工前先把接口文档对齐,避免各做各的。如果是一个人独立完成,节奏上建议先用两个星期把所有页面搭出来,再用三个星期完成后端逻辑,留出充足的时间写论文和准备答辩,不要拖到最后一周连数据库都没建好。
另外,源码下载下来不要直接当成自己的提交上去。导师和答辩老师一眼就能看出你是不是真懂。哪怕你用了别人的源码,也一定要把每一条SQL、每一个接口的调用关系弄明白,能自己从零搭一个简化版才说明你真正掌握了。优购这套系统本身不复杂,花两三周完整复刻一遍,比东拼西凑两个月更有把握。
