ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于Spring Boot的二次元手办商城微信小程序开发实战

基于Spring Boot的二次元手办商城微信小程序开发实战 1. 选题逻辑与项目定位1.1 为什么做手办垂类商城而不是“万能”电商选毕业设计题目这件事很多同学容易犯一个毛病做得太“大”。一上来就是“全品类电商平台”“校园二手交易系统”功能表列了二十几项结果写代码时步步卡壳最后交付时到处是半成品。我做这个“基于Spring Boot的二次元手办商场微信小程序”时核心思路是先砍需求把业务边界收窄到“二次元手办”这一个垂类然后再把所有功能做深、做完整。手办这个垂类有个特别好的点商品属性非常鲜明。普通商城商品可能就是标题、价格、库存、图片但手办商品天然带有IP名称、系列名称、比例尺寸比如1/7、1/8、高度、材质PVC、ABS、树脂、发售年份等专属字段。把这些字段做进数据库和筛选逻辑里就足以体现系统的个性化设计答辩时也能讲出完整的产品思路“我这个系统不是把通用电商换个皮而是针对手办爱好者群体的真实购买习惯做了字段级定制。”另外一个实际考虑是垂类商城的数据量和业务复杂度适中。毕设周期通常是三个月到半年一人完成你既不需要处理几百万条商品的水平分表也不需要搞复杂的推荐算法但你又必须跑通“商品展示—用户登录—购物车—下单—支付—后台管理”这条完整链路。手办商城正好是“麻雀虽小五脏俱全”的典型代表适合作为计算机毕业设计项目。1.2 技术栈选型Spring Boot 微信小程序原生 MyBatis-Plus后端选择了Spring Boot 2.7.x这是2024年之前生产环境用得最广泛的版本线之一网上资料多遇到问题搜到的解决方案基本都是针对这个版本的。很多同学问“为什么不用Spring Boot 3.x”因为3.x要求JDK 17以上而且部分第三方starter在老项目上的兼容包更新不一定及时对于毕设这种“求稳”的项目来说卡版本等于给自己埋雷。微信小程序端我选择了原生开发没有用uni-app。原因也简单毕设的重点是跑通整个业务链路而不是折腾跨端框架。原生小程序的页面结构清晰wxml、wxss、js、json四件套分工明确配合官方的开发者工具调试也方便。如果你后续想扩展再加uni-app重写一套也不是难事——但那是加分项不是必选项。ORM框架选了MyBatis-Plus主要是省事。单表CRUD、分页查询这些操作用它自带的方法就能完成不需要手写大段的XML映射文件。配合逻辑删除字段 TableLogic删除操作也变成了软删除对毕设来说这个特性非常实用展示效果也好。数据库方面用MySQL 8.0缓存用了Redis。Redis在这里承担两个功能一是管理token配合拦截器做登录鉴权二是缓存首页的轮播图和商品推荐列表减少数据库压力。这两块功能都不复杂但都能在答辩时讲出明确的“为什么要用Redis而不是只用数据库”的理由。2. 系统功能设计与数据库建模2.1 小程序端核心页面拆解小程序端的页面整体分为六个大部分首页、分类、购物车、订单、我的、商品详情。下面逐一说说每个页面的核心功能。首页顶部是搜索框支持按商品名称或IP名称模糊搜索比如你输入“初音未来”能把相关手办都带出来。搜索框下面是一张轮播图轮播图的数据来自后台配置状态为“启用”的才会展示在前端。再往下是分类导航和“新品首发”推荐列表推荐列表取的是数据库里最新上架的8件商品用创建时间倒序排列。首页的数据接口走Redis缓存第一次访问时从数据库查查到后缓存5分钟之后用户再进首页就直接读缓存。分类页面这里我做的是“二级分类联动”左侧是一级分类景品、手办、周边、模型右侧是对应分类下的商品列表。手办相关的分类还有“IP专区”的特性比如某个IP下的角色列表这个是在产品设计时额外加的作为一个亮点功能展示。购物车支持多选商品、修改数量、删除商品。购物车数据我实际是存在后端数据库里的没有用本地storage因为毕设答辩时老师可能现场用两台设备换着登录如果数据只在本地解释起来就比较尴尬。存后端的好处是逻辑完整能讲出“购物车内数据是否跟随账号同步、跨端是否一致”这种细节。订单页面分为全部、待付款、待发货、待收货、已完成五个tab。每个tab对应订单的不同状态前端根据后端返回的status字段来做tab切换展示。个人中心展示用户头像、昵称、订单入口、收货地址管理、联系客服入口。这里比较重要的一点是用户首次登录要引导去完善收货地址否则下单时没有地址可填。2.2 管理后台功能划分管理后台我用了比较轻量的方式实现——没有单独搭一套Vue前端而是用了Spring Boot的模板引擎加Bootstrap框架做了一套PC端Web页面由AdminInterceptor拦截器做登录保护。这样做的好处是部署简单后端一个打好的jar包扔到服务器上自带后台页面不用再单独部署前端项目对毕设来说是极大的简化。后台功能分成五个模块商品管理商品列表分页、新增商品、编辑商品、上架/下架、删除商品。列表页支持按分类、上架状态筛选。分类管理维护一级分类和二级分类的树形关系。订单管理展示所有订单支持按订单号、用户昵称、订单状态搜索管理员可以手动发货填写快递单号。轮播图管理上传图片、设置跳转链接、调整排序、启停轮播图。用户管理查看用户列表、冻结用户账号。2.3 商品表的字段设计手办商品表我单独设计了一套字段泛用字段和专属字段分开。基础字段包括商品标题、主图、详情图片用逗号分隔的多图列表、原价、现价、库存、销量、上架状态。手办专属字段包括IP名称、角色名、比例比如1/7、材质PVC/ABS、高度cm、发售年份、再版次数。为什么要把专属字段单独拆出来因为通用商品表塞不下这些字段而手办买家在选购时又非常关注这些参数。比如一个用户明确说“我只收1/7以上比例的手办”那在商品详情页就必须能清晰地展示这个信息。我在商品详情页把这些字段做成了一个参数表格配上图片展示效果很好。数据库总共设计了九张表用户表、收货地址表、商品分类表、商品表、轮播图表、购物车表、订单表、订单明细表、管理员表。订单表和订单明细表拆开是必须的因为一个订单可以包含多个商品每件商品各有数量、单价、小计。在设计订单表时要额外存一个“订单总金额”和“实付金额”因为存在优惠或者运费减免的可能总金额不等于实付金额的情况要通过字段体现。3. 核心流程实现与关键代码解读3.1 微信登录与token鉴权小程序登录的完整流程是小程序端wx.login()拿到临时code发送到后端后端拿着这个code去请求微信接口code2session换回openid和session_key。openid是用户在某个小程序下的唯一标识整个登录鉴权的核心就是它。拿到openid后后端先查数据库看这个用户是否已经存在不存在就自动注册一个新用户初始昵称设为“微信用户”不强制用户立即完善资料。然后生成一个token返回给前端。这里我使用的是JWTJSON Web Token把userId和openid放进去设置7天有效期。中间件部分写了一个LoginInterceptor用Spring MVC的拦截器机制实现。所有需要登录状态的接口路径都统一加了校验请求头里带token字段取出来解析校验校验通过就把userId放入Request域中后续Controller直接取用。如果token失效或缺失统一返回401状态码前端在封装的请求方法里统一跳转到登录页面有明显提示这里不只是拦截器也要求小程序端做一层wx.checkSession配合检查。曾经在这块踩过一个坑前端请求头字段名传的是Authentication后端拦截器取的是token导致登录状态一直校验失败。后来我把请求头字段名统一了才解决。这不是代码逻辑问题纯属前后端约定不一致但恰恰是实际开发中最常见的问题。3.2 首页数据缓存与商品列表接口商品列表接口的形态是GET /api/goods/page参数是page、size、categoryId、keyword返回分页数据。用MyBatis-Plus的Page对象处理分页配合LambdaQueryWrapper做条件查询。关键点是首页推荐的缓存处理。代码逻辑大概这样public R getHomeData() { String cacheKey home:recommend; String cached redisService.get(cacheKey); if (StringUtils.isNotBlank(cached)) { return R.ok(JSON.parseObject(cached)); } ListGoodsVO list goodsService.listRecommend(); redisService.set(cacheKey, JSON.toJSONString(list), Duration.ofMinutes(5)); return R.ok(list); }这个模式叫“缓存穿透与缓存击穿的基本防御”缓存里有就直接返回没有就查库再写入缓存。5分钟过期时间足够了因为商品推荐数据变化不频繁。写这段逻辑的时候可以用AOP做统一缓存控制但毕设项目直接写在一个service方法里更直观方便答辩讲解。商品详情页的接口是GET /api/goods/detail/{id}返回商品基本信息外加详情图片列表。详情页的浏览量计数我也做进去了点击一次就UPDATE goods SET view_count view_count 1这个字段可以在后台列表展示作为一个简单的数据指标。3.3 购物车与订单流程的状态机设计购物车的接口设计相对直接加入购物车、修改数量、勾选/取消勾选、删除、查询列表。购物车表的每条记录对应“某个用户购买的某件商品”包含userId、goodsId、数量、是否选中这四个关键字段。商家结算的时候只计算“选中”状态的购物车项。订单流程我建议用一个状态机来设计这是整个系统的技术亮点。订单状态从0到4分别是状态值含义可操作动作0待付款取消订单、去支付1待发货用户可申请退款2待收货用户确认收货3已完成用户可评价可选4已取消无操作每个状态的变化都会触发一些额外逻辑订单创建后要扣减库存取消订单要回补库存支付成功后要给商品增加销量数据。这些逻辑如果分散在Controller里代码会越写越乱。我建了一个OrderStateHandler来做状态流转的统一控制判断当前状态是否允许变更到目标状态不允许就抛业务异常。这个设计在答辩时是加分项。下单时还需要考虑超时未支付的问题。毕设场景下我用的是最简单直接的方案定时任务每5分钟扫描一次创建时间超过15分钟且状态为“待付款”的订单把它们自动置为“已取消”并回补库存。利用Spring的Scheduled注解即可实现不需要引入消息队列。3.4 支付模块的实现方案微信支付这块要实话实说对接真正的微信支付v3接口需要商户号、API证书、回调域名等一系列资质个人开发者很难完全走通。毕设项目通常有两种处理方式第一种是接入微信支付的沙箱环境或服务商模式但这需要额外申请流程麻烦。第二种是用一个“模拟支付”方案前端对接微信小程序的wx.requestPayment接口但后端在测试模式下直接把支付回调接口模拟成功订单直接改成待发货状态。我采用的是第二种方案同时在后端预留了WxPayService接口用策略模式封装了“真实支付”和“模拟支付”两种实现。本地配置payment.mocktrue时走模拟支付申请到真实支付权限后把开关关闭即可。展示效果时前端页面会弹一个“确认支付”的模拟框确定后订单流转到待发货前后端流程完全跑通。答辩时我主动和老师说“正式环境切PayService的实现类即可”老师理解的点头而不是追问为什么不能真实扣款。这一块要提醒大家演示前一定要把模拟支付的逻辑提前准备好不要现场尝试调用微信支付否则很尴尬。3.5 管理后台订单发货与商品上下架状态管理后台在PC端浏览器里操作Spring Boot里用Thymeleaf模板引擎渲染页面。商品新增和编辑页面用表单提交的方式图片上传走的是本地存储传上去后把图片访问路径存到数据库。一个比较重要的点是商品上下架状态与缓存的一致性。我从后台把某件商品下架后应该同步通知到小程序端而商品列表的缓存不会马上失效。所以我在上下架接口里增加了一步“主动删除缓存”的操作调用redisService.del(home:recommend)这样下次前端访问时就会从数据库重新拉取而不是拿旧数据。这类“缓存一致性”的问题在答辩时也是老师爱问的点提前解决并讲出来能体现出你对系统设计的思考深度。4. 开发中的大坑与实测排查4.0 环境与开发工具的准备这个项目我用的是IntelliJ IDEA 2023.1作为后端开发工具微信开发者工具作为小程序前端调试工具。刚开始配置环境时要注意JDK版本与Spring Boot版本的匹配。我用的是JDK 1.8 Spring Boot 2.7.x如果按照网上的新教程装了JDK 17再导入老项目时就会出现编译报错比如Error:(3, 32) java: 程序包org.springframework.boot不存在。因此环境初始化的顺序是先装JDK 1.8或11再装Maven配置好阿里云镜像仓库然后用IDEA导入项目等待依赖下载完成最后启动项目在浏览器里输入localhost:8080能看到首页才算环境准备完成。微信开发者工具的配置也容易踩坑。创建项目时选择“不使用云开发”AppID可以先选测试号接口判断合法域名时勾选“不校验合法域名”这样本地开发时就能直接访问本机的后端接口。4.1 小程序合法域名与本地调试这个问题是我觉得整个项目里最坑的一环。在微信开发者工具里默认开启“校验合法域名”如果你请求的后端接口是http://localhost:8080就会被拦截报“不在以下 request 合法域名列表中”。要解决可以在微信开发者工具中点击右上角“详情”—“本地设置”勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”即可。但这里有个隐患在真机预览模式下“不校验合法域名”这个选项是不生效的。如果你用手机预览就会发现接口完全请求不通。解决办法有两个一是把后端部署到公网服务器配置HTTPS域名二是用内网穿透工具做临时映射或者直接用微信开发者工具的“真机调试”功能它会通过中转把请求转发到本机效果还行。真机调试虽然能用但支付模拟等依赖回调的流程在真机调试下表现不稳定。我的经验是平时开发用模拟器正式演示前把后端部署到云服务器上用IP加端口访问测试阶段可以不配置HTTPS小程序端在“不校验域名”开关打开的情况下是可以正常访问的。4.2 token失效与401状态码的处理测试过程中最常见的报错就是接口返回401。表现是小程序端点击“我的”页面正常但点击“购物车”就提示登录失效跳回登录页。排查思路先开调试模式在Network面板里看购物车请求。如果请求头里没带token说明前端封装的请求方法在某个地方漏加了header。如果带了token但返回401说明后端解析token时出了问题。我在这个项目里的解决方案是前端封装一个request方法每次请求前先从wx.getStorageSync(token)取出token再塞到请求头里后端拦截器里对OPTIONS请求直接放行处理跨域。另外一个比较隐蔽的问题是JWT生成和解析时使用密钥不一致或者服务器时间和本地时间相差太大导致exp过期判断异常。如果用的是jjwt库建议在配置文件中单独维护一个jwt.secret不要写死在代码里方便不同环境下切换密钥。4.3 商品图片上传与访问路径问题后台上传图片时如果图片保存到了本项目的static/upload文件夹下那么部署到服务器后因为Spring Boot内嵌Tomcat的默认静态资源路径问题可能出现上传成功但访问404的情况。解决方式有两个一是用绝对路径保存比如/home/ubuntu/images/然后配置一个映射把/images/**映射到这个路径二是直接把图片保存到项目的static目录里随项目一起打包。我建议采用第一种因为把上传的图片和代码目录混在一起有一个问题每次重新打包部署都会把新增的图片覆盖或丢失。使用外部目录做存储代码更新和文件存储完全分开这是生产环境的常用实践。我在项目里配置了一个WebMvcConfigurer重写addResourceHandlers方法把/images/**映射到服务器上的绝对路径简洁可行。4.4 本地环境正常但部署服务器后接口报错这是一个高概率出现的问题。本地开发时数据库连接、Redis都是localhost部署到云服务器后如果不改配置项目启动不了或者接口一直报连接超时。解决方式是把application.yml里的数据库地址、Redis地址改成服务器IP或云数据库的公网地址同时要保证云服务器的安全组端口3306、6379、8080都放开了。还有一个坑是Linux服务器上MySQL默认的bind-address设置它可能只允许本机连接。如果你在服务器上用本机地址能连MySQL但远程用Navicat连不上就去检查/etc/mysql/mysql.conf.d/mysqld.cnf里的bind-address127.0.0.1改成0.0.0.0后重启MySQL即可。4.5 常见报错速查表整理了开发中遇到频率最高的几类问题方便你照着排查症状可能原因解决方案项目启动报端口占用8080端口被其他进程占用换端口或在yml中设置server.port访问接口报404Controller路径与前端请求路径不一致统一接口前缀比如/api前后端核对MyBatis-Plus查询返回null实体类字段名与表字段名不匹配开启驼峰命名映射map-underscore-to-camel-case: trueRedis连接失败服务器上Redis未启动或密码错误检查redis-cli ping是否返回PONG小程序请求报request:fail本地开发未勾选“不校验合法域名”开发者工具详情-本地设置中勾选跳过校验订单创建失败但无报错事务未回滚或库存字段类型错误在Service实现类上增加Transactional注解5. 答辩要点与项目扩展方向5.1 答辩时老师爱问的高频问题这个项目做完后答辩环节的核心其实就三个方向为什么要这么做、遇到了什么问题、怎么解决的。老师大概率会问以下问题提前想好答案“你的系统跟普通电商有什么区别”回答重点在于“垂类定制的商品模型领域专属筛选IP聚合”把商品字段的差异讲清楚。“Redis缓存过期时间怎么定的”答根据数据实时性要求首页推荐5分钟分类列表30分钟商品详情不缓存因为详情页的浏览量需要实时累加。“微信登录的openid和session_key分别做什么用”答openid用于识别用户唯一身份与数据库用户表关联session_key用于解密用户手机号等敏感信息本项目暂未用到解密功能所以只存不取。“如果用户下单后不支付库存什么时候释放”答通过定时任务每5分钟扫描超时15分钟的待付款订单自动取消并回补库存同时预留了接入消息队列延迟消息的方案。“购物车的数据存在本地还是服务端优缺点”答本项目存在服务端优点是跨端同步、后台能看到用户加购数据缺点是请求多了增加服务器压力但对于交流量场景完全够用。如果换成纯本地存储可以减负但无法做用户级数据分析。提前把这些问题的答案背熟答辩时表现得自然流畅基本就稳了。5.2 作品还能怎么扩展这个项目的扩展空间其实很大。比如接入真实微信支付v3如果具备商户资质把WxPayService中预留的真实支付实现补全即可实现真实扣款。增加订单评价功能用户在“已完成”订单里可以给商品打分、写评价评价信息回传到商品详情页展示增强社区感。商品搜索接Elasticsearch当前用的是MySQL模糊查询数据量大了之后可以换成全文检索引擎支持关键词分词和相关性排序。增加拼团/秒杀模块手办圈常有“限量发售”的玩法可以做一个“限量抢购”的子模块用Redis的原子性操作控制超卖。小程序端加用户积分体系购买商品得积分积分可抵扣金额可在“我的”页面查看积分明细。这些扩展方向不一定都要实现但作为系统设计的思考深度展示在项目文档的“未来展望”章节写清楚就很有价值。5.3 从完成代码到可演示项目还差的最后一公里代码写完只是第一步。期末答辩前我花了整整一天时间准备演示环境这条经验一定要分享出来不要直接拿开发环境演示提前准备一台干净的云服务器把项目以jar包形式部署起来数据库用source命令导入初始化SQLRedis启动好然后用手机真机扫码预览小程序关闭域名校验。确认所有流程连续演示两遍包括登录、浏览、加购物车、下单、支付模拟、后台发货、用户确认收货这一套流程跑通了演示环节基本就不会出岔子。写在最后开发这个手办商城小程序的过程我最大的体会是毕业设计项目的难度其实不取决于功能数量而取决于你是否真的把每一条业务链路想透了。很多人项目做一半做不下去不是因为技术难而是因为前期设计阶段偷了懒数据库字段没想清楚接口路径没规范到写代码时越写越乱。这套项目从选题到答辩前后花了我大约六周时间每天投入三四个小时。真正动手写代码的时间其实不多大部分时间花在了设计数据表、梳理接口文档和排查环境问题上。最后再分享一个小技巧项目里一定要准备一份init.sql把所有建表和初始化数据都整理好。无论换电脑、部署服务器还是答辩前重置环境都能快速拉起一个可演示的状态。这份SQL同样是答辩展示的亮点之一说明你有良好的工程习惯。希望这篇复盘能帮到正在选毕业设计题目或者正在开发类似项目的同学。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进