1. 项目概述
这个基于Vue.js和Spring Boot的酒店评分推荐系统,采用了协同过滤算法为核心推荐机制。作为一名从事推荐系统开发多年的工程师,我认为这种架构组合在当前互联网应用中非常典型——Vue.js提供了灵活高效的前端交互体验,Spring Boot则以其简洁的特性成为后端开发的首选框架。
系统主要解决了酒店预订场景中的个性化推荐问题。传统酒店预订平台往往只按价格、评分或距离排序,无法满足用户的个性化需求。通过协同过滤算法,系统能够分析用户的历史评分行为,找到兴趣相似的用户群体,从而为当前用户推荐可能感兴趣的酒店。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 技术选型解析
前端选择Vue.js框架主要基于以下考虑:
- 组件化开发模式适合构建复杂的单页应用
- 响应式数据绑定简化了UI与数据的同步
- 丰富的生态系统(如Element UI)加速开发进程
- 轻量级且学习曲线平缓
后端采用Spring Boot的优势在于:
- 自动配置减少了大量样板代码
- 内嵌Tomcat简化部署流程
- 与MyBatis-Plus的完美整合提升数据库操作效率
- 成熟的生态体系保障了系统稳定性
数据库方面,MySQL作为关系型数据库存储核心业务数据,Redis则用于缓存热门推荐结果和用户相似度矩阵,这种组合既保证了数据持久性,又提升了系统响应速度。
2.2 架构分层设计
系统采用经典的三层架构:
- 表现层:Vue.js构建的用户界面
- 业务逻辑层:Spring Boot实现的核心业务处理
- 数据访问层:MyBatis-Plus操作的MySQL数据库
各层之间通过定义良好的接口进行通信,这种松耦合设计使得各层可以独立演进和扩展。例如,未来如果需要替换推荐算法,只需修改业务逻辑层的相应模块,而不会影响其他层次。
3. 数据库设计详解
3.1 核心表结构
用户表(user)设计:
sql复制CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL,
`email` varchar(100) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
酒店表(hotel)设计:
sql复制CREATE TABLE `hotel` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`address` varchar(200) NOT NULL,
`price` decimal(10,2) DEFAULT NULL,
`score` decimal(3,1) DEFAULT NULL,
`description` text,
`image_url` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
评分表(rating)设计特别注意了复合索引的建立:
sql复制CREATE TABLE `rating` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`hotel_id` bigint NOT NULL,
`score` decimal(3,1) NOT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_hotel` (`user_id`,`hotel_id`),
KEY `idx_hotel` (`hotel_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 数据库优化实践
在实际部署中,我们针对评分表做了以下优化:
- 为(user_id, hotel_id)建立唯一索引,防止重复评分
- 为hotel_id单独建立索引,加速酒店评分统计查询
- 使用decimal(3,1)存储评分,确保精度且节省空间
- 定期归档历史评分数据到单独的表,保持主表高效
4. 协同过滤算法实现
4.1 算法原理深入
基于用户的协同过滤(UserCF)核心思想是"物以类聚,人以群分"。算法主要分为两个步骤:
- 用户相似度计算:使用皮尔逊相关系数衡量用户评分行为的相似性
- 评分预测:基于相似用户的评分预测目标用户对未评分项目的评分
皮尔逊相关系数的优势在于能够消除用户评分尺度差异的影响。例如,有的用户习惯打高分(4-5分),有的则较为严格(2-3分),皮尔逊系数通过减去用户平均分来消除这种偏差。
4.2 关键公式实现
用户相似度计算:
java复制public double calculateSimilarity(User u1, User u2) {
// 获取共同评分过的酒店
List<Hotel> commonHotels = findCommonRatedHotels(u1, u2);
if (commonHotels.isEmpty()) return 0.0;
double sum1 = 0.0, sum2 = 0.0, sum1Sq = 0.0, sum2Sq = 0.0, pSum = 0.0;
for (Hotel hotel : commonHotels) {
double score1 = getRating(u1, hotel);
double score2 = getRating(u2, hotel);
sum1 += score1;
sum2 += score2;
sum1Sq += Math.pow(score1, 2);
sum2Sq += Math.pow(score2, 2);
pSum += score1 * score2;
}
int n = commonHotels.size();
double num = pSum - (sum1 * sum2 / n);
double den = Math.sqrt((sum1Sq - Math.pow(sum1, 2) / n) *
(sum2Sq - Math.pow(sum2, 2) / n));
return den == 0 ? 0 : num / den;
}
评分预测实现:
java复制public double predictRating(User user, Hotel hotel) {
if (hasRated(user, hotel)) {
return getRating(user, hotel);
}
double sumSim = 0.0;
double sumRatings = 0.0;
for (User neighbor : findSimilarUsers(user)) {
if (hasRated(neighbor, hotel)) {
double sim = getUserSimilarity(user, neighbor);
sumSim += Math.abs(sim);
sumRatings += sim * (getRating(neighbor, hotel) - neighbor.getAvgRating());
}
}
if (sumSim == 0) {
return hotel.getAvgRating(); // 冷启动处理
}
return user.getAvgRating() + (sumRatings / sumSim);
}
4.3 算法优化策略
-
时间衰减因子:近期评分赋予更高权重,反映用户兴趣变化
java复制double timeWeight = 1.0 / (1 + Math.exp(-0.01 * daysSinceRating)); adjustedScore = baseScore * timeWeight; -
相似度阈值:只考虑相似度大于0.3的用户,提高推荐质量
-
结果多样性:在推荐列表中混入少量随机项目,避免过度特化
-
分块计算:对大规模用户数据分块处理相似度矩阵,降低内存消耗
5. 后端实现细节
5.1 Spring Boot项目配置
核心依赖配置(pom.xml):
xml复制<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-
