拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

基于Spring与协同过滤的电影周边商城系统设计与实现

基于Spring与协同过滤的电影周边商城系统设计与实现

做毕业设计选型的时候,很多人一上来就纠结:到底做什么题目能既满足学校要求,又不至于把自己难死。我最后定的是这个——基于Spring + 协同过滤推荐算法的电影周边商城系统。一句话概括就是:一个能根据用户购买、浏览行为,自动推荐电影周边商品的电商平台。说白了就是让商城像导购一样记住你买过什么、喜欢什么风格,然后给你推可能想买的电影衍生品。

这个题目选得值,因为它有三个天然优势:技术栈主流,Java + Spring全家桶拿出去面试也有人认;算法部分有真东西可写,协同过滤不是拿来凑字数的花架子;业务场景直观好演示,答辩时老师一看就明白你要做什么。我做完之后把整个项目复盘了一遍,从需求拆解、数据库设计,到协同过滤的落地实现,再到答辩现场导师最爱问的几个坑,全写在这篇里。

1. 项目整体拆解:一个电影周边商城到底要做什么

1.1 需求背后隐藏的真实场景

你是不是也觉得电影周边商城听起来就是个普通购物网站?表面看确实如此,但把它当成毕业设计来要求,就要多问一层:它和别人做的电商系统差别在哪?我的判断是,核心差别不在“卖货”的流程,而在“推荐”这个智能化的增色点。

真实的电商场景里,用户进入首页时看到的东西,很大程度上决定了会不会下单。像淘宝、京东这样的平台,首页都是千人千面的个性化推荐,而这个毕设要模拟的正是这种机制:新用户进来推热门爆款,老用户回来推他可能感兴趣的品类。比如用户买过《流浪地球》的车贴和手办,系统就该给他推《三体》的周边,而不是推哈利波特的围巾——虽然都是周边,但风格差异很大,随便推会让用户觉得“这平台不懂我”。

所以这个项目从需求层面拆开来看,其实是三层:

  • 第一层是基础电商能力:商品展示、购物车、下单、个人中心,这层是毕设的基本盘,也是工作量的大头。
  • 第二层是用户行为采集:记录用户的浏览、收藏、加购、购买记录,把它们转成结构化数据,供推荐算法使用。
  • 第三层是推荐引擎:基于行为数据计算相似度,生成推荐列表,推给前端展示。

很多同学做毕设时只把注意力放在第一层,第二层随便建两张表,第三层抄一个demo就算完事。实际上第二层和第三层才是这个题目区别于“普通商城”的关键。我建议从开工第一天就把三者当成一个整体来设计。

1.2 系统角色怎么划分

这个系统可以按用户侧和管理侧来划分角色。用户侧就是正常买东西的C端顾客,能完成注册登录、浏览商品、加入购物车、下单、收藏、评价这些动作。管理侧则是运营人员或管理员,负责商品上下架、库存管理、订单处理、用户管理、推荐结果的查看。

我在设计时把角色分成三类,简单但够用:

角色核心权限说明
游客浏览商品、查看推荐不登录也能看,方便演示漏斗转化
注册用户浏览、购买、收藏、评价、获取个性化推荐推荐功能的触发主体
管理员商品管理、订单管理、用户管理、数据统计后台运营维护

为什么要单列一个游客角色?因为推荐系统有一个经典问题叫冷启动,新用户没有任何行为记录,系统必须有一套针对游客的热门推荐策略。把这个角色单独拎出来,可以清晰展示两套推荐逻辑的差异,答辩时讲起来也更有层次。

1.3 功能模块的完整清单

立项之后我列了一张功能清单,直接照着做不会漏项:

  • 用户模块:注册、登录(含验证码)、个人信息维护、密码加密存储
  • 商品模块:商品分类(按电影IP、按周边类型)、商品详情、关键词搜索、分页展示
  • 购物车模块:加购、改数量、删除、批量结算
  • 订单模块:下单、订单列表、订单状态流转(待付款、已付款、已发货、已完成)
  • 收藏模块:收藏/取消收藏、收藏列表
  • 评价模块:对已购买商品打分和留言(打分数据是协同过滤的重要输入)
  • 推荐模块:热门排行、基于物品的协同过滤推荐、基于用户的协同过滤推荐、个性化推荐列表
  • 后台管理:商品CRUD、订单管理、用户管理、推荐算法的触发配置

