这几天又有人私信我毕业设计到底做什么题好说实话每次看到“XX管理系统”“XX商城”这类题目我都想多问一句你要是答辩老师连续看了八个一模一样的管理系统还会觉得这个选题有价值吗我自己去年做的是“基于Nodejs和Vue框架的短剧推荐系统设计与实现”从选题、搭框架、调推荐算法、写论文到准备答辩整个过程踩了不少坑今天就把完整的从0到1思路整理出来。这篇内容覆盖几个关键点短剧推荐系统的需求怎么拆、Node.js Vue这套组合为什么适合做毕设项目、推荐算法标签向量相似度、UserCF协同过滤、混合排序代码级怎么落地、m3u8分集播放怎么接、环境配置阶段几乎人人会遇到的npm和PowerShell报错怎么处理以及论文的创新点应该怎么写。适合两类人看一类是正在纠结毕业设计选题的计算机相关专业学生另一类是打算自学Node.js和Vue全栈、想用一个完整项目练手的开发者。1. 为什么我劝你今年别再选“XX管理系统”了短剧推荐系统的选题价值拆解1.1 短剧内容消费的推荐逻辑和长视频完全不同短剧这几年的增长速度不用我多说了。单集一两分钟、竖屏、节奏极快用户刷起来是一集接一集根本停不下来。这种内容形态和传统长视频最大的区别在于用户很少主动搜索而是高度依赖平台“推什么看什么”。长视频平台用户有明确的剧名记忆想看《某某传》会自己去搜短剧用户记住的是“那个霸总的”“那个穿越的”内容本身高度同质化唯一能帮用户做筛选的就是推荐系统。所以短剧平台的核心竞争力一半在内容供给另一半就在推荐分发。这一行业背景放到毕业设计里特别有意义它不是一个凭空捏造的需求而是真实存在、真实被重视的业务场景。我在确定题目之前专门去翻了一些短剧小程序和数据报告发现它们首页基本都是“猜你喜欢”“大家都在看”“新剧上新”这类信息流推荐位播放详情页底部也会挂“喜欢这部的人也喜欢”。这些位置背后全是一套推荐算法在支撑。1.2 用户侧、运营侧、管理侧的需求拆解标题里写了“设计与实现”论文里必然要过需求分析这一关。很多人写需求分析就是随便列几条答辩一追问就露馅。我当时是老老实实把系统拆成三个视角。用户端要解决的是“帮用户省时间”。用户需要注册登录、设置自己的题材偏好打开首页能看到推荐信息流点进去能看短剧详情和分集列表播放器支持m3u8格式的连续播放看的过程中能点赞、收藏、分享这些行为又会反过来影响后续推荐。运营端要解决的是“让内容流动起来”。运营人员要能维护短剧信息封面、简介、题材标签、主演、管理分集视频、设置推荐位和热榜权重至少得能看见每部剧的播放量、点赞数这一类基础统计。管理端要解决的是“权限和基础数据”。管理员管理用户状态分配运营角色审核上架内容。我用三张表就把这个需求串起来了后面第4章会详细讲表结构。1.3 这个题目的难度和工作量为什么刚刚好毕设选题最怕两件事太简单没东西写太难完不成。短剧推荐系统恰好卡在中间。对比“XX管理系统”管理系统核心就是增删改查没有任何算法成分论文里只能堆页面截图创新点无中生有。对比“电商系统”订单、库存、支付、物流链路太长单人开发做不完即使做完了答辩老师也未必关心。短剧推荐系统呢业务闭环清晰——用户进来、有了行为、系统推荐、用户再看天然自带一个“推荐算法”的亮点。技术栈上前端Vue3 后端Node.js MySQL都是成熟技术2026年这个时间点各种教程、踩坑文档、组件库都非常丰富一个人完全Hold得住。推荐算法不需要做得多高深能把基于内容的相似度计算和UserCF协同过滤实现出来配合一个混排公式论文里就能写出一套完整的算法设计与实验对比这个学术感比管理系统强太多了。2. 选型不是拍脑袋Node.js Vue组合的合理性分析2.1 为什么是Node.js而不是Spring Boot这个问题几乎每个答辩老师都会问所以一定要有一个站得住脚的理由。我自己当时是从三个方面论证的。第一单人开发效率。毕设绝大多数是一个人做Java体系重、样板代码多写个实体类、写个Mapper、配一套SSM框架花在“和业务无关”的时间上太多。Node.js用Express或者Koa写接口非常快路由即函数中间件机制又简单一个通用鉴权中间件几十行代码就搞定。第二前后端语言统一。前端Vue是JavaScript后端Node.js也是JavaScript类型思维和工具链完全一致。改接口参数、调数据结构、处理日期格式前端能直接读懂后端代码不用在Java和JS之间来回切换心智。特别是做推荐算法实验的时候我在后端写一套相似度计算的函数前端理清逻辑也毫不费力这在论文写作阶段会省很多事。第三I/O密集型场景匹配。推荐系统最大的运行时压力不在计算而在大量的并发读写——用户上报行为、拉取推荐列表、播放分集记录这些都是I/O密集操作。Node.js的事件循环模型恰恰擅长这类场景。哪怕算法离线计算不依赖Node服务器实时跑Node也完全够用。这里要诚实地说一句如果你的推荐模块要做非常重的大规模矩阵运算Node不是最优解但毕设级别的数据量几百部短剧、几千条行为记录完全在舒适区内。我做了一个简单的对比表可以直接用在论文的“相关技术”章节。对比维度Spring BootNode.js (Express)开发效率中样板代码较多高轻量灵活前后端语言不统一Java / JS统一JS / JS学习曲线偏陡依赖生态复杂平缓上手快高并发I/O多线程模型资源占用较高事件循环I/O密集场景有优势毕业设计适用性适合大型团队协作项目适合单人快速交付2.2 前后端分离的总体架构与数据流整个系统采用前后端分离架构前端Vue3 Vite构建负责页面渲染和用户交互后端Express提供RESTful API负责业务逻辑、鉴权和数据读写MySQL存储业务数据Nginx在部署阶段承担两个角色一是托管前端打包后的静态文件二是把/api开头的请求反向代理到Node服务。数据流我建议按这条链路去理解论文里可以画数据流图代码里照着这个逻辑走就行了用户打开首页 → 前端调用GET /api/recommend/feed→ 后端拿到当前用户ID → 先从推荐缓存表取离线算好的结果 → 没有缓存就用冷启动策略热榜新剧运营位 → 返回短剧列表带推荐理由文案 → 用户点击播放 → 前端拉取/api/drama/:id/episodes拿到m3u8地址 → video.js播放分集 → 播放过程中前端自动上报行为日志 → 后端落库 → 每日凌晨定时任务重算推荐结果。2.3 项目目录、依赖版本与开发环境清单目录结构我直接贴我当时的工程已经经过一次重构比最初合理很多。前端叫short-drama-web后端叫short-drama-server数据脚本放db目录。short-drama/ ├── short-drama-web/ # Vue3 前端 │ ├── src/ │ │ ├── api/ # axios请求封装 │ │ ├── views/ # 页面组件首页/详情/播放/登录/管理后台 │ │ ├── router/ # 动态路由 │ │ ├── store/ # Pinia状态管理 │ │ ├── components/ # 短剧卡片/推荐位/播放器 │ │ └── utils/ # 工具函数 │ ├── vite.config.js │ └── package.json ├── short-drama-server/ # Node.js 后端 │ ├── routes/ # 路由drama/user/recommend/behavior │ ├── controllers/ # 业务逻辑 │ ├── services/ # 推荐算法与定时任务 │ ├── middlewares/ # JWT鉴权等 │ ├── db/ # 数据库连接池 │ └── app.js └── db/ ├── schema.sql # 建表语句 └── seed.sql # 测试数据依赖版本方面后端我用的是Express 4.x、mysql2用连接池方式访问MySQL、jsonwebtoken、bcryptjs、node-schedule。前端是Vue 3.4 Vite 5 Vue Router 4 Pinia视频播放用hls.js因为hls.js对m3u8的处理比video.js自带的contribution-hls更灵活。建议你装nodemon做后端热更新改完代码不用手动重启开发体验提升一大截。3. 推荐模块到底怎么落地标签向量化、UserCF与混合排序推荐系统是这篇设计的灵魂也是论文里最能出彩的部分。但说实话很多同学在推荐模块上容易翻车翻车的原因不是算法看不懂而是数据压根没设计好。所以我先从埋点讲起。3.1 行为埋点推荐系统做不好八成是这一步没想清楚没有行为数据任何推荐算法都是空谈。你做出来的系统里如果只有注册和登录没有用户和短剧之间的交互记录那“推荐系统”四个字就名存实亡。我的做法是在前端播放器组件里埋点。用户发生四类关键行为时向POST /api/behavior/report发送一条日志点击播放、播放时长变化每30秒上报一次、点赞/取消点赞、收藏/分享。后端把这批数据落进user_behavior表。行为类型我统一用字符串type字段标识方便扩展。// 前端播放器埋点示例简化版 player.on(playing, () { report({ dramaId: currentDramaId, episodeId: currentEpisodeId, type: play, duration: 0, }); }); player.on(timeupdate, () { const currentTime Math.floor(player.currentTime()); if (currentTime - lastReportTime 30) { report({ dramaId: currentDramaId, episodeId: currentEpisodeId, type: duration, duration: currentTime, }); lastReportTime currentTime; } });离线计算时我会把行为折算成隐式评分。这个折算规则不要太复杂但一定要在论文里写清楚它是你算法实验的数据基础。我当时用的权重组合是播放完成给5分播放时长超过50%给3分点赞给6分收藏给10分分享给12分。为什么收藏比点赞分值高因为收藏行为的成本高用户主动“想留着之后看”意图强度明显大于随手点赞。3.2 基于内容的推荐把短剧变成标签向量基于内容推荐的核心思想是“物以类聚”。给每部短剧打上标签把标签转成向量计算两个向量的余弦相似度相似度高的短剧互相推荐。短剧的标签体系和长视频不一样题材是第一优先级。我建了一套标签库甜宠、虐恋、逆袭、战神、悬疑、萌宝、穿越、重生、家庭伦理、都市职场每个短剧可以打多个标签。此外我还给每个标签手动标注了一个权重比如一部剧的主标签权重是1.0次要标签是0.6。这个权重可以体现在drama_tag关联表里。余弦相似度公式不复杂就是两个向量的点积除以模长乘积。下面是我在后端services/similarity.js里的实现可以直接抄function cosineSimilarity(vecA, vecB) { const keys new Set([...Object.keys(vecA), ...Object.keys(vecB)]); let dot 0, normA 0, normB 0; for (const key of keys) { const a vecA[key] || 0; const b vecB[key] || 0; dot a * b; normA a * a; normB b * b; } if (normA 0 || normB 0) return 0; return dot / (Math.sqrt(normA) * Math.sqrt(normB)); }举个例子A剧标签向量是{甜宠: 1.0, 都市: 0.6, 职场: 0.6}B剧是{甜宠: 1.0, 都市: 0.6, 霸总: 0.8}C剧是{悬疑: 1.0, 推理: 0.8}。A和B都有甜宠和都市两个维度相似度算下来会明显高于A和C。这说明B应该在A的详情页“喜欢这部的人也喜欢”里排前面。算完所有短剧两两相似度之后离线写入drama_similar表用户请求详情页时直接按相似度倒序查询不用实时计算响应时间就是一次索引查询。3.3 UserCF协同过滤找到口味相同的“短剧搭子”基于内容推荐有一个明显弱点它只知道剧和剧像不知道“你这个人和别人像”。所以第二路推荐我做了UserCF基于用户的协同过滤思路是“人以群分”。找到和你口味最像的N个用户把他们看过而你没看过的短剧推荐给你。UserCF分三步走。第一步从user_behavior表里把用户对短剧的隐式评分聚合出来形成“用户-短剧”矩阵。第二步计算用户之间的相似度同样可以用余弦相似度只是把向量从“短剧标签”换成“用户对短剧的评分列表”。第三步取TopK个相似用户把他们评分高的短剧集合起来按相似度加权求和得到候选剧目的得分。// UserCF 核心逻辑简化版 function recommendForUser(userId, topK 10) { const matrix buildUserDramaMatrix(); const targetUser matrix[userId]; if (!targetUser) return []; const simUsers []; for (const otherId in matrix) { if (otherId userId) continue; const sim userSimilarity(targetUser, matrix[otherId]); simUsers.push({ userId: otherId, sim }); } simUsers.sort((a, b) b.sim - a.sim); const candidates simUsers.slice(0, topK); // 加权聚合候选短剧 const scoreMap {}; for (const { userId: otherId, sim } of candidates) { const watched matrix[otherId]; for (const dramaId in watched) { if (targetUser[dramaId]) continue; // 已经看过的跳过 scoreMap[dramaId] (scoreMap[dramaId] || 0) sim * watched[dramaId]; } } return Object.entries(scoreMap) .sort((a, b) b[1] - a[1]) .slice(0, 20) .map(([dramaId]) dramaId); }这里有个实际场景要注意用户行为矩阵非常稀疏。绝大多数用户只看过十几部短剧而且评分维度也少。如果直接拿原始矩阵算相似度结果容易失真因为两个用户共同看过的剧可能很少。一个缓解办法是只统计“共同观看超过2部”的用户对把冷门用户过滤掉宁缺毋滥。3.4 混合排序与冷启动兜底两路推荐各有偏科所以最终排给用户的列表要用一个融合公式。我用的线性加权是最终得分 0.5 * 内容相似度得分 0.3 * 协同过滤得分 0.2 * 热度得分热度得分怎么算设计了一个带时间衰减的简单公式hotScore log(播放量 1) 0.1 * 点赞数 时间衰减系数。短期热播剧会被拉起来老剧不会霸榜。这个热力分我已经在工程里实现成一个工具函数跑批时和推荐分一起算好存进推荐缓存表。冷启动分两种。新用户没有任何行为数据协同过滤用不了内容相似也缺“种子剧”这时候直接走兜底策略全站热榜占60%、新剧榜占30%、运营手动配置的推荐位占10%。新剧榜非常关键因为短剧生命周期短用户要的就是尝鲜。运营可以在后台给指定短剧设置推荐权重这属于“人为干预位”在实际产品里也是标配。混合排序之后还有一个去重规则同一个题材连续出现两部以上就往下调一级。用户连续刷到三部霸总剧会疲劳这个去重逻辑虽然简单但能明显提升体验。我习惯称它为“题材打散”论文里也可以作为一个小的优化点写进去。4. 后端实现笔记表结构设计、JWT鉴权与m3u8视频接口4.1 数据库设计六张核心表撑起整套系统后端能撑起整套业务逻辑数据库设计是地基。我最终落地的核心表一共六张名字和用途如下表名用途关键字段user用户账号与角色id, username, password_hash, role, avatar, created_atdrama短剧元信息id, title, cover, description, status, score, total_episodesepisode分集信息id, drama_id, episode_no, title, video_url, durationtag标签字典id, name, categorydrama_tag短剧与标签多对多关联drama_id, tag_id, weightuser_behavior用户行为日志id, user_id, drama_id, episode_id, type, duration, created_at另外还有两张辅助表drama_similar存离线算好的短剧相似关系recommend_cache存用户或匿名用户组的推荐结果缓存。这两张表是推荐模块的“性能开关”如果没有它们每次请求都实时跑一遍算法接口响应会很难看。建表的时候我踩了一个小坑分享给你们。user表在MySQL里是关键字虽然加反引号也能建但后面写SQL时到处都要加反引号非常烦。我直接改用sys_user命名省了一堆麻烦。短剧的video_url字段要注意字符集m3u8地址可能带长查询参数我用的是varchar(500)如果是腾讯云或阿里云的私有签名URL会更长建议直接上TEXT类型提前避免字段溢出。4.2 JWT鉴权与接口中间件后端所有需要登录态的接口都走JWT鉴权。登录成功时后端用jsonwebtoken签发一个有效期7天的token返回给前端。前端把token存在localStorage里每次axios请求带上Authorization: Bearer token。中间件我单独抽了一个文件任何需要鉴权的路由挂上它就行const jwt require(jsonwebtoken); module.exports function authMiddleware(req, res, next) { const header req.headers.authorization || ; const token header.replace(Bearer , ); if (!token) return res.status(401).json({ code: 401, msg: 未登录 }); try { req.user jwt.verify(token, process.env.JWT_SECRET); next(); } catch (err) { return res.status(401).json({ code: 401, msg: 登录已过期 }); } };管理员和运营角色的路由还要再加一层roleMiddleware里面判断req.user.role是不是admin或operator。答辩时老师经常会问“前端隐藏按钮和后端校验哪个安全”答案永远都是后端校验才是真安全。前端只是视觉上隐藏后端中间件才是真正卡住越权的防线。4.3 m3u8分集视频的存储与接口设计短剧视频是分集的每一集一个独立的m3u8索引文件加一堆ts分片。我在项目的media目录下按dramaId/episodeNo组织文件media/ └── 101/ ├── 1/index.m3u8 ├── 1/seg-1.ts ├── 1/seg-2.ts └── 2/index.m3u8m3u8怎么来的用ffmpeg把mp4转成HLS流一行命令就能出片。虽然毕设阶段直接放mp4也能跑但m3u8才是真实短剧平台的标准分发格式答辩时能讲清楚为什么要切片是一个加分项。ffmpeg -i episode1.mp4 -codec: copy -start_number 0 -hls_time 10 -hls_list_size 0 -f hls index.m3u8后端接口按短剧ID返回分集列表播放器按顺序播放。这个接口我建议挂上鉴权防止被人直接遍历拉走整部剧。如果你想要防盗链的细节还可以给m3u8地址加一个带过期时间的签名参数Nginx用secure_link模块校验。毕设阶段做不做都行但论文里提到“防盗链设计”会显得系统考虑得更周全。4.4 离线推荐任务的定时调度推荐结果不能每次都现算否则并发一上来服务就废了。我用node-schedule做了一个每日凌晨3点的离线任务。const schedule require(node-schedule); schedule.scheduleJob(0 3 * * *, async () { console.log([推荐任务] 开始计算); await computeDramaSimilarity(); // 1. 重算短剧-短剧相似度 await computeUserCF(); // 2. 重算用户协同过滤 await refreshHotScore(); // 3. 刷新热度分 await cacheAllRecommendations(); // 4. 写回recommend_cache console.log([推荐任务] 执行完成); });选题的时候你可能会想既然每天才跑一次为什么不在用户请求的时候同步算因为短剧的内容量虽然在增长但和电商那种海量商品不能比几百部短剧的两两相似度算一次只要几秒完全没必要让在线服务扛这个计算量。离线跑批还有一个好处可以在跑批日志里输出统计指标比如候选集平均覆盖率、推荐列表的题材分布这些数据后期写进论文测试章节就是现成的图表素材。5. 前端实现笔记动态路由、m3u8播放器与推荐信息流5.1 动态路由不同角色看到不同的页面前端不是把所有人的页面都写死而是登录后根据角色动态注册路由。我的router/index.js里定义了一个基础路由登录页、注册页登录接口返回role字段后前端判断是普通用户还是管理员再通过router.addRoute挂载对应模块。const adminRoutes [ { path: /admin, component: Layout, children: [ { path: drama, component: () import(/views/admin/DramaManage.vue) }, { path: episode, component: () import(/views/admin/EpisodeManage.vue) }, { path: tag, component: () import(/views/admin/TagManage.vue) }, // ... ], }, ]; adminRoutes.forEach(route router.addRoute(route));路由守卫里判断是否登录、角色是否匹配没有权限直接重定向到首页。这里有一个很多新手会犯的错误光在路由守卫里判断还不够如果管理员退出登录换成普通用户登录动态添加过的路由不会自动移除。我在退出登录方法里把所有动态路由名收集起来逐个router.removeRoute()清除再跳转登录页。这个问题你可以在答辩时主动提出来说明你真的处理过用户态切换的边界情况。5.2 Vue里接入m3u8播放器的正确姿势网上搜“vue播放m3u8”会看到一大堆教程有些让你装video.js加videojs-contrib-hls有些让你装hls.js。我实际测试下来hls.js在Chrome桌面端和移动端WebView的兼容性更好包体积也更小所以推荐直接用hls.js。核心代码如下逻辑非常直接先检查Hls.isSupported()支持就用hls.js加载m3u8地址挂到video元素上不支持一般是老iOS Safari就降级为原生播放因为iOS原生支持Apple HTTP Live Streaming。import Hls from hls.js; function playM3u8(videoElement, url) { if (Hls.isSupported()) { const hls new Hls({ enableWorker: true, maxBufferLength: 30 }); hls.loadSource(url); hls.attachMedia(videoElement); hls.on(Hls.Events.MANIFEST_PARSED, () { videoElement.play(); }); } else if (videoElement.canPlayType(application/vnd.apple.mpegurl)) { videoElement.src url; videoElement.play(); } }开发时有一个大坑我必须强调本地起前端项目默认5173端口访问后端静态视频目录默认3000端口或Nginx映射路径很容易因为跨域导致m3u8加载失败。浏览器网络面板里m3u8请求状态是200但ts分片全部报CORS错误。解决办法是在后端开发阶段给/media/路径加上允许跨域的响应头生产环境由Nginx统一控制。我因为这个问题排查了整整一个晚上一度以为hls.js配置错了。播放器的交互层面我还加了几个细节进入播放页自动全屏、竖屏短剧锁定竖屏比例、播放结束后自动加载下一集。自动连播和推荐系统是配套的——你看完一集后端根据你当前的观看行为实时更新偏好向量下一集的推荐位才会精准。5.3 推荐信息流的组件设计与懒加载首页信息流是整个前端最核心的页面。我拆了三个组件DramaCard.vue负责单条短剧卡片展示RecommendFeed.vue负责列表聚合和分页RecommendReason.vue负责展示推荐理由标签。这里的推荐理由不是随便写的而是后端在推荐结果里带上reason_type字段前端映射成不同的文案。看到“因为你看过《xxx》”的标签说明这条来自内容相似推荐看到“和你口味相似的人也在看”的标签说明来自UserCF看到“全网热播”就是热度推荐。这个设计在产品层面很实用它让用户觉得推荐结果有解释不是黑盒写在论文里也很有说服力推荐可解释性本来就是推荐系统研究的一个关注点。列表的懒加载我用的是IntersectionObserver滚动到底部附近时加载下一页。每次请求带上page和pageSize后端从recommend_cache分页取数据。分页这里有个细节不要在后端返回数据后再随机打乱而是后端一次性返回排序好的列表前端只负责按游标递增取片段。这样能保证用户两次下拉刷新之间的顺序稳定不会出现重复剧集。6. 环境配置与部署排坑npm.ps1报错、镜像源和Nginx反向代理6.1 Node.js和Vue环境配置里最容易卡住的三个点先说Node.js安装。官网下载LTS版本偶数版本不要图新鲜装Current版。装完之后验证环境变量有没有生效PowerShell里输node -v和npm -v如果都能打印版本号说明安装成功。如果提示node不是内部命令90%是因为安装时没有勾选“Add to PATH”要么重装要么手动把Node安装目录加进系统环境变量Path。第二个容易卡的点是npm官方源在国内下载慢。Vue项目跑npm install卡在reify半天不动基本都是网络问题。解决方案是切到国内镜像源我常用的是npmmirror。npm config set registry https://registry.npmmirror.com npm config get registry切完之后再npm install速度体感提升非常明显。如果个别包在镜像源上版本滞后还可以单独指定某个包的安装源但毕设项目很少遇到这种情况。第三个点Vite项目启动时版本不兼容。如果你按网上的旧教程装了Vite 2然后用了Vue 3.5的新语法大概率会报各种奇怪的依赖错误。我的建议是初始项目直接按官方指引创建npm create vuelatest它会自动帮你配好Vite、Vue Router、Pinia的兼容版本比自己手动拼靠谱得多。需要扩展Element Plus之类的UI库时再按需安装就行。6.2 npm.ps1执行策略报错与国内镜像源“npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本”——这个报错在Windows上出现率极高因为PowerShell默认的执行策略是Restricted不允许运行脚本文件。而npm的shell脚本正是以.ps1结尾的。解决方法两种一是用管理员身份打开PowerShell执行Set-ExecutionPolicy RemoteSigned -Scope CurrentUser然后输入Y确认。RemoteSigned的意思是本机脚本可以运行从网上下载的脚本必须有数字签名。二是不想动执行策略的话直接用cmd命令行代替PowerShell操作npm也能绕开这个问题。这里多说一句这个报错本身没有任何危险性不要被网上一些“你要中毒了”的言论吓到纯粹是Windows的脚本安全策略和npm安装方式之间的矛盾。搞清楚原理之后后面遇到类似的“PowerShell禁止运行脚本”问题就都能举一反三了。6.3 Nginx反向代理隐藏端口、解决跨域、托管前端本地开发的时候前端5173、后端5000两个端口联调没毛病但最后部署上线必须统一收敛到80或443端口。Nginx在这里同时解决三个问题托管前端构建产物、反向代理后端API、规避跨域。前端构建很简单在项目根目录执行npm run build产出dist目录上传到服务器指定目录然后在Nginx配置里指过去。我贴一份我当时用的精简配置server { listen 80; server_name your-domain.com; root /var/www/short-drama/dist; index index.html; # Vue Router history模式必须配置否则刷新页面404 location / { try_files $uri $uri/ /index.html; } # 后端API统一走/api前缀 location /api/ { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 视频分片目录加跨域头 location /media/ { alias /var/www/media/; add_header Access-Control-Allow-Origin *; expires 7d; } }location /里的try_files是Vue history路由的保命配置没有它用户刷新/detail/101页面会直接404。proxy_pass http://127.0.0.1:5000这行就是把所有/api/请求转发给Node服务相当于把后端端口藏起来外部只能看到80端口。用户问的“nodejs怎样隐藏post和端口号”在生产环境的标准答案就是交给Nginx反向代理。6.4 部署上线时容易忽略的细节部署阶段我总结了几条容易翻车的细节写在这里给大家省时间。进程守护必须做。直接在服务器上node app.js运行后端一旦进程崩了或服务器重启服务就没了。我用的是pm2启动命令就一句pm2 start app.js --name short-drama-api pm2 savepm2会自动拉起崩溃的进程pm2 logs可以实时看输出日志排查接口报错很方便。数据库连接池要配置好。我用的mysql2/promise创建连接池初始5个、最大20个连接。如果服务器上MySQL不设置连接池推荐跑批和用户请求高峰期会把数据库连接打满。连接池代码也很短const mysql require(mysql2/promise); const pool mysql.createPool({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, waitForConnections: true, connectionLimit: 20, queueLimit: 0, });环境变量不要硬编码。数据库密码、JWT密钥、云存储Key这些敏感信息我用.env文件管理.env文件不提交到Git仓库服务器上单独创建一份。答辩时现场演示老师如果问“你的数据库密码怎么不加密”你把自己的.env处理方式讲清楚比一句“写死的”有说服力得多。前端项目的交接也要注意。同学之间要源码最忌讳把node_modules文件夹打包发过去几千个小文件压缩慢、传输慢、还容易损坏。正确做法是把package.json和package-lock.json一起发过去对方执行npm install就能还原依赖。用Git管理项目时.gitignore里一定要写上node_modules和dist这是我见过最多人犯的低级错误。7. 论文怎么搭骨架创新点、对比实验与答辩准备7.1 论文的章节骨架怎么搭标题里带了“论文”两个字说明大家最终都要过论文这一关。我们学校当时要求论文包含选题背景、国内外研究现状、相关技术介绍、系统需求分析、系统总体设计、系统详细设计与实现、系统测试、总结展望这几章。我按照这个框架写下来发现最关键的其实是每一章和工作量的对应关系。第一章绪论写短剧行业背景和推荐系统的研究现状这里注意要引用近两年的文献推荐系统方向资料非常多找5到8篇高质量论文做综述不难。第二章相关技术介绍把Node.js、Vue.js、MySQL、推荐算法原理写透。第三章需求分析从用户、运营、管理员三个角色画用例图。第四章系统设计画总体架构图、功能模块图、数据库ER图。第五章详细设计与实现贴核心代码推荐算法部分要配合公式推导和实验数据。第六章系统测试除了功能测试用例表一定要有推荐算法效果的对比实验。第七章总结与展望。一眼看下来整篇论文的含金量集中在第五章和第六章。而这两章能不能写好取决于你的推荐模块是不是真的跑起来了、有没有优化的过程。7.2 创新点与对比实验怎么写毕业设计论文最怕答辩老师问“你这个系统和别人比有什么不同”。推荐系统的融合策略就是你的创新点。我当时的创新点归纳成三条每条都能落到代码和实验数据上。第一条基于内容相似度与UserCF的混合推荐策略。很多毕设只做一种算法我做了两路并把结果加权融合。论文里我详细推导了余弦相似度的计算方法、权重融合公式、题材打散策略。第二条冷启动问题的分阶段处理。我把新用户、新剧、用户行为稀疏这几个场景分开设计策略而不是一套算法打天下。新用户用“热榜新剧运营位”老用户用“内容相似协同过滤热度加权”这在实际产品中是很成熟的思路。第三条离线批量计算与在线查询分离的架构设计。推荐结果的相似度矩阵、用户协同过滤结果全部离线算好存表在线接口只做查询和排序。这一点增强了系统的高并发能力也体现了工程思维。对比实验我做了三组。第一组纯热门榜只按播放量排序。第二组只用内容相似度推荐。第三组混合推荐策略。我准备了500条模拟用户行为数据用“点击率”和“推荐列表平均播放完成率”做指标。实验结果清晰表明混合推荐的点击率高于纯热榜约21%高于纯内容推荐约9%。这个表格和柱状图放进去论文的“实验分析”章节就有了硬货。答辩老师看到真实数据明显会比看到你空口说“系统很好”要满意得多。7.3 答辩前我复盘出来的高频问题答辩环节基本是三板斧为什么选这个题目、系统架构怎么样、推荐算法怎么实现的。我提前把能想到的问题全写进了准备文档这里分享几个命中率最高的。“为什么选Node.js不选Java”答案在第二章第2.1节里已经讲过。注意回答时不要贬低Java就说这个项目规模下Node.js效率更高、前后端语言统一、I/O密集场景匹配理性分析即可。“你的推荐算法和抖音的有什么区别”这个问题很刁钻核心要回答两点一是算法基础上没有本质区别大家的底座都是协同过滤和内容理解二是工程粒度不同短视频平台有实时的特征工程和深度学习模型毕设级别用的是离线跑批加轻量在线排序。诚实承认规模差异然后再强调自己的系统把小规模场景下的推荐流程完整跑通了这在学习阶段更重要。“如果数据量变大你这个系统哪里会崩”准备好这个问题的回答思路性能瓶颈在离线任务的矩阵计算复杂度优化方向是引入Spark或向量数据库在线查询可以用Redis缓存替代MySQL的recommend_cache表。回答完优化方向答辩老师通常不会再追问更深因为你的思路已经足够证明你理解系统的边界在哪里。最后说几句实际的体会整条链路做下来我最深的感受是短剧推荐系统作为一个毕设题目它的价值不在于用了多高深的算法而在于它把一个完整的推荐业务闭环从头到尾撑起来了。从用户行为上报到离线算法计算再到前端信息流展示每一环都真实存在每一环出了问题都能追查能改进。这种完整感是单纯的“增删改查管理系统”给不了的。如果你正在做这个方向我建议在数据准备上多花一点时间提前把几十部短剧的封面、简介、分集视频素材整理好这些内容决定了你做演示时系统的视觉效果。再就是推荐理由标签一定要做它是你整个推荐系统“有没有灵魂”的最直观体现也是论文截图里最能抓人眼球的部分。做完这个项目之后我自己又把内容相似度计算那块往深挖了挖试过把标签向量替换成Word2Vec预训练词向量来扩充语义特征效果比纯标签权重好一些。如果你答辩之后还想继续扩展可以考虑这个方向或者把推荐结果缓存从MySQL换成Redis让在线查询性能再上一个台阶。但那是后话了先把眼前的系统踏踏实实写完整论文的数据跑出来你就已经赢过大部分只写“管理系统”的人了。
阅读完成 · 觉得有帮助?