修正AI陪聊列表返回最后聊天时间
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
package com.yolo.keyborad.mapper;
|
package com.yolo.keyborad.mapper;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.yolo.keyborad.model.dto.chat.ChattedCompanionLastChatDTO;
|
||||||
import com.yolo.keyborad.model.entity.KeyboardAiChatMessage;
|
import com.yolo.keyborad.model.entity.KeyboardAiChatMessage;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @author: ziin
|
* @author: ziin
|
||||||
@@ -9,4 +13,12 @@ import com.yolo.keyborad.model.entity.KeyboardAiChatMessage;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public interface KeyboardAiChatMessageMapper extends BaseMapper<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.baomidou.mybatisplus.extension.service.IService;
|
||||||
import com.yolo.keyborad.model.vo.ChatMessageHistoryVO;
|
import com.yolo.keyborad.model.vo.ChatMessageHistoryVO;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @author: ziin
|
* @author: ziin
|
||||||
@@ -42,6 +44,14 @@ public interface KeyboardAiChatMessageService extends IService<KeyboardAiChatMes
|
|||||||
*/
|
*/
|
||||||
List<Long> getChattedCompanionIds(Long userId);
|
List<Long> getChattedCompanionIds(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户与每个AI角色的最后聊天时间
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 角色ID到最后聊天时间的映射,顺序为最近聊天时间倒序
|
||||||
|
*/
|
||||||
|
Map<Long, Date> getLastChattedAtByCompanionId(Long userId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据聊天记录ID逻辑删除消息
|
* 根据聊天记录ID逻辑删除消息
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|||||||
import com.yolo.keyborad.mapper.KeyboardAiChatMessageMapper;
|
import com.yolo.keyborad.mapper.KeyboardAiChatMessageMapper;
|
||||||
import com.yolo.keyborad.common.ErrorCode;
|
import com.yolo.keyborad.common.ErrorCode;
|
||||||
import com.yolo.keyborad.exception.BusinessException;
|
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.KeyboardAiChatMessage;
|
||||||
import com.yolo.keyborad.model.entity.KeyboardAiChatSession;
|
import com.yolo.keyborad.model.entity.KeyboardAiChatSession;
|
||||||
import com.yolo.keyborad.model.vo.ChatMessageHistoryVO;
|
import com.yolo.keyborad.model.vo.ChatMessageHistoryVO;
|
||||||
@@ -17,7 +18,11 @@ import jakarta.annotation.Resource;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @author: ziin
|
* @author: ziin
|
||||||
@@ -83,35 +88,23 @@ public class KeyboardAiChatMessageServiceImpl extends ServiceImpl<KeyboardAiChat
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Long> getChattedCompanionIds(Long userId) {
|
public List<Long> getChattedCompanionIds(Long userId) {
|
||||||
// 1. 查询用户所有活跃会话
|
return getLastChattedAtByCompanionId(userId).keySet().stream().toList();
|
||||||
LambdaQueryWrapper<KeyboardAiChatSession> sessionWrapper = new LambdaQueryWrapper<>();
|
}
|
||||||
sessionWrapper.eq(KeyboardAiChatSession::getUserId, userId)
|
|
||||||
.eq(KeyboardAiChatSession::getIsActive, true);
|
|
||||||
List<KeyboardAiChatSession> activeSessions = sessionService.list(sessionWrapper);
|
|
||||||
|
|
||||||
// 2. 如果没有活跃会话,返回空列表
|
@Override
|
||||||
if (activeSessions == null || activeSessions.isEmpty()) {
|
public Map<Long, Date> getLastChattedAtByCompanionId(Long userId) {
|
||||||
return Collections.emptyList();
|
List<ChattedCompanionLastChatDTO> records = baseMapper.selectLastChattedCompanions(userId);
|
||||||
|
if (records.isEmpty()) {
|
||||||
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 提取活跃会话的 sessionId 列表
|
// LinkedHashMap 保留 SQL 按最后聊天时间倒序返回的顺序。
|
||||||
List<Long> activeSessionIds = activeSessions.stream()
|
return records.stream().collect(Collectors.toMap(
|
||||||
.map(KeyboardAiChatSession::getId)
|
ChattedCompanionLastChatDTO::getCompanionId,
|
||||||
.collect(java.util.stream.Collectors.toList());
|
ChattedCompanionLastChatDTO::getLastChattedAt,
|
||||||
|
(left, right) -> left,
|
||||||
// 4. 查询这些会话中的消息,获取 companionId,按最近聊天时间倒序
|
LinkedHashMap::new
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.yolo.keyborad.service.KeyboardAiCompanionService;
|
|||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -193,21 +194,8 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl<KeyboardAiCompan
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
Map<Long, KeyboardAiCompanionI18n> i18nMap = getCompanionI18nMap(companionIds, acceptLanguage);
|
Map<Long, KeyboardAiCompanionI18n> i18nMap = getCompanionI18nMap(companionIds, acceptLanguage);
|
||||||
|
|
||||||
// 批量统计点赞数
|
Map<Long, Long> likeCountMap = countLikesByCompanionId(companionIds);
|
||||||
LambdaQueryWrapper<KeyboardAiCompanionLike> likeWrapper = new LambdaQueryWrapper<>();
|
Map<Long, Long> commentCountMap = countCommentsByCompanionId(companionIds);
|
||||||
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()));
|
|
||||||
|
|
||||||
// 转换为VO并填充统计数据
|
// 转换为VO并填充统计数据
|
||||||
return companions.stream().map(entity -> {
|
return companions.stream().map(entity -> {
|
||||||
@@ -221,11 +209,12 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl<KeyboardAiCompan
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AiCompanionVO> getChattedCompanions(Long userId, String acceptLanguage) {
|
public List<AiCompanionVO> getChattedCompanions(Long userId, String acceptLanguage) {
|
||||||
// 获取用户聊过天的所有AI角色ID
|
// 获取用户聊过天的AI角色ID及每个角色的最后聊天时间
|
||||||
List<Long> chattedCompanionIds = chatMessageService.getChattedCompanionIds(userId);
|
Map<Long, Date> lastChattedAtMap = chatMessageService.getLastChattedAtByCompanionId(userId);
|
||||||
if (chattedCompanionIds.isEmpty()) {
|
if (lastChattedAtMap.isEmpty()) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
|
List<Long> chattedCompanionIds = lastChattedAtMap.keySet().stream().toList();
|
||||||
|
|
||||||
// 查询这些AI角色的详细信息(只返回已上线且可见的)
|
// 查询这些AI角色的详细信息(只返回已上线且可见的)
|
||||||
LambdaQueryWrapper<KeyboardAiCompanion> queryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<KeyboardAiCompanion> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
@@ -244,21 +233,8 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl<KeyboardAiCompan
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
Map<Long, KeyboardAiCompanionI18n> i18nMap = getCompanionI18nMap(companionIds, acceptLanguage);
|
Map<Long, KeyboardAiCompanionI18n> i18nMap = getCompanionI18nMap(companionIds, acceptLanguage);
|
||||||
|
|
||||||
// 批量统计点赞数
|
Map<Long, Long> likeCountMap = countLikesByCompanionId(companionIds);
|
||||||
LambdaQueryWrapper<KeyboardAiCompanionLike> likeWrapper = new LambdaQueryWrapper<>();
|
Map<Long, Long> commentCountMap = countCommentsByCompanionId(companionIds);
|
||||||
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()));
|
|
||||||
|
|
||||||
// 获取当前用户已点赞的角色ID
|
// 获取当前用户已点赞的角色ID
|
||||||
Set<Long> likedCompanionIds = companionLikeService.getLikedCompanionIds(userId, companionIds);
|
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.setLikeCount(likeCountMap.getOrDefault(entity.getId(), 0L).intValue());
|
||||||
vo.setCommentCount(commentCountMap.getOrDefault(entity.getId(), 0L).intValue());
|
vo.setCommentCount(commentCountMap.getOrDefault(entity.getId(), 0L).intValue());
|
||||||
vo.setLiked(likedCompanionIds.contains(entity.getId()));
|
vo.setLiked(likedCompanionIds.contains(entity.getId()));
|
||||||
|
vo.setCreatedAt(lastChattedAtMap.get(entity.getId()));
|
||||||
return vo;
|
return vo;
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
@@ -329,6 +306,34 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl<KeyboardAiCompan
|
|||||||
return vo;
|
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) {
|
private Map<Long, KeyboardAiCompanionI18n> getCompanionI18nMap(List<Long> companionIds, String acceptLanguage) {
|
||||||
String locale = RequestLocaleUtils.resolveLanguage(acceptLanguage);
|
String locale = RequestLocaleUtils.resolveLanguage(acceptLanguage);
|
||||||
if (companionIds == null || companionIds.isEmpty() || !StringUtils.hasText(locale)) {
|
if (companionIds == null || companionIds.isEmpty() || !StringUtils.hasText(locale)) {
|
||||||
|
|||||||
@@ -17,4 +17,20 @@
|
|||||||
<!--@mbg.generated-->
|
<!--@mbg.generated-->
|
||||||
id, user_id, companion_id, sender, content, emotion_detected, support_type, created_at
|
id, user_id, companion_id, sender, content, emotion_detected, support_type, created_at
|
||||||
</sql>
|
</sql>
|
||||||
|
<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>
|
</mapper>
|
||||||
Reference in New Issue
Block a user