我特别想提醒一点:评价模块别砍掉。很多同学觉得评价可有可无,实际上评分数据对协同过滤算法来说是最理想的输入信号。购买行为是二元信号(买或不买),评分是强度信号(很喜欢、一般、不喜欢),强度信号能让相似度计算更精准。保留评价模块,推荐算法的效果才能真正体现出来。

2. 为什么是Spring + 协同过滤:选型逻辑与核心原理

2.1 跑在Spring上的理由

技术选型上我几乎没有犹豫就锁定了Java + Spring。市面上做毕设的语言很多,Python写算法确实更顺手,但是加上“企业级开发”这个评价维度,Java + Spring依然是安全牌中的安全牌。

Spring Boot解决了大量配置繁琐的问题。你用Spring写一个Web项目,以前要配一堆XML,现在一个@SpringBootApplication注解就启动内置Tomcat,这对毕设周期来说太友好了。Spring MVC负责请求分发,前端请求直接打到Controller层,再往Service、Mapper逐层传递,这套分层架构模板非常成熟,写起来不费脑子。Spring还提供AOP能力,可以做统一的日志记录和权限拦截,这个在文档里写上会让项目显得规范。

答辩或面试时,老师很可能会问“为什么用Spring而不用其他框架”。我的标准回答是三层逻辑:第一,Spring Boot的自动配置特性降低了集成成本,能让注意力集中在业务和算法上;第二,Spring的分层架构与电商业务天然契合,Controller-Service-Mapper的划分让每个环节职责清晰;第三,Spring生态内的数据校验、事务管理、异常处理组件都非常成熟,省去了重复造轮子的时间。

2.2 协同过滤:从“物以类聚”到“人以群分”

协同过滤算法的核心思想其实用一句生活谚语就能说清楚:物以类聚,人以群分。它主要分为两派。

基于用户的协同过滤(UserCF)的思路是:找到和你兴趣相似的用户,看看他们买了什么,然后推荐给你。比如用户A和用户B都买过《千与千寻》的周边,那么说明两个用户的品味有重叠,这时候用户B最近买了《龙猫》的马克杯,而用户A还没买,系统就会把这个马克杯推荐给A。核心是算人与人之间的相似度。

基于物品的协同过滤(ItemCF)的思路则是:找到和你买过的商品相似的其他商品,直接推荐给你。比如很多买钢铁侠手办的人都顺带买了美国队长盾牌,那这两件商品之间就产生了相似关联,用户只要买过其中一件,另一件就会被系统想起。核心是算商品与商品之间的相似度。

两种方法我用一张表做了对比,做完之后觉得特别有必要写成文档:

维度UserCF 基于用户ItemCF 基于物品
计算对象用户-用户相似度物品-物品相似度
适用场景用户量少、物品量大的社区物品少、用户量大的电商
实时性用户行为变化后需重算相似度物品相似度相对稳定,可离线计算
解释性推荐理由较弱可以解释为“和你买的XX相似”
冷启动难度新用户无法立刻匹配相似用户新用户只要买过一件,立刻能推相似品

电商平台里ItemCF的落地效果通常比UserCF更直观,解释性也更好——“因为你喜欢XX,所以推荐YY”这句话用户能听懂,而“因为和你相似的人也喜欢YY”这种理由容易让人困惑。但我这个系统两个都做了:给用户展示推荐结果时,优先用ItemCF的结果;如果在后台分析用户群,则用UserCF算相似用户群。答辩时两种算法都能讲,显得有深度。

2.3 相似度计算的核心公式

协同过滤的关键环节是相似度计算。不管是算用户相似度还是物品相似度,最常用的就是余弦相似度。原理其实不难,把两个实体在维度空间里对应的评分向量拿出来,算两个向量夹角的余弦值。夹角越小,余弦值越接近1,两者越相似。

