体育用品电商平台在近年来呈现爆发式增长,但随之而来的问题是商品同质化严重、用户选择困难。传统的"一刀切"推荐方式往往导致用户淹没在海量商品中,难以快速找到真正符合自己需求的体育装备。这正是我们开发这套基于协同过滤算法的推荐系统的初衷——通过智能化的个性化推荐,帮助体育爱好者精准匹配适合自己的商品。
作为一名长期从事电商系统开发的工程师,我深刻理解推荐系统在提升转化率方面的价值。这套系统采用SpringBoot+Vue的前后端分离架构,核心在于协同过滤算法的实现。不同于简单的热门推荐,我们的系统能够分析用户行为数据(浏览、评分、购买),挖掘用户潜在兴趣,实现"千人千面"的精准推荐。
系统采用经典的三层架构:
前后端通过RESTful API进行通信,采用JWT进行身份认证。这种架构的优势在于:
除了基础字段,特别设计了sport_preference字段存储用户运动偏好标签。这里采用JSON格式存储多标签,例如:["running","yoga","hiking"]。这种设计便于后续的相似度计算。
sql复制CREATE TABLE `user_info` (
`user_id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password_hash` varchar(100) NOT NULL,
`email` varchar(100) DEFAULT NULL,
`gender` char(1) DEFAULT NULL,
`age_range` varchar(20) DEFAULT NULL,
`sport_preference` varchar(100) DEFAULT NULL,
`register_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`),
UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
这是推荐系统的核心数据源,记录了三种关键行为类型:
我们通过behavior_type和rating_value的组合,可以计算出用户对商品的综合兴趣度。
采用基于用户的协同过滤(UserCF)算法,核心思想是"相似用户喜欢的东西你也可能喜欢"。算法实现步骤如下:
相似度计算公式:
code复制sim(u,v) = ∑(r_ui - r̄_u)(r_vi - r̄_v) / √[∑(r_ui - r̄_u)²∑(r_vi - r̄_v)²]
java复制public class CFRecommender {
// 计算用户相似度
public Map<Long, Double> calculateUserSimilarities(long targetUserId) {
Map<Long, Double> similarities = new HashMap<>();
List<UserBehavior> targetBehaviors = behaviorDao.findByUserId(targetUserId);
// 获取所有其他用户
List<Long> otherUserIds = userDao.findAllIdsExcept(targetUserId);
for (Long otherUserId : otherUserIds) {
List<UserBehavior> otherBehaviors = behaviorDao.findByUserId(otherUserId);
double similarity = computeCosineSimilarity(targetBehaviors, otherBehaviors);
if (similarity > 0) {
similarities.put(otherUserId, similarity);
}
}
return similarities;
}
private double computeCosineSimilarity(List<UserBehavior> b1, List<UserBehavior> b2) {
// 实现余弦相似度计算
// ...
}
// 生成推荐
public List<Product> generateRecommendations(long userId, int topN) {
Map<Long, Double> similarities = calculateUserSimilarities(userId);
List<Long> nearestNeighbors = findTopKSimilarUsers(similarities, 10);
Map<Long, Double> productScores = new HashMap<>();
for (Long neighborId : nearestNeighbors) {
double similarity = similarities.get(neighborId);
List<UserBehavior> neighborBehaviors = behaviorDao.findByUserId(neighborId);
for (UserBehavior behavior : neighborBehaviors) {
long productId = behavior.getProductId();
double weightedScore = getBehaviorWeight(behavior) * similarity;
productScores.merge(productId, weightedScore, Double::sum);
}
}
// 过滤掉用户已经有过行为的商品
List<UserBehavior> userBehaviors = behaviorDao.findByUserId(userId);
Set<Long> interactedProducts = userBehaviors.stream()
.map(UserBehavior::getProductId)
.collect(Collectors.toSet());
return productScores.entrySet().stream()
.filter(e -> !interactedProducts.contains(e.getKey()))
.sorted(Map.Entry.<Long, Double>comparingByValue().reversed())
.limit(topN)
.map(e -> productDao.findById(e.getKey()))
.collect(Collectors.toList());
}
}
采用RESTful风格设计API端点:
code复制GET /api/recommendations - 获取推荐列表
POST /api/behavior - 提交用户行为
GET /api/products/{id} - 获取商品详情
关键推荐接口实现:
java复制@RestController
@RequestMapping("/api/recommendations")
public class RecommendationController {
@Autowired
private CFRecommender recommender;
@GetMapping
public ResponseEntity<List<ProductDTO>> getRecommendations(
@RequestParam(defaultValue = "10") int topN,
HttpServletRequest request) {
Long userId = getCurrentUserId(request);
List<Product> products = recommender.generateRecommendations(userId, topN);
List<ProductDTO> dtos = products.stream()
.map(this::convertToDTO)
.collect(Collectors.toList());
return ResponseEntity.ok(dtos);
}
// 其他辅助方法...
}
推荐列表组件关键代码:
vue复制<template>
<div class="recommendation-container">
<h3>为您推荐</h3>
<div v-if="loading" class="loading">加载中...</div>
<div v-else class="product-grid">
<product-card
v-for="product in products"
:key="product.id"
:product="product"
@click="handleProductClick(product.id)"
/>
</div>
</div>
</template>
<script>
import ProductCard from './ProductCard.vue';
export default {
components: { ProductCard },
data() {
return {
products: [],
loading: true
};
},
async created() {
try {
const response = await this.$http.get('/api/recommendations?topN=6');
this.products = response.data;
} catch (error) {
console.error('获取推荐失败:', error);
} finally {
this.loading = false;
}
},
methods: {
handleProductClick(productId) {
// 发送浏览行为
this.$http.post('/api/behavior', {
productId,
behaviorType: 'VIEW'
});
// 跳转到商品详情
this.$router.push(`/products/${productId}`);
}
}
};
</script>
采用Docker容器化部署:
使用docker-compose编排:
yaml复制version: '3'
services:
frontend:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./dist:/usr/share/nginx/html
backend:
build: ./backend
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- redis
- mysql
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=sport_recommend
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
mysql_data:
集成Spring Boot Actuator和Prometheus监控:
java复制@Configuration
@EnablePrometheusEndpoint
public class MonitoringConfig {
@Bean
public CollectorRegistry collectorRegistry() {
return new CollectorRegistry(true);
}
}
关键监控指标:
在初期版本中,我们发现推荐质量不稳定,主要原因是:
通过实践验证的几个有效策略:
调整后的推荐效果提升明显:
几个提升用户体验的小技巧:
这套系统在实际运行中取得了不错的效果,但推荐系统是一个需要持续优化的领域。下一步我们计划引入深度学习模型,进一步提升推荐的精准度。对于想要实现类似系统的开发者,我的建议是从简单的协同过滤开始,逐步迭代优化,不要一开始就追求复杂的算法模型。