From 3f7304c3ab617a7e41e9b374a2ba3a28279119f9 Mon Sep 17 00:00:00 2001 From: ziin Date: Mon, 20 Apr 2026 17:36:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3AI=E9=99=AA=E8=81=8A=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E8=BF=94=E5=9B=9E=E6=9C=80=E5=90=8E=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapper/KeyboardAiChatMessageMapper.java | 14 +++- .../dto/chat/ChattedCompanionLastChatDTO.java | 22 ++++++ .../service/KeyboardAiChatMessageService.java | 10 +++ .../KeyboardAiChatMessageServiceImpl.java | 45 +++++------- .../impl/KeyboardAiCompanionServiceImpl.java | 71 ++++++++++--------- .../mapper/KeyboardAiChatMessageMapper.xml | 18 ++++- 6 files changed, 119 insertions(+), 61 deletions(-) create mode 100644 src/main/java/com/yolo/keyborad/model/dto/chat/ChattedCompanionLastChatDTO.java diff --git a/src/main/java/com/yolo/keyborad/mapper/KeyboardAiChatMessageMapper.java b/src/main/java/com/yolo/keyborad/mapper/KeyboardAiChatMessageMapper.java index d62da73..c154d5c 100644 --- a/src/main/java/com/yolo/keyborad/mapper/KeyboardAiChatMessageMapper.java +++ b/src/main/java/com/yolo/keyborad/mapper/KeyboardAiChatMessageMapper.java @@ -1,7 +1,11 @@ package com.yolo.keyborad.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yolo.keyborad.model.dto.chat.ChattedCompanionLastChatDTO; import com.yolo.keyborad.model.entity.KeyboardAiChatMessage; +import org.apache.ibatis.annotations.Param; + +import java.util.List; /* * @author: ziin @@ -9,4 +13,12 @@ import com.yolo.keyborad.model.entity.KeyboardAiChatMessage; */ public interface KeyboardAiChatMessageMapper extends BaseMapper { -} \ No newline at end of file + + /** + * 查询用户在当前活跃会话中与每个AI角色的最后聊天时间 + * + * @param userId 用户ID + * @return 最近聊天时间聚合结果 + */ + List selectLastChattedCompanions(@Param("userId") Long userId); +} diff --git a/src/main/java/com/yolo/keyborad/model/dto/chat/ChattedCompanionLastChatDTO.java b/src/main/java/com/yolo/keyborad/model/dto/chat/ChattedCompanionLastChatDTO.java new file mode 100644 index 0000000..30fd800 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/model/dto/chat/ChattedCompanionLastChatDTO.java @@ -0,0 +1,22 @@ +package com.yolo.keyborad.model.dto.chat; + +import lombok.Data; + +import java.util.Date; + +/** + * 用户与AI角色最近聊天时间聚合结果 + */ +@Data +public class ChattedCompanionLastChatDTO { + + /** + * AI陪聊角色ID + */ + private Long companionId; + + /** + * 当前用户与该角色最后一次聊天时间 + */ + private Date lastChattedAt; +} diff --git a/src/main/java/com/yolo/keyborad/service/KeyboardAiChatMessageService.java b/src/main/java/com/yolo/keyborad/service/KeyboardAiChatMessageService.java index 2cd1ecc..1b82f5e 100644 --- a/src/main/java/com/yolo/keyborad/service/KeyboardAiChatMessageService.java +++ b/src/main/java/com/yolo/keyborad/service/KeyboardAiChatMessageService.java @@ -5,7 +5,9 @@ import com.yolo.keyborad.model.entity.KeyboardAiChatMessage; import com.baomidou.mybatisplus.extension.service.IService; import com.yolo.keyborad.model.vo.ChatMessageHistoryVO; +import java.util.Date; import java.util.List; +import java.util.Map; /* * @author: ziin @@ -42,6 +44,14 @@ public interface KeyboardAiChatMessageService extends IService getChattedCompanionIds(Long userId); + /** + * 获取用户与每个AI角色的最后聊天时间 + * + * @param userId 用户ID + * @return 角色ID到最后聊天时间的映射,顺序为最近聊天时间倒序 + */ + Map getLastChattedAtByCompanionId(Long userId); + /** * 根据聊天记录ID逻辑删除消息 * diff --git a/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiChatMessageServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiChatMessageServiceImpl.java index 6773a9a..ba4cddd 100644 --- a/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiChatMessageServiceImpl.java +++ b/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiChatMessageServiceImpl.java @@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yolo.keyborad.mapper.KeyboardAiChatMessageMapper; import com.yolo.keyborad.common.ErrorCode; import com.yolo.keyborad.exception.BusinessException; +import com.yolo.keyborad.model.dto.chat.ChattedCompanionLastChatDTO; import com.yolo.keyborad.model.entity.KeyboardAiChatMessage; import com.yolo.keyborad.model.entity.KeyboardAiChatSession; import com.yolo.keyborad.model.vo.ChatMessageHistoryVO; @@ -17,7 +18,11 @@ import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /* * @author: ziin @@ -83,35 +88,23 @@ public class KeyboardAiChatMessageServiceImpl extends ServiceImpl getChattedCompanionIds(Long userId) { - // 1. 查询用户所有活跃会话 - LambdaQueryWrapper sessionWrapper = new LambdaQueryWrapper<>(); - sessionWrapper.eq(KeyboardAiChatSession::getUserId, userId) - .eq(KeyboardAiChatSession::getIsActive, true); - List activeSessions = sessionService.list(sessionWrapper); + return getLastChattedAtByCompanionId(userId).keySet().stream().toList(); + } - // 2. 如果没有活跃会话,返回空列表 - if (activeSessions == null || activeSessions.isEmpty()) { - return Collections.emptyList(); + @Override + public Map getLastChattedAtByCompanionId(Long userId) { + List records = baseMapper.selectLastChattedCompanions(userId); + if (records.isEmpty()) { + return Collections.emptyMap(); } - // 3. 提取活跃会话的 sessionId 列表 - List activeSessionIds = activeSessions.stream() - .map(KeyboardAiChatSession::getId) - .collect(java.util.stream.Collectors.toList()); - - // 4. 查询这些会话中的消息,获取 companionId,按最近聊天时间倒序 - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(KeyboardAiChatMessage::getUserId, userId) - .in(KeyboardAiChatMessage::getSessionId, activeSessionIds) - .orderByDesc(KeyboardAiChatMessage::getCreatedAt); - - List messages = this.list(queryWrapper); - - // 5. 去重并保持顺序(按最近聊天时间) - return messages.stream() - .map(KeyboardAiChatMessage::getCompanionId) - .distinct() - .collect(java.util.stream.Collectors.toList()); + // LinkedHashMap 保留 SQL 按最后聊天时间倒序返回的顺序。 + return records.stream().collect(Collectors.toMap( + ChattedCompanionLastChatDTO::getCompanionId, + ChattedCompanionLastChatDTO::getLastChattedAt, + (left, right) -> left, + LinkedHashMap::new + )); } @Override diff --git a/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiCompanionServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiCompanionServiceImpl.java index 1e8451a..c4243d6 100644 --- a/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiCompanionServiceImpl.java +++ b/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiCompanionServiceImpl.java @@ -24,6 +24,7 @@ import com.yolo.keyborad.service.KeyboardAiCompanionService; import org.springframework.util.StringUtils; import java.util.Collections; +import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -193,21 +194,8 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl i18nMap = getCompanionI18nMap(companionIds, acceptLanguage); - // 批量统计点赞数 - LambdaQueryWrapper likeWrapper = new LambdaQueryWrapper<>(); - likeWrapper.in(KeyboardAiCompanionLike::getCompanionId, companionIds) - .eq(KeyboardAiCompanionLike::getStatus, (short) 1); - List likes = companionLikeService.list(likeWrapper); - Map likeCountMap = likes.stream() - .collect(Collectors.groupingBy(KeyboardAiCompanionLike::getCompanionId, Collectors.counting())); - - // 批量统计评论数 - LambdaQueryWrapper commentWrapper = new LambdaQueryWrapper<>(); - commentWrapper.in(KeyboardAiCompanionComment::getCompanionId, companionIds) - .eq(KeyboardAiCompanionComment::getStatus, (short) 1); - List comments = companionCommentService.list(commentWrapper); - Map commentCountMap = comments.stream() - .collect(Collectors.groupingBy(KeyboardAiCompanionComment::getCompanionId, Collectors.counting())); + Map likeCountMap = countLikesByCompanionId(companionIds); + Map commentCountMap = countCommentsByCompanionId(companionIds); // 转换为VO并填充统计数据 return companions.stream().map(entity -> { @@ -221,11 +209,12 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl getChattedCompanions(Long userId, String acceptLanguage) { - // 获取用户聊过天的所有AI角色ID - List chattedCompanionIds = chatMessageService.getChattedCompanionIds(userId); - if (chattedCompanionIds.isEmpty()) { + // 获取用户聊过天的AI角色ID及每个角色的最后聊天时间 + Map lastChattedAtMap = chatMessageService.getLastChattedAtByCompanionId(userId); + if (lastChattedAtMap.isEmpty()) { return List.of(); } + List chattedCompanionIds = lastChattedAtMap.keySet().stream().toList(); // 查询这些AI角色的详细信息(只返回已上线且可见的) LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); @@ -244,21 +233,8 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl i18nMap = getCompanionI18nMap(companionIds, acceptLanguage); - // 批量统计点赞数 - LambdaQueryWrapper likeWrapper = new LambdaQueryWrapper<>(); - likeWrapper.in(KeyboardAiCompanionLike::getCompanionId, companionIds) - .eq(KeyboardAiCompanionLike::getStatus, (short) 1); - List likes = companionLikeService.list(likeWrapper); - Map likeCountMap = likes.stream() - .collect(Collectors.groupingBy(KeyboardAiCompanionLike::getCompanionId, Collectors.counting())); - - // 批量统计评论数 - LambdaQueryWrapper commentWrapper = new LambdaQueryWrapper<>(); - commentWrapper.in(KeyboardAiCompanionComment::getCompanionId, companionIds) - .eq(KeyboardAiCompanionComment::getStatus, (short) 1); - List comments = companionCommentService.list(commentWrapper); - Map commentCountMap = comments.stream() - .collect(Collectors.groupingBy(KeyboardAiCompanionComment::getCompanionId, Collectors.counting())); + Map likeCountMap = countLikesByCompanionId(companionIds); + Map commentCountMap = countCommentsByCompanionId(companionIds); // 获取当前用户已点赞的角色ID Set likedCompanionIds = companionLikeService.getLikedCompanionIds(userId, companionIds); @@ -275,6 +251,7 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl countLikesByCompanionId(List companionIds) { + if (companionIds.isEmpty()) { + return Map.of(); + } + LambdaQueryWrapper likeWrapper = new LambdaQueryWrapper<>(); + likeWrapper.in(KeyboardAiCompanionLike::getCompanionId, companionIds) + .eq(KeyboardAiCompanionLike::getStatus, (short) 1); + List likes = companionLikeService.list(likeWrapper); + return likes.stream().collect(Collectors.groupingBy( + KeyboardAiCompanionLike::getCompanionId, + Collectors.counting() + )); + } + + private Map countCommentsByCompanionId(List companionIds) { + if (companionIds.isEmpty()) { + return Map.of(); + } + LambdaQueryWrapper commentWrapper = new LambdaQueryWrapper<>(); + commentWrapper.in(KeyboardAiCompanionComment::getCompanionId, companionIds) + .eq(KeyboardAiCompanionComment::getStatus, (short) 1); + List comments = companionCommentService.list(commentWrapper); + return comments.stream().collect(Collectors.groupingBy( + KeyboardAiCompanionComment::getCompanionId, + Collectors.counting() + )); + } + private Map getCompanionI18nMap(List companionIds, String acceptLanguage) { String locale = RequestLocaleUtils.resolveLanguage(acceptLanguage); if (companionIds == null || companionIds.isEmpty() || !StringUtils.hasText(locale)) { diff --git a/src/main/resources/mapper/KeyboardAiChatMessageMapper.xml b/src/main/resources/mapper/KeyboardAiChatMessageMapper.xml index 5c725bf..4620b14 100644 --- a/src/main/resources/mapper/KeyboardAiChatMessageMapper.xml +++ b/src/main/resources/mapper/KeyboardAiChatMessageMapper.xml @@ -17,4 +17,20 @@ id, user_id, companion_id, sender, content, emotion_detected, support_type, created_at - \ No newline at end of file + + + + + +