公式长这样:similarity = cos(A, B) = (A向量与B向量的内积) / (A向量模长 × B向量模长)。

举一个我实际调试时的例子。假设有两部电影的周边评分数据,商品A被用户U1、U2、U3分别评过5分、3分、4分,商品B被用户U1、U2、U3分别评过4分、3分、5分。把它当成三维空间里的两个向量:A = (5, 3, 4),B = (4, 3, 5)。内积是5×4 + 3×3 + 4×5 = 20 + 9 + 20 = 49,A的模长是根号下(25+9+16)=根号50,B的模长是根号下(16+9+25)=根号50,余弦值就是49/50 = 0.98。这说明两件商品的评分模式非常接近,用户在品味上把它们归为同类,可以互相推荐。

但要注意一个陷阱:评分向量里的0代表用户没评过分,如果直接把0当成分值代入计算,会把大量没打分的行为误判成负向行为,拉低相似度。我在实现时把缺失值处理成一个很小的占位值,或者干脆只在两个用户都有评分的商品维度上做计算,这个细节很大程度上影响了推荐质量。

2.4 我为什么把ItemCF作为主力

前面提到电商场景更适合ItemCF,我再展开说说实操层面的理由。首先,商品数量远小于用户数量,而且商品是相对稳定的,今天上架的手办明天不会变,但用户行为是每天都在涨的。ItemCF可以离线把商品相似度矩阵算好,存在一张表里,用户在线的实时推荐只需查表,响应速度很快。

我在系统里做的是:每天晚上用定时任务跑一遍全量行为数据,更新商品相似度表。运行时用户访问“猜你喜欢”接口,系统根据该用户最近的行为记录,去相似度表里取TopK个关联商品,再做一次过滤和排序。这样设计的好处是线上接口开销很小,拍个脑袋说,用户首页接口的响应时间基本在30毫秒以内,比实时现算相似度快一个数量级。

UserCF我放在了后台的“用户兴趣分析”功能里,管理员可以看到某些用户群的共同偏好,这个不是为了实时推,而是为了解释推荐来源。毕设论文里可以写“双通道推荐引擎”,技术含量看着高出不少。

3. 推荐系统落地:从评分矩阵到“猜你喜欢”列表

3.1 数据层的先行工作

推荐算法再漂亮,没有数据支撑就是空中楼阁。我这个项目里,所有推荐相关的数据都围绕一个核心棋盘来组织:用户对商品的评分矩阵。行是用户,列是商品,格子里的数值代表偏好强度,可能是评分,也可能是购买次数、收藏与否的映射值。

具体来说,我在MySQL里设计了四张与推荐直接相关的表:用户评分表(评价模块写入)、用户行为日志表(浏览、加购、收藏、购买都会落日志)、商品信息表、商品相似度表(离线计算的结果存储)。行为日志表特别关键,它是推荐系统的“原料仓库”。

行为数据怎么变成评分矩阵?我做了归一化映射处理:浏览一次计1分,收藏计3分,加购计4分,完成购买计5分。如果同一个用户对同一商品产生了多种行为,取最高分,不叠加。这样处理有两个好处,一是避免刷行为导致分数虚高,二是让评分含义清晰,接口里也更好解释。

3.2 离线计算的完整步骤

我在后台管理里写了一个“重新生成推荐数据”的按钮,点击后触发一整套离线计算流程。流程分五步,每一步都有明确的输出:

第一步,拉数据。从用户评分表和行为日志表里查出最近90天的用户-商品行为记录,组合成稀疏评分矩阵。为什么要限制90天跨度?因为用户兴趣会漂移,去年喜欢星际类电影不代表今年还喜欢,时间窗口设太长会把过时兴趣当成当前偏好。

第二步,算物品相似度。对矩阵里的每一对商品,先找出共同评分的用户,再计算余弦相似度。为了减少计算量,我只保留相似度大于0.3的配对关系,低于这个阈值的直接丢弃,因为它们对推荐结果几乎没有正向贡献。

第三步,存相似度表。把算好的相似度关系写入商品相似度表,字段设计是商品ID、关联商品ID、相似度分值、生成时间。一张表搞定所有物品关联。

