ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

微信小程序点餐盲盒系统:口味偏好建模与推荐算法完整复盘

微信小程序点餐盲盒系统:口味偏好建模与推荐算法完整复盘 选毕业设计题目的时候最怕的就是做了个“普通系统”——功能齐全但没什么亮点答辩时老师一句“这不就是增删改查吗”直接打穿。这个基于微信小程序的用户口味偏好点餐盲盒系统名字里就自带亮点微信小程序是载体口味偏好是数据价值盲盒是玩法创新。我第一次看到这个题目第一反应是“有点意思”第二反应是“这怎么做”。今天就把整个项目的设计思路、功能拆解、核心算法、实操过程以及踩过的坑全部整理出来。无论你是准备做这个题目的毕业生还是想给点餐系统加玩法的开发者都能从里面找到可以直接抄作业的东西。系统要解决什么问题一是用户的选择困难症打开菜单翻半天最后吃了跟上次一样的。二是餐厅的库存与客单价参考盲盒经济把剩余食材、新品、招牌菜混进盲盒既能去库存又能增加点餐乐趣。和市面上普通的点餐小程序最大的区别在于它会在“猜你喜欢”的基础上引入随机因子让每一次点餐都有惊喜感同时又不至于完全随机——否则用户第一次开到香菜榴莲披萨下次就不玩了。题目里还带了“源码LW文档”这个后缀LW指的就是论文文档。对毕业设计来说代码只是载体文档才是答辩的命根子所以我从一开始就有意识地把每个设计决策、表结构、算法公式沉淀到文档里后面写论文时省了大半时间。1. 项目核心需求与设计思路拆解1.1 拆解题目从标题能读出哪些隐藏需求一个合格的毕业设计题目每个词都带着约束和期望。先拆开看“微信小程序”限定了前端载体。意味着不需要做安卓/iOS原生App微信生态内即点即用。同时也意味着要处理好微信登录、支付或模拟支付、分享等平台特性。“用户口味偏好”这是系统的数据核心。菜品要有标签体系用户要有行为记录算法要有模型三者缺一不可。“点餐盲盒”这是交互核心。用户不能完全自由选菜只能选“盲盒”单人盲盒、双人盲盒、多人套餐盲盒等由系统随机开出菜品。这要求后端有一套随机推荐混合的抽取逻辑。“系统”毕业设计里的“系统”标准配置是后台管理端 小程序端 数据库 接口文档。换句话说不能只做小程序界面还要有完整的后台支撑。拆分完毕后你会发现这个题目其实是“经典点餐系统 推荐算法 游戏化玩法”的复合题。难点不在于每个功能都难而在于怎么把三个点串起来让逻辑自洽。我见过不少同学只把“盲盒”做成一个纯随机抽菜按钮结果推荐逻辑完全没体现论文里连“口味偏好”四个字都解释不清楚答辩时被追问得很惨。1.2 为什么选盲盒玩法产品逻辑成立技术才有意义盲盒这几年在消费市场非常火本质上是把“不确定性”变成“娱乐性”。但是放到点餐场景里问题来了用户点外卖是刚需饿了的时候你让他盲盒抽一个如果抽到不爱吃的体验很糟。所以这里的盲盒必须是“带偏好的随机”而不是“纯粹随机”。我在产品设计阶段把盲盒拆成三种模式惊喜盲盒系统根据口味偏好综合打分抽取一份或多份套餐用户无法选择具体菜价格固定半盲盒用户先选一个口味方向比如“今天想吃辣的”系统在这个方向内随机抽取多人盲盒选人数和预算系统生成整桌菜品包含荤素搭配和主食。这三种模式由易到难。第一种最简单适合处女版第三种算法复杂度和实际价值更高可以让论文加分。我当时只实现了前两种因为多人盲盒涉及套餐约束、营养搭配工作量会涨不少。如果你时间充裕可以考虑把第三种加进去答辩时是一个非常亮眼的“扩展功能”。1.3 口味偏好建模从“吃过的记录”到“可能喜欢的口味”口味偏好不是让用户注册时自己选一两个标签就完事了——那是伪偏好因为用户认知和实际口味往往脱节。我建议用三层数据来建模第一层显式反馈。用户主动设置的口味标签、下单时的偏好备注、对上次盲盒的评分喜欢/一般/不喜欢。这部分数据精确但稀疏因为不是每个用户都愿意填。第二层隐式反馈。用户浏览菜品详情时长、加入购物车但未下单的记录、历史订单中点的菜品类分布、下单频率最高的时段。这部分数据量大但受噪声影响大。第三层上下文特征。点餐时间午餐/晚餐/夜宵会影响口味偏好、季节因素夏天用户更偏向爽口食物、用户所在位置可判断口味区域分布但这个涉及地图能力毕设可以不做。建模时用最简单有效的方式给每个口味标签建权重表用户每消费一次相关菜品权重就更新一次。后面做推荐打分时直接用菜品标签乘以对应权重做线性加权。这就是可解释的、能写在论文里的模型答辩时老师问你“为什么这样设计”你能讲出逻辑而不是一句“用了神经网络”敷衍过去。2. 技术选型与系统架构设计2.1 前端小程序原生框架更稳uni-app跨界看需求在选型的时候我被圈子里各种跨端框架刷过屏像uni-app、Taro这类的确能一套代码多端运行那到底要不要用我的结论是毕业设计选原生小程序框架。原因很简单原生框架的学习曲线更平缓官方文档和社区案例最多遇到问题搜得到答案微信开发者工具调试很方便断点、Network、Storage面板都有很多同学在选毕设题目的时间点根本没有接触过vue/react语法用uni-app等于先学一遍vue再学一遍小程序生命周期时间成本直接翻倍。当然如果你本来就熟悉vue又想在答辩时展示“我可以扩展到H5/App”那用uni-app也没问题。但要注意跨端框架发布到微信小程序时生态组件库的兼容性会有坑比如个别动画库在安卓端表现异常修复起来比原生麻烦得多。我这里有个原则系统里的核心功能优先用微信原生能力无论是原生框架还是uni-app这个底线都不能动。2.2 后端与数据库毕业设计最稳组合Spring Boot MySQL后端我推荐Spring Boot MyBatis-Plus MySQL。这个组合在毕业设计里几乎是“标准答案”原因有三一是技术栈成熟校园答疑资源和网上教程都很多。二是Spring Boot的自动配置能省掉大量开发时间比如内嵌Tomcat、起步依赖、统一异常处理都写得很顺手。三是MySQL的生态和可视化工具Navicat、DataGrip都很成熟写SQL、做ER图、导出数据都方便。如果对Java不熟可以选FlaskPython或ExpressNode.js写起来更轻快但答辩时老师可能会追问为什么不用Java所以如果不是对Python特别自信建议老老实实上Spring Boot。数据库层面我把核心表拆成八张用户表、菜品表、口味标签表、菜品标签关联表、用户偏好表、订单表、订单详情表、用户反馈表。还有一张比较关键的用户行为日志表用于记录浏览、点击、下单等行为供偏好计算使用。日志表可能增长很快毕设不追求高并发但设计时要加索引否则本地数据量一大查询就会变慢。2.3 推荐算法三条路线从好讲到好用按战斗力选口味偏好推荐算法我总结了三条路线难度递增答辩“谈资”也递增路线一基于标签的加权评分适合大多数人核心逻辑是维护一张用户-口味标签权重表。用户每完成一次订单就对订单中的菜品标签进行权重累加。推荐时遍历在售菜品计算每个菜品的加权得分去掉用户不喜欢的标签比如用户标签中有“香菜”且权重为负再引入随机因子。路线二协同过滤ItemCF / UserCF如果菜品数量不大几百个以内用Item-Based协同过滤很合适计算菜品之间的相似度矩阵然后根据用户历史评分过的菜品推荐相似度高但没吃过的菜品。优点是可以发现用户“意想不到但会喜欢”的菜缺点是需要一定冷启动数据和相似度矩阵计算时间。路线三混合推荐最出彩但工作量最大把路线一的标签加权作为基础得分路线二的协同过滤作为相似加分最后再加一个声望/库存因子——比如招牌菜、时令菜、库存多的菜额外加分。混合推荐的公式写出来放到论文里一下就和其他人拉开差距。推荐结果产生后再叠加上盲盒的随机扰动。我最终实现的是“标签加权 随机扰动”的组合既保证了“盲盒”的惊喜感又确保了推荐结果不会跑偏。三条路线的对比贴在这里给大家参考路线核心逻辑优势劣势推荐度标签加权用户标签权重 x 菜品标签可解释性强实现简单依赖标签质量高毕设最稳协同过滤用户/菜品相似度能发现潜在喜好冷启动难中加分空间大混合推荐加权相似度库存因子效果最好论文素材多工作量最大较高时间充裕可选3. 核心功能模块与关键实现3.1 菜品管理与口味标签体系把一道菜变成一组向量菜品表本身很简单id、名称、价格、图片、描述、分类主食/小吃/饮品、库存、状态上架/下架、销量。但要让“口味偏好”发挥作用必须给菜品打标签。标签我建议单独建表而不是在菜品表里加一个字段。因为一道菜是多个口味的集合比如“麻婆豆腐”同时有“辣”“麻”“重口”“下饭”四个标签。用多对多关联表一个标签也可以被多道菜共享。标签词典的建立要从菜品本身出发。我建议先用Excel把每道菜的属性拆一遍然后合并同义词比如“微辣”和“香辣”保留一个还是都保留取决于你后续推荐逻辑的粒度。粒度太细会出现标签稀疏粒度太粗用户偏好无法区分。最后控制在20到30个标签比较舒服。我实际用的标签集合大概是这样的辣、微辣、麻辣、酸辣、甜、酸、咸、清淡、重口、香辣、蒜香、葱香、香脆、软糯、爽口、下饭、汤类、蒸、炒、炸、烤、凉拌、素、荤、海鲜、主食。每个菜品至少打3到5个标签。菜品在后台管理端维护管理员可以一键给新菜品勾选标签。3.2 用户偏好采集与数据表设计别让日志变成死数据用户口味偏好采集我从两个入口入手首次登录偏好设置页列出“辣/甜/清淡/酸/麻/香脆/软糯”等核心标签用户每次最多选5个并设置“禁忌口味”如不吃香菜、不吃折耳根这个数据直接写入用户偏好表。二次行为数据每次用户浏览菜品超过5秒、点击“再来一份”、下单、对盲盒点“不喜欢”都写入用户行为日志表。CREATE TABLE t_user_behavior_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, dish_id BIGINT NOT NULL, behavior_type VARCHAR(20) COMMENT browse/click/order/dislike, create_time DATETIME NOT NULL, INDEX idx_user_time (user_id, create_time) ) COMMENT 用户行为日志表;日志表的数据不能只存不读。我写了一个定时任务每天晚上批量汇总当天的行为日志按照行为类型加权下单权重3点击权重1浏览权重0.5不喜欢权重-5更新用户偏好表中对应标签的权重值。这样偏好模型会随着订单量增长越来越准确答辩时也能说“系统具备用户行为自学习能力”。偏好表中每个用户一行存储的是用户各个口味标签的权重值。为了方便查询我用了一个单独的表来存“用户-标签-权重”的三元组而不是拼成一长串字段。这样后面如果要扩展新的标签不需要改表结构加一行就行。3.3 盲盒推荐引擎加权打分加随机扰动这是整个系统最核心的环节。把它拆成三个步骤候选集过滤、得分计算、加权随机抽取。public BlindBoxResult drawBlindBox(Long userId, BlindBoxType type) { // 1. 候选集在售、库存0、且不属于用户禁忌口味的菜品 ListDish candidates dishMapper.listOnSaleDishes(); candidates.removeIf(dish - dish.hasAnyTag(userPreferenceMapper.getAvoidTags(userId))); // 2. 计算每个菜品的混合得分 ListDishScore scoreList new ArrayList(); for (Dish dish : candidates) { double baseScore preferenceService.calculatePreferenceScore(userId, dish.getId()); // 库存因子库存多的菜加分帮助餐厅去库存 double inventoryFactor 1.0 (dish.getStock() / 100.0); // 随机扰动控制在0.7~1.3之间保留盲盒惊喜感 double randomFactor 0.7 ThreadLocalRandom.current().nextDouble() * 0.6; double finalScore baseScore * inventoryFactor * randomFactor; scoreList.add(new DishScore(dish, finalScore)); } // 3. 按得分加权随机避免每次都是同一种“最推荐”的菜 return weightedRandomPick(scoreList); }注意随机扰动要不要用、用多大这个参数我调试了很多次。扰动太小推荐结果太固定用户觉得没意思扰动太大容易把偏好权重压过用户觉得不靠谱。最后我选择0.7到1.3这个区间既能保证高分菜品大概率被抽中又给了低分但符合条件的菜品一点出场机会。加权随机抽取的另一个细节是如果用户偏好标签很少所有菜的得分都差不多那么随机扰动就成了主导盲盒开出来就接近纯随机。这种情况其实是合理的因为数据不足时本来就应该给用户更多探索空间等用户积累订单后再慢慢收敛推荐方向。3.4 订单流程与盲盒状态机从下单到出餐的完整闭环用户选择盲盒类型后进入订单创建流程。这里有一个很有意思的产品决策开盒结果在支付前展示还是支付后展示我做的是支付前展示。理由小程序里做虚拟开盒相对灵活用户在支付前看到菜品组合如果不满意可以放弃如果支付后才展示用户会觉得这是“强买强卖”对小程序体验伤害很大。但你要注意毕设如果做的是模拟支付两边都行如果要对接真实微信支付那么“先开盒再支付”会涉及支付金额与订单内容不一致的问题需要额外的对账逻辑复杂度会高很多。稳妥起见建议使用模拟支付然后在论文里写明“生产环境可对接微信支付V3接口”。订单表设计上我加了一个字段blind_box_type用来标记这单来自哪种盲盒订单详情表存盲盒开出的具体菜品和数量。订单状态简化成五档状态含义触发条件待支付已生成盲盒订单但未支付用户点击购买已支付支付完成支付回调/模拟支付成功制作中后厨接单商家后台确认已完成送达/自取完成用户确认收货已取消订单关闭用户取消/超时未支付小程序端用状态机驱动页面切换后端用枚举类型加状态流转校验避免非法跳转。比如“已支付”不能直接跳到“已完成”必须经过“制作中”。这个状态机逻辑写清楚答辩时又是一个可以讲的点。4. 实操过程与踩坑记录4.1 数据库设计的几个隐藏坑第一坑用户偏好表别设计成“一个字段存所有标签”。有人想把用户的所有口味标签拼成一个字符串存进user_preference表写起来是方便了但后续每次要更新一个标签权重都得把整个字符串暴力解析一遍。正确做法是拆成多行一行一个标签权重。第二坑菜品库存字段的更新时机。盲盒开出的菜品要扣库存并且要放在同一事务里否则会出现并发下重复推荐同一个菜导致超卖。虽然毕设并发量不大但事务和乐观锁这些点写进文档里是加分项。第三坑订单详情没存菜品快照。如果菜品信息后续修改了价格用户的订单详情也会跟着变这在财务上是不允许的。订单详情表里要冗余一份下单时的菜品名称、单价、图片这样历史订单才不会被菜品主数据的变化“污染”。4.2 小程序端体验细节顶部导航、加载态、图片优化微信小程序顶部导航栏高度是个老生常谈的坑。不同机型胶囊按钮右上角的胶囊菜单高度不一样自定义导航栏时如果写死高度在部分安卓机上就会顶到胶囊下方。我最后选择优先使用微信提供的navigationStyle: custom配合getMenuButtonBoundingClientRect()动态计算导航栏高度这样iPhone和安卓都能适配。刘海屏和灵动岛这类特殊情况用安全区域safeArea判断。图片加载对点餐体验影响巨大。菜品图不能直接加载远程原图我做了两件事一是后端接口返回图片时拼接缩略图参数二是在小程序的image组件上开启lazy-load。实测一个列表页从加载6张大图优化成加载6张缩略图首屏时间大概提升了一倍。另外盲盒开盒动画的效果也很重要。我用了一个简单的CSS旋转透明度渐变动画配合“开盒中”的加载状态。动画别做太长否则用户会不耐烦。如果你是做毕设这里不需要太复杂的动画库原生CSS动画完全够用。4.3 前后端联调避坑统一拦截器、Token、异常提示小程序端的请求封装一定要做统一拦截器。我在utils/request.js里封装了wx.request统一处理三个问题请求头自动携带Token、响应码401时自动跳转登录页、接口异常时统一弹出Toast提示。没有这个拦截器你会发现每个页面都在重复写wx.request的 success 和 fail 回调代码量大不说调错还难查。Token这里说个容易踩的坑微信小程序登录拿的是wx.login返回的 code后端要拿 code 换取 openid 和 session_key然后再签发自己的 token 给前端。很多同学直接在前端就拿着 code 当登录凭证后端也没校验这在毕设里能跑通但会被老师一句话问住。正确做法是后端统一处理 code2session并且把 token 的过期时间和续期逻辑也写清楚。接口层的坑主要在错误码设计。不要只返回success/fail两个状态建议用标准的三段式code、message、data。业务错误码要跟 HTTP 状态码区分开比如“库存不足”是业务码 1001“用户未登录”是业务码 4010。这样前端拦截器才能精准提示而不是所有错误都弹“网络异常”。4.4 推荐接口性能优化缓存和提前计算盲盒推荐接口涉及候选菜品遍历、每个菜品标签查询、用户偏好权重查询。如果每次请求都实时查数据库在小数据量下没问题但本地测试数据一大接口响应时间会明显上升。我做了两个优化第一菜品和标签的关联关系在菜点上架时不怎么变完全可以用Redis缓存key 为dish:id:tags失效时间设为24小时。第二用户偏好权重可以上午预计算后放入内存每次用户下单后再增量更新。实时计算留给真正的动态行为比如用户刚点了一个“不喜欢”要立刻把它从候选集中移除。实测优化前盲盒接口平均耗时800毫秒优化后降到200毫秒以内。虽然这个数值在小程序端看差距不算大但答辩的时候这段优化记录很有说服力。5. 常见问题排查与避坑清单5.1 小程序审核被拒的几类高频原因如果毕设小程序想上线体验容易在微信审核环节被卡。我在提审时遇到最多的三类类目不符点餐类小程序需要选择“餐饮服务”类目如果选错会被拒支付方式没有接入微信支付的情况下页面不能有“微信支付”字样否则需要补充支付资质隐私协议涉及用户偏好数据收集必须在小程序后台配置用户隐私保护指引否则调用open-type时会提示api scope is not declared in the privacy agreement这个问题在我当时的版本里反复出现。解决方案是提前在开发者平台的“隐私协议配置”里声明用到的接口类型同时在设置页面展示隐私政策入口。不要等审核被拒再改那个流程很费时间。5.2 盲盒逻辑的边界情况处理盲盒逻辑要处理很多边界状态我从实际测试中整理了几条库存不足时怎么办候选集筛掉库存为0的菜但如果用户预算范围内的菜品全被筛掉要回退到“不筛库存只警告”策略用户设置太多禁忌标签可能导致候选集很小甚至为空。对策是设计禁忌标签的数量上限比如最多5个重复推荐同一用户连续两个盲盒开出同一道菜体验会很差。我加了一个简单策略最近N次盲盒中出现过的菜品权重乘0.5降低再次被抽中的概率。这些边界情况建议写成一个BlindBoxRule规则类把候选集过滤、禁忌排除、重复降低、随机扰动分开成独立规则方法。这样写的好处是后续调参或者加规则不需要动主流程代码只在规则列表里增删即可。5.3 数据稀疏与冷启动问题新用户没有任何行为数据推荐算法等于盲猜。我设计了冷启动方案新用户注册时会让选择感兴趣的标签用这个标签集合作为初始偏好如果连标签都不选就默认按全站热门菜品随机因子来抽等用户有了一两笔订单后再逐步用行为数据修正权重。这个方案简单可用而且“冷启动”三个字写进论文里是很专业的表达。另外在首次开盲盒时我刻意调大了随机因子范围从0.7~1.3调到0.3~1.7目的是让新用户多尝试不同菜品尽快积累行为数据。等用户订单满3单后再恢复正常的扰动区间。这个细节也是可以写进文档的“动态参数调整策略”。5.4 毕设答辩的几个加分点和小细节答辩时不需要堆砌高深概念把你每一步的“为什么”自圆其说最关键。我当时准备了三块一是盲盒推荐算法的数学表达把线性加权公式和随机扰动范围写在PPT里一眼就能让评委看出你有设计感。二是数据库ER图的完整性字段关系、主外键、冗余字段都要标注清楚这是评委会仔细看的部分。三是演示流程的稳定性——现场第一次开盲盒要快速出结果不要让老师等着转圈。提前把要演示的账号、菜品数据准备好网络环境也要测试。还有一个细节尽量给管理端做一个简单的数据看板比如今日订单量、热门菜品Top5、用户偏好标签分布。这会让人感觉你的系统完整度更高而不是只做了用户端小程序。最后补充一个实操心得写LW文档的时候别把技术选型和功能模块写得像流水账。我当时的做法是每一章开头先讲“这个模块解决了什么问题”再讲“怎么实现的”。这样的逻辑老师看着不累答辩时你照着文档讲也不容易卡壳。做这个项目代码量其实不算大真正的难点在于把“盲盒”和“口味偏好”这两个概念结合起来让逻辑自洽。整个过程我反复调整了几次随机因子的大小才在“惊喜”和“靠谱”之间找到平衡。如果让我重做一次我可能会在多人盲盒的算法上投入更多比如做荤素搭配和营养均衡约束这个方向如果展开论文的层次还能再上一个台阶。这套系统后续还能扩展“每日盲盒挑战”“好友拼单盲盒”这类社交玩法技术底子已经打好往上加功能不会太费劲。希望这篇复盘能给你省下一些踩坑的时间。
返回列表