尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
协同过滤推荐系统实战:SpringBoot3+Vue3+MyBatis全栈实现
1. 系统全景这套商品推荐系统到底做了什么先说结论这是一个非常典型的电商场景实战项目用 SpringBoot 3 Vue3 MyBatis 搭了一套前后端分离的商品推荐系统核心亮点是内置了协同过滤推荐算法能够根据用户的历史行为浏览、收藏、加购、下单、评分生成个性化的商品推荐列表。说白了就是你在淘宝、京东上看到的猜你喜欢那一块。适合谁来参考我建议这么定位如果你正在做毕业设计、想找一个能写在简历上的 Java 全栈实战项目或者你是刚入门的后端开发想搞清楚推荐系统到底是怎么在我的业务里跑起来的这套东西非常对口。它不是那种只停留在理论层面的 demo而是从 MySQL 表设计、后端接口、算法引擎到前端页面全部打通的可运行项目。我当时拿到这套代码的第一反应是终于见到一个不是只写了登录注册的项目了。它把一个完整的业务闭环给你做出来了——用户能浏览商品、产生行为数据、行为数据进入算法层、算法产出推荐结果、推荐结果通过接口吐给前端、前端再渲染成为你推荐的列表。这个闭环本身就是推荐系统里最核心的东西很多商业项目往里砸钱优化换的也无非是推荐效果的精度和实时性。再说说技术选型。SpringBoot 负责提供 RESTful 接口Vue3 负责渲染页面和交互MyBatis 负责操作 MySQL 数据前后端分离部署这组合本身没啥新鲜的但胜在扎实。真正让这套系统区别于普通 CRUD 项目的是它把协同过滤算法真正落到了业务代码里而不是嘴上说说。从这个角度看这个项目的参考价值不只是代码能跑而是让你完整走一遍算法模型 工程落地 产品呈现的全流程。这个经验比单纯刷几道算法题值钱得多。2. 协同过滤推荐引擎算法不只是调库关键是思路2.1 基于用户的协同过滤UserCF是怎么工作的协同过滤的基本假设一句话相似的人会喜欢相似的东西。所以在实现上它天然分成两条技术路线基于用户的 UserCF 和基于物品的 ItemCF。先讲 UserCF。它的核心步骤是这样的根据用户的历史行为数据构建用户-物品评分矩阵。纵向是用户横向是商品交叉点是这个用户对商品的行为强度比如浏览记1分、加入购物车记2分、购买记3分、评分用真实评分。计算用户之间的相似度找出和目标用户最像的 K 个邻居。把这些邻居有行为、但目标用户没有行为的商品按评分预测值从高到低排序取 Top-N 推荐给用户。这里最关键的环节就是相似度计算。常用的方案有三种余弦相似度、皮尔逊相关系数、杰卡德相似系数。电商场景里我给的建议是优先用余弦相似度因为实际评分数据往往是稀疏的大量用户只有少量行为而余弦相似度对稀疏数据的容忍度相对好一些。余弦相似度的计算逻辑是把两个用户各自的评分向量做内积再除以两个向量模长的乘积。公式写出来是这样sim(u, v) (u · v) / (|u| × |v|)举个具体例子。用户 A 对商品 1、2、3 分别评了 5、3、4 分用户 B 对商品 1、2、4 分别评了 4、2、5 分。那 A 的向量就是 [5, 3, 4, 0]B 的向量是 [4, 2, 0, 5]两个向量的点积是 5×4 3×2 4×0 0×5 26A 的模长是 sqrt(5²3²4²) ≈ 7.07B 的模长是 sqrt(4²2²0²5²) ≈ 6.71余弦相似度就是 26 / (7.07 × 6.71) ≈ 0.55。相似度越接近 1说明两个人品味越接近。然后你用这个相似度作为权重预测目标用户对某个未见商品的评分权重和评分加权求平均就得到一个预测得分。把所有候选商品按得分排序取前面几个输出这就是完整的 UserCF 流程。2.2 基于物品的协同过滤ItemCF为什么更适合电商ItemCF 的逻辑反过来喜欢商品 A 的用户往往也会喜欢和 A 相似的商品。它先通过所有用户的行为构建商品之间的相似度矩阵比如买了手机的人通常也买了充电器那手机和充电器之间的相似度就很高。电商场景里我强烈建议用 ItemCF原因很现实用户数量通常远大于商品数量用户相似度矩阵存储和计算开销巨大商品数量相对可控。用户兴趣变化快UserCF 推荐结果对用户实时行为敏感度不够ItemCF 只要有用户最新的行为就能立刻推荐相关的商品。电商推荐更讲究看了又看买了又买的关联推荐ItemCF 的结果天然符合这个逻辑。当然有一种情况你得用 UserCF比如做资讯推荐、社区内容推荐用户群体小且兴趣标签相对稳定UserCF 能挖掘出跨品类的潜在兴趣。2.3 评分数据的标准化处理协同过滤算法能不能跑出效果数据是关键。原始日志里你拿到的往往不是分数而是行为类型所以第一步要定义行为权重表。这是我项目里用的一套常见映射行为类型权重值说明浏览1弱意向权重最低收藏2表达了兴趣加入购物车3有明显购买意向提交订单4接近成交完成支付5强意向权重最高这样处理的原因很朴素加购的人比只看不买的人目标更明确支付成功的人欲望最强烈。用这种梯度权重去构建评分矩阵推荐出来的结果才不会推荐文章里那些只逛不买的噪音数据。2.4 冷启动问题怎么破冷启动是推荐系统绕不过去的坎。新用户进来没有任何行为数据算法算不出任何东西新商品上架没有任何用户碰过它它也不会出现在任何推荐列表里。我的做法是加一层兜底策略新用户直接给他推热门商品榜这个榜单就是全站商品的评分总数降序排列简单粗暴但有效新商品则在推荐结果里按时间衰减因子给一定的曝光权重让它有机会被看到攒出第一批行为数据。这套项目里我建议直接在代码里预留一个热门推荐接口冷启动用户走后端默认分流。用户产生 5 条以上行为后再切到协同过滤推荐效果会好很多。3. 系统架构与核心模块实现细节3.1 前后端分离的结构到底怎么拆这套系统的项目结构我拆开来看大概是这样的后端 SpringBoot 工程backend ├── controller -- REST 接口层 ├── service -- 业务逻辑层 ├── mapper -- MyBatis 数据访问层 ├── model -- 实体类与 DTO ├── recommend -- 协同过滤算法核心包 └── config -- 跨域、全局异常等配置前端 Vue3 工程frontend ├── api -- axios 接口封装 ├── views -- 页面组件 ├── router -- 路由配置 ├── store -- 状态管理Pinia └── components -- 通用UI组件按照这个结构组织前端每个页面只通过 axios 调后端接口拿数据后端只负责业务逻辑返回值谁都不越界。这也意味着你如果想把前端换成小程序、把后端接口接给 App都是无缝切换的这正是前后端分离的核心价值。3.2 数据库表设计推荐系统的基础设施MySQL 里到底要建哪些表我按实际需求给你一张表清单参考这套设计是完全可以跑的表名核心字段作用userid, username, password, nickname用户基础信息productid, name, category, price, cover, description商品基础信息user_behaviorid, user_id, product_id, behavior_type, create_time核心行为日志表推荐算法原材料user_ratingid, user_id, product_id, rating, create_time用户显式评分表recommend_resultid, user_id, product_id, score, create_time推荐结果缓存表order_infoid, user_id, order_no, total_amount订单主表order_itemid, order_id, product_id, quantity, price订单明细表行为表是绝对核心算法全靠它喂数据。所以设计上有一个很重要的原则行为数据一旦落库就不能改、不能删要当成日志表来维护。商品表和用户表有更新但行为表只追加不更新。我把 user_behavior 表设置了按月分区的策略避免单表数据膨胀后查询越来越慢。recommend_result 表也很关键它就是推荐结果的缓存层。协同过滤算法是离线批量算的总不能用户每次刷新页面都现算一遍那数据库和 CPU 都扛不住。正确的做法是定时任务每天凌晨跑一次算法把每个用户的推荐结果写入 recommend_result 表用户在线访问时直接查这张表返回性能极好。如果用户产生新行为又想实时看到推荐变化可以规避 rec_result 表改为在接口层动态融合实时计算出的候选集不过这是进阶玩法了。3.3 相似度计算的 Java 实现从公式到代码这里我直接给出可落地的核心代码片段。注意实际项目里你可以在 Java 里实现相似度计算也可以借助 Redis 的 sort set 加速但理解原始实现永远是第一步。public class CosineSimilarity { // 计算两个用户向量的余弦相似度 public static double calculate(MapInteger, Double userVectorA, MapInteger, Double userVectorB) { double dotProduct 0.0; double normA 0.0; double normB 0.0; for (Double value : userVectorA.values()) { normA Math.pow(value, 2); } for (Double value : userVectorB.values()) { normB Math.pow(value, 2); } if (normA 0.0 || normB 0.0) { return 0.0; } for (Map.EntryInteger, Double entry : userVectorA.entrySet()) { Integer productId entry.getKey(); if (userVectorB.containsKey(productId)) { dotProduct entry.getValue() * userVectorB.get(productId); } } return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); } }这里有个隐藏的细节很多人会踩坑用 Map 存向量时要补 0。如果一个用户对商品 X 没评过分你就不塞这个 key那算出来的点积其实是忽略了缺失维度的。有人觉得这是偷懒但其实对余弦相似度来说缺失维度的处理本身就是一种稀疏容忍策略效果可接受。真正的坑是你把缺失值当 0 塞进去两个都有大量缺失的用户会因为大量 0 值而拉高相似度属于纯噪音。3.4 推荐结果的生成与排序有了相似度和用户评分矩阵下一步就是预测目标用户对候选商品的评分并排序。核心伪代码逻辑如下// 基于用户相似度预测用户对物品的评分 public double predictRating(int userId, int productId, ListInteger nearestNeighbors, MapInteger, MapInteger, Double userRatingMatrix) { double weightedSum 0.0; double similaritySum 0.0; for (Integer neighborId : nearestNeighbors) { Double rating userRatingMatrix.get(neighborId).get(productId); if (rating null) { continue; } double sim CosineSimilarity.calculate( userRatingMatrix.get(userId), userRatingMatrix.get(neighborId) ); weightedSum sim * rating; similaritySum Math.abs(sim); } return similaritySum 0 ? weightedSum / similaritySum : 0.0; }这段代码的意图很清晰相似用户的意见权重更高不相似的人对推荐结果的影响趋近于零。然后再把用户没有行为过的所有商品都算一遍按预测分排序取 Top-N。但这里有一个性能隐患如果商品总量有几万条每个用户每次刷新都要全量算一遍系统就跑死了。所以项目中一般有两种策略一是离线把候选集缩小只计算用户行为商品对应的关联商品二是先查出相似度高于某个阈值比如 0.3的邻居用户再基于他们的行为集合生成候选集。实测下来Filter First、Calculate Later 的策略能把计算量降到全量的 1/10 到 1/100效果几乎不损失。4. 业务链路与接口实现推荐是怎么融进商品系统的4.1 用户行为采集接口采集接口是整个推荐系统的入口前端每个关键操作都要往这个接口打数据。这里实现了两个接口记录用户行为和查询推荐结果。行为采集接口设计如下RestController RequestMapping(/api/behavior) public class BehaviorController { PostMapping(/record) public ResultVoid record(RequestBody BehaviorDTO behavior) { // behavior 包含 userId, productId, behaviorType // 1. 写入 user_behavior 表 // 2. 同步更新 redis 中该用户的行为时间戳 // 3. 返回记录状态 } }前端埋点的触发时机包括进入商品详情页时触发浏览事件、点击收藏按钮时触发收藏事件、点击加入购物车时触发加购事件、支付成功后触发支付事件。每一个事件带的时间和用户 ID、商品 ID 就是算法的原材料。因为行为事件频率高日志表写入量会越来越大。我的一个实测经验是数据库连接池耗尽问题在这个接口上出现过不止一次。解决方法是写入行为时用异步线程池 批量刷盘策略把多条行为先攒在内存队列里积攒到一定量或一定时间再批量落库。即便中途服务重启顶多丢几条行为日志不影响推荐效果却能把接口性能提升一个档次。你要是跑这套源码强烈建议把异步写入这个优化加上。4.2 推荐查询接口与前端对接查询接口的逻辑也很简单优先从缓存表里读结果如果没有再算实时结果GetMapping(/{userId}/recommend) public ResultListProductVO getRecommendList(PathVariable Integer userId) { // 1. 查推荐结果缓存表 // 2. 如果缓存命中按商品信息 join 后返回 // 3. 如果缓存未命中属于冷启动用户走热门榜单兜底 }前端在商品列表页底部放一个猜你喜欢模块挂载时调这个接口拿到商品数据后渲染卡片。核心的一段 Vue3 逻辑长这样const recommendList ref([]) async function loadRecommend() { const res await getRecommendList(currentUser.id) recommendList.value res.data.map(item ({ ...item, priceText: ${item.price.toFixed(2)} })) } onMounted(loadRecommend)这里有个体验细节值得说前端展示推荐结果时一定要把推荐理由也展示出来比如因为你看过手机推荐你这款充电器。单纯推商品不给理由用户感知不到推荐的价值只能觉得很奇怪为什么平白无故出现这些商品。加了推荐理由后点击率和信任感都会明显提升。5. 工程落地避坑指南5.1 MyBatis 关于动态 SQL 和多表联查的经验推荐系统场景里 SQL 的特点是多表关联复杂、条件动态化。比如推荐结果要 join 商品信息、同时过滤掉已下单商品、还要按评分排序这个 SQL 如果写在注解里会非常痛苦。我建议所有复杂查询都用 XML 写。核心表 user_behavior 必须建索引而且要建联合索引(user_id, product_id)这是推荐算法最频繁的查询条件查某用户的所有行为、查某商品被哪些用户行为过。索引的重要性在数据量 50 万条行为记录后体现得尤其明显——没索引时一个相似度计算的查询能跑到秒级加了索引直接降到 50 毫秒以内。5.2 前后端分离的跨域与联调问题前后端分离项目最常见的第一个坑就是跨域。前端跑在 5173 端口后端跑在 8080 端口浏览器会拦截跨域请求。解决办法有两种后端加全局 CORS 配置或者前端 devServer 配置代理转发。我推荐用前端代理的方式因为生产环境前后端通常会部署在同一个域名下代理方式更接近真实部署形态。Vue3 Vite 下的代理配置// vite.config.js server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这样配合的话前端代码里写接口地址就直接写/api/product/list不用写完整的 localhost 地址一套代码走到生产环境也不用改前端代码只需要在 Nginx 里配好 /api 反向代理就能切换。5.3 算法层的一些优化点这套项目如果只是按基础逻辑跑起来做一个毕业设计是够的。但如果想让推荐效果更好可以做这几件小事第一计算 Top-N 推荐时把用户已购买商品从候选集里剔除不然会出现反复推荐已经买过的东西。你千万别觉得这是小事很多早期项目就栽在这上面用户都下单了还天天推荐同款体验非常差。第二加入时间衰减因子。用户上周的行为和三个月前的行为价值不应该一样。给每条行为乘一个 exp(-Δd) 的衰减权重最近的兴趣权重更高推荐结果会显得聪明很多。这套逻辑代码量不大效果却非常直观。第三数据量到了一定规模之后把计算从 Java 代码里搬出来。比如用 Redis 管理用户与用户的相似度矩阵用 xxl-job 定时把离线算好的结果推给 MySQL 的 recommend_result 表。这样既不让算法计算阻塞在线请求又能在用户请求推荐时保持毫秒级响应。选 Redis 的原因是它的排序数据结构天然适合存 Top-K 相似度和 Top-N 推荐列表。6. 常见问题与排查经验速查我把自己在这套系统调试中踩过的坑整理成一个速查表方便你直接对应排查。现象可能原因排查思路与解法系统启动后无法登录数据库初始化未完成检查 SQL 脚本是否执行过 user 表初始数据前端页面请求都是 401Token 校验或跨域问题看后端控制台是否有 CORS 相关异常确认代理是否生效推荐接口返回空列表目标用户行为数据不足先去 user_behavior 表查该用户是否有记录如果少于 5 条会走冷启动兜底相似度计算结果全为 0评分矩阵构建时 key 没对齐检查 user_rating 表是否能正常关联到 product_id接口很慢行为表没建索引给 user_behavior 表补联合索引实测效果极明显推荐结果里有已购商品过滤逻辑缺失在 SQL 里 join 订单明细表用 not exists 排除还有一个常见的理解性误区要说明协同过滤算出来的相似度不代表真实语义上的相似。比如啤酒和尿布在很多年轻爸爸的购物行为里同时出现算法判断它们相似度极高但这不代表工业设计上它们长得像。这是行为模式的相似不是内容属性的相似。你要是拿算法输出去做商品文案推荐就会闹笑话。理解这层区别才能正确使用协同过滤的结果。7. 一点实操体会这个项目跑通一遍之后最大的感受是推荐系统本质上不是一个算法问题而是一个数据工程问题。算法本身在经典教材里讲得很透论文也就那几篇但真正让你头疼的是数据埋点准不准、行为权重合不合理、存储查询扛不扛得住。我之前见过很多推荐系统项目算法代码写得光鲜亮丽结果一看埋点前端两个页面压根没接行为上报所有数据全靠 SQL 脚本造那这个系统本质上就是个 demo。所以我在实际做这套系统的时候反而把更多精力放在了行为采集链路的完整度上。先保证用户的每一次浏览、加购、下单都能真实地落到库里再去谈推荐效果。你把这个观念带到一个工作环境里会少走非常多弯路。最后再分享一个小技巧你在测试推荐效果的时候不要只用管理员账号测。管理员账号一般只会浏览几个固定商品数据量太少压根看不出效果。建议你先注册三五个普通账号每个账号刻意制造不同的行为偏好比如一个只看手机数码、一个只看家居生活然后看这几个账号收到的推荐列表差异大不大。如果差异大说明算法生效了如果推荐结果趋同多半是相似度计算或评分构建环节有问题。这个验证方法我每次做完推荐系统必用效率极高。
RELATED

相关推荐

基于YOLOv8的口罩检测系统:数据集、模型训练到UI部署全流程

基于YOLOv8的口罩检测系统:数据集、模型训练到UI部署全流程

这两年口罩检测项目的实际需求一直没断过,从园区门禁到课堂考勤,从工地安全到公共区域巡查,都需要一个能实时识别人员口罩佩戴情况的系统。网上类似的“基于YOLOv8的口罩检测系统”项目标题很多,但真正把数据集构建、模型训练、UI…

📅 2026/10/21 3:25:51
QT Socket 单程序双角色:客户端与服务器集成实战

QT Socket 单程序双角色:客户端与服务器集成实战

简介:这是一份面向Qt网络编程初学者与进阶开发者的TCP套接字实战项目源码,将客户端与服务器端功能集成于同一GUI程序中,帮助读者理解面向连接的可靠传输机制在桌面端的落地方式。资源包共13个文件,包含4个cpp源文件、3个ui界面文件…

📅 2026/10/21 3:25:51
C#读取PI系统实时数据:Web API与SDK实战指南

C#读取PI系统实时数据:Web API与SDK实战指南

我做过不少工业数据接入的活儿,发现很多刚接触这一块的人,一听到“PI系统”就觉得是个高不可攀的庞然大物,尤其是要用C#去对接实时数据时,更是无从下手。有些人翻遍官方文档,结果被一堆SDK概念砸得头晕;有些…

📅 2026/10/21 3:25:51
MORE NEWS

更多资讯

📰

AI写论文实战:千笔AI写作全流程测评与避坑指南

最近私信里被问得最多的一个词,就是“AI写论文”。打开任何学生聚集的社区,都能看到有人安利各种工具,其中“千笔AI写作”这个名字反复出现,评论区清一色的“收藏了”“真的好用”。我花了两周时间,拿真实选题跑了完整…

📰

构建个人技能树:Python命令行工具设计与实现

年初整理简历时,我发现一个特别尴尬的问题:简历上写了七八项技能,可真要我回答“你这项技能到底到了什么程度”“它依赖哪些前置知识”“下一步该补什么”,脑子里的答案全是“大概会一点”“用过一阵子”。这种模糊在面试里很容易…

📰

Redis缓存穿透击穿雪崩实战:故障链路与有效方案

Redis缓存穿透、缓存击穿、缓存雪崩,这三个词在我看来是后端面试的标配,也是线上事故的常客。我见过太多能把三者的定义倒背如流的人,但真到告警弹出的那一刻还是会认错问题——以为是穿透,查了半天发现是击穿;雪崩已经…

📰

三维重建的步骤-1(采集的是环绕物体均匀旋转一圈的视频)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

以太网高可用方案:网络架构、IP规划与Keepalived VIP漂移实录

前几天还有个朋友问我,说他们内网系统要做高可用,是不是必须得上一台硬件负载均衡设备?我跟他讲,很多时候不用那么折腾,传统的Ethernet以太网加上合理的IP规划和VIP(虚拟IP)方案,就能…

📰

Claude Code插件市场搭建指南:从JSON目录到Git分发实践

说实话,我最初接触"Claude Code 插件市场"这个概念时,下意识以为要搭一个类似 VS Code 插件商店的网页平台,要设计推荐算法、审核后台、用户体系那一整套。真正动手做下去才发现,事情远没有那么复杂,但也没有…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