毕设所有选题:
https://blog.csdn.net/2303_76227485/article/details/131104075
基于Java+Springboot+vue3+协同过滤推荐算法的农产品销售系统(源代码+数据库+2万字论文)
项目编号:252
一、系统介绍
本项目前后端分离,分为用户、商户、管理员3种角色。
1、用户:
- 首页门户:轮播广告、个性化推荐商品、分类导航、资讯浏览、真实邮箱注册
- 商品中心:商品列表/详情、关键词搜索
- 购物车:商品添加、数量调整、商品移除
- 订单管理:我的订单、下单支付、状态跟踪、物流查询、修改地址、申请退款、评价
- 用户中心:个人信息管理、密码修改、收货地址维护、我的收藏
2、商户:
- 商品管理、库存管理(出入库)、查看销售数据、处理订单、评价查看、个人信息、密码修改
3、管理员:
- 数据统计:销售数据、用户数据、商品分类环状图、商品柱状图
- 商品管理、分类管理
- 订单管理、购物车管理、评价管理、物流管理
- 库存管理、轮播图管理、用户管理、菜单管理、权限管理、公告管理、资讯管理
4、亮点:
- 使用协同过滤推荐算法推荐商品,基于用户已收藏商品,推荐相似用户感兴趣的商品
- 使用真实邮箱注册,丰富了系统实用性,完善用户体验
二、所用技术
后端技术栈:
- Springboot3
- mybatisPlus
- Jwt
- Spring Security
- Mysql
- Maven
前端技术栈:
- Vue2
- Vue-router
- axios
- elementPlus
- echarts
三、环境介绍
基础环境 :IDEA/eclipse, JDK17或以上, Mysql5.7及以上, Maven3.6, node14, navicat, qq邮箱smtp授权秘钥
所有项目以及源代码本人均调试运行无问题 可支持远程调试运行
四、页面截图
文档截图:


1、用户:
























2、商户:













3、管理员:
















