30款热门AI模型一站整合DeepSeek/GLM/Qwen 随心用限时 5 折。 点击领海量免费额度你肯定见过这样的场景打开一个电商网站首页推荐的商品恰好是你最近想买的或者刷短视频时系统推送的内容总能对上你的口味。这背后推荐系统功不可没。对于开发者而言理解并亲手搭建一个推荐系统是深入理解现代Web应用数据驱动逻辑的绝佳实践。今天我们就来聊聊如何基于SpringBoot和协同过滤算法从零构建一个商品推荐系统。很多人一听到“推荐系统”、“协同过滤”就觉得这是大厂算法工程师的专属领域门槛极高。实际上一个用于学习和理解核心原理的推荐系统其骨架远比想象中清晰。它本质上是一个数据驱动的Web应用收集用户行为计算相似度生成推荐列表并通过友好的界面展示出来。难点不在于算法本身有多深奥而在于如何将算法逻辑、数据存储、业务服务和前端展示有机地串联成一个稳定、可维护的工程系统。这正是SpringBoot这类框架大显身手的地方。这个项目将带你走完从数据表设计、算法核心实现到前后端联调的完整链路。你会发现一个推荐系统的核心价值不在于使用了多么前沿的算法而在于它能否将“用户-商品”的交互数据转化为一套稳定、可解释、可迭代的推荐流水线。我们将重点关注如何用工程化的思维把教科书上的协同过滤算法落地成一个可以实际运行、观察效果的SpringBoot应用。1. 先想清楚我们要构建一个什么样的推荐系统在动手写第一行代码之前我们必须明确系统的边界和目标。一个完整的商品推荐系统涉及召回、排序、重排等多个复杂阶段但对于学习和毕业设计级别的项目我们需要一个最小可行产品MVP。这个MVP的核心是验证协同过滤算法能否基于有限的数据产生合理的推荐结果。1.1 核心功能定义从数据到推荐我们的系统需要完成以下几个关键动作数据收集与存储记录用户对商品的行为如浏览、购买、评分。这是所有推荐算法的“燃料”。相似度计算这是协同过滤的心脏。无论是“用户相似”还是“商品相似”都需要一个数学度量如余弦相似度、皮尔逊相关系数来计算。推荐生成基于相似度为目标用户找出其相似用户喜欢的、但目标用户未曾接触过的商品。结果展示与反馈将推荐列表展示给用户并能够收集用户对推荐结果的反馈如点击、忽略形成数据闭环。基于这个链条我们的系统至少需要三个核心数据实体用户、商品、用户-商品交互行为。这直接决定了数据库表的设计。1.2 技术选型背后的逻辑为什么是SpringBoot MySQL输入材料中提到了SpringBoot、MySQL、MyBatis等技术栈这是一个非常经典且合理的Java Web后端选型。SpringBoot它极大地简化了Spring应用的初始搭建和开发过程。通过自动配置和起步依赖我们可以快速集成Web服务、数据库连接、事务管理等组件让我们能更专注于业务逻辑即推荐算法的实现而非繁琐的配置。MySQL作为关系型数据库它擅长存储结构化的用户信息、商品信息和行为记录。对于学习型项目数据量通常在万级以内MySQL完全能够胜任。它的稳定性和广泛的社区支持也是重要考量。MyBatis它是一个优秀的持久层框架通过XML或注解配置将Java方法调用与SQL语句灵活地映射起来。在推荐系统中我们经常需要执行一些复杂的查询例如查询某个用户的所有行为、查询与某商品相似的商品列表MyBatis能提供比JPA更直观、更可控的SQL编写方式。协同过滤算法我们将实现最基础的两种——基于用户的协同过滤UserCF和基于商品的协同过滤ItemCF。UserCF的核心是“兴趣相投的用户喜欢的东西你也可能喜欢”ItemCF的核心是“喜欢这个商品的用户也喜欢那些商品”。我们会看到在数据稀疏的情况下两者的表现和适用场景有所不同。明确了“做什么”和“用什么做”接下来就是搭建系统的骨架。2. 搭建系统骨架数据库设计与SpringBoot项目初始化工程化项目的第一步永远是设计数据模型和搭建项目基础结构。这一步走稳了后续的业务开发和算法集成才会顺畅。2.1 数据库表设计为推荐算法准备“数据粮仓”根据核心功能我们设计三张核心表这与输入材料中的思路一致但我们需要理解每个字段设计的意图用户表 (user_info)这张表存储用户的基本信息。除了登录验证所需的字段用户名、密码、邮箱user_avatar头像用于前端展示last_login_time和user_status可用于简单的用户活跃度判断或冷启动策略的优化。商品表 (product_info)存储商品的基本属性。product_category是至关重要的字段在协同过滤效果不佳时如冷启动可以基于商品类别进行简单的热门推荐或分类推荐作为算法推荐的补充或降级方案。用户行为表 (user_behavior)这是推荐系统的核心数据源。设计时需要特别注意behavior_type用数字编码区分行为类型例如1-浏览2-收藏3-购买4-评分。不同行为对用户兴趣的贡献权重不同通常购买 收藏 浏览在计算用户对商品的“兴趣分数”时可以引入权重因子。behavior_score如果系统支持显式评分1-5分这个字段就存储评分值。如果不支持则需要通过behavior_type和behavior_time等隐式反馈来构造一个“隐式评分”。关键点在实际计算前我们需要将原始行为数据转换为一个“用户-商品评分矩阵”。矩阵的行是用户列是商品矩阵中的值就是评分显式评分或隐式计算的兴趣度。这个矩阵通常是极度稀疏的——大部分用户只接触过极少部分的商品。-- 示例创建用户行为表 CREATE TABLE user_behavior ( behavior_id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL COMMENT 关联用户ID, product_id bigint NOT NULL COMMENT 关联商品ID, behavior_type tinyint NOT NULL COMMENT 1-浏览2-购买3-评分, behavior_score int DEFAULT NULL COMMENT 评分值1-5分, behavior_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 行为发生时间, PRIMARY KEY (behavior_id), KEY idx_user_id (user_id), KEY idx_product_id (product_id), KEY idx_user_product (user_id,product_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户行为记录表;建立合适的索引如idx_user_id,idx_product_id,idx_user_product对于提高行为数据查询效率至关重要。2.2 SpringBoot项目初始化用Maven构建项目基石使用Spring Initializr或IDE内置的创建工具初始化项目选择必要的依赖Spring Web提供RESTful API支持。Spring Data JPA / MyBatis Framework这里我们选择MyBatis以更灵活地控制SQL。MySQL Driver数据库连接驱动。Lombok简化实体类代码可选但推荐。项目结构大致如下recommendation-system/ ├── src/main/java/com/example/recommend/ │ ├── entity/ # 实体类对应数据库表 │ ├── mapper/ # MyBatis Mapper接口 │ ├── service/ # 业务逻辑层包含推荐算法核心实现 │ │ └── impl/ │ ├── controller/ # 控制器提供API接口 │ └── RecommendationSystemApplication.java # 启动类 ├── src/main/resources/ │ ├── mapper/ # MyBatis XML映射文件 │ ├── application.yml # 配置文件 │ └── data.sql # 初始化数据可选 └── pom.xml在application.yml中配置数据库连接和MyBatisspring: datasource: url: jdbc:mysql://localhost:3306/recommend_db?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.recommend.entity configuration: map-underscore-to-camel-case: true # 自动将下划线字段映射为驼峰属性完成这些一个具备数据持久化能力的Web服务骨架就搭建好了。接下来我们将注入灵魂——协同过滤算法。3. 注入系统灵魂协同过滤算法的工程化实现算法部分是核心但我们的目标不是追求极致的算法优化而是实现一个清晰、可运行、便于理解和调试的算法模块。我们将算法实现放在Service层。3.1 数据准备构建评分矩阵首先我们需要从数据库读取数据并将其转换为算法可用的数据结构。通常我们会在内存中构建一个MapLong, MapLong, Double即用户ID - (商品ID - 评分)。Service Slf4j public class DataPreprocessService { Autowired private UserBehaviorMapper userBehaviorMapper; /** * 构建用户-商品评分矩阵 * return Map用户ID, Map商品ID, 评分 */ public MapLong, MapLong, Double buildUserItemRatingMatrix() { ListUserBehavior behaviors userBehaviorMapper.selectAllWithScore(); // 自定义查询获取所有有评分的行为 MapLong, MapLong, Double matrix new HashMap(); for (UserBehavior behavior : behaviors) { Long userId behavior.getUserId(); Long itemId behavior.getProductId(); Double score behavior.getBehaviorScore().doubleValue(); // 假设使用显式评分 matrix.putIfAbsent(userId, new HashMap()); matrix.get(userId).put(itemId, score); } log.info(评分矩阵构建完成共{}个用户{}条评分记录, matrix.size(), behaviors.size()); return matrix; } /** * 构建商品-用户评分矩阵ItemCF用 * return Map商品ID, Map用户ID, 评分 */ public MapLong, MapLong, Double buildItemUserRatingMatrix(MapLong, MapLong, Double userItemMatrix) { MapLong, MapLong, Double itemUserMatrix new HashMap(); for (Map.EntryLong, MapLong, Double userEntry : userItemMatrix.entrySet()) { Long userId userEntry.getKey(); for (Map.EntryLong, Double itemEntry : userEntry.getValue().entrySet()) { Long itemId itemEntry.getKey(); Double score itemEntry.getValue(); itemUserMatrix.putIfAbsent(itemId, new HashMap()); itemUserMatrix.get(itemId).put(userId, score); } } return itemUserMatrix; } }注意如果只有隐式反馈浏览、购买我们需要设计一个规则将行为转化为分数例如购买5分收藏4分浏览2分。也可以考虑时间衰减近期行为权重更高。3.2 核心算法实现UserCF 与 ItemCF我们实现一个工具类来计算余弦相似度并在此基础上实现两种协同过滤。Component public class SimilarityCalculator { /** * 计算余弦相似度 * param vectorA 用户A的评分向量 (商品ID - 评分) * param vectorB 用户B的评分向量 (商品ID - 评分) * return 相似度范围[-1,1]这里通常取[0,1] */ public double cosineSimilarity(MapLong, Double vectorA, MapLong, Double vectorB) { // 找出两个用户都评价过的商品 SetLong commonItems new HashSet(vectorA.keySet()); commonItems.retainAll(vectorB.keySet()); if (commonItems.isEmpty()) { return 0.0; // 无共同评价商品相似度为0 } double dotProduct 0.0; double normA 0.0; double normB 0.0; for (Long itemId : commonItems) { double a vectorA.get(itemId); double b vectorB.get(itemId); dotProduct a * b; normA a * a; normB b * b; } // 避免除零 if (normA 0 || normB 0) { return 0.0; } return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); } }Service Slf4j public class CollaborativeFilteringService { Autowired private SimilarityCalculator similarityCalculator; Autowired private DataPreprocessService dataPreprocessService; Autowired private ProductService productService; // 用于获取商品详情 /** * 基于用户的协同过滤推荐 * param targetUserId 目标用户ID * param topN 返回推荐商品的数量 * return 推荐商品ID列表 */ public ListLong recommendByUserCF(Long targetUserId, int topN) { MapLong, MapLong, Double userItemMatrix dataPreprocessService.buildUserItemRatingMatrix(); MapLong, Double targetUserRatings userItemMatrix.get(targetUserId); if (targetUserRatings null || targetUserRatings.isEmpty()) { log.warn(目标用户{}无行为数据无法进行UserCF推荐, targetUserId); // 可在此处返回热门商品或随机商品作为冷启动方案 return productService.getHotProducts(topN); } // 1. 计算目标用户与其他所有用户的相似度 MapLong, Double userSimilarities new HashMap(); for (Long otherUserId : userItemMatrix.keySet()) { if (otherUserId.equals(targetUserId)) continue; MapLong, Double otherUserRatings userItemMatrix.get(otherUserId); double sim similarityCalculator.cosineSimilarity(targetUserRatings, otherUserRatings); if (sim 0) { // 只保留相似度为正的用户 userSimilarities.put(otherUserId, sim); } } // 2. 按相似度排序取最相似的K个用户K-Nearest Neighbors int K 20; // 近邻数量可配置 ListLong nearestNeighbors userSimilarities.entrySet().stream() .sorted(Map.Entry.Long, DoublecomparingByValue().reversed()) .limit(K) .map(Map.Entry::getKey) .collect(Collectors.toList()); // 3. 预测目标用户对未评分商品的兴趣度 MapLong, Double itemScoreMap new HashMap(); // 商品ID - 预测兴趣度 SetLong targetUserRatedItems targetUserRatings.keySet(); for (Long neighborId : nearestNeighbors) { double sim userSimilarities.get(neighborId); MapLong, Double neighborRatings userItemMatrix.get(neighborId); for (Map.EntryLong, Double entry : neighborRatings.entrySet()) { Long itemId entry.getKey(); Double rating entry.getValue(); // 只推荐目标用户未评价过的商品 if (!targetUserRatedItems.contains(itemId)) { // 累加 (邻居用户对该商品的评分 * 邻居与目标用户的相似度) itemScoreMap.put(itemId, itemScoreMap.getOrDefault(itemId, 0.0) rating * sim); } } } // 4. 按预测兴趣度排序返回TopN return itemScoreMap.entrySet().stream() .sorted(Map.Entry.Long, DoublecomparingByValue().reversed()) .limit(topN) .map(Map.Entry::getKey) .collect(Collectors.toList()); } /** * 基于商品的协同过滤推荐 * param targetUserId 目标用户ID * param topN 返回推荐商品的数量 * return 推荐商品ID列表 */ public ListLong recommendByItemCF(Long targetUserId, int topN) { // 实现逻辑类似 // 1. 构建商品-用户矩阵或直接复用转置后的矩阵 // 2. 找出目标用户评分过的商品集合A // 3. 对于集合A中的每个商品a找出与其最相似的商品集合B使用商品相似度计算 // 4. 将集合B中目标用户未评分的商品汇总并按相似度加权评分进行排序预测 // 5. 返回TopN // 代码结构与UserCF类似但计算主体从用户变为商品。 // 注意商品相似度计算同样使用余弦相似度但输入向量是“用户对该商品的评分”。 // 具体实现此处省略遵循与UserCF相同的模式。 return new ArrayList(); // 示例返回 } }3.3 算法选择与冷启动问题UserCF vs ItemCFUserCF更适合用户数相对较少、兴趣变化较快的场景如新闻推荐因为用户兴趣迁移快更依赖“人群”的实时喜好。ItemCF更适合商品数相对稳定、用户兴趣变化较慢的场景如电商、电影推荐因为商品相似度相对稳定且解释性强“因为你喜欢了A所以推荐相似的B”。在我们的商品推荐系统中ItemCF通常是更常见的选择。冷启动新用户无行为或新商品无评分无法通过协同过滤产生有效推荐。这是必须处理的工程问题。常见的解决方案包括热门推荐给新用户推荐近期最热门的商品。分类/标签推荐让新用户选择兴趣标签或根据注册信息推测推荐对应分类的商品。随机推荐作为一种简单的兜底策略。 在我们的Service中当检测到目标用户无行为数据时已经加入了返回热门商品的逻辑。算法模块准备就绪后我们需要通过API将其暴露给前端并考虑性能优化。4. 从原型到可用API设计、性能考量与前端集成一个可用的系统不仅要有正确的算法还要有合理的接口设计、一定的性能保障和友好的展示界面。4.1 设计RESTful API在Controller层我们提供清晰的API供前端调用。RestController RequestMapping(/api/recommend) Slf4j public class RecommendController { Autowired private CollaborativeFilteringService cfService; Autowired private ProductService productService; /** * 为用户生成个性化推荐 * param userId 用户ID * param algo 算法类型userCF / itemCF * param topN 推荐数量默认10 * return 推荐商品列表 */ GetMapping(/forUser) public ResultListProductVO getRecommendationsForUser( RequestParam Long userId, RequestParam(defaultValue itemCF) String algo, RequestParam(defaultValue 10) Integer topN) { ListLong recommendedProductIds; try { if (userCF.equalsIgnoreCase(algo)) { recommendedProductIds cfService.recommendByUserCF(userId, topN); } else { // 默认为itemCF recommendedProductIds cfService.recommendByItemCF(userId, topN); } } catch (Exception e) { log.error(推荐算法执行失败 userId: {}, algo: {}, userId, algo, e); // 降级策略返回热门商品 recommendedProductIds productService.getHotProductIds(topN); } // 根据商品ID列表查询商品详情并组装成前端需要的VO对象 ListProductVO productList productService.getProductDetailByIds(recommendedProductIds); return Result.success(productList); } }这里使用了Result作为统一的响应封装并加入了简单的降级策略算法失败时返回热门商品。4.2 性能优化与工程化思考直接使用上述代码在线计算推荐在用户量或商品量稍大时就会遇到严重的性能瓶颈。因为每次请求都要全量计算相似度时间复杂度很高。对于学习项目我们可以先关注逻辑正确性。但如果考虑生产环境必须引入优化策略离线计算 缓存这是最核心的优化。推荐结果的计算特别是相似度计算非常耗时不适合实时进行。离线层使用定时任务如Spring Scheduler或更专业的调度框架在夜间低峰期为所有活跃用户预计算好推荐列表并将结果用户ID - 商品ID列表存储到Redis或数据库中。在线层当用户请求推荐时API直接从Redis中读取预计算好的结果响应速度极快毫秒级。缓存更新当用户产生新的行为如购买后可以触发一个异步任务更新该用户及其相似用户的推荐列表缓存。相似度矩阵预计算UserCF中的用户相似度矩阵、ItemCF中的商品相似度矩阵相对稳定不需要每次请求都计算。可以定期如每天离线计算好整个相似度矩阵存入Redis或数据库。在线推荐时只需进行简单的查表和加权计算。算法简化与采样对于大规模数据可以使用更轻量级的相似度计算方法或者对用户/商品进行采样后再计算。数据库优化确保user_behavior表有合适的复合索引(user_id, product_id, behavior_time)以加速行为数据的查询和聚合。4.3 前端集成与展示输入材料提到了Vue.js前端。前后端分离的架构下前端通过Axios调用我们提供的/api/recommend/forUser接口获取推荐列表并以卡片、列表等形式展示商品图片、名称、价格等信息。 前端页面可以设计一个简单的下拉框让用户选择使用“UserCF”还是“ItemCF”算法来查看推荐结果这有助于直观对比两种算法的效果差异。同时页面应记录用户的点击行为并通过另一个API接口如POST /api/behavior回传到后端丰富行为数据形成闭环。5. 项目总结与进阶思考不止于跑通至此一个基于SpringBoot和协同过滤的商品推荐系统核心部分已经完成。它能跑起来能根据历史数据产生推荐。但如果你希望这个项目从“作业”升级为“作品”或者为未来的实际工作做准备还需要思考以下几个层面5.1 评估你的推荐系统一个推荐系统好不好不能只看“有没有推荐出来”需要有量化的评估。虽然离线评估在学术上更严谨但我们可以在项目中引入简单的在线评估逻辑点击率CTR在推荐位展示商品记录被点击的次数除以展示的次数。可以在数据库中为推荐结果增加exposure曝光和click点击字段。转化率用户从点击推荐商品到最终购买的比例。多样性推荐列表中的商品是否属于不同的类别避免过于同质化。 在Service层生成推荐列表后可以插入一条推荐记录到recommendation_log表记录用户、算法、推荐商品、时间戳。当用户点击或购买时更新对应记录的点击状态。定期分析这些日志就能对算法效果有个初步判断。5.2 从单一算法到混合策略单一的协同过滤算法有其局限性冷启动、稀疏性、流行度偏差。一个健壮的推荐系统通常是多种策略的混合Hybrid。加权混合同时用UserCF、ItemCF、热门推荐、分类推荐计算出一个分数按权重合并。切换混合根据用户状态切换策略。新用户用热门推荐老用户用协同过滤。特征组合在计算相似度时不仅考虑评分还可以引入商品类别、标签、用户画像等特征进行更精细的度量。 你可以尝试在项目中增加一个HybridRecommendService它内部调用多个不同的推荐器CF推荐器、热门推荐器然后按照规则融合结果。5.3 工程化与可观测性日志与监控在算法计算的关键步骤如读取数据量、相似度计算耗时、推荐列表生成打上详细的日志。使用SLF4J记录不同级别INFO, WARN, ERROR的日志便于问题排查。配置化将算法中的关键参数如近邻数K、相似度阈值、冷启动策略提取到配置文件中如application.yml这样无需修改代码就能调整系统行为。异常处理与降级正如我们在Controller中做的那样任何算法调用都应该有try-catch并准备好降级方案如返回热门商品保证接口的可用性。5.4 数据与算法的持续迭代推荐系统是一个典型的“数据驱动”和“算法驱动”结合的系统。它的效果严重依赖于数据的质量和数量。数据质量确保行为数据被正确、完整地收集。清洗异常数据如刷单产生的异常行为。反馈循环设计机制收集用户对推荐结果的负面反馈如“不感兴趣”按钮这些数据对于优化算法至关重要。A/B测试如果想严肃地优化推荐效果需要引入A/B测试框架将用户分流对比不同算法或参数下的核心指标CTR、转化率等。构建这个项目的过程其价值远不止于掌握SpringBoot和协同过滤。它是一次完整的数据应用工程化实践你经历了从业务需求分析、数据模型设计、算法逻辑实现、服务接口封装到性能考量和效果评估的全流程。这才是面对一个真实数据驱动型项目时最需要具备的系统性思维和能力。下次当你再看到“个性化推荐”这几个字时你脑海中浮现的将不再是一个黑盒而是一套清晰、可拆解、可优化的技术实现链路。 30款热门AI模型一站整合DeepSeek/GLM/Qwen 随心用限时 5 折。 点击领海量免费额度