|
@@ -165,8 +165,15 @@ public class MatchService {
|
|
|
return new MatchResult(false, null, 0);
|
|
return new MatchResult(false, null, 0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 获取已匹配过的用户列表(从Redis)
|
|
|
|
|
+ Set<String> matchedHistory = getMatchedHistory(userId);
|
|
|
Set<String> unmatched = redisMatchPool.getUserUnmatchedSet(userId);
|
|
Set<String> unmatched = redisMatchPool.getUserUnmatchedSet(userId);
|
|
|
|
|
+
|
|
|
|
|
+ // 打乱用户列表,增加随机性
|
|
|
|
|
+ Collections.shuffle(allUsers);
|
|
|
|
|
+
|
|
|
Map<String, Double> scores = new HashMap<>();
|
|
Map<String, Double> scores = new HashMap<>();
|
|
|
|
|
+ List<Map.Entry<String, Double>> qualifiedMatches = new ArrayList<>();
|
|
|
|
|
|
|
|
for (User user : allUsers) {
|
|
for (User user : allUsers) {
|
|
|
String otherUserId = String.valueOf(user.getUserId());
|
|
String otherUserId = String.valueOf(user.getUserId());
|
|
@@ -174,6 +181,12 @@ public class MatchService {
|
|
|
// 排除自己
|
|
// 排除自己
|
|
|
if (userId.equals(otherUserId)) continue;
|
|
if (userId.equals(otherUserId)) continue;
|
|
|
|
|
|
|
|
|
|
+ // 排除已经匹配过的用户(本次会话)
|
|
|
|
|
+ if (matchedHistory.contains(otherUserId)) {
|
|
|
|
|
+ log.debug("跳过已匹配过的用户: {}", otherUserId);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 排除已经不匹配的用户
|
|
// 排除已经不匹配的用户
|
|
|
if (unmatched.contains(otherUserId)) continue;
|
|
if (unmatched.contains(otherUserId)) continue;
|
|
|
|
|
|
|
@@ -190,28 +203,56 @@ public class MatchService {
|
|
|
double score = MatchingAlgorithmUtils.calculateMatchScore(userData, otherData);
|
|
double score = MatchingAlgorithmUtils.calculateMatchScore(userData, otherData);
|
|
|
scores.put(otherUserId, score);
|
|
scores.put(otherUserId, score);
|
|
|
|
|
|
|
|
- // 如果分数达到阈值,立即返回匹配成功
|
|
|
|
|
|
|
+ // 收集达到阈值的匹配
|
|
|
if (score >= threshold) {
|
|
if (score >= threshold) {
|
|
|
- log.info("从数据库匹配成功: userId={}, matchedUserId={}, score={}", userId, otherUserId, score);
|
|
|
|
|
- return new MatchResult(true, otherUserId, score);
|
|
|
|
|
|
|
+ qualifiedMatches.add(new AbstractMap.SimpleEntry<>(otherUserId, score));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 如果没有达到阈值的,返回分数最高的
|
|
|
|
|
|
|
+ // 如果有达到阈值的匹配,随机选择一个
|
|
|
|
|
+ if (!qualifiedMatches.isEmpty()) {
|
|
|
|
|
+ // 按分数排序,取前3个(如果有的话)
|
|
|
|
|
+ qualifiedMatches.sort((a, b) -> Double.compare(b.getValue(), a.getValue()));
|
|
|
|
|
+ List<Map.Entry<String, Double>> topMatches = qualifiedMatches.subList(0, Math.min(3, qualifiedMatches.size()));
|
|
|
|
|
+
|
|
|
|
|
+ // 从前3个中随机选择一个
|
|
|
|
|
+ Map.Entry<String, Double> selected = topMatches.get(new Random().nextInt(topMatches.size()));
|
|
|
|
|
+
|
|
|
|
|
+ // 记录匹配历史
|
|
|
|
|
+ addToMatchedHistory(userId, selected.getKey());
|
|
|
|
|
+
|
|
|
|
|
+ log.info("从数据库匹配成功: userId={}, matchedUserId={}, score={}, 候选数={}",
|
|
|
|
|
+ userId, selected.getKey(), selected.getValue(), qualifiedMatches.size());
|
|
|
|
|
+ return new MatchResult(true, selected.getKey(), selected.getValue());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有达到阈值的,从所有候选中选择分数最高的几个,随机返回一个
|
|
|
if (!scores.isEmpty()) {
|
|
if (!scores.isEmpty()) {
|
|
|
- Map.Entry<String, Double> best = scores.entrySet().stream()
|
|
|
|
|
- .max(Map.Entry.comparingByValue())
|
|
|
|
|
- .get();
|
|
|
|
|
|
|
+ List<Map.Entry<String, Double>> sortedScores = new ArrayList<>(scores.entrySet());
|
|
|
|
|
+ sortedScores.sort((a, b) -> Double.compare(b.getValue(), a.getValue()));
|
|
|
|
|
+
|
|
|
|
|
+ // 取前5个最高分的
|
|
|
|
|
+ List<Map.Entry<String, Double>> topScores = sortedScores.subList(0, Math.min(5, sortedScores.size()));
|
|
|
|
|
|
|
|
// 降低阈值要求,只要分数大于30就可以匹配
|
|
// 降低阈值要求,只要分数大于30就可以匹配
|
|
|
- if (best.getValue() >= 30.0) {
|
|
|
|
|
|
|
+ List<Map.Entry<String, Double>> acceptableMatches = topScores.stream()
|
|
|
|
|
+ .filter(e -> e.getValue() >= 30.0)
|
|
|
|
|
+ .collect(java.util.stream.Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ if (!acceptableMatches.isEmpty()) {
|
|
|
|
|
+ // 随机选择一个
|
|
|
|
|
+ Map.Entry<String, Double> selected = acceptableMatches.get(new Random().nextInt(acceptableMatches.size()));
|
|
|
|
|
+
|
|
|
|
|
+ // 记录匹配历史
|
|
|
|
|
+ addToMatchedHistory(userId, selected.getKey());
|
|
|
|
|
+
|
|
|
log.info("从数据库匹配成功(降低阈值): userId={}, matchedUserId={}, score={}",
|
|
log.info("从数据库匹配成功(降低阈值): userId={}, matchedUserId={}, score={}",
|
|
|
- userId, best.getKey(), best.getValue());
|
|
|
|
|
- return new MatchResult(true, best.getKey(), best.getValue());
|
|
|
|
|
|
|
+ userId, selected.getKey(), selected.getValue());
|
|
|
|
|
+ return new MatchResult(true, selected.getKey(), selected.getValue());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 记录最佳但未匹配的用户
|
|
// 记录最佳但未匹配的用户
|
|
|
- redisMatchPool.recordUnmatch(userId, best.getKey());
|
|
|
|
|
|
|
+ redisMatchPool.recordUnmatch(userId, sortedScores.get(0).getKey());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
log.info("数据库中未找到合适的匹配对象: userId={}", userId);
|
|
log.info("数据库中未找到合适的匹配对象: userId={}", userId);
|
|
@@ -223,6 +264,38 @@ public class MatchService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取用户的匹配历史(本次会话)
|
|
|
|
|
+ */
|
|
|
|
|
+ private Set<String> getMatchedHistory(String userId) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ String key = "match:history:" + userId;
|
|
|
|
|
+ Set<Object> history = redisTemplate.opsForSet().members(key);
|
|
|
|
|
+ if (history != null) {
|
|
|
|
|
+ return history.stream()
|
|
|
|
|
+ .map(Object::toString)
|
|
|
|
|
+ .collect(java.util.stream.Collectors.toSet());
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("获取匹配历史失败: userId={}", userId, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ return new HashSet<>();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 添加到匹配历史
|
|
|
|
|
+ */
|
|
|
|
|
+ private void addToMatchedHistory(String userId, String matchedUserId) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ String key = "match:history:" + userId;
|
|
|
|
|
+ redisTemplate.opsForSet().add(key, matchedUserId);
|
|
|
|
|
+ // 设置过期时间为1小时
|
|
|
|
|
+ redisTemplate.expire(key, 1, TimeUnit.HOURS);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("添加匹配历史失败: userId={}, matchedUserId={}", userId, matchedUserId, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 从User和UserProfile构建MatchData
|
|
* 从User和UserProfile构建MatchData
|
|
|
*/
|
|
*/
|