第四步,算推荐候选。对每个有行为记录的用户,取出他最近买过或评过分的商品集合,每件商品查相似度表,找出TopK相似商品,按“相似度 × 用户偏好分值”的加权公式算出推荐得分,再按得分降序截断到20条候选。

第五步,过滤与排序。把候选列表里用户已经买过的商品排除掉,再把商品上下架状态过滤掉,最后按得分排序,生成每个用户的推荐结果表。

这个“推荐得分 = 相似度 × 偏好分值”的加权公式是这个算法最朴素的版本,但它有个很实用的特点:用户对某商品评分越高,相似商品的得分越容易被放大。简单来说,非常喜欢的东西能带出更多相关推荐。

3.3 实时推荐接口如何实现

离线计算生成了用户的推荐结果表,但用户上线时不能直接把这20条一次性丢过去。我在接口层面做了两段式响应。

第一段是接口入参判断。用户必须处于登录状态,从Session或Token里解析出用户ID。如果是游客,就跳过个性化推荐,直接走热门商品排行接口。

第二段是查询推荐结果表,按用户ID取出前10条,再补充4条热门商品垫底。这样做的原因是推荐结果太精准会让用户舒服得过头,偶尔掺入热门商品能提升发现感。实际验证下来,带热门商品混合的列表,用户点击转化率比纯推荐列表要高出一些——这是电商运营里的常见经验:个性化与公共热点要平衡。

3.4 冷启动问题要怎么处理

冷启动是推荐系统绕不开的坎,毕设里不做这部分,答辩大概率会被盯上。冷启动分成三类:新用户没有历史行为,新商品没有评分记录,以及推荐系统刚上线时整个矩阵几乎是空的。

我的处理方案比较务实。对新用户,系统回退到热门排行策略,推荐全站销量Top10和评分Top10,等用户产生了行为再切换成个性化推荐。对新商品,上架后先给它打上“新品尝鲜”标签,在首页引导曝光,同时鼓励老用户评价,等积累了一定评分再进相似度计算。至于系统整体冷启动,我预置了一批模拟行为数据,用SQL脚本灌进去,让算法一上线就能跑起来。答辩时如果老师问“你这个系统初始没有数据怎么办”,你就可以把这套处理逻辑讲出来,这算是一个加分回答。

3.5 一个“抄作业”级别的实现片段

后端实现协同过滤时我用Java手写了一遍核心逻辑,没直接调第三方库,这一方面锻炼了基本功,另一方面也方便在论文里展开讲。提供一段最核心的余弦相似度计算代码,可以直接参考:

