做毕业设计选题目这事,十个人里有八个都会卡在同一个坎上:既要显得有技术含量,又怕工作量太大做不完,还得保证答辩的时候有东西可以讲。如果你正在为这个纠结,我强烈建议你认真看看"Spring Boot + 智能推荐"这个组合。它几乎是为毕业设计量身定做的题目:推荐算法可以讲深,Spring Boot可以把工程能力体现出来,而且外卖这个场景数据天然丰富、演示效果直观。这篇分享就把我当时从选题、技术选型、核心代码实现到答辩准备的完整思路拆开揉碎讲清楚,都是可以直接复用的经验。
1. 选题逻辑:为什么这个题目是毕业设计的"最优解"之一
1.1 它同时踩中了"算法"和"工程"两个加分点
很多同学选课题时会陷入一个误区:要么选纯管理系统,比如"图书管理系统""超市进销存系统",这类题目确实简单,但答辩老师一句"你这个系统哪里体现了计算机专业区别于培训班的水平"就能把你问住。要么反过来选纯算法研究,比如"基于深度学习的图像识别",听起来高大上,但以本科阶段的精力,光是把模型跑通、调出满意的准确率就够喝一壶的,前端页面和系统功能往往草草了事,最后系统不像系统,算法也没有深度。
智能外卖推荐系统天然避开了这两极。从系统层面看,它是一个完整的前后端分离项目,用户端、商家端、管理端三端齐全,涉及订单、商品、用户、评价、浏览记录这些标准业务模块,该有的CRUD一步不少。从算法层面看,它又确实有真正的"智能"成分——不再是简单地把数据库里的数据查出来展示,而是要通过用户的历史行为数据去预测他可能喜欢什么菜品。技术栈可以同时覆盖Spring Boot、Redis、MySQL、推荐算法、消息队列,几乎每一门核心课程的知识点都能在项目里找到落点。
1.2 外卖场景的数据价值比你想的要大
你可能觉得推荐系统常见于电商、短视频,但外卖场景其实更适合做推荐,原因有三个。第一,用户决策成本低,打开外卖App到下单往往只有几分钟,用户更倾向于快速浏览推荐内容而不是仔细搜索比价,这让推荐结果对最终转化的影响非常明显。第二,行为数据密集,一个用户一天内可能产生多次浏览、点击、加购、下单、评价行为,数据量积累速度远超传统电商。第三,位置和时间维度是天然的上下文特征,同样的用户在午餐时间偏好盖饭面食,晚餐可能想吃清淡一点的,在办公室和在家点外卖的选择也完全不同。这些额外维度的引入,让你的推荐算法可以从"用户—物品"二维关系扩展到"用户—物品—场景"三维匹配,这在毕业论文里是非常好的创新点。
1.3 这个题目的难度边界很清晰
选题时最怕的就是"不知道做到什么程度算完"。智能外卖推荐系统的难度是阶梯式的:基础版是商品管理加订单管理加简单的按销量排序;进阶级是基于用户行为数据的协同过滤推荐,能根据相似用户或相似菜品产生个性化结果;高配版是在此基础上引入Redis缓存、消息队列异步处理、冷启动策略、排序模型融合。你可以根据自己的实际水平选择做到哪一档,每一档都是完整可交付的项目,不会出现做到一半发现做不下去了的情况。我当时给自己的目标是进阶级加部分高配版功能,既保证了工作量,又留出了算法调优的余量。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 整体架构设计:先把地基的每一层想清楚再动手
2.1 技术选型的原则:能毕业、能答辩、能落地
技术选型是这个项目里最容易被忽视但实际最重要的决策。选型的原则我总结成九个字:能毕业、能答辩、能落地。"能毕业"指技术不能太旧,最好用当前主流版本;"能答辩"指你要能清楚解释每一个技术选型的原因,不能只是"网上都说这个好";"能落地"指你选的每一样东西都必须真的在项目里用起来,而不是装个依赖就完事。
我最终确定的技术栈如下:
| 层级 | 技术选型 | 在项目中的具体职责 |
|---|---|---|
| 后端框架 | Spring Boot 3.x | 提供RESTful API,管理业务逻辑和事务 |
| 持久层 | MyBatis Plus 3.5.x | 数据访问,避免手写大量重复SQL |
| 数据库 | MySQL 8.x | 存储用户、商家、菜品、订单等核心业务数据 |
| 缓存 | Redis 7.x | 缓存推荐结果、实时排行榜、用户会话 |
| 消息队列 | Redis Stream | 异步处理用户行为日志,削峰解耦 |
| 安全认证 | Spring Security + JWT | 登录认证与接口访问控制 |
| 推荐算法 | 协同过滤 + 内容召回 + 热度加权 | 生成个性化推荐列表 |
这里特别说一下为什么用Redis Stream而不是RocketMQ或RabbitMQ。对于毕业设计来说,额外部署一套RocketMQ服务器会增加环境配置的复杂度,而Redis你已经装了,Redis Stream从Redis 5.0开始就提供原生支持,它的消费者组机制完全够用,还能少维护一个中间件。答辩时老师如果问"为什么不用专门的消息队列",你可以回答:项目规模和数据量用Redis Stream足够承担,同时减少了系统组件数量,降低了部署和运维成本,这也是架构设计的一种取舍。
2.2 数据库设计:七张核心表搭建完整业务闭环
表结构设计直接决定你后面写代码的速度。我设计的是七张核心表,它们之间可以通过外键关系覆盖整个外卖业务流程。
用户表是基础,除了用户名密码手机号这些常规字段外,要预留一个taste_tags字段,用来存用户偏好标签的JSON数组,比如"辣、重口、快餐"。这张表是推荐系统的数据源头之一。
商家表和菜品表是一对多关系。商家表要有cuisine_type(菜系类型)、delivery_time(平均配送时间)、delivery_fee、rating、monthly_sales这些推荐算法需要用到的基础属性。菜品表要有category(菜品分类)、spicy_level(辣度)、image_url、price、status(上下架状态)。
用户行为表是整个推荐系统的灵魂。我设计了两种:browse_record记录浏览次数和停留时长,behavior_log用统一的格式记录所有行为事件,字段包括user_id、item_id、behavior_type(枚举值:BROWSE、CART、ORDER、RATING)、scene_type(场景类型:早餐、午餐、晚餐、夜宵)、create_time。推荐算法最依赖的就是这张表。
订单表和订单明细表负责交易闭环。评分表单独拆出来,设计成按order_id去重,一个订单只能评价一次,评价包含rating和comment,这一张表既是用户端的功能,也是推荐系统的显式反馈数据源。
这里给你一个建议:数据库设计文档一定要在编码前就画清楚ER图。不要在这里省时间,把表结构调整好,后面写代码会顺很多。
2.3 不用微服务,单体应用加模块化就够了
关于架构模式,很多同学会纠结要不要拆微服务。我的观点很明确:不要为了简历好看而硬上微服务。毕业设计最重要的是实现闭环,单体应用完全够用,而且部署简单、调试方便、答辩逻辑清楚。但单体不意味着代码结构可以乱写,我按功能做了模块划分:
code复制com.example.foodrecommend
├── controller // 接口层,只负责参数接收和结果封装
├── service // 业务逻辑层,核心算法和事务都在这层
│ └── recommend // 推荐服务独立分包,方便算法扩展
├── mapper // 数据访问层
├── entity // 数据库实体
├── dto // 数据传输对象,避免直接暴露实体
├── config // 配置类(Redis、Security、WebMvc等)
├── common // 统一返回结果、异常处理、工具类
└── task // 定时任务(榜单更新、数据清理)
这样的分层方式让你在做算法优化时只改动service/recommend下的代码,其他模块完全不受影响。答辩的时候,这个清晰的模块划分本身就是加分项。
3. 推荐引擎的实现:三路召回加排序融合,效果和性能两头抓
3.1 协同过滤:从用户行为中挖掘"相似性"
推荐系统的核心是协同过滤,算法本身并不复杂,核心思想就一句话:物以类聚,人以群分。
我实现了两种协同过滤。**基于用户的协同过滤(UserCF)**的思路是:找到与当前用户行为最相似的一批用户,把他们点过而当前用户没点过的菜推荐出去。计算相似度用皮尔逊相关系数或余弦相似度都行,我图省事直接用余弦相似度,公式是similarity = (A·B) / (|A|×|B|)。**基于物品的协同过滤(ItemCF)**的思路反过来:如果很多用户同时点了黄焖鸡和可乐,那这两样东西就被认为存在关联,当用户点了黄焖鸡时就把可乐推荐给他。
实战中ItemCF的效果通常优于UserCF,原因在于外卖场景用户数量远比菜品数量多,物品间的关系相对稳定,而且菜品相似度矩阵可以离线计算好存到Redis里,在线推荐时只需要查表加排序,性能非常快。我的实现方案是:每天凌晨用定时任务跑一遍全量数据,生成菜品相似度矩阵存入Redis,推荐时直接取矩阵数据计算评分,单次推荐响应时间控制在100毫秒以内。
java复制// 物品协同过滤核心逻辑
public List<Long> recommendByItemCF(Long userId, int topN) {
// 1. 获取用户最近有正向行为的菜品(加购、下单、高分评价)
List<Long> userItems = behaviorService.getUserPositiveItems(userId);
// 2. 初始化候选菜品及得分
Map<Long, Double> scores = new HashMap<>();
Map<Long, Double> simScores = new HashMap<>();
for (Long itemId : userItems) {
// 从Redis里直接读取该菜品的相似物品列表
List<SimilarItem> similarItems = similarityService.getSimilarItems(itemId);
for (SimilarItem similar : similarItems) {
if (userItems.contains(similar.getItemId())) {
continue;
}
// 加权累计得分:相似度越高、权重越大
scores.put(similar.getItemId(),
scores.getOrDefault(similar.getItemId(), 0.0)
+ similar.getSimilarity());
}
}
// 3. 排序取TopN
return scores.entrySet().stream()
.sorted(Map.Entry.<Long, Double>comparingByValue().reversed())
.limit(topN)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
这段代码看起来简单,但有两个关键点要注意。一是排除用户已经买过的菜品,否则推荐结果会出现用户刚点过的菜,体验很差。二是取行为数据时要限定时间窗口,只看最近三个月的行为,时间太久远的行为对当前偏好没有参考价值,反而会引入噪声。
3.2 基于内容的召回:解决冷启动和长尾问题
协同过滤有一个明显的短板:新用户没有任何行为数据,协同过滤无计可施。这时候就靠基于内容的推荐来兜底。
基于内容的推荐思路是:根据菜品的属性标签和用户注册时填写的偏好标签做匹配。我实现了一个简单的标签匹配算法:
java复制public List<Long> recommendByContent(Long userId, int topN) {
List<String> userTags = userService.getUserTasteTags(userId);
// 查找候选菜品:按用户偏好标签查询,排除已下单的
List<Dish> candidates = dishService.getDishesByTags(userTags);
return candidates.stream()
.filter(dish -> !orderService.hasUserOrdered(userId, dish.getId()))
.sorted((d1, d2) -> {
// 综合匹配度排序:标签重合度优先,其次按好评率、月销量
int tagCompare = Integer.compare(
countMatchTags(d2, userTags), countMatchTags(d1, userTags));
if (tagCompare != 0) return tagCompare;
return Double.compare(d2.getRating(), d1.getRating());
})
.limit(topN)
.map(Dish::getId)
.collect(Collectors.toList());
}
这个算法虽然简单,但效果很稳定,尤其对新用户非常友好。为了让标签匹配更准,我给每个菜品预处理时生成多个标签,除了菜系、辣度这些静态属性外,还通过菜品名称里的关键词提取出"鸡""牛肉""番茄"这类食材标签。这一步看似不起眼,但配合分词工具做一次预处理后,推荐结果的精准度会有一个明显提升。
3.3 排序融合:把热门度、距离、评分一起算进权重里
有了候选集之后,还不直接推荐,要做排序融合。单个算法的结果往往有偏:协同过滤容易推荐出一些冷门但关联度高的菜,质量没有保障;内容推荐容易重复;纯按热度排序又失去了个性化。我的做法是做一个多因素加权评分公式:
code复制最终得分 = 0.5 × 算法相关性得分 + 0.3 × 商家质量分 + 0.2 × 位置便利分
算法相关性得分来自协同过滤和内容推荐的融合结果(取并集后按各自得分归一化),商家质量分是商家的综合评分和月销量的归一化值,位置便利分基于用户收货地址到商家的距离转换得到。这个公式不是固定的,你可以根据自己项目的情况调整权重,但建议在系统里做成可配置的,答辩时可以现场演示调参前后推荐列表的变化,视觉效果非常好。
3.4 冷启动问题:新用户和新菜品分别怎么处理
冷启动是推荐系统里的经典问题,也是答辩时老师几乎必然会问的问题,必须提前准备好解决方案。
新用户冷启动我做了三层处理:引导用户在注册时选择口味偏好,作为基于内容推荐的基础;注册后进首页先推荐全站热门菜品和评分榜Top10,保证有东西可看;用户第一次产生行为数据后,系统会立即用这些行为数据做一次快速协同过滤推荐。新菜品冷启动则相对简单:新上架的菜品在前三天给一个流量加权,进入热门榜单获得曝光机会,待积累了一定量的用户行为后,逐步回归正常的推荐流程。
答辩时你可以把这个方案讲成"多级冷启动策略",它体现的是你考虑了推荐系统实际落地中会遇到的问题,而不是只在理想数据上做实验。
4. 用户行为数据的采集与异步处理:Redis Stream在项目中的真实用法
4.1 行为日志不能直接写数据库,这是性能大忌
外卖用户的行为是高频的,浏览一个页面可能产生多次行为记录。如果每次点击都直接同步插库,高并发场景下数据库压力会很大。所以行为日志的处理要异步化:用户端产生行为后先发给后端接口,后端把行为事件推入Redis Stream,然后有一个消费者去拉取消息并批量写入数据库。
这里用Redis Stream的好处是,消息可以留存在Redis里,消费者处理完一批再取下一批,天然支持削峰。而且Redis Stream的消费者组机制支持多个消费者并行处理,消息不会重复消费,比直接用Redis List做队列要可靠得多。
4.2 如何拉取Redis Stream队列消息:完整代码示例
热搜词里提到了"spring boot redis stream 如何拉取队列消息",这个问题我当初也卡了很久,网上资料少而且很多是旧版Spring Data Redis的写法。这里直接给出我验证过可用的实现方式。
先定义生产者的推送逻辑,在用户行为Controller里调用:
java复制@Service
public class BehaviorLogService {
@Resource
private StringRedisTemplate stringRedisTemplate;
private static final String STREAM_KEY = "stream:behavior";
public void pushBehaviorLog(BehaviorLogDTO dto) {
Map<String, String> message = new HashMap<>();
message.put("userId", String.valueOf(dto.getUserId()));
message.put("itemId", String.valueOf(dto.getItemId()));
message.put("behaviorType", dto.getBehaviorType());
message.put("sceneType", dto.getSceneType());
message.put("timestamp", String.valueOf(System.currentTimeMillis()));
// 添加消息到Stream
stringRedisTemplate.opsForStream().add(STREAM_KEY, message);
}
}
消费者端是最容易踩坑的地方。Spring Data Redis提供了StreamListener的注解方式,但在消费组模式下手动拉取更灵活可控,关键代码是这样的:
java复制// 生成消费者组(第一次初始化时创建,报错说明已存在)
try {
stringRedisTemplate.opsForStream().createGroup(STREAM_KEY,
ReadOffset.from("0"), "consumer-group-1");
} catch (Exception e) {
// 组已存在,忽略异常
}
// 消费者拉取消息
List<MapRecord<String, Object, Object>> messages = stringRedisTemplate
.opsForStream()
.read(Consumer.from("consumer-group-1", "consumer-1"),
StreamReadOptions.empty().count(100).block(Duration.ofSeconds(2)),
StreamOffset.create(STREAM_KEY, ReadOffset.lastConsumed()));
for (MapRecord<String, Object, Object> message : messages) {
Map<Object, Object> value = message.getValue();
// 处理消息,批量写入数据库
behaviorLogMapper.batchInsert(value);
// 确认消息已被处理
stringRedisTemplate.opsForStream().acknowledge(STREAM_KEY,
"consumer-group-1", message.getId());
}
这段代码里有两个特别重要的细节。第一,createGroup只能成功一次,重复创建会抛异常,所以要用try-catch包起来,但注意不能随便吞掉异常,最好判断异常信息里是否包含"BUSYGROUP"这个关键词。第二,acknowledge一定要做,如果不确认消息,消费者组里的消息会一直处于"未确认"状态,重连后可能会被重新投递,导致数据重复入库。
还有一个建议:Redis Stream里的原始数据不要直接丢弃,即使已经入库了也可以保留在Stream里设置一个过期时间。我一般设置Stream的maxlen为100万条,这样既保留了一定量的缓冲数据用于排查问题,又不会让Redis内存无限增长。
4.3 行为数据入库后怎么用:离线计算和在线计算的设计
行为数据写入MySQL后,推荐系统的数据流向分两条线。离线计算链路是:凌晨定时任务读取过去一天全部用户行为数据,重新计算菜品相似度矩阵、更新热门榜、更新用户偏好画像,结果存入Redis供全天使用。在线计算链路是:用户请求推荐接口时,实时读取该用户最近的行为数据,结合Redis中缓存的相似度矩阵和偏好画像,实时生成推荐列表。
这两条链路一个保证准确性,一个保证实时性。离线计算的结果全量更新但延迟高,在线计算只针对单个用户所以速度极快。把两者结合起来,既避免了每次请求都做全量大计算导致响应慢,又能在用户有最新行为时快速调整推荐结果。答辩时这两条链路的描述能直接体现出你的系统设计能力。
5. 业务功能模块的实现经验:用户端、商家端、管理端各有各的重点
5.1 用户端:推荐位设计比想象中更重要
用户端的页面不多,核心就是首页(推荐信息流)、菜品详情页、购物车、订单列表和个人中心。但推荐系统能力的体现主要在首页的设计上。
我做了三个推荐位:顶部是"为你推荐"个性化推荐列表,基于协同过滤和内容推荐融合生成;中部是"附近热卖"基于地理位置和销量综合排序;底部是"好评榜单"基于评分和时间衰减加权。三个推荐位对应三种不同的推荐策略,展示给用户的是完整的推荐逻辑,而不是单纯一个列表。
菜品详情页也要放一个"看了又看"的关联推荐区块,调用基于物品的协同过滤接口,返回与当前菜品相似度最高的几个菜品。这个功能实现成本很低,但对系统完整度的提升非常明显,一眼就让评委觉得"这个系统是真的做了推荐的"。
5.2 商家端:别让复杂业务拖累推荐数据质量
商家端的主要功能是菜品管理、订单处理、营业统计。对推荐系统来说,商家端最重要的功能是菜品上下架和菜品标签维护。我做了这样一个设计:商家新增菜品时,除了填写基本信息和价格外,还必须给菜品选择标签(菜系、口味、食材、适合场景),这些标签会直接被推荐系统的内容召回模块使用。如果某个菜品标签不完整,推荐系统会默认降低它的召回权重,这能反推商家把标签填完整,保证数据质量。
5.3 管理端:把推荐效果"可视化"
管理端除了常规的用户管理、商家管理、订单管理和举报处理外,我还专门加了一个"推荐数据看板"功能,用ECharts展示各类核心指标:推荐带来的点击量、推荐带来的下单量、推荐转化率、Top10热门菜品、用户偏好标签分布、各类行为数据的实时走势。这个看板有双重用途:一是让我自己在调优推荐算法时有数据可依,不用靠感觉;二是答辩演示时,这个页面比任何口水解释都有说服力,评委一眼就能看到你系统的"智能"程度。
5.4 Spring Security + JWT:安全认证的正确打开方式
外卖系统涉及用户订单和支付信息,接口不能裸奔。Spring Security加JWT是Spring Boot项目的主流方案,实现并不复杂但有几个坑要注意。
JWT工具类负责生成和解析Token,登录成功后把用户ID和角色信息放进Token,设置合理的过期时间。Security配置类里配置哪些接口不需要认证(比如登录、注册、菜品查询),哪些接口需要什么角色(比如商家端、管理端接口)。一个我自己踩过的坑是Spring Security的密码加密,千万不要用明文存密码或者自己写MD5,直接用BCryptPasswordEncoder,这是业界标准做法,答辩时被问到也能说清楚。
另一个坑是JWT的续期机制。如果Token过期时间太短,用户用着用着就掉线,体验很差;太长又有安全风险。我的方案是设置Token有效期7天,前端拦截所有返回401的响应,自动跳转到登录页让用户重新登录,不搞refresh token那套复杂的,对毕业设计完全够用。
6. 性能优化和测试:你的推荐系统不能只在数据少的时候"聪明"
6.1 Redis缓存设计:哪几层数据必须走缓存
推荐系统对性能有硬要求,接口响应时间直接决定用户体验。有用户本地测试时数据量很小,感觉不到性能问题,但答辩演示如果用造好的大数据集,接口慢就会被评委质疑。Redis缓存我分了三层。
第一层是推荐结果缓存。每个用户一份,key设计为recommend:user:{userId}:{sceneType},缓存时间10分钟。用户在10分钟内的重复请求直接走缓存,不重算推荐列表。
第二层是菜品相似度矩阵缓存。这是最大的缓存块,key设计为similar:item:{itemId},value是JSON数组,缓存时间24小时。离线任务更新矩阵后主动删除相关key让数据重新加载。
第三层是热门榜单缓存,key为hot:ranking:{sceneType},缓存时间5分钟,由定时任务每分钟刷新一次。
6.2 造数据是个技术活:规模化数据才能体现推荐价值
真正动手做推荐系统后你会发现,最难的不是写算法,而是找数据验证算法效果。我当初没有现成的真实外卖数据,所以写了一个数据模拟器。这个模拟器不是随机生成,而是模拟"有偏好"的用户行为:先定义10类用户(学生党、上班族、健身人群、无辣不欢型等),每类用户有各自的偏好标签、消费时段和价格敏感度,然后按这些特征生成对应的浏览、加购、下单行为。这样造出来的数据看起来非常接近真实用户,推荐算法也能跑出有意义的结果。
模拟器一次能生成100个用户、200个商家、1000个菜品和几十万条行为数据,基本能模拟出一个真正外卖平台的数据形态。
6.3 性能测试:推荐接口的耗时和并发表现
性能测试我用JMeter做了压测,核心指标就两个:接口平均响应时间和吞吐量。压测得到的典型数据是:推荐接口在100并发下,平均响应时间约180毫秒,每秒能处理约500个请求。这个成绩对毕业设计演示绰绰有余。如果你测出来性能很差,建议按这个顺序排查:先看是否走了Redis缓存(往往这里就挂了一大截),再看SQL有没有慢查询(用MyBatis Plus的日志输出时长,超过500毫秒的SQL要加索引),最后看有没有在循环里查数据库(这个是最常见的坑)。
7. 答辩准备:推荐系统项目被老师追问时怎么答
7.1 准备好这些高频问题的回答模板
答辩环节老师最喜欢问的推荐系统问题,我整理了几类供你参考。
第一类是算法原理类。比如"协同过滤的原理是什么",回答模板是:基于用户历史行为,计算用户之间或物品之间的相似度,根据相似邻居的行为生成推荐。如果被追问UserCF和ItemCF的区别,要点是UserCF更强调社交关系、适合用户少的场景,ItemCF更稳定、可解释性强、适合物品变化不频繁的场景。
第二类是工程实现类。比如"推荐结果为什么快",回答要点是:离线预计算加分层的Redis缓存,在线只做查表和排序。再比如"Redis Stream和数据库直接写入有什么区别",回答要点是:异步削峰、批量入库、通过消费者组保障消息可靠处理。
第三类是落地效果类。比如"推荐准确率怎么保证",你先讲离线指标(点击率预估、转化率提升),再强调项目里通过融合排序和实时行为反馈机制持续优化。真实场景中推荐系统的效果评估本来就不是只看单次准确率,而是看用户长期的留存和转化。
7.2 演示话术的设计:先讲场景,再讲技术
答辩演示时有几条关键的演示路径,提前设计好,不要临场手忙脚乱:
先从一个空白数据的新用户注册开始,展示冷启动策略的效果,系统推荐的是热门和评分高的菜。然后让这个用户浏览两个辣味菜品的详情页,再去首页看推荐列表,已经能看到"无辣不欢"口味的菜品出现了。这就直观展示了行为数据如何影响推荐结果。接着打开管理端推荐数据看板,展示行为数据的采集情况和推荐转化率曲线。最后展示Redis Stream的消息消费情况,让评委看到行为日志在被消费和入库的过程。
这套演示路径每一分钟都能让评委理解你做了什么事、用了什么技术、达到了什么效果,比干讲PPT有效得多。
7.3 后续可扩展的方向:提前埋一个开放性问题的种子
答辩最后一个问题往往是"你觉得系统还可以怎么改进"。这个问题回答得好非常加分。你可以说,推荐算法目前用的还是传统机器学习方法,后续可以考虑引入基于深度学习的推荐模型,比如Wide&Deep、DeepFM,处理用户和菜品的深层特征交互。数据处理层面可以引入实时流处理框架,让行为数据的处理延迟从分钟级降到秒级。配送调度方面,还可以把出餐时间、骑手位置考虑进推荐排序,做"可配送性感知"的推荐。
这些问题不是让你真做出来,而是展示你对自己项目的理解深度和视野宽度,证明你不是只会写代码,而是对整个方向有系统的思考。
去年这套方案带我的师弟也走了一遍完整流程,选这个题目前他也很犹豫,担心推荐系统这种听起来"太AI"的方向自己做不出来。实际做下来发现,只要把协同过滤原理吃透、把Redis Stream的用法跑通、把数据闭环搭好,就完全能交出一份高质量的项目,而且过程中学到的东西比做一个普通管理系统多得多。如果你已经决定要碰这个题目,尽早把数据库表结构设计好,把推荐算法的核心代码跑起来,后面就是按部就班的完善工作了。
