修正AI陪聊列表返回最后聊天时间

This commit is contained in:
2026-04-20 17:36:38 +08:00
parent 4ca6d36e80
commit 3f7304c3ab
6 changed files with 119 additions and 61 deletions

View File

@@ -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<KeyboardAiChatMessage> {
}
/**
* 查询用户在当前活跃会话中与每个AI角色的最后聊天时间
*
* @param userId 用户ID
* @return 最近聊天时间聚合结果
*/
List<ChattedCompanionLastChatDTO> selectLastChattedCompanions(@Param("userId") Long userId);
}

View File

@@ -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;
}

View File

@@ -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<KeyboardAiChatMes
*/
List<Long> getChattedCompanionIds(Long userId);
/**
* 获取用户与每个AI角色的最后聊天时间
*
* @param userId 用户ID
* @return 角色ID到最后聊天时间的映射顺序为最近聊天时间倒序
*/
Map<Long, Date> getLastChattedAtByCompanionId(Long userId);
/**
* 根据聊天记录ID逻辑删除消息
*

View File

@@ -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<KeyboardAiChat
@Override
public List<Long> getChattedCompanionIds(Long userId) {
// 1. 查询用户所有活跃会话
LambdaQueryWrapper<KeyboardAiChatSession> sessionWrapper = new LambdaQueryWrapper<>();
sessionWrapper.eq(KeyboardAiChatSession::getUserId, userId)
.eq(KeyboardAiChatSession::getIsActive, true);
List<KeyboardAiChatSession> activeSessions = sessionService.list(sessionWrapper);
return getLastChattedAtByCompanionId(userId).keySet().stream().toList();
}
// 2. 如果没有活跃会话,返回空列表
if (activeSessions == null || activeSessions.isEmpty()) {
return Collections.emptyList();
@Override
public Map<Long, Date> getLastChattedAtByCompanionId(Long userId) {
List<ChattedCompanionLastChatDTO> records = baseMapper.selectLastChattedCompanions(userId);
if (records.isEmpty()) {
return Collections.emptyMap();
}
// 3. 提取活跃会话的 sessionId 列表
List<Long> activeSessionIds = activeSessions.stream()
.map(KeyboardAiChatSession::getId)
.collect(java.util.stream.Collectors.toList());
// 4. 查询这些会话中的消息,获取 companionId按最近聊天时间倒序
LambdaQueryWrapper<KeyboardAiChatMessage> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(KeyboardAiChatMessage::getUserId, userId)
.in(KeyboardAiChatMessage::getSessionId, activeSessionIds)
.orderByDesc(KeyboardAiChatMessage::getCreatedAt);
List<KeyboardAiChatMessage> 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

View File

@@ -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<KeyboardAiCompan
.collect(Collectors.toList());
Map<Long, KeyboardAiCompanionI18n> i18nMap = getCompanionI18nMap(companionIds, acceptLanguage);
// 批量统计点赞数
LambdaQueryWrapper<KeyboardAiCompanionLike> likeWrapper = new LambdaQueryWrapper<>();
likeWrapper.in(KeyboardAiCompanionLike::getCompanionId, companionIds)
.eq(KeyboardAiCompanionLike::getStatus, (short) 1);
List<KeyboardAiCompanionLike> likes = companionLikeService.list(likeWrapper);
Map<Long, Long> likeCountMap = likes.stream()
.collect(Collectors.groupingBy(KeyboardAiCompanionLike::getCompanionId, Collectors.counting()));
// 批量统计评论数
LambdaQueryWrapper<KeyboardAiCompanionComment> commentWrapper = new LambdaQueryWrapper<>();
commentWrapper.in(KeyboardAiCompanionComment::getCompanionId, companionIds)
.eq(KeyboardAiCompanionComment::getStatus, (short) 1);
List<KeyboardAiCompanionComment> comments = companionCommentService.list(commentWrapper);
Map<Long, Long> commentCountMap = comments.stream()
.collect(Collectors.groupingBy(KeyboardAiCompanionComment::getCompanionId, Collectors.counting()));
Map<Long, Long> likeCountMap = countLikesByCompanionId(companionIds);
Map<Long, Long> commentCountMap = countCommentsByCompanionId(companionIds);
// 转换为VO并填充统计数据
return companions.stream().map(entity -> {
@@ -221,11 +209,12 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl<KeyboardAiCompan
@Override
public List<AiCompanionVO> getChattedCompanions(Long userId, String acceptLanguage) {
// 获取用户聊过天的所有AI角色ID
List<Long> chattedCompanionIds = chatMessageService.getChattedCompanionIds(userId);
if (chattedCompanionIds.isEmpty()) {
// 获取用户聊过天的AI角色ID及每个角色的最后聊天时间
Map<Long, Date> lastChattedAtMap = chatMessageService.getLastChattedAtByCompanionId(userId);
if (lastChattedAtMap.isEmpty()) {
return List.of();
}
List<Long> chattedCompanionIds = lastChattedAtMap.keySet().stream().toList();
// 查询这些AI角色的详细信息只返回已上线且可见的
LambdaQueryWrapper<KeyboardAiCompanion> queryWrapper = new LambdaQueryWrapper<>();
@@ -244,21 +233,8 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl<KeyboardAiCompan
.collect(Collectors.toList());
Map<Long, KeyboardAiCompanionI18n> i18nMap = getCompanionI18nMap(companionIds, acceptLanguage);
// 批量统计点赞数
LambdaQueryWrapper<KeyboardAiCompanionLike> likeWrapper = new LambdaQueryWrapper<>();
likeWrapper.in(KeyboardAiCompanionLike::getCompanionId, companionIds)
.eq(KeyboardAiCompanionLike::getStatus, (short) 1);
List<KeyboardAiCompanionLike> likes = companionLikeService.list(likeWrapper);
Map<Long, Long> likeCountMap = likes.stream()
.collect(Collectors.groupingBy(KeyboardAiCompanionLike::getCompanionId, Collectors.counting()));
// 批量统计评论数
LambdaQueryWrapper<KeyboardAiCompanionComment> commentWrapper = new LambdaQueryWrapper<>();
commentWrapper.in(KeyboardAiCompanionComment::getCompanionId, companionIds)
.eq(KeyboardAiCompanionComment::getStatus, (short) 1);
List<KeyboardAiCompanionComment> comments = companionCommentService.list(commentWrapper);
Map<Long, Long> commentCountMap = comments.stream()
.collect(Collectors.groupingBy(KeyboardAiCompanionComment::getCompanionId, Collectors.counting()));
Map<Long, Long> likeCountMap = countLikesByCompanionId(companionIds);
Map<Long, Long> commentCountMap = countCommentsByCompanionId(companionIds);
// 获取当前用户已点赞的角色ID
Set<Long> likedCompanionIds = companionLikeService.getLikedCompanionIds(userId, companionIds);
@@ -275,6 +251,7 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl<KeyboardAiCompan
vo.setLikeCount(likeCountMap.getOrDefault(entity.getId(), 0L).intValue());
vo.setCommentCount(commentCountMap.getOrDefault(entity.getId(), 0L).intValue());
vo.setLiked(likedCompanionIds.contains(entity.getId()));
vo.setCreatedAt(lastChattedAtMap.get(entity.getId()));
return vo;
}).collect(Collectors.toList());
}
@@ -329,6 +306,34 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl<KeyboardAiCompan
return vo;
}
private Map<Long, Long> countLikesByCompanionId(List<Long> companionIds) {
if (companionIds.isEmpty()) {
return Map.of();
}
LambdaQueryWrapper<KeyboardAiCompanionLike> likeWrapper = new LambdaQueryWrapper<>();
likeWrapper.in(KeyboardAiCompanionLike::getCompanionId, companionIds)
.eq(KeyboardAiCompanionLike::getStatus, (short) 1);
List<KeyboardAiCompanionLike> likes = companionLikeService.list(likeWrapper);
return likes.stream().collect(Collectors.groupingBy(
KeyboardAiCompanionLike::getCompanionId,
Collectors.counting()
));
}
private Map<Long, Long> countCommentsByCompanionId(List<Long> companionIds) {
if (companionIds.isEmpty()) {
return Map.of();
}
LambdaQueryWrapper<KeyboardAiCompanionComment> commentWrapper = new LambdaQueryWrapper<>();
commentWrapper.in(KeyboardAiCompanionComment::getCompanionId, companionIds)
.eq(KeyboardAiCompanionComment::getStatus, (short) 1);
List<KeyboardAiCompanionComment> comments = companionCommentService.list(commentWrapper);
return comments.stream().collect(Collectors.groupingBy(
KeyboardAiCompanionComment::getCompanionId,
Collectors.counting()
));
}
private Map<Long, KeyboardAiCompanionI18n> getCompanionI18nMap(List<Long> companionIds, String acceptLanguage) {
String locale = RequestLocaleUtils.resolveLanguage(acceptLanguage);
if (companionIds == null || companionIds.isEmpty() || !StringUtils.hasText(locale)) {

View File

@@ -17,4 +17,20 @@
<!--@mbg.generated-->
id, user_id, companion_id, sender, content, emotion_detected, support_type, created_at
</sql>
</mapper>
<resultMap id="LastChatResultMap" type="com.yolo.keyborad.model.dto.chat.ChattedCompanionLastChatDTO">
<result column="companion_id" jdbcType="BIGINT" property="companionId" />
<result column="last_chatted_at" jdbcType="TIMESTAMP" property="lastChattedAt" />
</resultMap>
<select id="selectLastChattedCompanions" resultMap="LastChatResultMap">
SELECT
m.companion_id,
MAX(m.created_at) AS last_chatted_at
FROM keyboard_ai_chat_message m
INNER JOIN keyboard_ai_chat_session s ON s.id = m.session_id
WHERE m.user_id = #{userId}
AND s.user_id = #{userId}
AND s.is_active = TRUE
GROUP BY m.companion_id
ORDER BY last_chatted_at DESC
</select>
</mapper>