1. 为什么这个组合值得做:Spring Boot + 微信小程序的选型逻辑
1.1 从毕设要解决的问题说起
最近有不少同学问我,毕设选题到底选什么方向最稳妥。我的回答一直是:不要追那些看起来很炫酷但做不出来的题目,而是找一个需求清晰、技术栈成熟、能完整跑通"从注册登录到下单交易"闭环的方向。基于微信小程序的个人商铺系统,恰好就是这样一类题目——你说它难,它确实不涉及复杂算法;你说它简单,从前端交互到后端接口再到数据库设计,每一样都得亲自动手,工程量一点都不小。
为什么这个题目值得反复推荐?因为它天然覆盖了毕业设计评审老师最在意的几个维度:
- 前端有独立小程序端,能展示界面设计能力和交互处理能力
- 后端有完整的业务逻辑,不是糊弄几个查数据库的接口就完事
- 有真实的状态流转:商品上架、加购物车、下单、支付回调、订单状态更新
- 有可演示的部署成果,而不是只能在本机跑通的半成品
更关键的是,这套系统里"个人商铺"的定位很清晰:不是要做多商户平台,而是做一个店主可以自己管理商品和订单的小系统。这个边界对学生完成度来说非常友好——不用处理复杂的平台分账、多角色权限矩阵,但又能体现完整的电商业务逻辑。
1.2 技术栈的分工与组合理由
这套系统我推荐的技术组合是:Spring Boot 2.x + MyBatis Plus + MySQL + Redis(可选)+ 微信小程序原生框架。有人会问,为什么不用Spring Cloud微服务,为什么不用Vue做后台管理?原因很简单——个人商铺系统根本到不了需要微服务的体量,强行引入只是在给自己挖坑。毕设评审看的是你把当前场景下的问题解决得多干净,而不是看你堆了多少技术名词。
把职责划分开来看:
| 技术组件 | 承担的职责 | 为什么是它 |
|---|---|---|
| Spring Boot | 后端接口服务、业务逻辑、鉴权 | 生态成熟、内置Tomcat、起步依赖简化配置 |
| MyBatis Plus | 数据持久层 | 单表CRUD几乎零SQL,分页查询直接支持,适合开发进度紧张的毕设 |
| MySQL | 核心数据存储 | 个人商铺的数据量级远到不了瓶颈,MySQL最稳妥 |
| Redis | 登录凭证缓存、热点数据缓存 | 可选项,但建议引入,用于存Token和首页商品缓存,也好在论文里写"缓存设计" |
| 微信小程序原生 | 用户端交互界面 | 不需要额外搭H5架构,工具链最直接,调试起来最方便 |
有同学想用若依框架或者Vue做后台管理,这当然可以,但我建议你冷静想一下时间成本。个人商铺系统的管理端功能无非就是商品管理、订单管理、数据统计这几个模块,直接做进小程序的一个"店主中心"(或者单独配一套简单的Vue页面),远比你接一套完整的后台权限框架要省时间。毕业设计最怕的不是功能不够,而是时间被基础框架的配置吞掉,最后核心业务草草收尾。
1.3 功能范围的收敛与边界划分
题目里说"个人商铺系统",这个词其实可以做得非常大,也可以做得非常聚焦。做过项目的同学都知道,最怕的就是需求蔓延。我在设计时画了一条清晰的边界线:
- 用户端(微信小程序):浏览商品、搜索商品、查看商品详情、加入购物车、提交订单、微信支付、查看订单、确认收货、个人资料
- 店主端(小程序内嵌管理页或独立后台):商品发布、商品上下架、库存修改、订单发货、查看销售统计
这听起来不复杂,但每一块展开都有细节。比如"订单发货"之后,用户端的订单状态要同步变化;"商品上下架"之后,首页列表要能正确过滤;"库存修改"之后,下单时能不能买超。整个系统的技术难度其实就是这些边界条件中的状态一致性、参数合法性和异常兜底。
对于毕设而言,把以上功能完整做出来,绝对够写了。不要再往上加秒杀、优惠券、积分商城这类东西——它们确实能加分,但如果核心链路还没做稳就加这些,到时候答辩现场一旦演示卡壳,一个问题答不上来反而扣分。
2. 数据模型设计:先把商品、订单、用户这三张核心表看透
2.1 用户与店铺信息如何合并建模
个人商铺系统里面,最容易被小看的表就是用户表。很多同学一上来就按电商平台的思路设计了一堆表:用户表、店铺表、店铺配置表、会员表……结果后面对接微信登录的时候,发现字段大都用不上,反而还要来回调整。
我的建议是:把用户和店铺合并在一张用户表里维护。思路是这样的:用户表加一个字段如role(0表示普通买家,1表示店主),再加一组店铺信息字段(shop_name、shop_avatar、shop_desc)。默认注册进来的用户是买家角色,店主通过设置页把自己的店铺信息补齐后才有"店铺"概念。
这样做的好处非常实际:系统是"个人商铺",核心假设是一个人既买又卖——不需要做复杂的多店铺绑定关系,也不需要单独建店铺成员表,角色和店铺信息的一对一关系在一张表里就闭合了。
每个微信用户首次登录时,后端根据wx.login()拿到的openid去查用户表,如果不存在则自动创建记录。这里需要注意一个坑:千万不要用前端传过来的昵称和头像直接覆盖数据库里的用户信息,因为微信要求头像昵称填写能力调整后,新版本里获取头像昵称需要用户主动点击授权组件,否则拿到的是默认灰色头像。所以用户表的nickname和avatar字段要允许为空,等用户在自己的设置页主动保存后再更新。
2.2 商品表字段设计与状态机
商品表是店铺系统的门面。我在设计时参考了真正商城系统的核心字段,但砍掉了一堆个人店铺用不到的属性:
CREATE TABLE `product` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `shop_id` bigint(20) DEFAULT NULL COMMENT '所属店铺ID', `title` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品标题', `subtitle` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '商品副标题', `main_image` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '主图URL', `images` text COLLATE utf8mb4_unicode_ci COMMENT '商品轮播图URL,逗号分隔', `detail` text COLLATE utf8mb4_unicode_ci COMMENT '商品详情富文本', `category_id` bigint(20) DEFAULT NULL COMMENT '分类ID', `price` decimal(10,2) NOT NULL COMMENT '销售价格', `original_price` decimal(10,2) DEFAULT NULL COMMENT '原价,用于划线展示', `stock` int(11) NOT NULL DEFAULT '0' COMMENT '库存', `sales_count` int(11) NOT NULL DEFAULT '0' COMMENT '销量', `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态:0下架,1上架', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;商品状态只有两个:0下架、1上架。不要设计成"审核中"这个状态——那是平台型电商才需要的,个人商铺自己管店,商品上架后直接进入可售状态。但有一点要处理好:用户端所有商品查询,必须强制带上status = 1条件。哪怕你首页和搜索都加了,但管理页的统计接口容易忘,最后会出现下架商品还能在某个二级页面看到的情况,演示时很尴尬。
商品分类表可以做成两级:大分类+小分类。但个人店铺的商品量通常几十到上百件,我的建议是做成单层分类就够了,表里存category_id,前端按分类筛选时直接用这个ID过滤。再加一个冗余字段category_name存分类名,这样列表页做展示时不用联表查分类名称。
2.3 订单表与订单明细表:核心链路的表怎么设计
订单是电商系统的核心,所以订单表的设计决定了你的扣库存、支付回调、状态流转能不能顺畅做。我在项目里用了分表思路但没真分表——表结构按标准电商订单模型来:
CREATE TABLE `orders` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `order_no` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '订单编号', `user_id` bigint(20) NOT NULL COMMENT '下单用户ID', `shop_id` bigint(20) NOT NULL COMMENT '店铺ID', `total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额', `pay_amount` decimal(10,2) NOT NULL COMMENT '实付金额', `pay_type` tinyint(4) DEFAULT NULL COMMENT '支付方式:1微信支付', `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '订单状态:0待付款,1待发货,2待收货,3已完成,4已取消', `receiver_name` varchar(32) NOT NULL, `receiver_phone` varchar(16) NOT NULL, `receiver_address` varchar(255) NOT NULL, `remark` varchar(255) DEFAULT NULL COMMENT '买家备注', `pay_time` datetime DEFAULT NULL, `ship_time` datetime DEFAULT NULL, `confirm_time` datetime DEFAULT NULL, `del_flag` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;订单明细表则和商品表关联,保存下单那一刻商品的快照信息:
CREATE TABLE `order_item` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `order_id` bigint(20) NOT NULL, `product_id` bigint(20) NOT NULL, `product_title` varchar(64) NOT NULL COMMENT '商品标题快照', `product_image` varchar(255) DEFAULT NULL COMMENT '商品图片快照', `price` decimal(10,2) NOT NULL COMMENT '成交单价', `quantity` int(11) NOT NULL COMMENT '购买数量', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;为什么订单明细要存商品标题和图片快照,而不是下单时去商品表联查?因为商品后续可能改标题、换图、甚至删除,如果订单明细只存product_id,用户打开历史订单时会看到一张已经变掉的商品信息,这是电商系统最基础的一致性常识。做毕设时这一点写进论文里非常加分,回答答辩问题时也能体现你真的理解了这个设计背后的原因。
订单表状态我这里用了五个状态,加上逻辑删除字段。这里有一个常见的坑:不要把取消的订单物理删除,因为你做统计接口时是按订单维度聚合的,物理删了订单,明细和统计都会乱。用del_flag或维持status=4(已取消)即可。
2.4 辅助表与字段命名中的注意事项
除了以上三张核心表,还需要购物车表、轮播图表、地址表(如果做收货地址管理)。购物车表的核心是user_id + product_id的唯一约束,这样同一个用户往购物车加同一件商品时做的是数量累加而不是插入新记录。
字段命名的建议是全程保持一致性:时间字段要么都叫create_time/update_time,要么都叫gmt_created/gmt_modified,别一会一套。MyBatis Plus的代码生成器帮你生成实体时可以统一风格,但手工写SQL的时候很容易混,好在大部分查询都用框架封装的方法,混的问题不大。
另一个值得注意的点是金额字段。金额一律用decimal类型,不要用double或float。这不算什么高深知识,但答辩切入"浮点精度问题"是很好的提问方向,你先做对,心里有底。
3. 后端接口实现的几个关键关口
3.1 登录认证:小程序端凭证如何与后端安全对接
微信小程序的登录流程,说白了是换Token的三步:
- 小程序端调用
wx.login()获得临时code - 小程序端把
code传给后端接口 - 后端拿着
code请求微信接口换openid和session_key,然后生成自己的登录凭证返回前端
这里核心点是:你自己的后端要自己签发一份Token(比如UUID),把openid和用户ID存进Redis设置过期时间,再把Token返回给小程序。后续所有请求在请求头里带token,后端通过拦截器解析Token得到用户身份。
很多同学会想,既然wx.login已经能拿到openid了,为什么还要自己签发Token?直接拿着openid当前后端的用户标识不就行了?这里回答两点:一是openid是敏感用户标识,长期暴露给前端传输不合适;二是后端要做会话过期管理,比如7天有效期,你在Redis里给Token设过期时间就实现了,这是毕设里能写清楚的"会话管理"逻辑。
拦截器实现时注意两个细节。第一,拦截器要放行登录接口、支付回调接口和静态资源,否则回调请求连不上。第二,解析Token的用户信息建议存到ThreadLocal里,业务代码直接用UserContext.getUserId()取,避免每个接口都重复解析一遍。这个小工具类在答辩时也是可以展开讲的点。
3.2 下单链路:扣库存、生成订单与失败回滚
下单接口是整个后端业务逻辑最密集的地方。标准的流程是:
- 校验参数:商品是否存在、是否上架、购买数量是否为正整数
- 校验库存:当前库存是否大于等于购买数量
- 扣减库存:
UPDATE product SET stock = stock - #{quantity} WHERE id = #{productId} AND stock >= #{quantity} - 创建订单主记录和订单明细
- 返回订单号,前端拉起支付
看起来不难,但如果不加事务和锁,并发场景就会超卖。第一步,整个流程方法上标注@Transactional(rollbackFor = Exception.class),保证库存扣减失败时订单创建也回滚。第二步,扣库存SQL中带stock >= #{quantity}这个条件,并且要检查update返回的影响行数——如果影响行数为0,说明库存不足,直接抛出业务异常。
这里我要多说一句:不要用"先查库存再扣库存"的两步操作。因为查和扣之间,可能有另一个请求已经把最后的库存买走了。必须用一条带条件的UPDATE原子完成。这条SQL看着简单,写明白之后,答辩老师问并发安全你至少有一个完整答案。
创建订单时要用订单号。在个人商铺这个量级,我的方案是yyyyMMddHHmmss + 随机数,同时数据库给order_no加唯一索引,生成时如果冲突就重试。这个方案不如雪花算法精密,但代码量少、理解成本低,足够应付毕设场景。
3.3 订单状态流转与支付回调处理
支付环节是很多同学最头疼的,因为涉及真实商户号、证书、回调验签。个人商铺系统在设计时可以分两种方式实现:
- 真实接入微信支付:需要已认证的小程序、商户号、API密钥和证书。上线部署之后走真实支付链路。
- 模拟支付:本地开发时点击"立即支付"直接在后端把订单置为已支付,方便测试全流程。不需要纠结支付证书等环境问题。
我的建议是代码层面两种都支持,用配置开关切换。真实支付时,下单成功后后端调用统一下单接口拿到支付参数返回小程序;用户支付成功后,微信服务器向你的回调地址发通知。回调处理有几个必须做的点:
- 验签:用商户API密钥对回调参数做签名验证,防止伪造通知
- 幂等性:回调可能多次到达(微信支付回调机制设计成最多可达多次),处理前必须先查订单当前状态,如果已经是已支付则直接返回成功,不能再改状态
- 金额校验:回调里的
total_fee要和数据库订单的实付金额一致,不一致不能更新状态
这几个点是电商系统中支付模块的硬核逻辑。哪怕你答辩时只做到了模拟支付,只要你能把真实支付链路的设计思路讲清楚,比那些把回调接口写死成"全部成功"的同学强得多。
3.4 管理侧接口的鉴权隔离
店主端功能(商品上下架、发货、统计)和小程序买家端共用一套Spring Boot服务。这里很容易出现一个安全问题:任意登录用户都能调店主接口。解决方法不复杂:
- 用户角色用
role字段区分,0普通买家,1店主 - 管理接口的URL前缀统一用
/admin/**,拦截器里校验role必须为1 - 操作的商品只能属于当前用户店铺——防止店主A去修改店主B的商品
最后这一点特别容易漏。很多同学校验了角色但没校验归属,结果线上数据互相覆盖,演示的时候两个测试账号能改同一批商品,非常尴尬。正确写法是:修改商品时先按id + shop_id联查,查不到就报"无权操作"。
4. 小程序前端:从页面到交互的实现细节
4.1 首页数据加载思路:静态缓存与下拉刷新结合
小程序首页一般包含:顶部搜索框、分类导航、商品瀑布流。数据加载逻辑我建议分为两个层面。
首次加载时,调用后端GET /api/index接口,一次性返回分类列表和默认推荐商品列表。这个接口后端可以做Redis缓存,缓存时间60秒,减少重复查询数据库。第二次进入页面时,先展示缓存数据,小程序端再静默调用接口更新——这就是常见的"骨架屏+异步更新"体验。
下拉刷新和到底加载更多要走分页逻辑。用page和size两个参数传给后端,MyBatis Plus的Page对象直接搞定。返回的数据里要有hasMore字段,前端根据这个字段决定是否显示"已经到底了"。
这里有个很实际的经验:图片路径千万别直接存相对路径。小程序image组件加载相对路径(比如/upload/1.jpg)时,解析的是小程序包内的资源路径,不是你的服务器路径。正确做法是数据库存完整URL(https://你的域名/upload/1.jpg),开发阶段可以用局域网IP拼上去,部署后再替换成线上域名。我见过不少同学上线后图片全裂,就是栽在这个细节上。
4.2 商品详情页与购物车的状态设计
商品详情页承担了三个任务:展示商品信息、选择购买数量、加入购物车或立即购买。
"加入购物车"的数据结构只要能支撑两个操作就行:加购和改数量。小程序本地用Storage存一份购物车数据,可以即时响应用户操作,但问题是换了设备购物车就丢了。更稳妥的做法是购物车记录直接落到后端,每次加购调接口,登录后从后端拉取购物车列表。作为毕设,我建议做后端存储版本——实现起来不复杂,而且论文里能多写一个模块。
购物车接口设计上,我推荐这样三个:
POST /api/cart/add 添加商品 PUT /api/cart/update 修改商品数量/选中状态 DELETE /api/cart/remove 删除购物车条目页面展示时直接GET /api/cart/list返回当前用户的购物车列表,每项带上商品当前价格。这里要注意:加入购物车时存的价格只是参考,最终结算要以订单生成时的实时价格为准,前端不能直接把购物车里的价格当作结算金额传给后端。后端接口要重新查库计算订单总额。
4.3 订单确认页与支付拉起流程
从购物车勾选商品点"去结算",进入订单确认页。这里要展示收货地址、商品清单、金额明细。用户确认后点"提交订单",前端把以下数据传给后端:
itemList:商品ID和数量addressId:收货地址ID(或者直接传姓名电话地址,个人商铺建议直接传值,省去地址管理表)remark:备注
后端创建订单成功后返回orderNo和支付参数。前端拿到支付参数调wx.requestPayment,拉起微信支付面板。用户完成支付后,小程序端不要立刻跳转到"支付成功"页面,而是静默调一次GET /api/order/{orderNo}查询订单状态,以接口返回为准更新页面状态,防止"实际支付成功但页面显示待付款"的差状态。
本地没有真实支付条件时,可以做一个"模拟支付"按钮,点后调一个开发专用接口把订单标记为已支付。这个按钮建议用环境判断控制,process.env.NODE_ENV或配置里的isMockPay开关,上线后即使漏关了,也要保证只能在小程序开发者工具里生效,避免真实用户误点。
4.4 订单列表:不同状态不同操作的页面联动逻辑
订单列表页最常见的实现方式是用Tab切换:全部、待付款、待发货、待收货、已完成。前端每个Tab对应一个status查询条件,切Tab时重新调接口。
订单卡片上不同状态显示不同操作按钮:
| 订单状态 | 用户操作 | 店主操作 |
|---|---|---|
| 0待付款 | 去支付、取消订单 | 无 |
| 1待发货 | 无 | 发货 |
| 2待收货 | 确认收货 | 无 |
| 3已完成 | 删除订单(逻辑删) | 无 |
| 4已取消 | 删除订单(逻辑删) | 无 |
状态操作的后端接口分别是:POST /api/order/pay、POST /api/order/cancel、POST /api/order/confirm,店主端是POST /api/admin/order/ship。每次状态变更都要校验当前状态是否符合流转条件。例如"确认收货"只允许从待收货(2)流转,如果订单已经是已完成(3),接口要直接拒绝。
页面联动上有一个体验细节:用户点"确认收货"后,不只是改当前订单卡片的按钮显示,还要同步更新订单列表上方的Tab角标数量。最简单的方式是每次状态变更成功后,重新拉一次订单状态汇总统计(待付款、待发货、待收货各多少单),数据量小,刷一次不伤性能。
5. 联调、部署与答辩坑位准备
5.1 本地联调最容易被卡住的三个问题
第一,小程序合法域名校验。开发者在工具的"详情-本地设置"里勾选"不校验合法域名",才能访问本地后端接口。但毕设答辩前的演示如果要在别人电脑上跑,后端接口的域名一定要配好,否则小程序连不上服务。
第二,局域网真机调试的IP问题。如果用真机预览而不是开发者工具,不能填localhost或127.0.0.1,要填电脑的局域网IP,且手机和电脑必须在同一个WiFi下。后端启动时也要监听0.0.0.0,而不是默认的127.0.0.1,否则外部设备访问不到。
第三,虚拟支付合规问题。微信小程序虚拟支付管控比较严格,虚拟商品(比如会员、充值、虚拟课程)在个人类目小程序里很难过审。个人商铺建议卖实体商品,避开虚拟支付类目限制。这一点不仅影响上线审核,答辩演示时也可能被问到"你的小程序是否能发布",你要能回答清楚你卖的是什么类型商品。
5.2 部署上线的轻量方案
毕设部署方案我推荐用一台云服务器,配置1核2G足够。具体步骤:
- 服务器装MySQL 5.7或8.0,创建数据库并导入初始化SQL
- 装JDK 8或11,Spring Boot项目打包成
jar后用nohup java -jar方式启动,或者用systemd做成服务 - 装Nginx,把前端静态资源(如果有管理后台页面)和后端接口做反向代理
- HTTPS证书配置:微信小程序的
request请求只允许HTTPS域名,所以一定要配置SSL证书
不需要上Docker,不需要K8s。一个人维护一个小项目,一条命令启动一个jar包是最可靠的方式。真要在答辩时展示"上线可用",你可以在小程序的体验版里填上正式服务器域名,手机上直接打开演示,这份完整度比全靠本地演示要高很多。
5.3 答辩演示的关键路径设计
答辩演示最重要的是主流程顺畅。我建议的演示顺序是:
- 进入小程序首页,浏览商品列表,下拉刷新
- 搜索一个商品,进入详情页,加入购物车
- 购物车结算,填写收货信息,提交订单
- 模拟或真实支付,订单状态变为待发货
- 切换店主账号,在管理侧看到新订单,点击发货
- 切换回买家账号,订单状态变为待收货,确认收货
- 展示店主统计页面:今日订单数、销售额、商品销量排行
这条路径覆盖了系统的所有核心模块。每个环节之间都有状态联动,评审老师跟着走一遍,能直观地知道系统的完整度。
演示时容易被问到的深度问题,提前准备好答案:
- 扣库存的具体SQL和并发场景下的做法
- 支付回调的验签和幂等处理
- 登录Token的生成、过期与刷新策略
- 订单状态机的设计,为什么用数值状态而不是字符串
- 数据库索引设计,订单表为什么加
order_no唯一索引 - 如何防止普通用户调店主接口
5.4 论文写作时的模块划分建议
如果你的论文是配合这个系统一起写,章节建议这样划分:需求分析(功能性需求+非功能性需求)、系统设计(架构设计+数据库设计+接口设计)、系统实现(按用户模块和管理模块分章节写)、系统测试(功能测试+接口测试,给出测试用例表格)。
数据库设计章节把订单主表、明细表的关系画清楚,并解释为什么订单明细要存商品快照。接口设计章节列出几个核心接口的请求响应示例,尤其是下单接口和支付回调接口。测试章节不要只写"系统运行正常",要写具体的测试用例:正常下单、库存不足下单、重复支付回调、下架商品访问等边界场景。
我在实际做这类项目的过程中发现,很多同学的代码功能没问题,但论文里一张表、一个时序图都拿不出来,答辩现场被追着问细节就只能支支吾吾。所以平时写代码时就要同步积累素材,核心接口的调用链、数据库表的字段说明、关键逻辑的流程图,做一步存一步,最后成稿时会非常省力。