每年毕业季,计算机专业的同学都在同一个问题上来回拉扯:毕业设计到底选什么题目才既拿得出手、又不容易翻车。今天要聊的这套“锦江酒店大数据分析与个性化推荐系统”,就是我眼中的标准答案之一,技术栈横跨爬虫、大数据存储、离线计算、推荐算法、前后端分离可视化和部署,工作量饱满,关键点清晰,答辩时有东西可讲,代码实现也不至于让人秃头。
整套系统的名字已经说明了它的全部技术构成:Django写后端API,Vue做前端界面和可视化图表,Hadoop负责海量酒店数据集的存储与离线分析,爬虫解决数据来源问题,协同过滤推荐算法完成个性化推荐的核心逻辑。它面向的是酒店、民宿、客栈这类住宿业务场景,做的是两件事:第一,把酒店数据摆到屏幕上看清楚,比如价格分布、评论热度、区域排行;第二,让每个用户看到的酒店推荐列表不一样,真正做到“千人千面”。
这篇博文会把这套系统从数据采集到前端展示的完整链路全部拆开,包括技术选型的原因、Hadoop环境搭建的坑、协同过滤的代码实现思路、Django和Vue联调时的典型问题。无论你是准备拿它当毕设参考,还是想了解大数据应用类项目怎么落地,这篇内容都能提供一份可以直接抄作业的路线图。
1. 项目全景与设计拆解
开箱一个系统之前,先得明白它解决的是什么问题。住宿预订场景里,用户面对几百上千家酒店、民宿、客栈,筛选成本极高。用户要的是“哪些酒店更适合我”,平台要的是“怎么把合适的房源推到用户面前”。这套毕设的核心,就是把数据从零散的网页信息变成结构化的数据资产,再用算法完成个性化匹配。
1.1 需求拆解与功能模块划分
从标题可以看出,需求本身被拆成了几个清晰的模块,这也是毕设选题最值得参考的地方。
数据采集层负责从公开网站上抓取酒店名称、价格、评分、评论内容、位置、设施等字段,解决“数据从哪里来”的问题。数据分析层基于Hadoop的分布式存储与MapReduce离线计算能力,对采集到的数据进行统计加工,比如不同城市的酒店均价、价格区间分布、评论数量排行等维度,这些统计结果最终会以可视化图表的形式展示在前端页面。
推荐层是系统的技术亮点,利用用户对酒店的评分或行为数据构建协同过滤模型,计算出用户可能感兴趣的酒店列表。业务层和后端服务层由Django完成,负责提供RESTful API、用户登录注册、推荐结果查询、统计数据查询。前端展示层由Vue实现SPA应用,配合ECharts绘制图表,用户在里面可以看到数据看板、浏览酒店列表、查看个性化推荐结果。
整套系统形成一条完整的数据流水线:爬虫采集原始数据、Hadoop与MySQL分层存储、Django组织业务逻辑、Vue渲染可视化结果。每一层职责单一,层与层之间通过接口或文件解耦,这种分层架构在答辩时可以很清晰地讲出设计思路。
1.2 技术栈选型:为什么偏偏是它们几个
每个被选中的技术都有不可替代的理由。
数据库层面的选型几乎是毕设大数据方向的标配。Hadoop能撑起“大数据分析”这个题眼,分布式文件系统适合存放海量非结构化数据,MapReduce能完成离线统计任务,面试官或答辩老师问起来,这东西能讲的东西非常多。MySQL则负责业务数据,用户信息、评分数据、最终的统计结果等结构化数据放在关系型数据库里,Django的ORM访问起来非常顺手。
后端选择Django而不是Spring Boot,核心原因是Python生态里爬虫、数据分析、机器学习与Web开发一体化协同的便利性。爬虫用requests和BeautifulSoup,推荐算法用pandas、numpy,后端用Django,全程一种语言,代码切换成本几乎为零。
前端使用Vue主要因为它组件化开发效率高,与ECharts这种基于JavaScript的图表库配合成熟。管理后台、用户端、数据看板在这种场景下拆成组件开发极为顺畅,Vue生态的vue-router、axios等配套也成熟稳定。
协同过滤算法作为推荐系统的核心,选它不是因为它最先进,而是因为它最经典、效果可解释、适合教学演示。基于用户的协同过滤推荐相似用户喜欢的东西,基于物品的协同过滤推荐相似物品,这两种思路写起来不复杂,计算过程透明,答辩时可以讲得头头是道,而且数据规模在毕设场景下完全撑得住。
1.3 系统总体架构与数据流转过程
完整的调用链路可以描述为:爬虫定时抓取酒店信息与评论,原始数据一份落入HDFS,另一份清洗后写入MySQL;Hadoop上的MapReduce任务读取HDFS文件,统计价格、热度、区域等维度,结果回写MySQL中的统计表;Django后端通过ORM读取统计数据和酒店数据,经过推荐算法模块生成个性化推荐列表,通过RESTful API暴露给前端;Vue前端调用API获取数据,用户在小程序端或管理后台看到图表和推荐酒店。
这里有一个容易被忽略的设计点:Hadoop和Django不是直连的,中间用MySQL做了一个汇总与中转。为什么要这样设计?因为Django直接读HDFS极其别扭,而MapReduce任务本身也不适合频繁被业务系统调用。Hadoop负责批量离线计算,MySQL负责在线实时查询,各司其职,数据经过“HDFS计算回写MySQL”这个标准流程完成打通,这也是工业界离线数仓的经典路径。
2. 数据采集:爬虫设计与清洗落库
没有数据,后头的算法和可视化就全成了空中楼阁。爬虫模块在整个项目里是基石,决定了后面能做多少事情。
2.1 爬虫目标与数据字段设计
要支撑后续的分析和推荐,需要采集的信息分成几个层级。基础层是酒店属性:酒店名称、所属城市、详细地址、经纬度、星级档次;价格层是当前房型的日均价格、价格区间;口碑层是综合评分、评论数量、评论正文;设施层是WiFi、停车场、早餐、游泳池等标签。这些字段基本覆盖了统计分析和推荐算法对数据维度的需求。
字段设计在爬虫启动前就要定义好。建议直接定义Python字典结构,后面存MySQL时字段名能一一对应。评论正文是推荐算法的“原材料”,因为可以通过评论里提取用户偏好,也可以通过评论长度、热度等维度做统计分析。所以评论必须抓,而且多多益善。我建议每个酒店至少抓取两页评论,大约40条左右,一套项目做下来有几千条评论规模,做统计已经像模像样了。
2.2 爬虫实现步骤与反爬对抗策略
数据源选择上建议用公开的酒店信息聚合网站,而不是某一家酒店官网,聚合网站的酒店列表更丰富,页面结构也更规整。爬虫技术栈用requests加BeautifulSoup加lxml足够,requests发起HTTP请求,BeautifulSoup配合lxml解析HTML提取节点,这套组合足够简单可靠。
以酒店列表页为例,直接请求目标列表页面,在HTML里定位酒店列表容器,逐个提取酒店名称、评分、评论数、价格和详情页链接。详情页需要再发一次请求,解析出酒店的地址、经纬度、设施列表和评论区域。抓取过程要注意通过解析链接规律构造请求,控制页数范围在10到20页之间,数据量控制在300到500条酒店记录加若干条评论,已经足够做分析和推荐了。
反爬对抗是爬虫绕不开的话题,实际项目里最常用的策略有四件套:第一,伪装User-Agent,从常见浏览器UA池里随机挑一个,不要每次请求都用同一个;第二,控制请求频率,代码里写time.sleep加上random.uniform生成延时,控制在1到3秒之间,瞬间高频请求几乎必然被封;第三,构造Referer和Cookie,让请求看起来像正常浏览器访问;第四,必要的重试机制,遇到HTTP 403或503时休息几秒再尝试,单条记录失败的不要影响整体流程,记入日志即可。
注意:爬虫类项目务必将抓取频率控制在合理范围,仅用于学习研究,不要对目标站点造成访问压力。这也是毕业设计里老师一定会问的合规性问题,提前想好这句话。
2.3 数据清洗与入库流程
原始数据基本是不能直接用的,我见过太多人把爬下来的数据一股脑塞进库里,结果统计时冒出一堆房价为0的记录,评分还有两位数这种离谱数值。清洗流程一定要做成一条独立的流水线。
第一步去重,以酒店名加城市作为唯一键,重复的直接丢弃。第二步处理缺失值,价格为空或等于0的记录,要么补全国平均价,要么直接过滤,我建议价格仍由正常统计渠道获得,太离谱的样本直接删掉,对后续分析更干净。第三步统一格式,价格转成float,评论数转成int,纬度、经度统一成小数,设施字段用逗号拼接成一整串文本。第四步做敏感数据脱敏,比如用户昵称、手机号这类字段不需要的干脆不采集。
清洗后的数据同时做两件事:写入MySQL时按表结构批量insert;写一份CSV文件保存到本地,后续上传到HDFS。MySQL表设计建议三张表:hotel酒店表、review评论表、user用户表,推荐系统还需要一张rating评分表。如果没有真实评分数据,可以基于评论内容自动生成模拟评分,比如评论中“很好、棒”这类正向词占比越高评分越高,用jieba分词和情感词库做一个简单的情感打分。
3. Hadoop大数据分析层:环境搭建与离线统计任务
Hadoop在这个项目里的存在感,主要靠伪分布式环境的搭建过程和MapReduce统计分析任务来体现。对毕设来说,证明你理解大数据技术栈并能实操,比单纯跑通更重要。
3.1 Hadoop伪分布式环境搭建要点
我用的是Hadoop 3.x版本,运行在一台8G内存的Ubuntu虚拟机上。伪分布式模式只需要一个节点,NameNode和DataNode、ResourceManager和NodeManager都在同一台机器上,对笔记本配置要求不高,也能完整展示HDFS上传文件、MapReduce执行任务的全流程。
环境搭建有几个关键步骤特别容易踩坑。安装好JDK后修改/etc/profile,配置JAVA_HOME和HADOOP_HOME,PATH里追加$HADOOP_HOME/bin和$HADOOP_HOME/sbin,注意修改完一定执行source /etc/profile让环境变量生效,有很多同学在这反复失败却始终没搞明白原因。
接着配置ssh免密登录。Hadoop的脚本会通过ssh启动远程进程,如果没有免密配置,每次启动都要输密码,严重影响效率。执行ssh-keygen -t rsa生成密钥,再用ssh-copy-id localhost把公钥拷贝到本机,之后就能免密登录了。这一步看似小儿科,实际是环境能跑通的关键前提。
然后修改Hadoop配置文件:core-site.xml里设置NameNode地址和临时目录;hdfs-site.xml设置副本数为1(伪分布式下副本数为3必然出错,因为只有一台DataNode);yarn-site.xml设置ResourceManager地址和内存相关参数。配置文件修改完毕后先格式化NameNode:hdfs namenode -format,格式化之前必须确保HDFS没有启动,否则会报错。
启动验证阶段,执行start-dfs.sh和start-yarn.sh,然后用jps命令检查进程,看到NameNode、DataNode、ResourceManager、NodeManager四个进程就说明基本成功了。浏览器访问9870端口可以看到HDFS管理界面,访问8088端口是YARN的资源管理器界面。
3.2 MapReduce离线统计任务设计
Hadoop的价值要在具体计算任务中体现,我给这套系统设计了三个统计维度。
价格区间统计任务以CSV格式的酒店数据为输入,map阶段按城市分组,读取每条酒店记录的价格字段,根据价格区间阈值输出“城市+区间”作为key,1作为value。reduce阶段累加计数,最终输出每个城市各区间的酒店数量。这样就能得到“北京经济型酒店多少家、高端酒店多少家”这类统计结果。
评论热度统计任务分析每条评论的长度和数量,map阶段按城市输出评论总数和评论总长度,reduce阶段计算平均评论长度,用于衡量用户在哪些城市更愿意写长评。
区域热门酒店排行任务按城市分组统计酒店评分总数,reduce阶段取Top10,得到每个区域口碑最好的酒店列表。这些结果落回MySQL后,前端可视化就直接有数据可用了。我把三个任务统一打成一个jar包提交到YARN,通过命令行执行hadoop jar提交运行。整个过程跑下来,Hadoop部分的工作量在答辩时是肉眼可见的大。
3.3 分析结果回写业务库的策略
MapReduce计算完成后输出到HDFS的结果,需要导回MySQL供业务系统使用。推荐的做法是在每个Reduce任务的cleanup阶段,通过JDBC连接MySQL直接写入统计表。这样任务跑完数据自动落库,后续只要定期重新触发统计任务就能刷新数据。
另一条可行路径是先把结果输出为CSV文件到HDFS,再通过sqoop将文件导入MySQL。但毕设环境里额外装sqoop太重了,我建议直接JDBC写入。这里有个体验上的小技巧:把统计表的写入操作封装在Reduce的setup方法里建立连接,在cleanup里批量写入,避免每条记录都开关一次数据库连接。
4. 协同过滤推荐算法:核心原理与落地方案
推荐算法是这套系统的门面,也是答辩时候选人一定会被深挖的地方。讲清楚原理、写清楚代码、分析清楚效果,这块做好了整个项目的技术深度就有了。
4.1 协同过滤的基本思想与选型逻辑
协同过滤的核心思想概括成一句话就是:人以群分,物以类聚。基于用户的协同过滤(UserCF)找到与当前用户兴趣相似的其他用户,把那些相似用户喜欢的酒店推荐给当前用户;基于物品的协同过滤(ItemCF)找到与用户曾经喜欢的酒店相似的酒店,然后推荐出去。
那这个场景到底选UserCF还是ItemCF?我的建议是两个都实现,推荐结果按权重混合。原因是住宿场景有很强的地域属性,用户在不同城市的需求差异巨大。一个用户在北京喜欢高档酒店,不代表他在成都也想住高档酒店,纯UserCF在这种场景下效果不稳定。ItemCF基于物品相似度计算,结果更容易解释,用户也更容易接受“因为你喜欢这些酒店,所以推荐相似酒店”的逻辑。把两种算法的结果按6比4加权混合,实测效果和解释性都更好。
4.2 评分矩阵构建与相似度计算
协同过滤的一切计算都建立在评分矩阵之上。矩阵的行是用户,列是酒店,值是对应评分。
在真实项目中用户对酒店的显式评分很少,需要从行为数据构造隐式评分。一个可以直接使用的经验公式是:评分等于打分的平均值乘以评论情感得分系数,情感得分范围为0.8到1.2。用户对某酒店评论了且打高分,评分就高;如果只看过没评论,评分设为默认中值3分。这样做的好处是把稀缺的显式偏好和一抓一大把的浏览行为结合起来,矩阵的稀疏度会明显下降。
相似度计算采用调整后的余弦相似度。直接计算用户对两件物品评分的余弦夹角,公式上是两个评分向量的点积除以两个向量模长的乘积。但实际使用中需要先做均值中心化处理,减去该用户的平均评分再算余弦,否则用户打分习惯的不同会严重干扰相似度结果。喜欢打高分的用户和严格打低分的用户可能实际偏好完全一致,但不做中心化时相似度却很低。
为了让小白也能理解,打个比方:两个人都喜欢住江景房,一个习惯给所有酒店打4分以上,一个从不超过3分,原始分数毫无可比性。把每个人的评分减去自己的平均分后,正负号才真正反映“这个人对这个酒店的态度是偏高还是偏低”。
4.3 推荐生成与冷启动处理
预测用户对未评分酒店的评分,核心计算逻辑是找到与目标用户最相似的K个用户(通常K取10到20),用这K个用户对该酒店的评分加权平均得到预测值,权重就是相似度。加权平均的目的是让更相似的用户评价比重更大。
纯协同过滤的致命伤是冷启动问题。新用户没有任何行为数据,新酒店也没有任何评分记录。这个问题必须在毕设里给出解决方案,否则答辩会被问到卡壳。我的方案是一个混合推荐策略:对没有行为记录的新用户,直接返回热门酒店推荐,热度分等于平均评分乘以评论数的对数,既考虑口碑也考虑被关注程度;对没有评分记录的新酒店,使用基于内容的推荐兜底,计算它与用户依据地理位置和设施标签产生的偏好之间的匹配度,同时结合同城市同价位区间的热门酒店作为补充。
4.4 协同过滤核心代码实现
下面是关键代码的骨架,完整代码在项目里也就一百多行:
import numpy as np import pandas as pd from sklearn.metrics.pairwise import cosine_similarity def build_user_item_matrix(ratings_df): # 构建用户-酒店评分矩阵,缺失值填0 matrix = ratings_df.pivot_table( index='user_id', columns='hotel_id', values='rating' ).fillna(0) return matrix def center_rating(matrix, user_mean): # 均值中心化:减去每个用户的平均评分 return matrix.sub(user_mean, axis=0) def user_based_cf(matrix, target_user, top_k=15): # 计算目标用户与其他用户的余弦相似度 user_sim = cosine_similarity(matrix) sim_df = pd.DataFrame(user_sim, index=matrix.index, columns=matrix.index) # 取最相似的K个用户 target_sim = sim_df[target_user].sort_values(ascending=False)[1:top_k+1] # 加权平均预测评分 target_vector = matrix.loc[target_user] unrated_hotels = target_vector[target_vector == 0].index predictions = {} for hotel in unrated_hotels: weighted_sum = 0 sim_sum = 0 for other_user, sim_value in target_sim.items(): rating = matrix.loc[other_user, hotel] if rating > 0: weighted_sum += sim_value * rating sim_sum += sim_value if sim_sum > 0: predictions[hotel] = weighted_sum / sim_sum # 按预测分排序取TopN top_n = sorted(predictions.items(), key=lambda x: x[1], reverse=True)[:10] return top_n实际项目里比这段代码要完整得多,包含了物品相似度预计算缓存、两种算法的结果融合、冷启动兜底分支。但核心逻辑就是上面这两三部曲;矩阵构建、相似度计算、加权预测。只要把这段思路讲清楚,面试官就知道你是真写了代码而不是拿现成文件充数。
5. Django后端与Vue前端:接口设计与可视化联动
Hadoop跑出数据,推荐算法算出结果,最终都要通过Web系统呈现出来。这部分的工程质量直接决定了演示效果。
5.1 Django项目结构与RESTful接口设计
Django项目我采用标准的MVT加上DRF(Django REST Framework)扩展,后端整体变成提供JSON数据的API服务器,前端页面完全由Vue接管。项目内部按功能拆分成几个app,包括用户管理app、酒店数据app、推荐引擎app、数据统计app。
API设计遵循RESTful风格,核心接口覆盖整个系统功能面:
| 接口路径 | 方法 | 功能说明 |
|---|---|---|
| /api/register | POST | 用户注册 |
| /api/login | POST | 用户登录,返回JWT令牌 |
| /api/hotels | GET | 酒店列表,支持分页与关键词过滤 |
| /api/hotels/ | GET | 查看酒店详情 |
| /api/recommend/<user_id> | GET | 获取个性化推荐列表 |
| /api/statistics/hotel-price | GET | 返回价格分布统计 |
| /api/statistics/region-ranking | GET | 返回区域排行统计 |
| /api/user/ /ratings | POST | 提交用户评分 |
Django后端实现时几个关键点要特别注意。用户认证我使用simplejwt插件,签发JWT令牌,前端每次请求在Authorization头带上Token,请求需要登录才能访问的接口时在视图函数上加permission_classes修饰。跨域问题使用django-cors-headers解决。在settings.py中配置CORS_ALLOW_ALL_ORIGINS为True,开发阶段直接全放行,部署时再收紧。
酒店列表与统计接口使用Django的ORM查询,统计结果表是MapReduce任务写入到MySQL的,ORM模型和表结构完全对应,查询周期极短。推荐接口内部调用推荐引擎模块,先查用户评分数据,再走到推荐算法代码,最后返回酒店ID列表并关联出酒店完整信息。
5.2 Vue前端项目结构与可视化组件开发
Vue前端使用Vue 3加Vite构建,相比Vue 2加Webpack体验上轻快很多。项目目录下按页面拆分为登录页、数据看板页、酒店列表页、推荐结果页、个人中心页。路由使用vue-router,数据请求使用axios封装统一的request模块,在拦截器里自动附加JWT令牌,响应错误时统一处理提示和跳转。
数据看板页是可视化的重头戏,我用ECharts实现了四个核心图表。第一张地图展示不同城市的酒店数量和均价热力,地图数据需要GeoJSON,网上能找到免费的全国地级市GeoJSON数据,加载后配置visualMap组件将均价映射到颜色深浅。第二张柱状图展示各城市酒店价格区间分布,横向维度是价格区间,纵向是酒店数量。第三张饼图展示酒店星级分布。第四张词云图基于Hadoop统计和分词处理后的评论热点词生成,需要echarts-wordcloud扩展包。
图表数据全部从后端API动态获取,页面加载时统一发起请求,拿到数据后通过computed属性转换成ECharts需要的格式。这里有个高频坑:ECharts图表数据更新时,如果使用setOption时没有设置notMerge参数,旧数据容易残留。我的做法是在每次更新前先调用chart.clear(),再调用setOption,彻底避免数据错乱问题。
5.3 前端页面与后端接口的联动调试
前后端分离开发最让人头疼的就是联调阶段。前端开发服务器跑在5173端口,后端跑在8000端口,跨域问题无法回避。除了后端启用cors-headers外,前端还需要在vite.config.js里配置devServer的proxy代理,把以/api开头的请求全部代理到后端服务。
export default defineConfig({ server: { port: 5173, proxy: { '/api': { target: 'http://127.0.0.1:8000', changeOrigin: true } } } })配置完成后,前端请求写/api/xxx而不是完整地址,开发时完全感觉不到跨域的存在。本地开发时,前后端要在两个终端分别启动,后端用python manage.py runserver,前端用npm run dev。联调阶段常用的技巧是先用浏览器直接访问后端API地址验证接口数据,再回到前端页面调试渲染逻辑,逐步定位问题出在接口侧还是界面侧。
6. 常见问题与避坑经验
系统开发过程中一定会遇到各种奇奇怪怪的报错,我把几个最常见、最容易卡住的问题整理成清单,每一条都是实打实踩过的坑。
6.1 爬虫数据质量问题
爬虫最大的坑是数据抓回来清洗不干净导致的连锁反应。有一版我只做了去重没做价格区间的异常过滤,结果推荐算法跑出来的TopN酒店全部是单价9.9元或者99999元的异常记录。排查了大半天才定位到是脏数据问题。这里提醒大家,数据清洗不是在爬虫写完以后再做,而应该在字段设计阶段就同步考虑规则。每跑完一轮爬虫,先打印几条样本数据肉眼检查一遍,再考虑入库。
6.2 Hadoop环境启动失败
Hadoop启动不了大概能占到大数据方向毕设问题的一半。进程起不来先别急着翻日志,按顺序排查:先确认hostname是否和配置中的一致,然后确认ssh免密能否通过,接着确认JAVA_HOME是否配置正确,最后查看logs目录下对应进程的日志文件。NameNode起不来的高频原因之一是磁盘空间不足,默认存储在/tmp目录下,经常跑着跑着临时文件爆掉。建议在core-site.xml里把hadoop.tmp.dir改成自己指定的目录,一劳永逸。
6.3 Django执行查询与删除对象时的认知陷阱
Django的ORM对新手有一套隐藏的坑。用QuerySet批量删除对象时,模型里重写的delete方法不会被执行,只有遍历逐个删除才会触发。当时为了清理测试数据,调用filter().delete()后以为自定义逻辑会走,结果日志里什么都没输出,排查了很久才发现是这个机制。另外执行查询时,QuerySet是惰性的,只有真正需要数据时才访问数据库,分析性能问题时要留意数据库实际执行的查询次数。类似细节在系统设计文档里写清楚,答辩时反而能成为加分点,说明你对框架的运行机制有深入理解。
6.4 协同过滤的推荐效果不好排查
如果推荐出来的酒店用户完全不想看,优先检查评分矩阵的稀疏度,用户平均行为数量只有一两条的话,相似度计算全凭噪声数据,结果基本是随机的。解决的办法是两个,一个是增加隐式评分数据,把浏览、收藏行为也折算成评分;另一个是K值调参,K取太大把不相似的用户圈进来,K取太小结果随机波动大。我最终把K定为15,两种算法加权比6比4,实测是平衡效果和稳定性的较优组合。
6.5 Vue安装依赖与环境配置的坑
Vue项目最容易卡在依赖安装阶段,不同Node版本对依赖兼容性影响极大。我用了Node 18,安装Vite 4加Vue 3的组合目前最稳。npm install偶尔会因为网络问题失败,可以换用国内镜像源。前端报错里有一类特别常见:模块类型不匹配,比如某个组件用了require引入ESM模块。解决思路是统一用import语法,配置package.json里的type字段为module,保持代码风格一致,能避免大量莫名其妙的报错。
6.6 前后端联调时的鉴权问题
JWT令牌过期用户被强制下线,这个逻辑本身正常,但前端要处理好过期后的自动刷新和跳转。我的做法是在axios响应拦截器里判断HTTP 401状态码,清除本地存储的用户状态,跳转回登录页并提示登录已过期。注意不要把用户手动退出登录也误判成Token过期,需要根据后端返回的具体错误码区分。
写在最后的一些经验分享
整套系统做完,我最大的体会是毕设项目的节奏控制很重要。标准化流程是先花两周时间搭好技术骨架:Hadoop环境、Django项目、Vue项目全部跑通最小可用版本;再花三周时间填充业务功能:爬虫写完能存数据,推荐算法有初步结果,可视化图表有数据可画;最后两周做联调、打磨界面、准备演示数据和答辩材料。前期只要把骨架搭对,后面填肉非常快。最怕的就是一开始就纠结算法效果好不好、图表做的精不精美,本末倒置。
最后再分享一个演讲和演示的小技巧:答辩前一定准备一份干净的演示数据,数据量不用大但分布要有特点,让算法推荐结果里有一眼就看出“这个推荐有道理”的案例。演示时从用户浏览某个酒店开始,到推荐结果中出现同类酒店收尾,评委对你的系统理解深度马上不一样。毕业设计这东西,做完只是第一步,能讲明白才是真的本事。