fix(ai-companion): 修复点赞状态与评论回复展示逻辑
- 分页查询接口新增当前用户点赞状态返回 - CommentVO 新增 replies 与 replyCount 字段支持嵌套回复 - 评论服务支持查询一级评论及其前 999 条回复 - 免登录白名单新增 /ai-companion/comment/page 接口
This commit is contained in:
@@ -2,6 +2,6 @@
|
|||||||
"active": true,
|
"active": true,
|
||||||
"started_at": "2026-01-26T13:01:18.447Z",
|
"started_at": "2026-01-26T13:01:18.447Z",
|
||||||
"original_prompt": "刚刚回滚了代码,现在AI陪聊角色评论需要使用KeyboardAiCompanionCommentLikeService添加一个评论点赞接口,用来记录点赞和取消点赞。 ulw",
|
"original_prompt": "刚刚回滚了代码,现在AI陪聊角色评论需要使用KeyboardAiCompanionCommentLikeService添加一个评论点赞接口,用来记录点赞和取消点赞。 ulw",
|
||||||
"reinforcement_count": 8,
|
"reinforcement_count": 10,
|
||||||
"last_checked_at": "2026-01-27T10:35:42.226Z"
|
"last_checked_at": "2026-01-27T11:00:42.142Z"
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
|||||||
"/ai-companion/page",
|
"/ai-companion/page",
|
||||||
"/chat/history",
|
"/chat/history",
|
||||||
"/ai-companion/comment/add",
|
"/ai-companion/comment/add",
|
||||||
"/speech/transcribe"
|
"/speech/transcribe",
|
||||||
|
"/ai-companion/comment/page"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -34,9 +34,10 @@ public class AiCompanionController {
|
|||||||
private KeyboardAiCompanionLikeService aiCompanionLikeService;
|
private KeyboardAiCompanionLikeService aiCompanionLikeService;
|
||||||
|
|
||||||
@PostMapping("/page")
|
@PostMapping("/page")
|
||||||
@Operation(summary = "分页查询AI陪聊角色", description = "分页查询已上线的AI陪聊角色列表")
|
@Operation(summary = "分页查询AI陪聊角色", description = "分页查询已上线的AI陪聊角色列表,包含点赞数、评论数和当前用户点赞状态")
|
||||||
public BaseResponse<IPage<AiCompanionVO>> pageList(@RequestBody PageDTO pageDTO) {
|
public BaseResponse<IPage<AiCompanionVO>> pageList(@RequestBody PageDTO pageDTO) {
|
||||||
IPage<AiCompanionVO> result = aiCompanionService.pageList(pageDTO.getPageNum(), pageDTO.getPageSize());
|
Long userId = StpUtil.getLoginIdAsLong();
|
||||||
|
IPage<AiCompanionVO> result = aiCompanionService.pageListWithLikeStatus(userId, pageDTO.getPageNum(), pageDTO.getPageSize());
|
||||||
return ResultUtils.success(result);
|
return ResultUtils.success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ public class AiCompanionVO {
|
|||||||
@Schema(description = "评论总数")
|
@Schema(description = "评论总数")
|
||||||
private Integer commentCount;
|
private Integer commentCount;
|
||||||
|
|
||||||
|
@Schema(description = "当前用户是否已点赞")
|
||||||
|
private Boolean liked;
|
||||||
|
|
||||||
@Schema(description = "创建时间")
|
@Schema(description = "创建时间")
|
||||||
private Date createdAt;
|
private Date createdAt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @author: ziin
|
* @author: ziin
|
||||||
@@ -45,4 +46,10 @@ public class CommentVO {
|
|||||||
|
|
||||||
@Schema(description = "评论创建时间")
|
@Schema(description = "评论创建时间")
|
||||||
private Date createdAt;
|
private Date createdAt;
|
||||||
|
|
||||||
|
@Schema(description = "回复列表(仅一级评论有值,默认返回前3条)")
|
||||||
|
private List<CommentVO> replies;
|
||||||
|
|
||||||
|
@Schema(description = "回复总数")
|
||||||
|
private Integer replyCount;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,16 @@ public interface KeyboardAiCompanionService extends IService<KeyboardAiCompanion
|
|||||||
*/
|
*/
|
||||||
IPage<AiCompanionVO> pageList(Integer pageNum, Integer pageSize);
|
IPage<AiCompanionVO> pageList(Integer pageNum, Integer pageSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询已上线的AI陪聊角色(带当前用户点赞状态)
|
||||||
|
*
|
||||||
|
* @param userId 当前用户ID
|
||||||
|
* @param pageNum 页码
|
||||||
|
* @param pageSize 每页数量
|
||||||
|
* @return 分页结果
|
||||||
|
*/
|
||||||
|
IPage<AiCompanionVO> pageListWithLikeStatus(Long userId, Integer pageNum, Integer pageSize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据AI人设ID获取系统提示词
|
* 根据AI人设ID获取系统提示词
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import com.yolo.keyborad.service.UserService;
|
|||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -104,12 +105,51 @@ public class KeyboardAiCompanionCommentServiceImpl extends ServiceImpl<KeyboardA
|
|||||||
.orderByDesc(KeyboardAiCompanionComment::getCreatedAt);
|
.orderByDesc(KeyboardAiCompanionComment::getCreatedAt);
|
||||||
IPage<KeyboardAiCompanionComment> entityPage = this.page(page, queryWrapper);
|
IPage<KeyboardAiCompanionComment> entityPage = this.page(page, queryWrapper);
|
||||||
|
|
||||||
// 获取所有用户ID
|
// 获取所有一级评论ID
|
||||||
List<Long> userIds = entityPage.getRecords().stream()
|
List<Long> topCommentIds = entityPage.getRecords().stream()
|
||||||
.map(KeyboardAiCompanionComment::getUserId)
|
.map(KeyboardAiCompanionComment::getId)
|
||||||
.distinct()
|
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 批量查询回复(每条一级评论取前3条回复)
|
||||||
|
Map<Long, List<KeyboardAiCompanionComment>> repliesMap = Map.of();
|
||||||
|
Map<Long, Long> replyCountMap = Map.of();
|
||||||
|
if (!topCommentIds.isEmpty()) {
|
||||||
|
// 查询所有回复
|
||||||
|
LambdaQueryWrapper<KeyboardAiCompanionComment> replyWrapper = new LambdaQueryWrapper<>();
|
||||||
|
replyWrapper.in(KeyboardAiCompanionComment::getRootId, topCommentIds)
|
||||||
|
.eq(KeyboardAiCompanionComment::getStatus, 1)
|
||||||
|
.orderByAsc(KeyboardAiCompanionComment::getCreatedAt);
|
||||||
|
List<KeyboardAiCompanionComment> allReplies = this.list(replyWrapper);
|
||||||
|
|
||||||
|
// 按rootId分组,每组取前3条
|
||||||
|
repliesMap = allReplies.stream()
|
||||||
|
.collect(Collectors.groupingBy(KeyboardAiCompanionComment::getRootId));
|
||||||
|
|
||||||
|
// 统计回复数量
|
||||||
|
replyCountMap = repliesMap.entrySet().stream()
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, e -> (long) e.getValue().size()));
|
||||||
|
|
||||||
|
// 每组只保留前3条
|
||||||
|
repliesMap = repliesMap.entrySet().stream()
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
Map.Entry::getKey,
|
||||||
|
e -> e.getValue().stream().limit(999).collect(Collectors.toList())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收集所有需要查询的用户ID(一级评论 + 回复)
|
||||||
|
List<Long> userIds = new ArrayList<>(entityPage.getRecords().stream()
|
||||||
|
.map(KeyboardAiCompanionComment::getUserId)
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
repliesMap.values().stream()
|
||||||
|
.flatMap(List::stream)
|
||||||
|
.map(KeyboardAiCompanionComment::getUserId)
|
||||||
|
.forEach(uid -> {
|
||||||
|
if (!userIds.contains(uid)) {
|
||||||
|
userIds.add(uid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 批量查询用户信息
|
// 批量查询用户信息
|
||||||
Map<Long, KeyboardUser> userMap = Map.of();
|
Map<Long, KeyboardUser> userMap = Map.of();
|
||||||
if (!userIds.isEmpty()) {
|
if (!userIds.isEmpty()) {
|
||||||
@@ -117,33 +157,55 @@ public class KeyboardAiCompanionCommentServiceImpl extends ServiceImpl<KeyboardA
|
|||||||
userMap = users.stream().collect(Collectors.toMap(KeyboardUser::getId, u -> u));
|
userMap = users.stream().collect(Collectors.toMap(KeyboardUser::getId, u -> u));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前用户已点赞的评论ID
|
// 收集所有评论ID用于查询点赞状态(一级评论 + 回复)
|
||||||
List<Long> commentIds = entityPage.getRecords().stream()
|
List<Long> allCommentIds = new ArrayList<>(topCommentIds);
|
||||||
|
repliesMap.values().stream()
|
||||||
|
.flatMap(List::stream)
|
||||||
.map(KeyboardAiCompanionComment::getId)
|
.map(KeyboardAiCompanionComment::getId)
|
||||||
.collect(Collectors.toList());
|
.forEach(allCommentIds::add);
|
||||||
Set<Long> likedCommentIds = commentLikeService.getLikedCommentIds(userId, commentIds);
|
Set<Long> likedCommentIds = commentLikeService.getLikedCommentIds(userId, allCommentIds);
|
||||||
|
|
||||||
// 转换为VO
|
// 转换为VO
|
||||||
Map<Long, KeyboardUser> finalUserMap = userMap;
|
Map<Long, KeyboardUser> finalUserMap = userMap;
|
||||||
return entityPage.convert(entity -> {
|
Map<Long, List<KeyboardAiCompanionComment>> finalRepliesMap = repliesMap;
|
||||||
CommentVO vo = new CommentVO();
|
Map<Long, Long> finalReplyCountMap = replyCountMap;
|
||||||
vo.setId(entity.getId());
|
|
||||||
vo.setCompanionId(entity.getCompanionId());
|
return entityPage.convert(entity -> {
|
||||||
vo.setUserId(entity.getUserId());
|
CommentVO vo = convertToVO(entity, finalUserMap, likedCommentIds);
|
||||||
vo.setParentId(entity.getParentId());
|
|
||||||
vo.setRootId(entity.getRootId());
|
// 填充回复列表
|
||||||
vo.setContent(entity.getContent());
|
List<KeyboardAiCompanionComment> replies = finalRepliesMap.getOrDefault(entity.getId(), List.of());
|
||||||
vo.setLikeCount(entity.getLikeCount());
|
List<CommentVO> replyVOs = replies.stream()
|
||||||
vo.setCreatedAt(entity.getCreatedAt());
|
.map(reply -> convertToVO(reply, finalUserMap, likedCommentIds))
|
||||||
vo.setLiked(likedCommentIds.contains(entity.getId()));
|
.collect(Collectors.toList());
|
||||||
|
vo.setReplies(replyVOs);
|
||||||
|
vo.setReplyCount(finalReplyCountMap.getOrDefault(entity.getId(), 0L).intValue());
|
||||||
|
|
||||||
// 填充用户信息
|
|
||||||
KeyboardUser user = finalUserMap.get(entity.getUserId());
|
|
||||||
if (user != null) {
|
|
||||||
vo.setUserName(user.getNickName());
|
|
||||||
vo.setUserAvatar(user.getAvatarUrl());
|
|
||||||
}
|
|
||||||
return vo;
|
return vo;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将评论实体转换为VO
|
||||||
|
*/
|
||||||
|
private CommentVO convertToVO(KeyboardAiCompanionComment entity, Map<Long, KeyboardUser> userMap, Set<Long> likedCommentIds) {
|
||||||
|
CommentVO vo = new CommentVO();
|
||||||
|
vo.setId(entity.getId());
|
||||||
|
vo.setCompanionId(entity.getCompanionId());
|
||||||
|
vo.setUserId(entity.getUserId());
|
||||||
|
vo.setParentId(entity.getParentId());
|
||||||
|
vo.setRootId(entity.getRootId());
|
||||||
|
vo.setContent(entity.getContent());
|
||||||
|
vo.setLikeCount(entity.getLikeCount());
|
||||||
|
vo.setCreatedAt(entity.getCreatedAt());
|
||||||
|
vo.setLiked(likedCommentIds.contains(entity.getId()));
|
||||||
|
|
||||||
|
// 填充用户信息
|
||||||
|
KeyboardUser user = userMap.get(entity.getUserId());
|
||||||
|
if (user != null) {
|
||||||
|
vo.setUserName(user.getNickName());
|
||||||
|
vo.setUserAvatar(user.getAvatarUrl());
|
||||||
|
}
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.yolo.keyborad.service.KeyboardAiCompanionService;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -83,6 +84,58 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl<KeyboardAiCompan
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPage<AiCompanionVO> pageListWithLikeStatus(Long userId, Integer pageNum, Integer pageSize) {
|
||||||
|
Page<KeyboardAiCompanion> page = new Page<>(pageNum, pageSize);
|
||||||
|
LambdaQueryWrapper<KeyboardAiCompanion> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
queryWrapper.eq(KeyboardAiCompanion::getStatus, 1)
|
||||||
|
.eq(KeyboardAiCompanion::getVisibility, 1)
|
||||||
|
.orderByDesc(KeyboardAiCompanion::getSortOrder)
|
||||||
|
.orderByDesc(KeyboardAiCompanion::getPopularityScore);
|
||||||
|
IPage<KeyboardAiCompanion> entityPage = this.page(page, queryWrapper);
|
||||||
|
|
||||||
|
// 获取所有角色ID
|
||||||
|
List<Long> companionIds = entityPage.getRecords().stream()
|
||||||
|
.map(KeyboardAiCompanion::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 批量统计点赞数
|
||||||
|
Map<Long, Long> likeCountMap = Map.of();
|
||||||
|
if (!companionIds.isEmpty()) {
|
||||||
|
LambdaQueryWrapper<KeyboardAiCompanionLike> likeWrapper = new LambdaQueryWrapper<>();
|
||||||
|
likeWrapper.in(KeyboardAiCompanionLike::getCompanionId, companionIds)
|
||||||
|
.eq(KeyboardAiCompanionLike::getStatus, (short) 1);
|
||||||
|
List<KeyboardAiCompanionLike> likes = companionLikeService.list(likeWrapper);
|
||||||
|
likeCountMap = likes.stream()
|
||||||
|
.collect(Collectors.groupingBy(KeyboardAiCompanionLike::getCompanionId, Collectors.counting()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量统计评论数
|
||||||
|
Map<Long, Long> commentCountMap = Map.of();
|
||||||
|
if (!companionIds.isEmpty()) {
|
||||||
|
LambdaQueryWrapper<KeyboardAiCompanionComment> commentWrapper = new LambdaQueryWrapper<>();
|
||||||
|
commentWrapper.in(KeyboardAiCompanionComment::getCompanionId, companionIds)
|
||||||
|
.eq(KeyboardAiCompanionComment::getStatus, (short) 1);
|
||||||
|
List<KeyboardAiCompanionComment> comments = companionCommentService.list(commentWrapper);
|
||||||
|
commentCountMap = comments.stream()
|
||||||
|
.collect(Collectors.groupingBy(KeyboardAiCompanionComment::getCompanionId, Collectors.counting()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前用户已点赞的角色ID
|
||||||
|
Set<Long> likedCompanionIds = companionLikeService.getLikedCompanionIds(userId, companionIds);
|
||||||
|
|
||||||
|
// 转换为VO并填充统计数据和点赞状态
|
||||||
|
Map<Long, Long> finalLikeCountMap = likeCountMap;
|
||||||
|
Map<Long, Long> finalCommentCountMap = commentCountMap;
|
||||||
|
return entityPage.convert(entity -> {
|
||||||
|
AiCompanionVO vo = BeanUtil.copyProperties(entity, AiCompanionVO.class);
|
||||||
|
vo.setLikeCount(finalLikeCountMap.getOrDefault(entity.getId(), 0L).intValue());
|
||||||
|
vo.setCommentCount(finalCommentCountMap.getOrDefault(entity.getId(), 0L).intValue());
|
||||||
|
vo.setLiked(likedCompanionIds.contains(entity.getId()));
|
||||||
|
return vo;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSystemPromptById(Long companionId) {
|
public String getSystemPromptById(Long companionId) {
|
||||||
KeyboardAiCompanion companion = this.getById(companionId);
|
KeyboardAiCompanion companion = this.getById(companionId);
|
||||||
|
|||||||
Reference in New Issue
Block a user