public double cosineSimilarity(Map<Integer, Double> vectorA, Map<Integer, Double> vectorB) { // 找出两个向量共同出现的维度 Set<Integer> commonKeys = new HashSet<>(vectorA.keySet()); commonKeys.retainAll(vectorB.keySet()); if (commonKeys.isEmpty()) { // 没有共同维度时相似度视为0 return 0.0; } double dotProduct = 0.0; // 内积 double normA = 0.0; // A的模长 double normB = 0.0; // B的模长 for (Integer key : commonKeys) { dotProduct += vectorA.get(key) * vectorB.get(key); } for (Double value : vectorA.values()) { normA += value * value; } for (Double value : vectorB.values()) { normB += value * value; } if (normA == 0.0 || normB == 0.0) { return 0.0; } return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); }

这段代码每次调用前,我会传入以用户ID为Key、评分为Value的Map,两个用户向量做好对齐。性能不算最优,但对毕设规模的数据是完全够用的。如果你想显得更专业,可以用Apache Commons Math库里的CosineSimilarity类,但手写版本更容易在论文里展开讲解。

4. 完整系统实现链路:从数据库设计到接口联调

4.1 数据库设计的核心表

电商系统的数据库设计是地基,地基本来就是两张重点表,推荐系统再加三张,整个表结构就有条理了。我的数据库里主要表结构如下:

用户表存用户ID、用户名、密码(BCrypt加密后存储)、昵称、头像、注册时间。商品表存商品ID、商品名称、所属电影IP、商品分类、价格、库存、主图地址、上下架状态。订单表存订单编号、用户ID、商品ID、数量、总价、订单状态、下单时间。订单明细单独拆出来,因为一次订单可能会包含多件商品。购物车表存用户ID、商品ID、数量、加购时间,用复合唯一索引保证同一用户同一商品只有一条记录。

推荐相关表有三个:用户评分表存用户ID、商品ID、评分、评价内容、评价时间;用户行为日志表存用户ID、商品ID、行为类型、行为时间;商品相似度表存商品ID、关联商品ID、相似度、更新时间。

设计这些表时有个细节值得注意:用户行为日志表不要做成永久表,我是按月分区的,超过6个月的数据可以归档到备份表。日志表增长速度远快过业务表,如果不控制,查询性能会肉眼可见地下降。

4.2 后端接口的关键设计

后端接口我按RESTful风格设计,核心接口清单如下:

  • POST /api/user/register:注册,参数是用户名和密码,密码在Service层用BCrypt加密。
  • POST /api/user/login:登录,成功后返回Token。我用JWT做无状态认证,前端把Token存在LocalStorage里,每次请求带上。
  • GET /api/product/list:商品列表,支持分类ID、关键词、页码、条数四个参数。
  • GET /api/product/detail/{id}:商品详情,返回基本信息并附带该商品的相似推荐。
  • POST /api/cart/add:加购,参数是用户ID、商品ID、数量。加购前在后端校验商品库存是否足够。
  • POST /api/order/create:下单,接收结算的商品列表,Service层开启事务处理库存扣减和订单生成。
  • GET /api/product/hot:热门排行,按销量和评分综合排序返回Top10。
  • GET /api/recommend/{userId}:个性化推荐列表,查推荐结果表返回。
  • GET /api/recommend/item/{itemId}:相似商品推荐,给商品详情页用的。

事务处理这块我用Spring的@Transactional注解搞定。下单时先扣库存再生成订单,两步必须同生共死,如果在扣库存时失败但订单却生成了,数据就对不上了。这个在论文里属于“事务一致性设计”的知识点,可以单独展开写。

4.3 前端页面怎么规划

前端我用的方案是Thymeleaf模板引擎加上一些静态资源,配合Bootstrap做页面样式,没有引入复杂的Vue或React。原因很现实:毕设项目时间紧,前后端分离会多出跨域、Token刷新、接口联调一整条链路,而服务端渲染可以把页面快速撑起来,演示时效果一样完整。

页面规划方面我做了六个页面:首页(热门商品+个性化推荐+横幅)、商品列表页(左侧分类导航+右侧商品卡片)、商品详情页(商品信息+评价列表+相似推荐)、购物车页、结算与订单页、个人中心页。后台管理端我用了一套简洁的后台模板,包含商品管理、订单管理、用户管理、数据统计四个页面。

推荐结果在前端怎么展示?我在首页写了一个“猜你喜欢”区域,循环遍历推荐接口返回的商品列表,渲染成商品卡片。夜间定时任务跑完后,推荐结果保存在数据库里,所以首页的响应速度很快,不存在算法计算阻塞页面的问题。

4.4 定时任务与推荐数据刷新

前面几次提到定时任务,这里把实现细节说清楚。我用Spring自带的@Scheduled注解,没有引入Quartz框架,避免了额外配置。核心配置代码如下:

@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行 public void refreshRecommendData() { // 1. 从行为表和评分表拉取数据 // 2. 计算商品相似度矩阵 // 3. 生成每个用户的TopK推荐 // 4. 写入商品相似度表和推荐结果表 log.info("推荐数据刷新完成"); }

这里有个比较坑的点:@Scheduled默认是单线程执行的,如果你同时配置了好几个定时任务,它们会排队执行,排在后面的要等前面的跑完。我这个系统只有一个推荐刷新任务,所以不受影响。如果你后续加了定时清理日志的任务,建议在配置类上加上@EnableScheduling并配置线程池,让不同任务并行跑,避免互相阻塞。

定时任务为什么选在凌晨2点?因为系统有真实用户使用时,凌晨是访问高峰的低谷,不容易对业务产生干扰。同时凌晨跑完,早上用户打开首页就能看到新推荐的更新,体验最好。

4.5 权限拦截与操作安全

电商系统里权限控制是必须有的。管理员接口不能随便让别人调,我用Spring MVC的拦截器做了一版简单的权限校验:放行游客可访问的接口,拦截需要登录的接口,校验Token或Session。管理员接口再加一道角色校验,只有管理员角色的Token才放行。

除了权限,还有几个安全细节值得在毕设里体现:密码不能明文存,我用BCrypt加密,这是业内公认的存储方案;下单接口要防止重复提交,前端按钮防抖是一层,后端用订单号唯一约束兜底;商品库存不能是负数,数据库层面加了非负约束。

这些点单独看都不复杂,合起来却能体现一个完整工程的基本素养。答辩时老师说“你这个系统挺完整”,其实就是这些细节叠出来的整体印象。

5. 常见问题与排查技巧实录

5.1 这个项目最典型的高发问题

做毕设过程中踩过的坑,比看十篇教程都管用。我按活跃度整理了四个最高发的问题,每一个都是血泪教训。

第一个是推荐结果全为空的Bug。现象是首页“猜你喜欢”区域空荡荡,排查发现是商品相似度表里没有生成数据。原因在定时任务执行时,评分矩阵从行为日志表里读取,但测试环境里用户行为很少,余弦相似度算出的值全低于0.3阈值,所有商品配对都被过滤掉了,最终一张表空无一物。解决方法是把阈值降到0.1,并灌入一批模拟评分数据后重新执行任务。

第二个是下单时库存变负数。这个Bug很典型,订单创建接口的扣库存逻辑写在了事务外面,导致并发请求下单时,两个线程同时读到库存为1,然后都做了扣减操作,都在事务内提交,库存就变成了-1。解决方法是把扣库存和生成订单放在同一个@Transactional方法里,并在SQL层面加stock >= quantity条件,让数据库兜底。

第三个是中文乱码问题。前后端数据交互时,JSON里中文变成了问号,排查后发现是Spring Boot配置里没有显式设置server.servlet.encoding.charset=UTF-8。加上配置并确保页面meta charset="UTF-8"一致,乱码立刻消失。

第四个是同一个用户推荐结果长期不更新。排查定时任务本身没报错,但发现用户行为日志表里根本没有新增数据——原因是前端收藏、加购时没调用行为记录接口,日志只在评分时写入。解决方法是设计一个统一的行为采集入口,用户登录后的所有商品交互都调一次这个接口。

5.2 问题排查速查表

我把常见问题整理成一张速查表,放到项目文档里,当时答辩老师看后赞了一句“做工扎实”:

问题现象可能原因排查思路解决方案
推荐列表为空相似度表无数据或阈值太高检查定时任务日志,查询相似度表行数降低阈值,预置模拟行为数据
库存扣为负数事务边界不对或SQL无并发保护查看日志确认扣减与下单是否在同一事务合并事务,SQL加stock >= quantity
中文乱码编码配置不一致查看请求响应头里的Content-TypeSpring Boot配置UTF-8,页面meta声明一致
推荐结果不更新行为日志无新数据或任务未执行查日志表最新记录时间,查任务日志统一行为采集入口,检查cron表达式
登录后接口返回401Token过期或拦截器放行错误查看拦截器路径配置延长Token有效期,修正放行路径
游客访问个性化接口报错接口强制要求登录态前端未做游客分支后端先判断用户身份,游客返回热门列表

5.3 调试推荐算法的独家方法

推荐算法不像普通接口,返回数据对不对一眼就能看出。我的调试思路是“数据可视化检查法”:每次离线计算完成后,把相似度最高的五组商品配对输出到日志里,人工判断这五组是否合理。比如日志显示“《复仇者联盟4》手办”和“《雷神3》手办”的相似度最高,这合理;如果“《千与千寻》徽章”和“《速度与激情8》车模”相似度排第一,那算法八成有问题,而问题往往出在评分数据太少或者存在刷单行为上。

另外我还建了一个简单的“推荐命中率”统计功能:记录用户在推荐位点击了哪些商品,点击次数除以推荐曝光次数,得出点击率。这个指标在答辩时特别好用,它证明的不只是推荐功能跑通了,还能证明推荐质量在提升。我当时跑出来的点击率数据是:个性化推荐区17%左右,热门区9%左右,两组对比直接说明个性化推荐确实比一刀切的热门推送更有用。

5.4 答辩环节的高频提问与应对

毕设答辩时老师的问题来来去去就是那几个,我提前准备了一套应答话术,现场用得很顺。

老师常问“协同过滤和基于内容的推荐有什么区别”。我的回答是:协同过滤只看用户群体的行为共性,不关心商品本身的属性;基于内容的推荐则要看商品标签与用户画像的匹配,比如电影周边可以按照电影IP来打标签然后匹配。协同过滤的优点是能发现标签之外隐含的偏好,缺点是依赖行为数据量,所以冷启动阶段我会用基于内容的热门标签兜底。

老师常问“你的推荐算法在哪里体现创新”。实话说提问的对话场景下就是要你证明不是简单糊弄。我回答时强调两点:一是设计了行为日志到评分矩阵的归一化映射,让浏览、收藏、加购、购买都能进入推荐模型,而不是只用评分数据;二是引入了时间窗口过滤,只保留最近90天的行为,让推荐结果能跟随用户兴趣漂移。这两点算常规改良,但在毕设层面已经能拿出来说了。

老师还会问“系统怎么保证性能”。我的回答路径是:离线推荐的计算量集中在夜间任务,线上接口只做查询;商品相似度表用索引优化,推荐结果表按用户ID主键查询;首页接口有短暂缓存,避免同一用户重复请求反复查库。回答完这一串,老师基本不会再往深处追了。

5.5 提升项目亮点的三个方向

如果你的时间比较充裕,我建议在这三个方向里挑一个做提升,效果立竿见影。

第一个方向是“热门榜与个性化推荐的AB测试”。在首页加一个开关,一半用户只看到热门商品,一半用户看到个性化推荐,后台统计两边的点击率差异。这个不只是功能亮点,还带上了实验设计的思想,论文的完整度直接上了一个台阶。

第二个方向是“推荐解释模块”。在推荐卡片上展示“因为你收藏了《星际穿越》原声带,所以推荐这张海报”,需要把相似度表里的关联路径挖出来拼成自然语言。这个做出来的视觉冲击很强,演示时很容易让老师眼前一亮。

第三个方向是把推荐算法从纯Java手写改成混合策略,引入简单的内容标签匹配做冷启动兜底,再结合协同过滤做成熟用户的个性化推荐。这样系统在用户全生命周期都有推荐逻辑,论文的叙述也更完整。

6. 一个能直接迁移到其他项目的工程经验

最后分享一点这个项目带给我的通用经验,不针对电影周边商城本身,但做任何带推荐功能的毕设都能用得上。

我发现很多人在写协同过滤的时候,都把注意力放在算法公式上,忽略了数据是算法的土壤。我前面推的模拟数据脚本,说白了就是把几十个虚构用户的行为记录直接插进数据库,让算法一上线就有东西可算。这个脚本写起来比算法本身还费时间,但它的价值在于,没有它你根本没法验证算法到底对不对。完成一个推荐系统,代码量其实只有三分之一,另外三分之一是想清楚数据从哪里来、怎么组织,剩下三分之一是把推荐结果变成用户看得懂的界面。

我还养成了一个习惯:每写完一个模块,先在自己的笔记本上用Postman把接口全集跑一遍,把响应时间记下来。当时记录的关键接口平均响应时间都在100毫秒以内,首页接口40毫秒左右,推荐接口50毫秒,订单创建因为有事务最慢也就150毫秒。这些数字放在论文的“性能测试”章节里,比空口说“系统性能良好”有说服力得多。

这套思路换到别的项目上也一样成立:先确认业务里最核心的数据是什么,再决定用什么算法去处理它,最后把处理结果落到用户能感知的位置。顺序反了,项目基本都会卡壳。希望这篇复盘能让你少踩几个坑,做出一个答辩时能挺直腰板说话的毕设系统。

返回列表