我们按领域拆分为三个独立服务:
前端 Vue 应用通过网关(或直接)调用商品服务和推荐服务,聚合数据后渲染。服务间通信采用 HTTP + JSON,Python 端使用 Flask 轻量暴露接口,Java 端通过 OpenFeign 声明式调用。
使用 Spring Boot 3.x + Spring Data JPA + MySQL。以商品服务为例,核心实体与仓库:
// Product.java
@Entity
public class Product {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String category;
private BigDecimal price;
private Integer stock;
// getters/setters
}
// ProductRepository.java
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByCategory(String category);
}商品服务提供 REST 接口:
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired private ProductRepository repo;
@GetMapping("/{id}")
public ResponseEntity<Product> getById(@PathVariable Long id) {
return repo.findById(id).map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@GetMapping("/batch")
public List<Product> getBatch(@RequestParam List<Long> ids) {
return repo.findAllById(ids);
}
}用户服务类似,重点记录 UserHistory(userId, productId, timestamp)。
我们采用 基于物品的协同过滤(ItemCF),使用 Pandas 和 Scikit-learn 计算相似度。Flask 提供 /recommend 端点,接收 user_id,返回推荐商品 ID 列表。
# app.py
import pandas as pd
from flask import Flask, request, jsonify
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
app = Flask(__name__)
# 模拟数据:用户-物品评分矩阵(实际可从Java服务拉取或共享数据库)
data = pd.DataFrame({
'user_id': [1,1,1,2,2,3,3,3],
'product_id': [101,102,103,101,104,102,103,105],
'rating': [5,4,3,5,5,4,3,5]
})
# 构建用户-物品矩阵
user_item_matrix = data.pivot(index='user_id', columns='product_id', values='rating').fillna(0)
item_similarity = cosine_similarity(user_item_matrix.T)
item_sim_df = pd.DataFrame(item_similarity, index=user_item_matrix.columns, columns=user_item_matrix.columns)
def recommend(user_id, top_n=5):
if user_id not in user_item_matrix.index:
return [] # 新用户冷启动
user_vector = user_item_matrix.loc[user_id]
# 找出用户已购商品
purchased = user_vector[user_vector > 0].index.tolist()
if not purchased:
return []
# 计算每个未购商品的加权得分
scores = {}
for item in user_item_matrix.columns:
if item in purchased:
continue
# 计算该物品与用户已购物品的相似度加权评分
sim_sum = 0
weight_sum = 0
for p in purchased:
sim = item_sim_df.loc[item, p]
rating = user_vector[p]
sim_sum += sim * rating
weight_sum += sim
if weight_sum > 0:
scores[item] = sim_sum / weight_sum
# 排序取top_n
recommended = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:top_n]
return [int(item) for item, score in recommended]
@app.route('/recommend', methods=['GET'])
def get_recommend():
user_id = request.args.get('user_id', type=int)
if not user_id:
return jsonify({'error': 'missing user_id'}), 400
rec_list = recommend(user_id)
return jsonify({'user_id': user_id, 'recommended_product_ids': rec_list})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001)此引擎轻量且无状态,可横向扩展。
在 Java 服务中,我们通过 Spring Cloud OpenFeign 声明式调用推荐引擎。首先定义 Feign 客户端:
@FeignClient(name = "recommend-service", url = "${recommend.service.url:http://localhost:5001}")
public interface RecommendClient {
@GetMapping("/recommend")
Map<String, Object> getRecommendations(@RequestParam("user_id") Long userId);
}然后在推荐聚合服务(或网关)中使用:
@Service
public class RecommendService {
@Autowired private RecommendClient recommendClient;
@Autowired private ProductClient productClient; // 调用商品服务
public List<Product> getRecommendProducts(Long userId) {
Map<String, Object> resp = recommendClient.getRecommendations(userId);
List<Integer> productIds = (List<Integer>) resp.get("recommended_product_ids");
if (productIds == null || productIds.isEmpty()) {
return Collections.emptyList();
}
// 将 Integer 转为 Long
List<Long> ids = productIds.stream().map(Long::valueOf).collect(Collectors.toList());
return productClient.getBatch(ids); // 批量查询商品详情
}
}为了保证高可用,我们为 Feign 配置 重试和降级(使用 Resilience4j):
@Bean
public Retryer retryer() {
return new Retryer.Default(100, 1000, 3); // 间隔100ms,最多3次
}
// 降级工厂
@Bean
public RecommendClient fallback() {
return userId -> {
log.warn("推荐服务不可用,返回默认推荐");
return Map.of("recommended_product_ids", List.of(101, 102, 103));
};
}前端使用 Vue 3 + Axios 调用 Java 网关,统一入口。核心组件如下:
<template>
<div>
<h2>为您推荐</h2>
<el-row :gutter="20">
<el-col :span="6" v-for="p in products" :key="p.id">
<el-card :body-style="{ padding: '10px' }">
<img :src="p.image" style="width:100%"/>
<div>{{ p.name }}</div>
<div>¥{{ p.price }}</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'
const products = ref([])
const userId = ref(1) // 实际从登录态获取
onMounted(async () => {
const resp = await axios.get(`/api/recommend?userId=${userId.value}`)
products.value = resp.data
})
</script>网关层聚合 Java 服务和 Python 服务的调用,前端只需一次请求,获得完整商品数据。
本文成功搭建了一个 Java 全栈 + Python AI 的混合推荐系统,核心价值在于:
这套模式广泛适用于推荐、图像识别、自然语言处理等 AI 附属服务。未来可演进为 gRPC 通信以获得更高性能,或引入服务网格(Istio)进行流量治理。跨语言不再是障碍,而是架构组合的优势。掌握此范式,开发者便可在全栈与 AI 之间自由穿梭。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。