五、浏览地址
-
用户账号密码:user/123456
-
商户账号密码:farmer/123456
-
管理员账户密码:admin/123456
六、部署教程
-
使用Navicat或者其它工具,在mysql中创建对应名称的数据库,并执行项目的sql文件
-
使用IDEA/Eclipse导入springboot项目,若为maven项目请选择maven,等待依赖下载完成
-
修改application.properties里面的数据库配置和qq邮箱smtp授权秘钥,src/main/java/org/example/springboot/SpringbootApplication.java启动后端项目
-
vscode或idea打开vue项目
-
在编译器中打开terminal,执行npm install 依赖下载完成后执行 npm run serve,执行成功后会显示访问地址
七、协同过滤部分代码
java
@Service
public class RecommendService {
private static final Logger LOGGER = LoggerFactory.getLogger(RecommendService.class);
@Autowired
private OrderMapper orderMapper;
@Autowired
private FavoriteMapper favoriteMapper;
@Autowired
private ProductMapper productMapper;
// 计算用户相似度矩�?
private Map<Long, Map<Long, Double>> calculateUserSimilarity() {
// 构建用户-商品行为矩阵
Map<Long, Set<Long>> userProductMap = new HashMap<>();
// 获取所有订单数据(已完成的订单�?
LambdaQueryWrapper<Order> orderWrapper = new LambdaQueryWrapper<>();
orderWrapper.eq(Order::getStatus, 3); // 已完成状�?
List<Order> orders = orderMapper.selectList(orderWrapper);
// 获取所有收藏数据(有效的收藏)
LambdaQueryWrapper<Favorite> favoriteWrapper = new LambdaQueryWrapper<>();
favoriteWrapper.eq(Favorite::getStatus, 1); // 收藏状态为1
List<Favorite> favorites = favoriteMapper.selectList(favoriteWrapper);
// 构建用户-商品映射,购买行为权重为2,收藏行为权重为1
for (Order order : orders) {
Set<Long> products = userProductMap.computeIfAbsent(order.getUserId(), k -> new HashSet<>());
products.add(order.getProductId());
products.add(order.getProductId()); // 添加两次表示更高权重
}
for (Favorite favorite : favorites) {
userProductMap.computeIfAbsent(favorite.getUserId(), k -> new HashSet<>())
.add(favorite.getProductId());
}
// 计算用户相似�?
Map<Long, Map<Long, Double>> similarityMatrix = new HashMap<>();
List<Long> userIds = new ArrayList<>(userProductMap.keySet());
for (int i = 0; i < userIds.size(); i++) {
Long user1 = userIds.get(i);
Map<Long, Double> userSimilarities = new HashMap<>();
similarityMatrix.put(user1, userSimilarities);
for (int j = i + 1; j < userIds.size(); j++) {
Long user2 = userIds.get(j);
double similarity = calculateCosineSimilarity(
userProductMap.get(user1),
userProductMap.get(user2)
);
userSimilarities.put(user2, similarity);
similarityMatrix.computeIfAbsent(user2, k -> new HashMap<>())
.put(user1, similarity);
}
}
return similarityMatrix;
}
// 计算余弦相似�?
private double calculateCosineSimilarity(Set<Long> set1, Set<Long> set2) {
if (set1 == null || set2 == null || set1.isEmpty() || set2.isEmpty()) {
return 0.0;
}
// 计算交集
Set<Long> intersection = new HashSet<>(set1);
intersection.retainAll(set2);
// 添加最小阈�?
if (intersection.isEmpty()) {
return 0.0;
}
// 计算余弦相似�?- 交集大小作为点积,除以两个集合大小的平方根乘�?
double numerator = intersection.size();
double denominator = Math.sqrt(set1.size()) * Math.sqrt(set2.size());
double similarity = numerator / denominator;
LOGGER.debug("计算相似�? set1={}, set2={}, similarity={}", set1, set2, similarity);
return similarity;
}
// 为指定用户生成推�?
public Result<?> generateRecommendations(Long userId) {
try {
// 获取用户的订单和收藏数据
LambdaQueryWrapper<Order> orderWrapper = new LambdaQueryWrapper<>();
orderWrapper.eq(Order::getUserId, userId)
.eq(Order::getStatus, 3); // 已完成的订单
List<Order> userOrders = orderMapper.selectList(orderWrapper);
LambdaQueryWrapper<Favorite> favoriteWrapper = new LambdaQueryWrapper<>();
favoriteWrapper.eq(Favorite::getUserId, userId)
.eq(Favorite::getStatus, 1); // 有效的收�?
List<Favorite> userFavorites = favoriteMapper.selectList(favoriteWrapper);
// 获取用户的商品集�?
Set<Long> userProducts = new HashSet<>();
userOrders.forEach(order -> userProducts.add(order.getProductId()));
userFavorites.forEach(favorite -> userProducts.add(favorite.getProductId()));
if (userProducts.isEmpty()) {
LOGGER.warn("用户 {} 没有任何订单或收藏记录", userId);
}
// 获取用户相似度矩�?
Map<Long, Map<Long, Double>> similarityMatrix = calculateUserSimilarity();
// 获取相似用户
Map<Long, Double> similarUsers = new HashMap<>();
// 获取当前用户与其他用户的相似�?
if (similarityMatrix.containsKey(userId)) {
similarUsers.putAll(similarityMatrix.get(userId));
}
// 获取其他用户与当前用户的相似�?
for (Map.Entry<Long, Map<Long, Double>> entry : similarityMatrix.entrySet()) {
if (entry.getValue().containsKey(userId)) {
similarUsers.put(entry.getKey(), entry.getValue().get(userId));
}
}
// 动态调整相似度阈�?
double similarityThreshold;
if (userProducts.size() < 3) { // 新用�?
similarityThreshold = 0.2;
} else if (userProducts.size() > 10) { // 活跃用户
similarityThreshold = 0.4;
} else {
similarityThreshold = 0.3;
}
// 过滤和排序相似用�?
similarUsers = similarUsers.entrySet()
.stream()
.filter(entry -> entry.getValue() >= similarityThreshold)
.sorted(Map.Entry.<Long, Double>comparingByValue().reversed())
.limit(10)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
// 收集推荐商品及其得分
Map<Long, Double> productScores = new HashMap<>();
for (Map.Entry<Long, Double> entry : similarUsers.entrySet()) {
Long similarUserId = entry.getKey();
double similarity = entry.getValue();
// 获取相似用户的订单和收藏
List<Order> similarUserOrders = orderMapper.selectList(
new LambdaQueryWrapper<Order>()
.eq(Order::getUserId, similarUserId)
.eq(Order::getStatus, 3)
);
List<Favorite> similarUserFavorites = favoriteMapper.selectList(
new LambdaQueryWrapper<Favorite>()
.eq(Favorite::getUserId, similarUserId)
.eq(Favorite::getStatus, 1)
);
// 计算推荐分数(订单权�?,收藏权�?�?
for (Order order : similarUserOrders) {
if (!userProducts.contains(order.getProductId())) {
productScores.merge(order.getProductId(), similarity * 2, Double::sum);
}
}
for (Favorite favorite : similarUserFavorites) {
if (!userProducts.contains(favorite.getProductId())) {
productScores.merge(favorite.getProductId(), similarity, Double::sum);
}
}
}
List<Product> recommendations;
if (productScores.isEmpty()) {
LOGGER.info("没有找到相似用户,使用基于销量的推荐");
recommendations = productMapper.selectList(
new LambdaQueryWrapper<Product>()
.orderByDesc(Product::getSalesCount)
.last("LIMIT 12")
);
} else {
List<Map.Entry<Long, Double>> sortedProducts = new ArrayList<>(productScores.entrySet());
sortedProducts.sort((e1, e2) -> e2.getValue().compareTo(e1.getValue()));
List<Long> recommendedIds = sortedProducts.stream()
.limit(12)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
recommendations = productMapper.selectList(
new LambdaQueryWrapper<Product>()
.in(Product::getId, recommendedIds)
);
}
return Result.success(recommendations);
} catch (Exception e) {
LOGGER.error("生成推荐失败: {}", e.getMessage());
return Result.error("-1", "生成推荐失败: " + e.getMessage());
}
}
// 定时更新推荐
public void updateRecommendations() {
try {
// 获取所有用户ID
List<Long> userIds = orderMapper.selectList(new LambdaQueryWrapper<>())
.stream()
.map(Order::getUserId)
.distinct()
.toList();
// 为每个用户生成推�?
for (Long userId : userIds) {
generateRecommendations(userId);
}
LOGGER.info("成功更新所有用户推");
} catch (Exception e) {
LOGGER.error("更新推荐失败: {}", e.getMessage());
}
}
}