
1. 内容整体设计与思路拆解1.1 为什么是民宿推荐系统而不是酒店预订系统先说个现象。很多人在做毕业设计时一提到旅游住宿类系统第一反应就是酒店管理系统民宿预订平台但这类题目在答辩时很容易被打低分。原因很简单——功能太通用、业务逻辑太单薄基本就是增删改查加一个订单表评委一眼就看穿了。而北京市民宿推荐系统这个题目看似只是把酒店换成了民宿实际上整个系统的业务模型、数据结构和推荐逻辑完全变了一个维度。民宿具备极强的非标准化特征——一套四合院、一间胡同房、一座山景别墅它们的价格体系、房型结构、配套设施、客群画像都差异极大。酒店可以用房型价格空房数量三个字段打通全流程但民宿不行它需要更多的特征描述、更复杂的筛选维度、更个性化的匹配逻辑。这个项目的核心选题优势就在于它把信息管理升级成了智能匹配。同样是SpringBoot框架酒店系统做的事情是录数据、查数据而推荐系统做的事情是理解用户、匹配资源。1.2 面向首都特色的业务场景拆解题目里有一个极其重要的关键词首都特色。这意味着系统不能做成一版全国通用的民宿平台而是要针对北京特有的旅居场景做功能设计。我把首都特色拆成四个维度文化场景维度胡同四合院、老北京风情房、什刹海/南锣鼓巷周边的特色小院、长城脚下的乡村民宿。商旅场景维度国贸、中关村、望京等商务区附近的短租公寓适合出差人群。度假场景维度京郊的密云、延庆、怀柔、平谷等区域的温泉民宿、山景木屋、亲子农庄。交通枢纽维度北京站、北京南站、首都国际机场周边的过渡性住宿。这四个维度直接决定了数据模型该怎么设计。普通民宿系统只需要城区地址两个字段而这个系统需要增加文化标签适合人群周边景点交通信息等特征字段才能支撑后续的推荐逻辑。1.3 技术选型为什么是SpringBoot有的同学可能纠结过用SSHStruts2SpringHibernate还是SSMSpringSpringMVCMyBatis甚至想用Servlet原生开发。我给你的建议是毕设选题选SpringBoot是最稳妥且性价比最高的选择。SpringBoot的优势在于约定大于配置。以前用SpringMVC搞一个web项目光配置文件就有applicationContext.xml、spring-mvc.xml、mybatis-config.xml还得处理jar包冲突一整天都在和环境搏斗。SpringBoot只需要一个启动类加几个注解就能把项目跑起来这对毕设周期来说非常友好。但这并不意味着SpringBoot简单到没有东西可写。恰恰相反你可以利用SpringBoot生态展示很多有价值的点Spring Data JPA或MyBatis Plus做数据持久层省时省力。Spring Security做登录认证和权限控制引入JWT实现无状态校验。Spring Cache Redis做民宿热门数据的缓存提升查询性能。定时任务做数据的定期更新和统计报表。就算核心功能都是增删改查用SpringBoot的姿势去实现写出来的代码质量、架构层次、扩展性都会比传统SSM高出几个级别。2. 核心模块拆解与数据模型设计2.1 民宿基础信息的数据建模推荐系统的地基是数据。如果你连民宿数据都建模建不完整后面谈推荐算法全是空中楼阁。民宿信息表我建议至少包含以下核心字段字段类型说明idLong主键自增nameString民宿名称cover_imageString封面图URLdescriptionText房东写的详细描述price_per_nightBigDecimal每晚价格注意用BigDecimal不用doublemax_guestsInteger最多入住人数room_countInteger房间数量areaString所在区域朝阳、海淀、密云等addressString详细地址lat / lngBigDecimal经纬度坐标后续做地图功能用house_typeString房源类型四合院/公寓/别墅/农家院facilitiesString配套设施用逗号分隔或JSON存储cultural_tagsString文化标签如胡同长城温泉suitable_forString适合人群如亲子情侣商务scoreBigDecimal综合评分statusInteger上架状态 0-下架 1-上架关于配套设施和标签字段的存储设计这里有个经验值得说一下。很多同学一看到多个配套第一反应是建关联表民宿表和设施表多对多。理论上没错但对于毕设级别的系统这种设计会显著增加查询复杂度——每次展示民宿列表都要JOIN三张表而且推荐逻辑还要基于标签做匹配如果不加缓存性能很难看。建议的做法是配套设施和文化标签直接用字符串存字段值用逗号分隔例如无线网络,热水,空调,投影仪。查询时用LIKE配合分库分表不用毕设数据量撑不起这么复杂的架构。用MySQL的FIND_IN_SET或模糊查询足够用了。后面做推荐匹配时把字符串split成数组在内存里做标签匹配计算即可。提示这里不是为了教育你不做规范化而是要让你理解数据量级决定设计思想。毕设级系统SQL查询性能和开发效率之间的平衡点是偏向开发效率的。如果你打算考研后再扩展成商业项目再重构数据模型也不迟。2.2 用户画像与行为数据存储推荐系统的核心是理解用户。要理解用户你得先记录用户的行为。用户相关的数据表建议设计如下用户表userid、username、passwordBCrypt加密存储、avatar、phone、email、roleUSER/ADMIN、create_time。用户画像表user_profileid、user_id、preferred_area偏好区域、preferred_house_type偏好房型、preferred_tags偏好标签、budget_min、budget_max、travel_purpose出行目的商旅/度假/文化体验、create_time、update_time。用户行为表user_behaviorid、user_id、house_id、behavior_typeview/favorite/book、create_time。这里我想强调一下行为数据采集。很多毕设项目只做了收藏和预订两个行为浏览行为完全不记录这是不够的。浏览行为的价值极大——用户收藏一个民宿可能出于以后可能去但用户浏览一个民宿并停留很久说明他当下对这个房源感兴趣。在推荐逻辑里浏览行为的权重通常低于收藏和预订但它是短期内最能反映用户意图的信号。实现方案也简单在民宿详情页加载时异步调一个接口写一条view类型的行为记录。要注意的是频繁写库会对数据库造成压力可以加一层简单的内存队列每攒够20条或者间隔30秒统一批量写入。这一步虽然小但在答辩时能讲出防抖设计和批量写入两个亮点。2.3 民宿推荐系统的核心推荐逻辑推荐系统的算法不需要搞得像大厂那么复杂。毕设的核心目标是功能完整、逻辑清晰、面试答辩能讲清楚。所以我建议采用基于内容的推荐Content-based 热度推荐的加权融合方案。具体推荐流程分三步**第一步冷启动阶段。**用户刚注册还没有任何行为数据此时推荐策略是热门民宿 高分民宿。热门指数可以按以下公式计算热度分 浏览权重 × 0.3 收藏权重 × 0.3 预订权重 × 0.4其中浏览、收藏、预订的原始数据来自于行为表用定时任务每天凌晨跑一次把计算结果更新到redis中用户请求推荐接口时直接读redis响应速度极快。**第二步画像匹配阶段。**用户有了行为数据后系统分析该用户的画像字段preferred_tags、preferred_area、budget区间在民宿表中进行匹配打分。匹配规则大概是这样的民宿的cultural_tags和preferred_tags重合一个20分。民宿的area等于preferred_area30分。民宿的price_per_night在预算区间内25分。民宿的suitable_for包含用户的travel_purpose15分。民宿评分高于4.510分。每条民宿都会拿到一个画像匹配分按分数降序排列取前20条作为候选集。**第三步综合排序。**将热度分和画像匹配分做加权融合最终得分 0.4 × 热度归一化值 0.6 × 画像匹配归一化值最终得分降序输出前10条作为推荐结果。这个逻辑我建议你写在服务层Service层的RecommendService类中单独封装成一个方法接口调用时传入用户id和数量参数即可。**为什么选内容推荐而不是协同过滤**协同过滤算法User-Based / Item-Based CF虽然在原理上更有科技感但它有一个致命前提需要稠密的用户行为矩阵。毕设系统根本不可能积累到足够多的有效行为数据冷启动问题严重——新用户没有相似用户、新房源没有历史评分。硬上协同过滤的结果往往是推荐效果稀烂答辩时被追问几个问题就答不上来。而基于内容的推荐解释起来非常直白系统根据你浏览和收藏民宿的特征标签在海量房源中寻找与你偏好相似的其他民宿。这种解释连完全不懂算法的老师都能听懂而且逻辑真的能跑通推荐效果肉眼可见地合理。2.4 后台管理模块与城市信息管理推荐系统的前端只是展示和交互后台管理模块才是系统完整性的证明。管理端我建议至少包含四个功能块民宿管理管理员对民宿信息进行发布、编辑、上下架操作。这里要做一个审核流程模拟——房东提交的民宿信息先进入待审核状态管理员审核通过后才可以在前台展示。这个流程能体现权限控制的思路。订单管理列出全部订单按状态筛选待支付/已支付/已入住/已完成/已取消管理员可以查看订单详情、处理退款请求。用户管理查看用户列表、禁用/启用账号、重置密码。数据统计用ECharts做一个简单看板展示民宿数量分布按区域、订单趋势近7天/近30天、热门民宿TOP10。这个功能虽然不复杂但视觉效果非常好能极大提升整个项目的完整度分数。含北京城市的特殊性后台还需要一个区域管理模块将民宿按北京行政区划分东城、西城、朝阳、海淀、丰台、石景山、门头沟、房山、通州、顺义、昌平、大兴、怀柔、平谷、密云、延庆。每个区域可以配置描述信息和景点标签方便前台按行政区浏览民宿时展示区域文化背景。3. 核心功能实现与关键代码详解3.1 民宿列表筛选与搜索功能民宿列表页面是系统的门面很多用户不会直接看推荐而是习惯先搜索筛选。所以搜索筛选功能必须做到顺手。搜索条件设计关键字搜索民宿名称、简介、区域名模糊匹配。区域筛选16个行政区下拉选择。房型筛选四合院、公寓、别墅、农家院。价格区间滑块选择最小和最大价格。评分筛选只看4.5分以上等选项。排序方式默认推荐排序、价格从低到高、评分从高到低、热度排序。这个功能如果用原生SQL一条条拼条件写很容易写出隐患。我建议使用Spring Data JPA的Specification或者MyBatis的if动态SQL实现条件组合。给一个MyBatis的动态SQL示例select idsearchHouses resultTypecom.example.entity.House SELECT * FROM house where if testkeyword ! null and keyword ! AND (name LIKE CONCAT(%, #{keyword}, %) OR description LIKE CONCAT(%, #{keyword}, %) OR area LIKE CONCAT(%, #{keyword}, %)) /if if testarea ! null and area ! AND area #{area} /if if testhouseType ! null and houseType ! AND house_type #{houseType} /if if testminPrice ! null AND price_per_night gt; #{minPrice} /if if testmaxPrice ! null AND price_per_night lt; #{maxPrice} /if if testminScore ! null AND score gt; #{minScore} /if /where choose when testsort price_asc ORDER BY price_per_night ASC /when when testsort price_desc ORDER BY price_per_night DESC /when when testsort score ORDER BY score DESC /when otherwise ORDER BY popularity DESC /otherwise /choose /select注意这里有个容易踩的坑——MyBatis动态SQL里的小于号需要转义成lt;否则XML解析会报错。这个细节我当年调试了很久才反应过来提前写出来省得你们再踩一遍。3.2 民宿详情页与地图集成方案民宿详情页是用户决策的关键页面也是展示技术细节的好地方。我建议包含以下模块图片轮播区民宿外观图、房间图、周边环境图。图片存OSS或服务器本地都可以数据库只存URL路径。基本信息区价格、房型、面积、可住人数、床型、规格。配套设施区用tag标签展示所有facility字段的值。民宿描述区展示房东撰写的详细描述。地图定位区根据经纬度显示民宿位置。用户评价区展示该民宿的历史评价列表和评分。预订卡券区选择入住日期、离店日期、入住人数实时计算总价。其中地图定位区是首都特色这个题眼的重要体现。北京的地域属性非常强用户找民宿时会极度关注这个房子离地铁站多远到故宫打车多久。用地图展示位置能非常直观地传递这个信息。地图方案我推荐使用Leaflet OpenStreetMap开放街景地图。原因有三第一Leaflet是纯前端开源库不用注册key也不用担心配额第二代码量极简十几行就能渲染出带标记的地图第三毕设答辩时老师在本地环境打开页面就能看到地图功能——高德和百度地图虽然更精细但需要注册开发者账号有一个AppKey申请流程有些同学申请慢或者临时出问题就卡住了。前端接入代码示意// 初始化地图 const map L.map(houseMap).setView([lat, lng], 14); // 加载OpenStreetMap图层 L.tileLayer(https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png, { attribution: copy; OpenStreetMap contributors }).addTo(map); // 添加民宿位置标记 L.marker([lat, lng]) .addTo(map) .bindPopup(b${houseName}/bbr/${address}) .openPopup();如果你的毕设时间充裕可以把地图从展示型升级为搜索型——在地图上展示多个候选民宿的标注点用户点击标注点可以查看民宿卡片和基础信息点击查看详情跳转详情页。这块逻辑在答辩时是绝对的亮点因为评委看到的大部分毕设都只做到了列表详情二维展示你多了图找房的交互维度印象分直接拉满。3.3 订单流程与支付状态管理订单是民宿推荐系统的核心交易功能状态流转必须严谨。传统电商的订单状态机是这样的待支付→已支付→已确认→已入住→已完成还有一条异常分支待支付超时取消、用户主动取消。对于毕设场景我不建议接入真实的支付接口微信支付/支付宝都要求企业资质个人开发者无法轻易申请但可以把支付模块做完整模拟支付前端弹出支付模拟页面用户点击确认支付后调用后端接口修改订单状态为已支付。真实支付预留在支付接口层设计一个统一的PaymentService接口包含一个pay(orderId)方法。现阶段实现类是MockPaymentServiceImpl模拟支付后续如果需要接入真实支付新增一个WechatPaymentServiceImpl实现类即可无需改动业务层代码。这让我想到一个重要的设计原则面向接口编程。虽然多数毕设都是直接一个Service类怼一套实现但如果你能在支付、短信通知、对象存储这几个模块中都用接口实现的模式代码架构的层次感就体现出来了这在答辩时被问到你如何考虑系统扩展性时会是一个有力的回答。订单数据表设计要注意几个字段order_no订单号格式建议为时间戳随机数如20250303153012482917加上唯一索引防止并发冲突。house_id关联民宿表。user_id关联用户表。check_in_date / check_out_date入住和离店日期用于计算总价。total_price总价 每晚价格 × 入住天数。guest_count入住人数。contact_name / contact_phone入住人信息和联系方式。status0待支付 1已支付 2已入住 3已完成 4已取消 5已退款。create_time / pay_time / check_in_time / check_out_time各状态流转的时间戳。另外有个细节值得处理当用户选好日期提交预订后系统应该对民宿在所选日期区间的可用性做一次校验。最简单的实现是查订单表中是否存在状态为0、1、2且日期区间与该用户选择区间有重叠的订单。如果有重叠则提示该日期已被预订。在数据量不大时一条SQL加个循环就能搞定效果比做复杂的排期表要实用得多。3.4 用户评价系统实现评价系统是民宿推荐逻辑中不可缺少的一环。评价数据不仅展示到详情页还会回写民宿的评分字段直接影响推荐排序中的评分权重。评价表的字段设计id、user_id、house_id、order_id关联订单确保只有真实入住过的用户才能评价rating整数评分1~5星content评价内容images评价图片可为空create_time前端展示评价时按评分从高到低、时间从新到旧排列。管理员可以在后台删除不当评论。关于评分回写逻辑我建议用累计均值而不是实时重新计算。每次新评价插入时同时更新民宿表的score字段更新公式为新评分 (旧评分 × 旧评价数 新评价分数) / (旧评价数 1)同时把评价数加1。这样做的好处是避免每天凌晨跑定时任务去重算所有民宿的分数既省事又即时。4. 权限控制与系统安全设计4.1 基于Spring Security JWT的登录认证毕设项目不搞登录认证等于自行车没装链条——跑都跑不起来。但很多同学只会用最原始的Session方式登录后把用户id存入session每次请求从session里取。这种方式不是说不行只是答辩时有点拿不出手。我建议使用Spring Security JWTJSON Web Token实现无状态登录认证。核心思路用户登录成功后后端生成一个JWT token里面封装用户id、用户名、角色信息并设置合理过期时间建议2小时。前端将token存储在localStorage中每次请求在Header中添加Authorization: Bearer token。后端通过Spring Security的OncePerRequestFilter过滤器解析token校验合法性后将用户信息放入SecurityContext中。JWT生成和校验的核心代码// 生成token String token Jwts.builder() .setSubject(user.getUsername()) .claim(userId, user.getId()) .claim(role, user.getRole()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() 1000 * 60 * 60 * 2)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact();// 解析token Claims claims Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(token) .getBody();需要注意的安全事项secretKey不要硬编码在代码里放在application.yml配置文件中答辩时讲到这点可以顺便提一句生产环境中应从环境变量或密钥管理系统读取技术严谨性再加一分。前端Axios请求拦截器全局添加Authorization请求头避免每个接口单独处理token这也是一个体现工程化能力的细节。4.2 接口层面的权限校验有了JWT认证后还要在接口层做两级权限控制第一级管理员接口保护。民宿管理、用户管理、统计报表等接口只有roleADMIN的用户才能访问。用Spring Security的注解就能简单实现PreAuthorize(hasRole(ADMIN)) GetMapping(/admin/house/list) public Result adminHouseList() { ... }但这里要提一个关键坑点Spring Security默认的角色前缀是ROLE_。如果你的用户表里存的是ADMIN注解上就应该写hasRole(ADMIN)框架实际会自动拼接成ROLE_ADMIN做比对。如果存的是admin小写又会匹配不上。所以存储和校验的大小写规范必须统一别在这种细节上被扣分。第二级用户资源隔离。用户只能查看和操作自己的订单、自己的评价、自己的收藏。后端在查询数据时强制带上当前登录用户的id作为查询条件不能只靠前端隐藏按钮来防。例如查询我的订单接口SQL必须是WHERE user_id 当前登录用户id而不是查全部订单再过滤。5. 前端页面设计与交互体验5.1 整体页面规划与技术方案前端是整个系统的脸面做得好看直接拉高整体印象分。但我不建议使用Vue全家桶再搭一个完整的前后端分离项目——除非你的毕设周期非常充裕否则会有两个问题一是工作量翻倍二是答辩展示时环境和接口调试更容易翻车。我的建议是用Thymeleaf模板引擎做服务端渲染结合原生JavaScript Bootstrap框架。这样做的优势在于SpringBoot对Thymeleaf的集成非常顺滑页面直接放在src/main/resources/templates目录下通过Controller返回视图名即可。无需跨域配置前后端调试成本低部署也简单——一个jar包直接发。使用Bootstrap的栅格系统和组件库页面做出来规整不丑。当然如果你对Vue已经比较熟练也可以选择前端用Vue3 Element Plus后端纯API。这不是技术路线对错问题而是工作量分配问题选前者你能把更多时间投入到推荐算法和业务逻辑上选后者代码量多但技术栈看起来更市场化。根据自己的实际水平决定。5.2 首页设计——推荐系统的主阵地首页是整个项目给评委的第一印象必须精心设计。我的建议结构如下顶部导航栏Logo、首页、民宿游览、我的订单、登录/注册按钮。登录后显示用户头像和退出按钮。搜索栏放在页面核心位置一个大搜索框 搜索民宿按钮。推荐位基于当前登录用户或游客默认热门推荐展示10条推荐民宿以卡片列表形式展示每张卡片包含封面图、名称、区域、评分、价格。分类导航按房型分类展示全部、四合院、公寓、别墅、农家院下方展示对应分类下的代表性民宿。区域推荐模块用卡片形式展示北京几个特色住宿区如胡同文化区-东城/西城京郊度假区-密云/怀柔点击可跳转到对应区域的民宿列表页。热门榜单按热度排序的TOP10民宿榜单。首页不需要放太多信息关键在于干净、有层次、突出推荐位。民宿卡片组件的HTML示意Thymeleafdiv classcard house-card th:eachhouse : ${recommendList} img th:src${house.coverImage} classcard-img-top alt民宿封面 div classcard-body h5 classcard-title th:text${house.name}民宿名称/h5 p classcard-text span classbadge bg-primary th:text${house.area}区域/span span classbadge bg-info th:text${house.houseType}房型/span span classtext-warning★ span th:text${house.score}4.8/span/span /p div classd-flex justify-content-between align-items-center span classprice¥span th:text${house.pricePerNight}388/span/晚/span a th:href{/house/detail/{id}(id${house.id})} classbtn btn-sm btn-primary查看详情/a /div /div /div5.3 民宿详情页的交互细节详情页除了前面说的地图和评价有两个交互细节要特别注意日期选择和价格实时计算。入住日期和离店日期用HTML5的input typedate组件选择日期后用JavaScript监听change事件自动计算入住天数并更新总价。这一步虽然功能很小但能体现前端交互与后端数据的联动如果页面调用后端接口返回价格计算公式优惠、周末加价等技术含量更高。登录态判断。用户在点击立即预订按钮时后端Controller中先判断当前是否登录。如果没有登录不能直接报错应该重定向到登录页并在登录成功后自动跳回原详情页。这个流程需要在前端存储回跳URL细节虽小但体验差异很明显。6. 推荐算法与个性化匹配的深入实现6.1 标签体系设计与打标签策略前面多次提到标签这是推荐系统的灵魂。标签体系设计得好不好直接决定推荐效果的上限。我建议构建一个两层标签体系第一层文化属性标签。重点展现首都特色。例如胡同、四合院、老北京、故宫、长城、颐和园、南锣鼓巷、798、温泉、滑雪、星空、山景。第二层功能属性标签。主要描述住宿功能。例如亲子、情侣、商务、聚会、带宠物、做饭、烧烤、庭院、露台、独立厨房、停车场。在设计民宿信息录入表单时管理员或房东可以给每个民宿勾选最多8个标签。前端展示时标签以徽章形式展示在民宿卡片上。标签匹配算法可以再细化一层不只是看标签是否重合还要看标签的稀有度。比如四合院这个标签在全部民宿中占比很低当一个用户浏览了带四合院标签的民宿说明他具有明确的文化体验需求这个标签在匹配时的权重就应该更高。而无线网络这种几乎每家都有的标签区分度低权重应该降低。加权公式可以用TF-IDF的思想简化实现标签权重 1 / log(1 该标签在全部民宿中出现的次数)这个处理逻辑虽然简单但在答辩时你能说出引入TF-IDF思想降低高频无区分度标签的权重突出稀有标签的区分能力这就是一个有深度的回答。6.2 推荐接口的完整实现流程推荐接口GET /api/recommend?userIdxxsize10的实现可以拆分为四步。第一步读取用户画像。从user_profile表读取用户偏好如果画像为空则走冷启动逻辑直接返回热门民宿列表。第二步获取候选集。从house表中筛选status1已上架的民宿如果用户画像中有preferred_area则优先只用该区域的民宿作为候选集缩小数据范围如果preferred_area为空则使用全部民宿作为候选集。第三步逐条打分。遍历候选集通过画像匹配逻辑计算每条民宿的画像分上一节已经定义了打分规则然后从Redis中读取热度分做归一化处理后加权融合。第四步排序与包装。按综合得分降序排列截取前N条将民宿信息、匹配得分、推荐理由包装成统一响应结构返回前端。推荐理由这个设计我觉得很实用。前端展示时可以这样显示因为您近期浏览过胡同特色民宿为您推荐这间四合院改造的精品客栈。这种带解释的推荐在答辩时增加的是系统可解释性评委一听就知道你的推荐算法不是黑盒能说出道理来含金量完全不同。推荐打分核心代码public ListRecommendResult recommend(Long userId, int size) { // 1. 获取用户画像 UserProfile profile profileService.getByUserId(userId); if (profile null || !hasPreference(profile)) { return hotHouseService.getHotList(size); } // 2. 取候选民宿 ListHouse candidates houseService.getOnShelfHouses(profile.getPreferredArea()); // 3. 打分 ListRecommendResult results new ArrayList(); for (House house : candidates) { double profileScore scoreByProfile(house, profile); double hotScore hotHouseService.getHotScore(house.getId()); // 归一化后加权融合 double finalScore 0.4 * normalize(hotScore) 0.6 * normalize(profileScore); results.add(new RecommendResult(house, finalScore, buildReason(house, profile))); } // 4. 排序返回 return results.stream() .sorted((a, b) - Double.compare(b.getScore(), a.getScore())) .limit(size) .collect(Collectors.toList()); }6.3 相似民宿推荐详情页底部我建议加一个相似民宿推荐模块。这个模块的技术实现和首页推荐有区别但可以复用同一种打标签逻辑相似度 重合标签数 / 最大标签数比如用户当前浏览的民宿A标签是[胡同,四合院,庭院,亲子]候选民宿B标签是[胡同,庭院,老北京,情侣]重合标签是胡同庭院共2个最大标签数是4相似度就是0.5。筛选时先取相似度最高的前三名再过滤掉当前民宿本身。便宜大碗的实现不需要引入任何向量计算库效果在可视化上又是合理直观的。7. 缓存、定时任务与性能优化7.1 Redis缓存在推荐系统中的具体应用系统如果每次请求推荐接口都去数据库全表扫描打分性能和数据库压力都非常难看。我建议通过Redis缓存做三层优化。第一层热门民宿列表缓存。热门榜前20的民宿id列表缓存key为hot:house:list过期时间设为1小时。每天定时任务更新或者用户在行为表中产生新的收藏/预订后主动淘汰缓存。第二层用户画像推荐缓存。同一个用户短时间内重复请求推荐接口结果应该是一样的。所以把推荐结果缓存起来key为recommend:user:{userId}过期时间30分钟。用户产生新的行为后淘汰该缓存。第三层民宿详情缓存。民宿详情页的访问频率很高但民宿信息很少变化。可以把民宿详情缓存起来key为house:detail:{houseId}过期时间24小时。管理员编辑民宿后主动删除对应缓存。Redis在SpringBoot中的配置非常简单引入依赖和配置redis地址后直接用StringRedisTemplate或RedisTemplate操作即可。7.2 定时任务热度计算与数据统计推荐系统必须有一个定时任务模块作用是每晚凌晨计算前一天的数据内容。推荐一个非常轻量的方案Spring自带的Scheduled注解不需要额外引入任务调度中间件。定时任务建议定义三个每日凌晨2点计算所有民宿的7日热度分写入hot_house_batch表或Redis缓存。每日凌晨3点统计前一天的订单量、浏览量、新注册用户数写入operation_statistics表。每日凌晨4点清理过期的用户会话或未支付的废单将状态为待支付且创建时间超过30分钟的订单标记为取消。定时任务的格式参数说明// 每天凌晨2点执行 Scheduled(cron 0 0 2 * * *) public void calculateHotScore() { ... }一个容易踩的坑是服务重启后定时任务会连续执行两次如果上一次执行因停机中断所以任务内部要有幂等保护——执行前检查任务日志表中有没有今天该任务的执行记录有就直接跳过。8. 测试部署与常见问题排查8.1 测试用例怎么设计不要到时候手忙脚乱地只做手动测试建议至少用JUnit写几个核心测试用例会显得更有工程意识。测试重点放在三个层面推荐服务测试Mock用户画像和行为数据验证推荐结果中不包含下架房源验证打分排序无异常。订单服务测试模拟用户下单、模拟支付、订单取消验证状态流转是否正确。安全权限测试未登录用户访问受保护接口返回401管理员接口被普通用户访问返回403。Controller层用MockMvc测试Service层可以只测纯逻辑。不用追求覆盖率但核心业务一定要覆盖。8.2 部署环境准备毕设演示时最常见的惨剧是开发环境跑得好好的换一台电脑演示就炸了。为了不丢这个分提前做好部署物MySQL版本统一优先选用5.7或8.0不要兼容性问题。JDK版本统一SpringBoot 2.x配JDK 8或11SpringBoot 3.x要求JDK 17。别开发用JDK17部署机器用JDK8启动就会直接报错。数据库脚本写一份完整的init.sql包含建库建表和测试数据。测试数据至少插入50条不同区域、不同类型、不同价位的民宿记录数据越丰富推荐效果的差异性越好展示。打包命令mvn clean package -DskipTests生成jar包后java -jar 项目名.jar --server.port8080即可运行。如果还想提升档次写一个deploy.sh脚本内容包含停止旧进程、备份jar包、启动新jar包、输出日志。这个脚本在工作中是基本操作写出来还能顺带证明你有实际部署经验。8.3 毕设常见Bug和排查方法结合做类似项目的经验最容易踩的坑我总结成一张速查表问题现象可能原因排查方法启动时报端口被占用上次服务没关干净用netstat -ano前端页面中文乱码编码不统一确认IDEA全局编码为UTF-8数据库连接URL加characterEncodingutf8图片加载不出来图片路径是绝对路径使用相对路径或统一访问静态资源映射JWT解析报错secretKey长度不够HS256算法要求密钥至少256位长度至少32字节登录提示用户名或密码错误密码加密方式不一致检查是否所有注册接口都使用了同一BCrypt加密方法定时任务执行了多次部署了多个实例单机部署正常不会触发检查是否有多个启动的进程Thymeleaf页面引用静态资源404路径写错CSS/js放static目录下页面引用必须加th:href{/css/style.css}的thymeleaf语法其中有一项我要单独强调。本地开发有测试数据、但部署到答辩环境后推荐接口返回空——这个问题十有八九是数据库没导入测试数据或者民宿表的status字段没有设置成1上架状态推荐查询里默认过滤掉下架民宿结果候选集就是空的。记得部署时检查一下SELECT COUNT(*) FROM house WHERE status 1这个数字不为0推荐接口才可能有数据返回。实战心得与扩展方向我做完这个项目后回头复盘有几条比较深的体会。第一推荐系统项目最忌假大空。你不需要在系统里写一个多么高深莫测的算法但必须把为什么这么做想清楚。内容推荐 热度加权的方案朴素但有效重点是整个推荐链路是真正跑通的——用户点浏览、系统记行为、画像匹配打分、排序输出、前端展示这条链路每一环都有数据和代码支撑答辩时你可以全程边演示边讲逻辑而不是对着PPT念。第二数据初始化比想象中重要。民宿系统的展示效果如何很大程度上取决于初始数据是否鲜活。我建议你维护一份真实感强的测试数据北京16个行政区都分布一些房源价格从一晚180元的青旅床位到一晚3000元的四合院别墅都有标签尽量多样化。有了丰富数据搜索筛选项有东西可筛推荐算法有数据可算图表有信息可展示整个系统的完整度会显得特别扎实。第三这套系统的扩展空间其实很大。后续还可以做的是主流地图API接入替换开源地图实现真实城市POI兴趣点检索引入协同过滤做混合推荐用用户行为日志训练模型增加房东端小程序让房东自己管理房源生成报表的定时任务升级为邮件推送。你可以把这几个扩展方向写进论文的展望与下一步工作章节显得你对项目的未来有思考。最后分享一个小技巧如果你打算在答辩现场演示推荐效果提前准备一个新鲜账号在答辩前用该账号浏览和收藏几个特定类型的民宿让推荐接口有充足的行为数据。现场演示时首页推荐列表会出现和你的操作强相关的房源这个系统真的记住了我的行为的体验比任何讲解都有说服力。