每年到这个时间点,后台总会收到很多类似的私信:毕设题目下来很久了,系统做了一半卡住了,导师催着要中期检查,网上找的源码跑不通,代码下载下来一打开全是报错。尤其是“电商系统”“推荐系统”这类毕业设计,看起来烂大街,实际要做得完整、跑得顺、论文写得下去,真没那么容易。这篇就以我最近带学生完整梳理过的“SpringBoot+Vue协同过滤算法体育商品推荐系统”为例,把从选题、架构、算法落地、数据库设计、部署运行到论文答辩的全链路经验写清楚。项目自带完整源码、SQL脚本和接口文档,非常适合当作Java Web方向的毕设主体,也适合想练手完整全栈项目的同学照着复现。
这个项目能在答辩中站住脚,靠的并不是“商城+推荐”这么简单的拼凑,而是它把协同过滤算法真正落到了业务闭环里:用户登录、浏览、加购、下单、评分,系统记录行为数据,推荐模块基于历史数据计算相似用户或相似商品,再生成“猜你喜欢”“相似商品”等推荐列表。串起了前端交互、后端接口、数据库存储、算法计算四条链路,无论是写论文还是答辩证,都有实实在在的东西可以讲。
1. 毕设选题的考量和这个项目的功能全景
1.1 为什么选“体育商品推荐”而不是做一个普通商城
很多同学一上来就问:我做商城系统行不行?说实话,单纯做商城问题很大。用户管理、商品管理、购物车、订单,这套东西早就是培训班入职作业的水平,答辩老师看多了,三个问题就能让你冷场:你的系统解决了什么问题?和别人的商城有什么本质区别?技术亮点在哪里?
体育商品推荐系统就不一样,它把“推荐”作为核心功能,商城只是支撑推荐的业务载体。为什么要选体育商品来做?因为体育商品的用户行为特征非常典型:球拍、跑步鞋、健身器材、运动服饰,不同用户群体偏好差异很大,而且存在明显的长尾效应。比如一个篮球爱好者频繁浏览篮球鞋和护具,一个瑜伽用户常看瑜伽垫和运动内衣,这种行为差异恰好是协同过滤算法发挥价值的场景——通过分析“与你相似的用户的购买行为”来推送你可能感兴趣的商品,比纯热门推荐更有说服力。
从毕设角度看,这个题目还有一个隐形优势:算法结果可以做可视化。你可以在前端页面展示“为你推荐”的列表,在论文里放推荐前后的点击对比、用户相似度矩阵截图,这些都是导师喜欢看到的“工作量和原创性”。
1.2 系统的功能清单与角色权限
整个系统按角色分为普通用户端和管理员端,我用一张表把功能模块列全,方便你对号入座:
| 功能模块 | 角色 | 核心功能说明 |
|---|---|---|
| 注册登录 | 用户/管理员 | JWT鉴权,登录后携带token访问受保护接口 |
| 商品浏览 | 用户 | 分页查询体育商品,按分类筛选、关键词搜索 |
| 商品详情 | 用户 | 查看规格参数、库存、评分、推荐相似商品 |
| 购物车 | 用户 | 加入购物车、修改数量、删除、结算 |
| 订单管理 | 用户/管理员 | 下单、支付模拟、取消订单、发货状态流转 |
| 商品管理 | 管理员 | 商品CRUD、上下架、库存调整、图片上传 |
| 分类管理 | 管理员 | 运动分类维护(篮球、跑步、健身等) |
| 用户管理 | 管理员 | 用户列表、状态禁用、角色分配 |
| 评分评论 | 用户 | 对已购商品打分,写入评分数据供算法使用 |
| 推荐列表 | 用户 | 首页“猜你喜欢”、详情页“相似商品推荐” |
| 数据统计 | 管理员 | 销量排行、用户活跃度简单图表 |
需要注意的一点:评分评论模块千万别省。协同过滤算法最核心的输入是“用户—商品”的评分矩阵,如果没有评分功能,算法只能退化成基于购买记录的0/1矩阵,效果大打折扣。我见过好几个学生的代码里推荐算法写得没问题,但系统界面压根没有让用户打分的入口,数据全靠手动造,答辩时被导师一句话问住:“你的推荐结果是怎么触发的?”所以评分入口必须作为用户常规操作纳入闭环。
2. 整体架构设计:SpringBoot与Vue如何分工协作
2.1 后端分层:从Controller到Mapper的一整套规范
后端采用的是典型的分层架构,标准SpringBoot工程结构。很多新手拿到源码直接跑,跑通就结束了,但答辩老师往往会追问“为什么这样分层”,所以这个部分你需要理解而不是死记。
我按实际项目里的包名顺序解释:
controller:接收前端请求,做参数校验,调用service层,返回统一格式的Result对象。这一层只负责“接活”,不写业务逻辑。service:业务逻辑的核心。比如下单时要检查库存、计算总价、生成订单号、扣减库存,这些都在service层完成。推荐算法也在这里被调用。dao/mapper:MyBatis的Mapper接口,配合XML文件操作数据库。SQL都写在XML里,Java代码里不拼SQL字符串,这是防止SQL注入的基本要求。entity:实体类,对应数据库表结构。日常项目中还会拆出dto(传输对象)和vo(视图对象),避免前端拿到多余字段。config:各类配置类,比如WebMvc拦截器、跨域配置、MyBatis分页插件配置。common:统一返回结果、全局异常处理、工具类。
为什么要单独强调统一返回结构?前端和后端联调时,最大的痛点就是接口返回格式五花八门。有的接口返回data是对象,有的返回数组,出错时有的返回字符串提示,有的返回null。前端处理起来非常痛苦。项目里定了统一的Result<T>结构:code表示状态、msg表示提示信息、data装载业务数据,成功是200,失败是500,未登录是401。前端在axios响应拦截器里统一判断code,就能做到一套逻辑处理所有接口的异常。
2.2 前端路由、状态管理与跨域处理
前端是Vue2还是Vue3这里不抬杠,项目用的是Vue2 + Element UI,这也是目前毕设生态里最稳妥的组合。Vue3 + Element Plus当然更潮,但网上资料、组件踩坑记录、其他同学的代码,都是用Vue2这套居多。毕设求稳,Vue2足够。
前端的设计重点有三个。
第一个是路由权限控制。系统分用户和管理员两个角色,不能只靠菜单隐藏来区分权限,因为懂点前端的都能通过URL直接跳转。项目中在Vue Router里配置了路由的meta信息,标出哪些页面需要admin角色,在路由守卫beforeEach里读取本地存储的token和用户角色,没有权限就重定向到403页面。这个逻辑看起来简单,但很多同学的代码里没有做,属于答辩加分点。
第二个是axios封装。项目里所有请求都走一个统一的axios实例,baseURL指向后端服务地址,请求拦截器里自动带上token,响应拦截器里统一处理code。跨域问题在这里说清楚:开发环境下前端用webpack的proxy代理把/api转发到后端,线上部署后用Nginx做反向代理。很多同学开发时直接用axios请求后端地址,发现报跨域,就在后端单独配CORS允许所有来源,这属于临时方案,答辩时最好说清楚生产环境的正确做法。
第三个是Element UI组件化。商品列表复用同一个表格组件,分页组件统一封装,上传组件对接后端的图片上传接口。这里有个容易被忽略的小问题:Element UI的Upload默认用action属性直接提交到后端,不走axios实例,也就不会自动带token。项目里图片上传接口是白名单的,但如果你的后端要求登录才能上传,就得用http-request属性自己定制上传逻辑,手动加请求头。这个小坑值得写进论文的“难点与分析”里。
2.3 接口文档设计与前后端对接方式
这个项目带了接口文档,这一点非常重要。很多学生自己写的系统,接口没有文档,前后端全靠聊天软件传话,出了Bug都不知道是前端参数传错还是后端返回结构变化。接口文档的规范做法是:协议HTTP、方法GET/POST、路径、请求参数、返回值示例,全部统一表格化。
接口文档中要重点关注的几个接口是:
/api/user/login:参数username、password,返回token和用户信息。/api/product/page:分页查询商品,参数pageNum、pageSize、categoryId、keyword。/api/cart/add:加入购物车,参数productId、quantity。/api/order/create:下单,参数是购物车选中项id列表。/api/rate/submit:用户对商品打分,参数productId、score、content。/api/recommend/collaborative:核心推荐接口,根据当前登录用户计算推荐商品列表。
接口文档还有个作用——论文中的“系统设计”章节可以直接引用接口表,导师会觉得你的设计过程完整规范,而不是直接从网上扒了段代码就交差。
3. 协同过滤算法:从理论到可落地的代码实现
3.1 选型:用户协同过滤还是物品协同过滤
协同过滤算法分两大类:基于用户的(User CF)和基于物品的(Item CF)。做毕设时最纠结的就是这个选型问题。我的建议是:主推基于用户的协同过滤,同时用物品协同过滤做详情页的“相似商品”。原因有三点:
第一,基于用户的协同过滤适合用户数量远小于商品数量的场景。体育商品的SKU动辄几千上万,而毕设系统的注册用户一般就几百个测试账号,计算用户之间的相似度矩阵的复杂度远低于商品相似度矩阵。
第二,基于用户的推荐结果解释性强。“因为与你兴趣相似的用户A购买了篮球鞋,所以为你推荐篮球鞋”,这种逻辑在论文里画图非常直观,答辩老师容易理解。
第三,基于物品的协同过滤可以顺手做相似商品推荐,同一套算法逻辑写了两次,既丰富了系统功能,又能在论文里体现“对比分析”。导师问“你为什么不只用一种算法”时,你能给出业务场景差异的合理解释。
3.2 用户—商品评分矩阵与相似度计算
协同过滤的核心输入是评分矩阵,行是用户,列是商品,单元格是评分。但实际业务中90%以上的单元格是空的——用户不可能对所有商品打分。所以项目里推荐算法模块的第一步,是构建一个稀疏矩阵的Map形式,而不是傻乎乎地建二维数组几千乘几千,那样内存直接爆掉。
// 伪代码:构建用户-商品评分Map // key: userId, value: Map<productId, score> Map<Long, Map<Long, Double>> userRatings = new HashMap<>(); // 从数据库加载所有评分记录,逐条填充 List<Rating> ratings = ratingMapper.selectAll(); for (Rating rating : ratings) { userRatings.computeIfAbsent(rating.getUserId(), k -> new HashMap<>()) .put(rating.getProductId(), rating.getScore().doubleValue()); }相似度计算用的是余弦相似度。为什么选余弦而不是皮尔逊相关系数?因为项目里的评分数据极度稀疏,余弦相似度处理稀疏向量时表现更稳定,而且计算逻辑容易用向量几何解释——用两个向量夹角的余弦值衡量相似程度,夹角越小说明用户口味越接近,这个说理在论文里两三句话就能讲透,图也很好画。
// 计算两个用户对共同评分商品的相似度 public double cosineSimilarity(Map<Long, Double> userA, Map<Long, Double> userB) { Set<Long> commonItems = new HashSet<>(userA.keySet()); commonItems.retainAll(userB.keySet()); // 没有共同评分商品,相似度为0 if (commonItems.isEmpty()) { return 0.0; } double dotProduct = 0.0; double normA = 0.0; double normB = 0.0; for (Long item : userA.keySet()) { normA += Math.pow(userA.get(item), 2); } for (Long item : userB.keySet()) { normB += Math.pow(userB.get(item), 2); } for (Long item : commonItems) { dotProduct += userA.get(item) * userB.get(item); } if (normA == 0.0 || normB == 0.0) { return 0.0; } return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); }算法里还有一个很容易被忽略的点:要过滤掉当前用户已经购买或评过分的高分商品,不然推荐结果里会出现用户已经买过的商品,看起来非常蠢。项目里在生成推荐列表时会先把用户已有的交互商品id集合排除掉,只推荐真正“未接触”的候选商品。
3.3 Java实现推荐的完整计算流程
推荐接口的完整流程比想象中要长,很多人以为算法就是一行代码调个函数,实际上从拿到当前用户id到返回商品列表要经历五步:
第一步,加载当前用户的所有评分记录,如果没有评分记录则走“冷启动”策略。冷启动是本系统的核心亮点之一,我后面单独讲。
第二步,遍历所有其他用户,逐个计算当前用户与他们的相似度,过滤掉相似度为0的用户,按相似度降序排列,取出Top-N个相似用户。N通常在5到10之间,参数可以在推荐配置里调整。
第三步,统计Top-N相似用户的所有评分商品,按相似度作为权重加权计算预测评分。公式是:预测评分 = 所有相似用户对该商品评分的加权平均,权重就是相似度。
第四步,排序取前K个商品,过滤掉当前用户已交互过的商品,封装成推荐结果返回前端。K建议10到20,太少没有选择余地,太多用户体验差。
第五步,把推荐结果的同时记录到推荐日志表,方便后期统计推荐效果、分析冷启动命中率,这也是论文数据的一个来源。
这里需要提醒的是,在线推荐和离线推荐的区分。项目里的推荐算法是实时计算的——用户刷新页面就会重新计算一次推荐结果。但真实生产环境不可能每次请求都全量遍历用户、计算矩阵,而是提前离线算好存入Redis或数据库,定时更新。在论文里可以提到这个优化思路,但毕设代码里实时计算反而是加分项,因为能现场演示算法运行过程,导师问“推荐结果是怎么来的”时,你可以打断点调试展示每一步计算。
3.4 冷启动问题的务实处理
协同过滤有个先天的缺陷叫“冷启动”:新用户没有行为数据,系统无法判断他的偏好,推荐模块就会失效。这个问题是答辩必问题目,甚至可以说,对冷启动的处理方式直接决定了你论文的档次。
项目里采用的方案是两层兜底:
第一层是“热门榜兜底”。当用户没有评分、没有浏览记录、没有购买记录时,推荐接口直接返回数据库里的热门商品TopK。热门程度用销量加权和评分加权综合排序,SQL可以这样写:
SELECT p.*, (COALESCE(p.sales_count, 0) * 0.7 + COALESCE(AVG(r.score), 0) * 10 * 0.3) AS hot_score FROM product p LEFT JOIN rating r ON p.id = r.product_id GROUP BY p.id ORDER BY hot_score DESC LIMIT 20;第二层是“行为试探”。用户登录后不管有没有评分,浏览记录和搜索记录都会被记进行为日志表。推荐模块读取行为日志里用户最近浏览过的商品分类,如果用户浏览过两次以上篮球分类的商品,就把篮球分类的其他热门商品混入推荐列表,并降低全局热门商品的占比。这个策略让用户感觉“系统开始懂我了”,极大提升演示效果。
答辩时如果把这两层策略讲清楚,导师基本不会再揪着冷启动追问,因为这个问题的复杂性和应对思路已经超出了普通毕设的预期。
4. 数据库设计:SQL脚本背后的业务支撑
4.1 核心表结构与关系
数据库是整套系统的地基,表设计得不好,后面写业务和算法都要返工。这个项目的SQL脚本里核心表可以画成这样几组:
用户侧:sys_user(用户表)、user_address(收货地址表) 商品侧:product(商品表)、category(分类表)、product_image(商品轮播图表) 行为侧:rating(评分表)、cart(购物车表)、order(订单表)、order_item(订单明细表)、browse_log(浏览日志表) 推荐侧:recommend_log(推荐记录表)
其中最容易设计失误的是order表和order_item表的拆分。有些同学会把下单的商品直接存成JSON字符串塞在订单表的一个字段里,这种做法答辩会被批死。正确做法是记住一个原则:订单主表只存订单维度的信息,商品明细拆到子表。订单表存订单号、用户id、总金额、状态、创建时间;订单明细表存订单id、商品id、商品名称、单价、数量、小计。为什么要冗余商品名称和单价快照?因为商品可能改名、可能调价,订单是历史交易记录,必须把交易时的快照保存下来。这个点写进论文的话,导师会觉得你有实战思维,而不是只会照抄教程。
4.2 行为数据表的设计细节
协同过滤算法的数据来源是评分表rating,它的设计比普通业务表更讲究。表里除了user_id和product_id外,必须包含score字段和一个create_time字段。
socre字段的取值范围建议设计为1到5的整数,对应前端显示五星评分。为什么不用百分制?因为五星评分在用户认知中更直观,答题时的公式计算也更方便——余弦相似度、加权平均时,1到5的整数不会产生过大极值,避免单个高分用户主导相似度判断。
create_time虽然不起眼,但它在推荐中有一个重要作用:时间衰减。一个用户一年前给某商品打5分,和昨天给另一个商品打5分,对预测当前偏好的权重应该是不同的。项目里可以在计算相似度前,对评分按时间做一次衰减加权,越近的评分影响力越大。这个细节做进去之后,论文里的算法复杂度又高了一个档次,而且代码量很小,大概十几行就行。
浏览日志表的设计也要提前规划。不能只存userId、productId和time三个字段,建议附带type字段区分浏览、搜索、点击等行为类型。搜索引擎推广等行为的数据才是活的数据,纯靠造数据演示,答辩时导师抽查一条日志都解释不清来源。
5. 部署运行实操:从拿到源码到界面跑通
5.1 环境准备清单
拿到项目源码后,第一步不是急着启动代码,而是把环境确认好。我整理了一份常用的版本组合,照着装不容易踩坑:
| 组件 | 版本建议 | 说明 |
|---|---|---|
| JDK | 1.8 | SpringBoot 2.x的标准搭配,不建议直接上17,兼容性问题多 |
| Maven | 3.6+ | 管理后端依赖,配置阿里云镜像加速下载 |
| MySQL | 5.7或8.0 | 8.0注意驱动版本要和pom中一致 |
| Node.js | 14或16 | 对应Vue2的webpack构建,太新的Node版本易报OS错误 |
| 开发工具 | IDEA 2022+ | 社区版即可满足毕设需求 |
安装时有个高频问题:Maven下载依赖总是失败或卡住。这个基本是网络问题,把settings.xml里的镜像源换成阿里云镜像,问题立即解决。Node方面,老项目装依赖时经常报node-sass安装失败,这是最让人崩溃的一步。解决方案是设置SASS_BINARY_SITE环境变量指向国内镜像,或者直接换用dart-sass替代。
5.2 导入数据库与后端启动的易错点
后端的启动流程看起来很简单:导入SQL、改配置、点启动。但实际操作中每一步都有坑。
SQL脚本导入时要注意数据库字符集和排序规则要选utf8mb4,否则商品描述里如果有emoji或者特殊符号,插入会直接报错。如果用的是Navicat导入大SQL脚本,中途断开是最常见的问题——这时不要在Navicat里傻等,改用命令行source方式导入更稳定。
改配置时重点看application.yml里的数据源配置。数据库账号、密码、库名一定和本地一致。SpringBoot 2.4以后的配置文件中,驱动类的写法变了,MySQL 8.0以上用com.mysql.cj.jdbc.Driver,老版本才是com.mysql.jdbc.Driver。很多项目跑不起来都是这个细节。
启动后端时如果发现端口被占用,直接在application.yml里改server.port。项目默认端口是8080,如果改了,前端axios的baseURL也要同步改。
5.3 前端项目启动与联调验证
前端启动命令是标准的npm install加npm run serve。npm install如果卡住不动,多半是依赖包版本冲突,建议先把package-lock.json删除再重新安装。
前端启动成功但登录不上的问题,绝大多数出在跨域。开发环境下Vue项目的vue.config.js里配置了devServer代理,你需要确认代理目标地址和后端实际地址一致:
module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', // 后端服务地址 changeOrigin: true } } } };联调验证时,建议按这个顺序走一遍:注册新用户、登录、浏览商品、加入购物车、下单、给商品打5分、刷新首页看推荐列表里是否出现高分商品相关的内容。一条链路走通,整个系统的核心业务就全部验证过了。推荐算法的效果不会立刻显现,因为新用户要积累评分数据才有协同过滤的依据,所以演示前一定要准备两个及以上有丰富操作行为的老账号。
6. 论文写作与答辩准备:如何把推荐算法讲清楚
6.1 论文结构安排与重点章节
毕设论文的一般结构是固定的七章左右,但重点是第三章“系统设计”和第四章“核心算法实现”。导师评阅论文时,最关注的就是这两章。
第三章系统设计中,除了常规的功能模块设计图,一定要画两张图:系统整体架构图和技术架构图。系统整体架构图展示用户、前端、后端、数据库四层关系;技术架构图展示前端Vue组件、后端SpringBoot模块、推荐算法模块、MySQL数据层之间的调用链。图表比纯文字说明直观得多,导师扫一眼就能判断你的系统是否完整。
第四章核心算法实现是全篇的重头。写作时不要一上来就贴全量代码,而要先写算法流程、再写关键代码片段、最后给出推荐效果的测试截图。流程用自然语言描述加简单流程图,关键代码选相似度计算那段,测试截图放两个不同用户登录后首页推荐列表的对比。我见过不少学生论文里把整个RecommenderService.java文件贴进附录,正文部分却没写清楚算法逻辑,导师根本不可能读完一百行代码来猜你的思路。
6.2 答辩现场常被追问的六个问题
根据我带毕设的经验,推荐算法类系统在答辩时被追问的问题非常集中,提前准备等于开卷考试:
第一个问题:“为什么用协同过滤而不用别的推荐算法?”答:因为系统面向的场景是用户行为数据丰富、但用户和商品画像信息不足的电商平台,协同过滤不需要商品特征标签和用户画像就能推荐;同时对比了基于内容的推荐后,发现协同过滤能发现跨品类关联,例如购买篮球的用户也会购买运动护腕,这是内容特征无法直接挂钩的。
第二个问题:“评分矩阵很稀疏,怎么保证推荐效果?”答:冷启动用热门榜兜底,行为数据累积后用最近活跃相似用户集合替代全量用户计算,再配合时间衰减提高近期评分的权重。说明这个局限性和应对思路即可。
第三个问题:“相似度有没有改进空间?”答:加了同品类过滤规则,即在计算相似用户时拆分品类计算相似度,再汇总加权。实际上协同过滤算法可以对不同品类的行为分开建模,推荐篮球类商品时只参考用户篮球品类的评分历史,推荐效果明显好于全品类统一计算。
第四个问题:“推荐结果为什么不用深度学习?”答:毕设的数据规模小,深度学习网络在小样本上容易过拟合,而且缺乏可解释性。协同过滤在中小规模数据集上效果不差,且能直观解释推荐来源,适合当前系统的数据量级。
第五个问题:“系统性能怎么样?并发高怎么办?”答:实话实说毕设系统是低并发场景,算法实时计算满足当前需求;高并发场景可以通过离线预计算推荐结果、存入缓存、定时刷新来解决。不飙脏话、不吹牛,答出优化方案就够了。
第六个问题:“如果用户恶意刷评分怎么办?”答:可以加简单风控逻辑,比如限制同一用户对同一商品24小时内不可重复评分、评分后不可修改多次。项目里已经做了评分去重逻辑,直接展示代码即可。
6.3 从项目源码延伸到个人亮点的优化思路
多数同学的毕设就止步于“跑通源码”,但我一直建议,答辩前至少做一处小而真实的优化,哪怕只改了一个点,也要能完整讲清楚。这里的优化不一定是复杂功能。举几个低成本高回报的思路:
第一个思路是把推荐算法做成可配置。把相似用户Top-N、推荐商品K值、时间衰减半衰期这三个参数抽出来放到配置文件里,界面加一个管理员的参数配置页面。答辩时可以直接现场调整参数,展示推荐列表的变化,效果非常惊艳。
第二个思路是加一个推荐效果统计页面。统计每个推荐位商品的曝光次数和点击次数,简单算一个点击率,用图表展示。这个本质上追加了一张日志表和两个查询接口,但导师会觉得你有数据闭环意识。
第三个思路是引入Redis缓存推荐结果。把热点商品的推荐结果缓存5分钟,降低实时计算压力。这个改动技术含量不高,但在论文里能写出“引入缓存层缓解计算压力”的优化方案,面试场景也能开启这个话题。
7. 我实际操作后的几个提醒
最后想把自己带学生时反复踩到的问题集中说一遍,希望你能绕过这些坑:
第一,源码跑通不算完成的,必须把数据跑出推荐效果。很多同学导入SQL后,库里只有几十条商品和几个测试账号,推荐结果几乎随机,答辩一打开就露怯。建议自己造一批有规律的数据:两三个用户评分了同一批篮球商品,另一批用户评分了瑜伽商品,确保推荐接口能输出符合预期的差异化结果。
第二,接口文档不要只当摆设。接口文档是答辩的隐藏评分项,导师翻阅文档时如果发现接口路径、参数、返回和代码不一致,印象分会大打折扣。拿到项目后先把接口文档对着代码过一遍,有出入就改。
第三,不要图省事把推荐算法塞在Controller里。有个毕业生的代码里把整个推荐计算逻辑写在Controller方法中,一百多行挤在一起,答辩被导师提问代码结构时支支吾吾。正确做法是拆成RecommenderService、SimilarityCalculator、DataLoader三个类,各自职责清晰,表述也容易。
第四,演示前准备两个“身份”:一个是有丰富行为数据的老用户,一个刚注册的新用户。老用户展示协同过滤的个性化推荐列表,新用户展示冷启动的热门推荐,一次演示把系统的两种策略都呈现出来,导师想看什么都有的放矢。
这套项目从技术栈到算法选型,再到业务闭环,对Java Web方向的毕设来说是一个覆盖面很全的载体。代码能跑、算法能讲、论文有料、答辩能打,四件事全占了。如果你正准备这个方向,不妨按我上面的思路把项目重新过一遍,把每一步背后的为什么都搞明白,答辩时你会发现自己比想象中从容很多。