修正AI陪聊列表返回最后聊天时间
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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逻辑删除消息
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user