ARTICLE DETAIL

资讯详情

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

办公用品直售推荐系统毕设全解析:SpringBoot+Vue与协同过滤实战

办公用品直售推荐系统毕设全解析:SpringBoot+Vue与协同过滤实战 每年三四月份实验室的群里总会准时冒出同一类问题“学长毕设选什么题好”去年我也经历过这个阶段。选“日常办公用品直售推荐系统”这个题目一开始并不是因为它多高大上而是我给自己定了几条硬标准技术栈必须是现在企业里真正在用的、功能不能简单到答辩没话说但也不能复杂到一个人做不完、最好还能带一点“算法亮点”撑住提问环节。最终定下的组合是SpringBoot Vue MySQL核心卖点在“推荐”两个字上。这篇文章就把我做完整个项目的前因后果、数据库设计、推荐算法的实现思路、开发踩过的坑、以及论文和答辩的准备工作完整讲一遍。目标读者很明确正在做毕业设计、或者想快速上手前后端分离全栈项目的同学。我会尽量把“为什么这么做”也讲清楚而不只是丢出一堆结论。1. 选题评估与技术选型为什么这个题目适合绝大部分毕设场景1.1 办公用品直售这个业务场景的独特优势很多同学选题喜欢追热点什么智慧校园、社区团购、二手交易结果做出来的功能和普通商品系统没什么区别反而因为业务边界拉得太宽答辩时被老师追问“你这个和电商系统有什么本质不同”就直接卡壳。办公用品直售这个场景有个天然好处业务角色清晰、流程闭环、数据特征鲜明。用户是普通员工或行政采购人员商品是中性笔、打印纸、硒鼓这类标准化程度很高的东西。直售意味着没有复杂的商家入驻、分销、拼团这种烂摊子但订单、购物车、库存这些电商核心闭环一个都不少。并且办公用品的购买行为有非常强的“周期性”和“同类偏好”比如某个部门经常采购A4纸、某个用户总买黑色签字笔——这些结构化的行为数据正好是推荐算法最好的练兵场。选这个题目在答辩时讲业务背景会非常自然中小企业缺乏行政采购管理系统线下询价比价成本高办公用品标准化程度高、适合线上直售加上需要选品推荐降低搜索成本。1.2 技术栈确定的几个现实原因SpringBoot基本是当前Java后端开发的事实标准应届生简历上写“熟悉SpringBoot开发”几乎是底线。Vue又是国产前端框架中组件生态最成熟的配合Element UI做后台界面效率极高。MySQL在这个体量的项目里是杀鸡用牛刀但胜在稳定、资料多、出问题一搜就有答案。实际开发时我做了几个更细的选择这些都是踩过坑对比过的对比项选型原因持久层框架MyBatis-Plus单表CRUD不用写XML分页插件开箱即用比原版MyBatis省一半时间权限方案JWT 拦截器前后端分离下免去Session同步问题比整合Spring Security更可控前端UI库Element UIVue2中文文档友好、组件全表格和表单处理效率高构建工具Maven毕业设计不需要Gradle的灵活性Maven在IDEA里最顺手部署方式前后端分离部署 Nginx和真实生产环境一致也方便在部署文档中展示运维能力1.3 一个重要的心态准备毕设是“展示你的解决问题能力”不是“秀花活”很多同学在选型时容易被“新技术”牵着走比如非要用微服务把系统拆成四个模块或者引入Elasticsearch做搜索。我不反对挑战自我但前提是你真的明白自己在干什么。毕业设计的时间是有限的你要在三个月内完成需求分析、编码、测试、论文、答辩PPT任何一个环节拉胯都会全盘被动。“用最简单可靠的技术栈解决一个完整的问题”本身就是工程能力的体现。我的推荐模块最终只用了代码里实现的协同过滤算法没有引入额外中间件但我在论文里把算法的原理、评测指标、冷启动处理写得清清楚楚——这比简单调用一个现成推荐服务更能体现你的思考深度。2. 数据库设计推荐系统的地基比你想的更吃细节2.1 角色划分与全局功能模块整个系统我分成了两个端前台用户端和管理员后台。用户端注册登录、商品分类浏览、商品搜索商品详情购物车管理下单结算订单历史查询收藏和浏览足迹首页推荐位。管理员端商品管理增删改查、上下架、库存调整分类管理订单管理发货、状态流转用户管理推荐结果日志查看。功能看过去不复杂但数据库如果设计得不好后面开发会处处想砸键盘。2.2 核心表结构说明我的数据库一共设计了十张表这里把最重要的几张拿出来说user用户表用户ID、用户名、加密密码、手机号、部门/公司名、角色标识1为普通用户0为管理员、创建时间。我建议加一个company_name字段办公用品采购往往以部门或公司为单位这个字段在后面做“群体用户画像”时有用。product商品表商品ID、分类ID、商品名称、主图URL、详细描述、价格、库存、销量、上下架状态、创建时间。销量字段是一个典型的空间换时间的设计推荐排序和热门榜单都直接读它不用实时统计订单表。category分类表办公用品天然可分书写工具、纸品本册、桌面收纳、打印耗材、会议用品等。一个两层分类就够了。cart_item购物车表主键、用户ID、商品ID、数量、勾选状态、加入时间。注意加一个普通索引(user_id)否则超过几千条数据后按用户查购物车都会变慢。orders订单表订单号、用户ID、总金额、订单状态待付款/待发货/已发货/已完成/已取消、收货人、收货地址、创建时间、支付时间、发货时间。订单号我用的策略是“时间戳 用户ID后四位 随机数”这样在一张表里也不会撞车。order_item订单明细表为了满足“一个订单包含多个商品”的经典数据库设计范式订单主表之外必须有明细表字段是订单号、商品ID、商品快照名、购买单价、数量。快照名和单价很重要——用户后来修改商品价格也不影响历史订单的可读性这是电商系统里公认的细节。behavior_log行为日志表用户ID、商品ID、行为类型浏览/加购/下单/收藏、操作时间。这张表是推荐算法的原始数据来源。我当时担心数据量爆炸其实完全不用怕——这种体量的毕设项目完全没有性能问题而且写这篇表的时候你还能在论文里理直气壮地写“基于用户行为数据进行推荐”比空口说白话有说服力得多。2.3 为什么我把推荐结果单独建表而不是实时算这是我在设计阶段最纠结的地方。最初的设想是在用户请求首页推荐的时候实时计算用户相似度、找出候选商品、排序再返回。后来我发现完全没必要而且会造成性能灾难。我的最终方案是每天凌晨利用定时任务跑一次推荐计算把每个用户最可能感兴趣的Top-N商品结果写入recommend_result表用户ID、商品ID、推荐分值、推荐原因、生成时间。用户请求首页时直接查这张表返回速度极快。这个设计在论文里有明确的优点第一将耗时的离线计算和实时的接口请求解耦第二方便管理员在后台核查“系统究竟给用户推荐了什么东西”第三能准确的统计推荐算法在次日带来的真实转化数据——比如某用户昨天被推荐了哪几个商品今天是否发生了购买。这种设计思路在真正的互联网公司同样很常见写进论文里很有说服力。3. 推荐引擎实现从“能跑”到“能讲清楚算法原理”3.1 算法选型思路毕业设计的推荐模块我建议不要盲目追求深度学习。一是数据量完全撑不起来二是你自己也未必能解释清楚。最佳的平衡点是经典的协同过滤。我最终实现的是基于用户的协同过滤User-Based Collaborative Filtering即找到与当前用户购买/浏览行为最相似的K个用户把这些用户喜欢的、当前用户还没见过的商品作为推荐候选我在这个基础逻辑之上做了一点加权修正。3.2 用户相似度计算的代码实现相似度计算我用了余弦相似度。核心思路是先把用户的行为转成一个商品评分向量比如浏览1分加购2分下单3分收藏2分为什么下单分数是3分因为下单行为的购买意图最强浏览行为最弱。这些分数权重不是拍脑袋定的建议做成常量并在论文里解释其合理性。简化后的实现逻辑如下我把它写成Java伪代码方便你理解// 1. 从行为日志表读取用户行为构建 用户-商品-评分 映射 MapInteger, MapInteger, Double userItemScoreMap buildUserItemScore(); // 2. 计算两两用户之间的余弦相似度 public double cosineSimilarity(MapInteger, Double vectorA, MapInteger, Double vectorB) { SetInteger commonItems new HashSet(vectorA.keySet()); commonItems.retainAll(vectorB.keySet()); if (commonItems.isEmpty()) return 0.0; double dot 0, normA 0, normB 0; for (Integer itemId : vectorA.keySet()) normA vectorA.get(itemId) * vectorA.get(itemId); for (Integer itemId : vectorB.keySet()) normB vectorB.get(itemId) * vectorB.get(itemId); for (Integer itemId : commonItems) dot vectorA.get(itemId) * vectorB.get(itemId); return dot / (Math.sqrt(normA) * Math.sqrt(normB)); } // 3. 取TopK相似用户后对候选商品进行加权打分 for (Long similarUserId : topKUsers) { for (Map.EntryInteger, Double entry : userItemScoreMap.get(similarUserId).entrySet()) { Integer itemId entry.getKey(); if (currentUserItems.contains(itemId)) continue; // 过滤已购买/已浏览过的商品 double score entry.getValue() * sim(contextUserId, similarUserId); recommendScoreMap.merge(itemId, score, Double::sum); } }这段代码我单独放出来是因为答辩时老师极大概率会问“相似度怎么算的”“如何过滤用户已经买过的商品”“同一个商品在多个相似用户那都出现怎么办”。把这三句话讲清楚算法环节就稳了。3.3 冷启动问题的解决方案冷启动是推荐系统绕不开的问题新用户刚注册没有行为数据怎么推荐新商品刚上架没有销量怎么被推荐我对这两个问题的处理很朴素新用户默认推荐“热门商品池”即按销量和浏览量综合排序取TopN。用户一旦产生浏览或购买行为下次推荐就切换为基于协同过滤的个性化结果。新商品新上架商品在前7天获得一个额外的“新品加权”综合排序公式变为热度分 0.6 * 销量分 0.3 * 浏览量分 0.1 * 新品加权分防止新商品永远沉底。这个模块你是可以在答辩时展开讲半小时的。任何一个评审老师都知道冷启动是推荐系统最重要的工程问题之一你说出了这两个方案哪怕不完美比一句“我们用了协同过滤算法”要强十倍。3.4 定时任务的实现方式每天凌晨1点执行推荐全量计算我用的是SpringBoot自带的定时任务Scheduled(cron 0 0 1 * * ?) public void recommendTask() { // 1. 删除昨天的推荐结果 // 2. 重新计算所有活跃用户的Top-N商品 // 3. 批量写入 recommend_result 表 }注意一个细节定时任务建议加上“可配置开关”比如写个变量存在数据库配置表里。这样你在演示系统的时候就不需要真的等到凌晨1点可以直接在后台手动触发计算答辩现场效果会很加分。4. 前后端开发中的关键细节登录态、购物车和联调节奏4.1 Vue项目搭建与基础环境前端用的Vue2 Element UINode版本为14.18以上。如果用的Node版本过高可能出现digital envelope routines::unsupported的报错这是很多同学爬不出去的一个坑。我当时为了解决这类问题重新安装了Node 14后来发现其实可以通过设置环境变量NODE_OPTIONS--openssl-legacy-provider解决但为了稳妥直接换Node版本反而省事。Vue项目结构我习惯分成views页面、components可复用组件、router路由配置、api请求封装、store全局状态。推荐模块单独放了一个components/RecommendShelf.vue组件用来渲染推荐位的商品卡片任何页面想展示推荐商品直接引入即可。4.2 登录认证与会话管理的完整链路JWT方案我单独强调一下。流程是用户登录成功后后端将一个token返回前端存在localStorage里后续所有请求在axios拦截器里统一带上axios.interceptors.request.use(config { const token localStorage.getItem(token); if (token) config.headers.Authorization Bearer token; return config; });后端在拦截器中校验JWT的有效性以及过期时间。不用把JWT方案做得太复杂——能在代码里说明“为什么选JWT而不选Session”并且说出一两条理由比如横向扩展更好、无状态就已经超出大部分本科生水平了。4.3 购物车与订单的接口设计购物车接口有五个基本操作加入购物车、修改数量、勾选/取消勾选、删除、查询购物车列表。每一个接口都跟着当前登录用户ID走服务端不信任前端传过来的用户ID而是从JWT里解析出用户身份。这个点是安全方面最简单的体现答辩时值得主动说出来。订单流程的接口顺序是确认下单从购物车勾选商品生成订单→ 订单预创建 → 扣减库存 → 清空购物车对应项 → 返回订单号。这里面最需要注意的是“扣减库存”和“创建订单”之间的数据一致性。毕设项目可以不引入分布式事务但要明确说明只用MySQL事务并在代码里加上Transactional注解来保证同一数据库中的表级原子性。4.4 前后端联调的教训我前两周开发效率很慢因为正好踩了前后端分离阶段典型的几类坑跨域问题启动Vue开发服务器默认端口是8080而SpringBoot也是8080。把SpringBoot端口改到8081并在后端配置CorsFilter白名单放行http://localhost:8080。配置时注意不要用allowedOrigins(*)因为跨域凭证和通配符不能共存最好明确指定来源。字段名不一致后端用驼峰命名productName前端却习惯写product_name结果数据渲染不出来。统一约定后端返回JSON直接用驼峰前端跟着适配。路由历史模式404开发环境没事打包之后部署到服务器时刷新路由就404。原因是Vue是SPA应用所有的路径最终都应该指向index.html需要在Nginx中配置try_files兜底。5. 最容易让毕业设计翻车的九个坑环境、数据库和部署实录5.1 SpringBoot版本选择别盲目追高我最初用SpringBoot 3.x确实能跑但随之而来的是一连串兼容性问题部分第三方库还没有适配jakarta.*包名、很多网上搜到的解决方案都是针对SpringBoot 2.x的。对于毕设项目我强烈建议使用SpringBoot 2.7.x配合JDK 1.8或11这是目前教程覆盖度最高、坑最少的稳定组合。如果你真的因为某些原因只能用高版本那要特别注意javax和jakarta的导入路径变化以及MyBatis-Plus是否发布了对应的适配版本。这些细节能将你从“框架配置一整天”的泥沼里拉出来。5.2 MySQL 5.7安装与配置的关键注意点Windows上安装MySQL 5.7教程铺天盖地但有几个反直觉的点值得提醒安装时若选“Server only”更加清爽默认端口3306不能跟本地其他服务冲突下载地址要选带msi的版本不要下载zip包手动配置除非你真的很懂MySQL初始化流程设置密码时建议用简单好记的密码比如root/123456毕设环境追求可用性优先——生产环境当然不能这么干但你的本地开发环境没必要给自己设障碍字符集务必设置成utf8mb4不要用utf8否则商品名里的特殊符号、表情符号可能存不进去商品昵称带emoji或者特殊字符时就会踩坑。5.3 Maven依赖下载被墙或超时的处理方案Maven中央仓库在国内访问经常不稳定。我当时的做法非常简单粗暴在settings.xml里配置阿里云镜像并指定JDK编译版本为1.8或者11。有这两个固定操作你之后每次新建项目都会顺畅很多。mirror idaliyunmaven/id mirrorOfcentral/mirrorOf urlhttps://maven.aliyun.com/repository/public/url /mirror5.4 文件上传与图片资源访问路径商品的图片上传我使用的是本机目录存储方案图片上传到服务器的/upload目录然后通过一个WebMvc配置将该目录映射为虚拟访问路径。开发环境这样做完全够用但有几个注意点绝对路径不要写死配置在application.yml里上传文件的请求大小限制要在配置里调大否则稍微高清一点的图片会直接报错。5.5 Nginx部署和SPA刷新404给前端打包后上传到服务器部署方式以Nginx为主配置文件注意这样写server { listen 80; server_name your-server-ip; location / { root /home/project/dist; index index.html; try_files $uri $uri/ /index.html; # 解决Vue路由刷新404的核心配置 } location /api/ { proxy_pass http://localhost:8081/api/; # 反向代理到SpringBoot } }这个配置里最重要的就是try_files这一行。不知道这一行的同学10个人里至少有8个会在部署验收时被前端路由直接打击到想摔电脑。5.6 数据库脚本的导出规范毕业设计提交的“源码数据库”里数据库脚本一定要规范命名比如office_supplies.sql。注释要写清楚每个表都要有明确用途说明。建议脚本里同时包含建库、建表、初始化的数据至少一个管理员账号和几个测试商品。否则老师拿到之后跑到本地根本复现不出来给分一定受影响。6. 论文写作结构和答辩准备的实战经验6.1 论文的章节结构设计我的论文是按照这类系统最常见的六章结构来组织的绪论背景、意义、国内外研究现状、论文结构安排。相关技术介绍SpringBoot、Vue、MySQL、协同过滤简述。系统分析可行性分析、功能需求分析、非功能需求分析。系统设计总体架构设计、功能模块设计、数据库设计、推荐算法设计。系统实现各功能模块的界面截图与核心代码说明。系统测试测试环境、测试用例、测试结果、性能测试。这个结构不新颖但胜在完全符合本科毕业论文的规范流程。有些同学试图搞创新结构反而容易被老师挑逻辑问题。几个写作上的小技巧技术介绍章节不要抄百度百科要用一个“本文中这些技术分别解决了什么问题”的视角来写系统设计章节一定给出数据库ER图和推荐算法流程图但要注意格式规范系统实现章节别只放截图每张截图下面要有1-2段的文字说明“为什么这么设计”“这个界面展示了什么功能”。6.2 测试章节怎么写才能不幼稚很多同学的测试章节就是“打开登录页面输入用户名密码点击登录成功”这太流水账了。我建议用三张表结构化呈现功能测试用例表包含编号、测试项、操作步骤、预期结果、实际结果、是否通过。推荐模块专项测试相同的用户行为数据下跑推荐算法检验返回的结果是否合理。性能测试表可以用Postman或JMeter简单测一下接口响应时间数据不在多在于你真的测过。比如我测了以下场景给一个用户构造“多次浏览A4纸并下单”的历史行为模拟其在第二天打开首页看看推荐位是否出现其他品牌A4纸或相关打印耗材。通过这个测试用例老师的注意力会从“能不能登录”转向“这个系统的亮点功能是否有效”意义完全不同。6.3 答辩现场的常见问题和应答思路答辩不要背稿子但可以提前准备几个高频问题的回答框架“这个系统的难点在哪里”回答模板一个是推荐模块的冷启动问题新旧用户如何差异化处理一个是订单流程中库存扣减与数据一致性的保障。“为什么选择基于用户的协同过滤”回答框架用户行为数据量适中同学部门之间的采购偏好高度相关和办公用品购买场景的“群体决策”属性契合。“用户相似度怎么计算为什么选余弦相似度”回答框架把用户行为映射为向量余弦相似度对向量长度不敏感更适合描述两个用户行为分布上是否一致。“如果用户数特别大这个推荐算法还能工作吗”回答框架可以预计算离线结果并引入物品协同过滤或者Spark分布式计算进行横向扩展。这其实是在问你扩展性只要思路正确即可。6.4 部署文档的整理心得很多同学做完系统部署文档随便写两句。但我发现答辩时老师会翻看部署文档的完整程度。我建议部署文档包含环境要求JDK版本、Node版本、MySQL版本、数据库初始化步骤、后端打包启动步骤、前端打包步骤、Nginx配置说明、常见问题。每一条都要精确到你实际验证过的命令。我实际整理的时候会把每一步的命令和截图都带上。哪怕你只是把当时敲过的命令复制到文档里也比空洞的“打开终端执行”强。7. 一些你没问我但我想告诉你的事这个项目从头到尾我实际开发时间大约用了三周但前期设计和“试错”花了一个多月论文与文档又占了半个月。回头总结经验最想跟你们分享的是第一不要试图用代码的绝对量来证明你的工作量。一个结构干净、注释明确、能跑通完整业务流程的系统加上你清清楚楚讲明白的推荐算法逻辑远比一个堆砌了无数页面但一团乱麻的系统强得多。第二花一天时间把数据库设计清楚后面能省一周时间。我见过太多的同学项目写了一半开始加字段、改表结构、甚至重建表真的会崩溃。第三演示前务必把环境完整启动裸跑一遍。真的每年都有人答辩现场发现数据库密码不对、端口被占用、前端找不到后端接口这种事。提前写一个启动脚本比什么都靠谱。如果你正在做类似的毕设不妨参考我刚才说的数据库表设计把behavior_log表建好把定时推荐的任务调通再耐心做完那几张测试用例表。这个项目的推荐部分一旦能完整跑起来并讲清楚答辩就已经成功了一大半了。
返回列表