ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Spring Boot + 微信小程序个人商铺系统:毕设设计与实现全攻略

2026/10/5 7:24:39 拓冰建站 浏览量
Spring Boot + 微信小程序个人商铺系统:毕设设计与实现全攻略 最近连着带了几届毕业设计碰到好几个同学想做一个“个人商铺”方向的小程序题目都很接近比如“基于Spring Boot的微信小程序个人商铺系统设计与实现”。这个题目之所以受欢迎一是技术路线特别标准Spring Boot加微信小程序基本就是当前Java后端加移动端最主流的组合二是业务模型不算复杂不会一上来就陷入多商户、分销、物流对接这类无底洞但又能把用户、商品、购物车、订单这一整条电商链路跑通作为毕设的系统性和完整度都够。这篇文章我会按照我实际带项目的思路把这个题目从选题、技术选型、表结构设计、登录对接、列表分页、下单扣库存一直讲到最后部署上线的坑。里面提到的方案都是我实际验证过、学生能独立复现的不是教科书上的标准答案但一定是可以直接抄作业的那种。适合准备做类似毕设的同学也适合想快速搭一个“小程序Spring Boot”商城骨架的开发者。1. 选题为什么值得做技术方案怎么定1.1 毕设选题的“性价比”分析一个毕设题目好不好我的判断标准从来不是越难越好而是三个字性价比。所谓性价比是指技术栈覆盖面、工作量和最终展示效果之间要平衡。个人商铺系统这个题目技术栈覆盖了微信小程序前端、Java后端接口、MySQL数据库、服务器部署四个层面不管你将来写简历还是答辩每一块都拿得出手。工作量上又不需要你做真正电商平台的复杂功能像物流轨迹、运费模板、售后维权、优惠券这些都能砍掉开发周期压到三到四个月是现实的。最关键的还是展示效果。小程序天然不需要安装答辩时评委扫个码就能看到实际运行效果比你在电脑上开一个localhost页面直观多了。而且“个人商铺”这个定位很讨巧它是完整电商系统的最小可用闭环但你可以随时往里面加东西。我见过一些同学一上来就选“大型分布式电商系统”听着很牛结果光搭微服务就花了一个多月最后核心业务都没写完。毕业设计的核心是展示你的完整工程能力和问题解决能力不是比谁题目喊得响。1.2 Spring Boot加微信小程序组合的几个关键判断技术选型不是跟风每一层都要能说出理由。后端选Spring Boot而不是传统的SSMSpring、Spring MVC、MyBatis本质上是因为Spring Boot解决了配置文件地狱的问题。以前SSM项目要手写一堆XML配置学生经常在配置阶段卡两周。Spring Boot用自动配置加约定优于配置一个application.yml就能把数据源、端口、日志都搞定内置Tomcat还能直接打成jar包运行这对毕业设计太重要了——你不需要在答辩机器上装Tomcat一条java -jar命令项目就跑起来了。Java版本和Spring Boot版本这里要特别说一句我建议用Java 8配Spring Boot 2.7.x不要一上来就追新。这几年Spring Boot 3.x已经普及了但它要求Java 17而且很多老版本的依赖和教程都不兼容。有些同学下载了最新的Spring Initializr模板结果发现MyBatis、FastJSON这些包全是错的折腾半天才明白是版本太高导致。别问我是怎么知道的每年都有学生栽在这上面。小程序端用原生微信小程序开发不要因为uniapp热就去用。uniapp的优点是跨端可以同时输出H5、App和小程序但个人商铺这种项目只需要在小程序一个端跑原生框架比uniapp的调试链路更短遇到问题百度出来的解决方案也更多。如果你以后想接外包或者做跨端项目再学uniapp不迟但毕设不要给自己增加变量。1.3 Maven项目结构怎么搭Banner这些彩蛋要不要花时间工程构建方式我统一建议用Maven原因是学校机房、答辩电脑上大概率装了Maven而且IDEA对Maven的支持比Gradle更顺滑。Maven的核心概念就是groupId、artifactId、version三个坐标你用IDEA的Spring Initializr生成项目后这些都有不用手搭。一个标准Spring Boot项目的目录结构大概长这样shop-backend/ ├── pom.xml ├── src/main/java/com/example/shop/ │ ├── ShopApplication.java // 启动类 │ ├── controller/ // 接口层 │ ├── service/ // 业务层 │ ├── mapper/ // 数据访问层 │ ├── entity/ // 实体类 │ ├── dto/ // 前端传输对象 │ ├── config/ // 跨域、拦截器、分页等配置 │ └── common/ // 统一返回结构、异常处理 └── src/main/resources/ ├── application.yml // 核心配置 └── mapper/ // XML文件如果不用注解SQL这里有个细节很多教程会把所有类堆在三个包里controller、service、mapper各一层就完了。我建议你至少加一个common包放统一返回结果和全局异常处理答辩时被问到“如何统一处理异常”就可以直接指代码。加一个dto包避免直接把数据库实体类暴露给前端这也是加分项。Spring Boot启动时默认有个Spring的字符画很多同学喜欢用Banner生成器搞一个自定义图案。这件事我建议五分钟搞定就行不能在配置Banner上花一下午。Banner是个纯视觉彩蛋对项目本身没有任何功能价值答辩老师也不会因为这个给你加分。2. 先把业务闭环想清楚再做表结构设计2.1 个人商铺系统的功能模块和页面清单做系统设计之前一定要先列页面清单。页面清单能逼你确认每个角色到底看到什么、操作什么。个人商铺系统按照用户视角拆大概需要这些小程序页面首页展示推荐商品和分类入口商品列表页支持分类筛选、关键字搜索、分页加载商品详情页展示大图、价格、库存点击加入购物车或立即购买购物车页单选、全选、删除、金额汇总订单确认页填写/选择收货地址、提交订单订单列表页按状态切换待支付、已支付、待收货、已完成个人中心页展示用户头像昵称、我的订单入口后端管理端可以做得简单一点哪怕没有好看的前端页面只要有一组管理接口能维护商品、分类、订单状态就行。有些同学觉得没有管理后台就不完整其实从小程序的定位来看个人商铺的后台本身就不是重点做一个极简的Web页面或者直接用接口调试工具管理数据都是接受的。这样一套页面下来业务闭环就清楚了用户登录 → 浏览商品 → 加入购物车 → 提交订单 → 支付模拟→ 管理端发货 → 用户确认收货。2.2 核心表结构设计字段和命名一个都不能乱数据库设计是整个项目的定海神针表结构不对后面写多少代码都别扭。我习惯把核心的表控制在六张左右用户表、分类表、商品表、购物车表、订单表、订单明细表。如果做地址功能再加一张收货地址表但个人商铺场景下也可以直接用用户表中的默认地址字段顶替新手不建议一上来就做多收货地址。表名字段命名要统一我建议所有表名都加t_前缀便于和其他系统表区分。字段上每张表都要包含id主键、create_time、update_time。create_time用来记录创建时间update_time记录最后更新时间这两个字段是硬性的任何人看到表结构都会认为你考虑过数据审计。商品表设计的时候有几个字段必须重点想清楚id, name, subtitle, main_image, price, stock, sales, category_id, status, deletedmain_image存的是商品主图地址后端接口返回时要补全域名否则小程序端显示不出来。price用decimal(10,2)不要用float或double浮点数计算金额会出现精度丢失这是经典面试考点你答辩时可以主动提。stock是库存下单扣减的时候要用后面我展开说。sales记录销量方便按销量排序。status表示上架下架1上架0下架前端列表页只查上架商品。deleted是逻辑删除0正常1删除。毕设阶段可以不写物理删除但逻辑删除字段建议保留这是企业级开发的标准做法。订单表设计要特别注意状态字段和多表关联。订单表里存order_no订单编号、user_id、total_amount、status、receiver_name、receiver_phone、receiver_address。为什么不把订单状态直接写在商品表里而是单独用订单表因为一个订单包含多种商品订单状态是跟着整个订单走的不是跟着单个商品走的。如果一个订单里有三件商品你只在商品表记录状态那就乱套了。订单明细表单独拆出来是因为一个订单可能买多个商品每个商品的数量、价格、快照信息必须独立记录。商品价格以后可能会改但订单里的商品价格不能跟着改所以明细表要把下单时的价格冗余存储一份。2.3 下单扣库存的正确做法一个UPDATE解决的问题下订单是整个系统里最容易写错的部分常见错误就是先查询库存是否足够够的话再UPDATE库存然后插入订单。这个逻辑在并发请求下来的时候会出大问题两个用户同时查到库存还剩1件都判断可以买结果都下单成功库存变成-1。毕设答辩很爱问这个问题所以我建议直接在代码里用一条带条件的UPDATE语句解决超卖int rows productMapper.reduceStock(productId, quantity); if (rows 0) { throw new RuntimeException(库存不足); }对应的SQL是UPDATE t_product SET stock stock - #{quantity} WHERE id #{productId} AND stock #{quantity}这条语句的意思是只有当库存大于等于购买数量时才执行库存扣减并且把剩余库存减掉。UPDATE是行级原子操作数据库会保证并发环境下不会重复扣。影响行数为0就说明库存不够这个判断是非常可靠的。下单过程还涉及插入订单表、插入订单明细表、更新库存这三个动作必须放在同一个事务里不然会出现订单创建成功但库存没扣或者库存扣了但订单失败的问题。Spring里给Service方法加Transactional注解就能搞定这是标准的声明式事务用法。3. 小程序登录与后端鉴权别用Session硬套3.1 微信登录的完整流程从code到openid再到token小程序登录和传统网页登录有个本质区别小程序没有密码输入框它靠微信的开放能力实现身份识别。正常流程是这样用户打开小程序时小程序端调用wx.login()微信会返回一个临时登录凭证code这个code有效期只有五分钟而且只能用一次。小程序把code传到你的后端接口后端拿着这个code再去微信官方接口换openid和session_key。openid是用户在某个小程序下的唯一标识相当于身份证号。后端实现上用RestTemplate或者HttpClient请求微信接口就行Spring Boot自带了RestTemplate不过新版推荐用WebClient但毕设用RestTemplate更简单String url https://api.weixin.qq.com/sns/jscode2session? appid appid secret secret js_code code grant_typeauthorization_code; RestTemplate restTemplate new RestTemplate(); String response restTemplate.getForObject(url, String.class);拿到openid后你要做两件事查一下用户表里有没有这个openid没有就自动注册一个新用户然后把用户id和openid信息按自己的规则生成一个token返回给小程序。小程序后续每次请求都把token放在Authorization请求头里后端根据token识别用户身份。为什么不用Session因为小程序的请求都是通过HTTP发起的Session依赖Cookie而小程序的wx.request对Cookie的支持有限真机上经常丢。Token方案更直接可靠也方便以后扩展。3.2 后端统一鉴权拦截器加放行名单你要做一个拦截器统一校验request请求头里的token。Spring Boot里可以用HandlerInterceptor实现然后在WebMvcConfigurer里注册拦截器并配置放行名单。用户登录接口肯定要放行商品列表、商品详情、首页这些浏览接口也可以放行方便用户没登录也能逛。但加入购物车、提交订单、查看订单这些操作必须登录。拦截器代码不复杂就是从request.getHeader(Authorization)里取出token解析用户id放到ThreadLocal或request属性里之后Controller就能直接拿当前用户id了。我在这个环节一定要强调一点很多同学把时间花在复杂加密和签名上其实完全没有必要。毕设阶段token用JWT就够了如果没有引入JWT库自己用UUID生成一个随机串存到Redis或者内存Map里都可以。等你真正要商用、防重放攻击的时候再考虑签名认证项目不同阶段有不同复杂度标准。3.3 页面顶部导航栏适配和用户离开小程序的处理小程序页面的导航栏有一个坑如果你用的是默认导航栏直接在app.json或页面json里配置navigationBarTitleText就行简单省事。如果你想要自定义顶部导航栏那就要适配右上角胶囊按钮的高度。这也是新手容易写崩的地方自定义导航栏组件时顶部安全区高度和胶囊位置要动态计算。官方给的方法是用wx.getMenuButtonBoundingClientRect()获取胶囊按钮的位置信息再通过wx.getSystemInfoSync()拿到状态栏高度两相结合计算出导航栏高度。另外监听用户离开小程序是一个非常实用的体验优化点。很多同学不知道微信小程序有onHide和onShow监听。用户在浏览过程中切到微信聊天再回来你的页面其实经历了隐藏和重新显示两个事件。如果你在订单确认页、支付页这类有状态页面上最好在onShow里重新刷新一次页面数据避免用户看到的还是离开前的旧状态。4. 列表加载更多、购物车联动与订单状态流转的实现细节4.1 商品列表加载更多的正确姿势onReachBottom加分页参数小程序页面的商品列表不能一次性加载全部数据否则图片一多页面会卡死。标准的做法是触底加载更多。小程序端页面滚动到底部会自动触发onReachBottom生命周期函数。你要在这个函数里判断当前是否还有更多数据有就请求下一页。后端接口要做分页我用的是MyBatis-Plus的分页插件。先在配置类里注册分页拦截器Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }然后Service里用Page对象查询PageProduct page new Page(pageNum, pageSize); LambdaQueryWrapperProduct wrapper new LambdaQueryWrapper(); wrapper.eq(Product::getStatus, 1); wrapper.orderByDesc(Product::getSales); productMapper.selectPage(page, wrapper);返回给前端时除了商品列表还应该返回总条数total前端用hasMore标志位控制“是否还有下一页”。前端一个小技巧是加一个isLoading锁防止用户连续快速滑动时重复发起请求。onReachBottom() { if (this.data.isLoading || !this.data.hasMore) return; this.setData({ isLoading: true, pageNum: this.data.pageNum 1 }); this.loadGoods(() { this.setData({ isLoading: false }); }); }很多同学的列表加载功能会越滑越乱要么不停重复加载相同数据要么加载完就没反应排除下来八成是hasMore和isLoading没配合好。4.2 购物车单选、全选、金额联动的思路购物车页的逻辑看起来简单写起来容易乱。核心点有两个选中状态的存储方式和金额汇总的计算时机。我的建议是购物车列表里每个商品项维护一个selected布尔值数据从后端或本地缓存加载后这个字段必须存在。点击某个商品前面的圆圈更新当前项的selected同时重新遍历整个列表计算已选商品的总金额。全选按钮和单项选择要联动全选选中时所有项的selected都为true只要有任意一项取消全选按钮就自动取消。这里不要偷懒每次点击都要重新计算“是否所有项都选中”。微信小程序里实现单选可以用checkbox组件但要注意checkbox的选中状态是受checked属性控制的你需要配合bindchange事件同步数据。个人体感是这种自定义风格的购物车用view加自定义图标比官方checkbox更好控制样式也更容易在答辩时讲清楚。金额展示有个细节计算总和时用浮点加法页面会显示出一长串小数比如19.999999。你可以在后端把金额都封装成整数分或者在前端toFixed(2)格式化但更推荐后端返回时将totalAmount保留两位小数前端只负责展示。4.3 订单状态机用固定的订单状态顺序跑通全流程订单状态是整个系统里最需要讲清楚的部分。如果状态逻辑混乱消费者看到的和商家处理的动作会对不上。我设计的订单状态是一组固定的整数枚举比如0待支付1已支付/待发货2已发货/待收货3已完成4已取消状态流转路径是这样的用户下单成功生成订单状态为0用户模拟支付成功状态变为1个人商铺的“老板”在后台点击发货状态变为2用户在订单列表看到已发货点击确认收货状态变为3。这里一定要用常量或枚举类保存这些数字千万别在业务代码里散落一堆魔法值。你写order.getStatus() 1别人看不懂但写成order.getStatus() OrderStatusEnum.PAID.getCode()一眼就明白。支付环节我强烈建议做一个“模拟支付”而不是真接微信支付。真实微信支付需要企业资质、商户号和复杂的回调验签毕设阶段耗时太长。在订单确认页弹一个确认弹窗点击“模拟支付成功”后后端把订单状态从待支付改成已支付即可。答辩时主动说明这是模拟支付逻辑接口已预留是完全可以接受的。4.4 商品搜索与分类筛选的低成本实现个人商铺的商品搜索不需要引入Elasticsearch这种重型方案MySQL的模糊查询就够了。小程序搜索框把关键字传给后端后端在商品表的商品名和副标题字段里做LIKE匹配。分类筛选更简单商品表有category_id字段前端传分类ID后端用查询条件过滤即可。这里我建议分类级联逻辑整体不要超过两级个人商铺的商品量级一般不大分类层级做深了纯粹是给自己找麻烦。如果你想让搜索体验更好可以加一个搜索历史功能小程序端用wx.setStorageSync保存最近搜索的关键字下次进入搜索页时直接展示。这个功能很小但答辩时展示出来会有不错的加分效果因为代表了你有体验优化的意识。5. 本地联调、真机调试与部署上线的实操记录5.1 本地联调三部曲合法域名、baseUrl和跨域配置第一次把小程序和后端跑通配置工作占一半。第一步在微信开发者工具右上角“详情-本地设置”里勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”。后端地址写http://localhost:8080不加这个勾选是请求不通的。第二步把后端的请求地址统一封装到一个文件里不要在每个页面里硬编码。我在实践中习惯在utils/config.js里放一个baseUrl变量项目上线时只需要改这一个文件。第三步后端处理跨域。小程序通过wx.request请求后端如果后端不做跨域配置浏览器能通但小程序会报错。Spring Boot里加一个CorsConfiguration的配置Bean或者直接用CrossOrigin注解都可以解决。真机调试的时候有个关键差异手机上连http://localhost:8080肯定不通localhost指的手机本机。你必须在电脑上启动项目然后用电脑的局域网IP手机和电脑连同一个Wi-Fi才能访问。另外http协议在真机上默认被微信拦截真机预览时想在非HTTPS环境下请求只能在开发者工具里勾选“不校验合法域名”后配合预览功能使用。这些都是新手必踩的坑。5.2 图片上传、静态资源映射和文件存储个人商铺系统需要用到商品图片我建议后端提供两个接口图片上传和图片访问。图片上传接口接收小程序端wx.uploadFile传过来的文件后端把文件保存到服务器的一个固定目录比如/usr/local/shop/upload然后把访问路径返回给前端。为了能让小程序能通过URL访问到图片Spring Boot需要配置静态资源映射。自2008年之后Spring Boot默认的静态目录是classpath下的/static但上传到服务器磁盘上的文件不在classpath里所以你要加一段配置Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceHandler(file: uploadDir /); }这里要记住数据库里存储的图片路径要存相对路径比如/upload/20240601/xxx.jpg后端返回给前端的时候前面拼接服务器域名或IP。如果你直接存完整路径换服务器或者改端口后图片就全挂了。5.3 常见问题速查表这些坑我每届都能遇到现象根本原因解决方案列表无限加载停不下来hasMore没有在最后一次加载后置为false或onReachBottom触发太频繁检查请求返回的total和已加载数量控制isLoading锁小程序里图片全显示不出来图片存的是相对路径未拼域名或本地图片路径在手机上不可访问字段存取相对路径返回前拼上完整URL数据库查询出来的时间比实际慢8小时JDBC连接串没指定时区URL加serverTimezoneAsia/Shanghai真机调试请求不了接口localhost指向手机或没有关闭域名校验改用电脑局域网IP并勾选不校验合法域名接口404或者访问不到Controller扫描包路径错误确保启动类在controller包的外层点击下单提示库存不足但数据库库存很足事务没有提交或扣减SQL条件判断有误检查Transactional和stock #{quantity}条件Maven依赖导入报错一片红Spring Boot版本太高或依赖版本不匹配统一Spring Boot为2.7.xJava版本用8这个表强烈建议你收藏碰到类似问题直接对号入座。我见过太多同学卡在一个奇怪的问题上两三天最后发现就是一行配置的事。6. 毕设答辩的加分方向与我的个人经验6.1 低成本扩展方向管理后台、消息通知和更多业务细节如果你的核心功能已经写完还剩下一点时间我建议优先做这三件事成本低、效果明显。第一做一个极简的Web管理后台。不需要多好看用纯HTML加一个表格页面就行功能就是商品上架下架、修改价格库存、订单发货。这样你答辩的时候可以演示“老板在电脑上发货用户在手机端马上看到订单状态变化”整个演示闭环特别完整。第二订单状态变化的消息通知。小程序有订阅消息功能下单、发货、确认收货时都可以给用户推送状态变化。这一步不需要真的让用户点击订阅按钮你可以做一个模拟实现或者干脆只把订阅消息的流程图讲清楚。第三商品收藏和浏览历史。这两个功能实现非常相似都是一张关联表加两个接口但会让你的系统看起来比别人的更丰满。答辩时展示“我收藏了这个商品然后在个人中心能快速进入收藏列表”是很直观的加分点。6.2 答辩时老师最容易问到的几个问题我作为参与过答辩评审的人可以告诉你老师大概率会问什么。“为什么用Spring Boot而不用SSM”——回答自动配置、内置容器、生态完善能快速构建独立运行的应用。“MyBatis-Plus和MyBatis有什么区别”——回答MyBatis-Plus提供了通用Mapper和分页插件减少重复SQL编写适合中小型项目。“如果两个用户同时抢购最后一个商品怎么保证不超卖”——直接指着你的reduceStock方法说使用原子UPDATE和库存条件判断数据库层面的行锁保证了并发安全。“你的支付为什么是模拟的真实支付怎么对接”——坦诚说毕设阶段申请不了商户号已经预留了支付接口和回调逻辑后续接入微信支付只需要替换支付实现层。“用户token过期了怎么办”——回答前端拦截403响应并引导重新登录后端可以设置token过期时间。这些问题没有一个是刁难人的都是你项目里真正出现过、考虑过的点所以不要背答案把自己做过的细节讲清楚就好。6.3 一点个人的实在建议这个项目做完不要急着删掉。我建议你把整个项目从新建工程开始自己完整地再敲一遍或者至少把核心流程登录、商品列表、下单扣库存从头走一遍你会发现第一遍做的时候很多地方是不求甚解的第二遍才能真的讲清楚每个步骤的原因。很多同学依赖网上的现成源码我理解但为了答辩能顺利通过至少你要能画清楚时序图能解释清楚每张表之间的关系。你的代码可以是参考的但你的表达必须完全内化成自己的东西。做毕设的过程也是技术成长的缩影。从这个项目里学到的接口设计、事务处理、并发控制、前后端联调远比你死记硬背面试八股文有价值。等你工作了会发现这些能力每天都在用。希望这篇帖子能帮你少走点弯路顺利把系统跑通答辩拿个好成绩。