Compare commits
5 Commits
f28e6b7c39
...
ecab353802
| Author | SHA1 | Date | |
|---|---|---|---|
| ecab353802 | |||
| 234ea0c241 | |||
| e1aa1ce4e8 | |||
| c8d8046bf4 | |||
| 0e863288c8 |
@@ -116,7 +116,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
"/chat/history",
|
||||
"/ai-companion/comment/add",
|
||||
"/speech/transcribe",
|
||||
"/ai-companion/comment/page"
|
||||
"/ai-companion/comment/page",
|
||||
"/ai-companion/liked"
|
||||
};
|
||||
}
|
||||
@Bean
|
||||
|
||||
@@ -17,6 +17,8 @@ import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* @author: ziin
|
||||
* @date: 2026/1/26
|
||||
@@ -52,4 +54,31 @@ public class AiCompanionController {
|
||||
boolean result = aiCompanionLikeService.toggleLike(userId, req.getCompanionId());
|
||||
return ResultUtils.success(result);
|
||||
}
|
||||
|
||||
@GetMapping("/liked")
|
||||
@Operation(summary = "获取当前用户点赞过的AI角色列表", description = "查询当前用户点赞过的所有AI角色,返回角色详细信息")
|
||||
public BaseResponse<List<AiCompanionVO>> getLikedCompanions() {
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
List<AiCompanionVO> result = aiCompanionService.getLikedCompanions(userId);
|
||||
return ResultUtils.success(result);
|
||||
}
|
||||
|
||||
@GetMapping("/chatted")
|
||||
@Operation(summary = "获取当前用户聊过天的AI角色列表", description = "查询当前用户聊过天的所有AI角色,返回角色详细信息")
|
||||
public BaseResponse<List<AiCompanionVO>> getChattedCompanions() {
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
List<AiCompanionVO> result = aiCompanionService.getChattedCompanions(userId);
|
||||
return ResultUtils.success(result);
|
||||
}
|
||||
|
||||
@GetMapping("/{companionId}")
|
||||
@Operation(summary = "根据ID获取AI角色详情", description = "根据AI角色ID查询角色详细信息,包含点赞数、评论数和当前用户点赞状态")
|
||||
public BaseResponse<AiCompanionVO> getCompanionById(@PathVariable Long companionId) {
|
||||
if (companionId == null) {
|
||||
throw new BusinessException(ErrorCode.COMPANION_ID_EMPTY);
|
||||
}
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
AiCompanionVO result = aiCompanionService.getCompanionById(userId, companionId);
|
||||
return ResultUtils.success(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,15 @@ import com.yolo.keyborad.model.dto.chat.ChatMessageReq;
|
||||
import com.yolo.keyborad.model.dto.chat.ChatReq;
|
||||
import com.yolo.keyborad.model.dto.chat.ChatSaveReq;
|
||||
import com.yolo.keyborad.model.dto.chat.ChatStreamMessage;
|
||||
import com.yolo.keyborad.model.dto.chat.SessionResetReq;
|
||||
import com.yolo.keyborad.model.vo.AudioTaskVO;
|
||||
import com.yolo.keyborad.model.vo.ChatMessageHistoryVO;
|
||||
import com.yolo.keyborad.model.vo.ChatMessageVO;
|
||||
import com.yolo.keyborad.model.vo.ChatSessionVO;
|
||||
import com.yolo.keyborad.model.vo.ChatVoiceVO;
|
||||
import com.yolo.keyborad.service.ChatService;
|
||||
import com.yolo.keyborad.service.KeyboardAiChatMessageService;
|
||||
import com.yolo.keyborad.service.KeyboardAiChatSessionService;
|
||||
import com.yolo.keyborad.service.impl.QdrantVectorService;
|
||||
import io.qdrant.client.grpc.JsonWithInt;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -55,6 +58,9 @@ public class ChatController {
|
||||
@Resource
|
||||
private KeyboardAiChatMessageService aiChatMessageService;
|
||||
|
||||
@Resource
|
||||
private KeyboardAiChatSessionService aiChatSessionService;
|
||||
|
||||
|
||||
@PostMapping("/message")
|
||||
@Operation(summary = "同步对话", description = "发送消息给大模型,同步返回 AI 响应,异步生成音频")
|
||||
@@ -131,4 +137,24 @@ public class ChatController {
|
||||
userId, req.getCompanionId(), req.getPageNum(), req.getPageSize());
|
||||
return ResultUtils.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/session/reset")
|
||||
@Operation(summary = "重置会话", description = "重置与AI角色的聊天会话,将当前会话设为不活跃并创建新会话,后续聊天记录将绑定到新会话")
|
||||
public BaseResponse<ChatSessionVO> resetSession(@RequestBody SessionResetReq req) {
|
||||
if (req.getCompanionId() == null) {
|
||||
throw new BusinessException(ErrorCode.COMPANION_ID_EMPTY);
|
||||
}
|
||||
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
var newSession = aiChatSessionService.resetSession(userId, req.getCompanionId());
|
||||
|
||||
ChatSessionVO vo = ChatSessionVO.builder()
|
||||
.sessionId(newSession.getId())
|
||||
.companionId(newSession.getCompanionId())
|
||||
.resetVersion(newSession.getResetVersion())
|
||||
.createdAt(newSession.getCreatedAt())
|
||||
.build();
|
||||
|
||||
return ResultUtils.success(vo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.yolo.keyborad.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.yolo.keyborad.model.entity.KeyboardAiChatSession;
|
||||
|
||||
/*
|
||||
* @author: ziin
|
||||
* @date: 2026/1/28 16:20
|
||||
*/
|
||||
|
||||
public interface KeyboardAiChatSessionMapper extends BaseMapper<KeyboardAiChatSession> {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.yolo.keyborad.model.dto.chat;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/*
|
||||
* @author: ziin
|
||||
* @date: 2026/1/28
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "会话重置请求")
|
||||
public class SessionResetReq {
|
||||
|
||||
@Schema(description = "AI陪聊角色ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long companionId;
|
||||
}
|
||||
@@ -75,4 +75,8 @@ public class KeyboardAiChatMessage {
|
||||
@TableField(value = "created_at")
|
||||
@Schema(description="消息创建时间")
|
||||
private Date createdAt;
|
||||
|
||||
@TableField(value = "session_id")
|
||||
@Schema(description = "会话Id")
|
||||
private Long sessionId;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.yolo.keyborad.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
|
||||
/*
|
||||
* @author: ziin
|
||||
* @date: 2026/1/28 16:20
|
||||
*/
|
||||
|
||||
/**
|
||||
* 用户与AI陪伴角色的聊天会话表,用于支持聊天重置与关系重启
|
||||
*/
|
||||
@Schema(description="用户与AI陪伴角色的聊天会话表,用于支持聊天重置与关系重启")
|
||||
@Data
|
||||
@TableName(value = "keyboard_ai_chat_session")
|
||||
public class KeyboardAiChatSession {
|
||||
/**
|
||||
* 聊天会话唯一ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
@Schema(description="聊天会话唯一ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@TableField(value = "user_id")
|
||||
@Schema(description="用户ID")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 陪伴角色ID
|
||||
*/
|
||||
@TableField(value = "companion_id")
|
||||
@Schema(description="陪伴角色ID")
|
||||
private Long companionId;
|
||||
|
||||
/**
|
||||
* 会话重置版本号,用于标识第几次重新开始陪伴关系
|
||||
*/
|
||||
@TableField(value = "reset_version")
|
||||
@Schema(description="会话重置版本号,用于标识第几次重新开始陪伴关系")
|
||||
private Integer resetVersion;
|
||||
|
||||
/**
|
||||
* 是否为当前活跃会话(true=当前使用中)
|
||||
*/
|
||||
@TableField(value = "is_active")
|
||||
@Schema(description="是否为当前活跃会话(true=当前使用中)")
|
||||
private Boolean isActive;
|
||||
|
||||
/**
|
||||
* 会话创建时间
|
||||
*/
|
||||
@TableField(value = "created_at")
|
||||
@Schema(description="会话创建时间")
|
||||
private Date createdAt;
|
||||
|
||||
/**
|
||||
* 会话结束时间(用户重置或系统关闭会话时记录)
|
||||
*/
|
||||
@TableField(value = "ended_at")
|
||||
@Schema(description="会话结束时间(用户重置或系统关闭会话时记录)")
|
||||
private Date endedAt;
|
||||
}
|
||||
29
src/main/java/com/yolo/keyborad/model/vo/ChatSessionVO.java
Normal file
29
src/main/java/com/yolo/keyborad/model/vo/ChatSessionVO.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package com.yolo.keyborad.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/*
|
||||
* @author: ziin
|
||||
* @date: 2026/1/28
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@Schema(description = "会话信息VO")
|
||||
public class ChatSessionVO {
|
||||
|
||||
@Schema(description = "会话ID")
|
||||
private Long sessionId;
|
||||
|
||||
@Schema(description = "AI陪聊角色ID")
|
||||
private Long companionId;
|
||||
|
||||
@Schema(description = "会话版本号")
|
||||
private Integer resetVersion;
|
||||
|
||||
@Schema(description = "会话创建时间")
|
||||
private Date createdAt;
|
||||
}
|
||||
@@ -33,4 +33,12 @@ public interface KeyboardAiChatMessageService extends IService<KeyboardAiChatMes
|
||||
* @return 聊天记录列表(时间正序)
|
||||
*/
|
||||
List<KeyboardAiChatMessage> getRecentMessages(Long userId, Long companionId, int limit);
|
||||
|
||||
/**
|
||||
* 获取用户聊过天的所有AI角色ID列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 聊过天的AI角色ID列表(按最近聊天时间倒序)
|
||||
*/
|
||||
List<Long> getChattedCompanionIds(Long userId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.yolo.keyborad.service;
|
||||
|
||||
import com.yolo.keyborad.model.entity.KeyboardAiChatSession;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/*
|
||||
* @author: ziin
|
||||
* @date: 2026/1/28 16:20
|
||||
*/
|
||||
public interface KeyboardAiChatSessionService extends IService<KeyboardAiChatSession> {
|
||||
|
||||
/**
|
||||
* 获取用户与AI角色的活跃会话,如果不存在则创建新会话
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param companionId AI角色ID
|
||||
* @return 活跃会话
|
||||
*/
|
||||
KeyboardAiChatSession getOrCreateActiveSession(Long userId, Long companionId);
|
||||
|
||||
/**
|
||||
* 获取用户与AI角色的活跃会话ID,如果不存在则创建新会话
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param companionId AI角色ID
|
||||
* @return 活跃会话ID
|
||||
*/
|
||||
Long getOrCreateActiveSessionId(Long userId, Long companionId);
|
||||
|
||||
/**
|
||||
* 重置会话:将当前活跃会话设为不活跃,并创建新的活跃会话
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param companionId AI角色ID
|
||||
* @return 新创建的活跃会话
|
||||
*/
|
||||
KeyboardAiChatSession resetSession(Long userId, Long companionId);
|
||||
}
|
||||
@@ -38,4 +38,12 @@ public interface KeyboardAiCompanionLikeService extends IService<KeyboardAiCompa
|
||||
* @return 已点赞的AI角色ID集合
|
||||
*/
|
||||
Set<Long> getLikedCompanionIds(Long userId, List<Long> companionIds);
|
||||
|
||||
/**
|
||||
* 获取用户点赞过的所有AI角色ID列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 已点赞的AI角色ID列表
|
||||
*/
|
||||
List<Long> getAllLikedCompanionIds(Long userId);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import com.yolo.keyborad.model.entity.KeyboardAiCompanion;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.yolo.keyborad.model.vo.AiCompanionVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* @author: ziin
|
||||
* @date: 2026/1/26 13:51
|
||||
@@ -37,4 +39,29 @@ public interface KeyboardAiCompanionService extends IService<KeyboardAiCompanion
|
||||
* @return 系统提示词
|
||||
*/
|
||||
String getSystemPromptById(Long companionId);
|
||||
|
||||
/**
|
||||
* 获取用户点赞过的AI角色列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 点赞过的AI角色列表
|
||||
*/
|
||||
List<AiCompanionVO> getLikedCompanions(Long userId);
|
||||
|
||||
/**
|
||||
* 获取用户聊过天的AI角色列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 聊过天的AI角色列表
|
||||
*/
|
||||
List<AiCompanionVO> getChattedCompanions(Long userId);
|
||||
|
||||
/**
|
||||
* 根据ID获取AI角色详情(带点赞数、评论数和当前用户点赞状态)
|
||||
*
|
||||
* @param userId 当前用户ID
|
||||
* @param companionId AI角色ID
|
||||
* @return AI角色详情
|
||||
*/
|
||||
AiCompanionVO getCompanionById(Long userId, Long companionId);
|
||||
}
|
||||
|
||||
@@ -89,6 +89,9 @@ public class ChatServiceImpl implements ChatService {
|
||||
@Resource
|
||||
private KeyboardAiChatMessageService aiChatMessageService;
|
||||
|
||||
@Resource
|
||||
private KeyboardAiChatSessionService aiChatSessionService;
|
||||
|
||||
@Resource
|
||||
private ElevenLabsService elevenLabsService;
|
||||
|
||||
@@ -457,10 +460,14 @@ public class ChatServiceImpl implements ChatService {
|
||||
private void saveChatMessages(Long userId, Long companionId, String userContent, String aiResponse) {
|
||||
Date now = new Date();
|
||||
|
||||
// 获取或创建活跃会话
|
||||
Long sessionId = aiChatSessionService.getOrCreateActiveSessionId(userId, companionId);
|
||||
|
||||
// 保存用户消息 (sender=1)
|
||||
KeyboardAiChatMessage userMessage = new KeyboardAiChatMessage();
|
||||
userMessage.setUserId(userId);
|
||||
userMessage.setCompanionId(companionId);
|
||||
userMessage.setSessionId(sessionId);
|
||||
userMessage.setSender((short) 1);
|
||||
userMessage.setContent(userContent);
|
||||
userMessage.setCreatedAt(now);
|
||||
@@ -469,13 +476,14 @@ public class ChatServiceImpl implements ChatService {
|
||||
KeyboardAiChatMessage aiMessage = new KeyboardAiChatMessage();
|
||||
aiMessage.setUserId(userId);
|
||||
aiMessage.setCompanionId(companionId);
|
||||
aiMessage.setSessionId(sessionId);
|
||||
aiMessage.setSender((short) 2);
|
||||
aiMessage.setContent(aiResponse);
|
||||
aiMessage.setCreatedAt(now);
|
||||
|
||||
// 批量保存
|
||||
aiChatMessageService.saveBatch(List.of(userMessage, aiMessage));
|
||||
log.info("聊天记录保存成功, userId: {}, companionId: {}", userId, companionId);
|
||||
log.info("聊天记录保存成功, userId: {}, companionId: {}, sessionId: {}", userId, companionId, sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,4 +44,20 @@ public class KeyboardAiChatMessageServiceImpl extends ServiceImpl<KeyboardAiChat
|
||||
Collections.reverse(messages);
|
||||
return messages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getChattedCompanionIds(Long userId) {
|
||||
// 使用原生SQL查询用户聊过天的所有角色ID,按最近聊天时间倒序
|
||||
LambdaQueryWrapper<KeyboardAiChatMessage> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(KeyboardAiChatMessage::getUserId, userId)
|
||||
.select(KeyboardAiChatMessage::getCompanionId)
|
||||
.groupBy(KeyboardAiChatMessage::getCompanionId)
|
||||
.orderByDesc(KeyboardAiChatMessage::getCompanionId);
|
||||
|
||||
List<KeyboardAiChatMessage> messages = this.list(queryWrapper);
|
||||
return messages.stream()
|
||||
.map(KeyboardAiChatMessage::getCompanionId)
|
||||
.distinct()
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.yolo.keyborad.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.yolo.keyborad.mapper.KeyboardAiChatSessionMapper;
|
||||
import com.yolo.keyborad.model.entity.KeyboardAiChatSession;
|
||||
import com.yolo.keyborad.service.KeyboardAiChatSessionService;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/*
|
||||
* @author: ziin
|
||||
* @date: 2026/1/28 16:20
|
||||
*/
|
||||
@Service
|
||||
public class KeyboardAiChatSessionServiceImpl extends ServiceImpl<KeyboardAiChatSessionMapper, KeyboardAiChatSession> implements KeyboardAiChatSessionService {
|
||||
|
||||
@Override
|
||||
public KeyboardAiChatSession getOrCreateActiveSession(Long userId, Long companionId) {
|
||||
// 查询当前活跃会话
|
||||
LambdaQueryWrapper<KeyboardAiChatSession> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(KeyboardAiChatSession::getUserId, userId)
|
||||
.eq(KeyboardAiChatSession::getCompanionId, companionId)
|
||||
.eq(KeyboardAiChatSession::getIsActive, true);
|
||||
KeyboardAiChatSession activeSession = this.getOne(queryWrapper);
|
||||
|
||||
if (activeSession != null) {
|
||||
return activeSession;
|
||||
}
|
||||
|
||||
// 不存在活跃会话,创建新会话
|
||||
// 先查询该用户与该角色的最大版本号
|
||||
LambdaQueryWrapper<KeyboardAiChatSession> maxVersionWrapper = new LambdaQueryWrapper<>();
|
||||
maxVersionWrapper.eq(KeyboardAiChatSession::getUserId, userId)
|
||||
.eq(KeyboardAiChatSession::getCompanionId, companionId)
|
||||
.orderByDesc(KeyboardAiChatSession::getResetVersion)
|
||||
.last("LIMIT 1");
|
||||
KeyboardAiChatSession lastSession = this.getOne(maxVersionWrapper);
|
||||
int newVersion = lastSession != null ? lastSession.getResetVersion() + 1 : 1;
|
||||
|
||||
// 创建新会话
|
||||
KeyboardAiChatSession newSession = new KeyboardAiChatSession();
|
||||
newSession.setUserId(userId);
|
||||
newSession.setCompanionId(companionId);
|
||||
newSession.setResetVersion(newVersion);
|
||||
newSession.setIsActive(true);
|
||||
newSession.setCreatedAt(new Date());
|
||||
this.save(newSession);
|
||||
|
||||
return newSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getOrCreateActiveSessionId(Long userId, Long companionId) {
|
||||
return getOrCreateActiveSession(userId, companionId).getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyboardAiChatSession resetSession(Long userId, Long companionId) {
|
||||
Date now = new Date();
|
||||
|
||||
// 查询当前活跃会话
|
||||
LambdaQueryWrapper<KeyboardAiChatSession> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(KeyboardAiChatSession::getUserId, userId)
|
||||
.eq(KeyboardAiChatSession::getCompanionId, companionId)
|
||||
.eq(KeyboardAiChatSession::getIsActive, true);
|
||||
KeyboardAiChatSession activeSession = this.getOne(queryWrapper);
|
||||
|
||||
// 如果存在活跃会话,将其设为不活跃
|
||||
if (activeSession != null) {
|
||||
activeSession.setIsActive(false);
|
||||
activeSession.setEndedAt(now);
|
||||
this.updateById(activeSession);
|
||||
}
|
||||
|
||||
// 查询该用户与该角色的最大版本号
|
||||
LambdaQueryWrapper<KeyboardAiChatSession> maxVersionWrapper = new LambdaQueryWrapper<>();
|
||||
maxVersionWrapper.eq(KeyboardAiChatSession::getUserId, userId)
|
||||
.eq(KeyboardAiChatSession::getCompanionId, companionId)
|
||||
.orderByDesc(KeyboardAiChatSession::getResetVersion)
|
||||
.last("LIMIT 1");
|
||||
KeyboardAiChatSession lastSession = this.getOne(maxVersionWrapper);
|
||||
int newVersion = lastSession != null ? lastSession.getResetVersion() + 1 : 1;
|
||||
|
||||
// 创建新的活跃会话
|
||||
KeyboardAiChatSession newSession = new KeyboardAiChatSession();
|
||||
newSession.setUserId(userId);
|
||||
newSession.setCompanionId(companionId);
|
||||
newSession.setResetVersion(newVersion);
|
||||
newSession.setIsActive(true);
|
||||
newSession.setCreatedAt(now);
|
||||
this.save(newSession);
|
||||
|
||||
return newSession;
|
||||
}
|
||||
}
|
||||
@@ -100,4 +100,16 @@ public class KeyboardAiCompanionLikeServiceImpl extends ServiceImpl<KeyboardAiCo
|
||||
|
||||
return isLiked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getAllLikedCompanionIds(Long userId) {
|
||||
LambdaQueryWrapper<KeyboardAiCompanionLike> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(KeyboardAiCompanionLike::getUserId, userId)
|
||||
.eq(KeyboardAiCompanionLike::getStatus, (short) 1)
|
||||
.select(KeyboardAiCompanionLike::getCompanionId);
|
||||
List<KeyboardAiCompanionLike> likes = this.list(queryWrapper);
|
||||
return likes.stream()
|
||||
.map(KeyboardAiCompanionLike::getCompanionId)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.yolo.keyborad.model.entity.KeyboardAiCompanionLike;
|
||||
import com.yolo.keyborad.model.vo.AiCompanionVO;
|
||||
import com.yolo.keyborad.service.KeyboardAiCompanionCommentService;
|
||||
import com.yolo.keyborad.service.KeyboardAiCompanionLikeService;
|
||||
import com.yolo.keyborad.service.KeyboardAiChatMessageService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
@@ -36,6 +37,9 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl<KeyboardAiCompan
|
||||
@Resource
|
||||
private KeyboardAiCompanionCommentService companionCommentService;
|
||||
|
||||
@Resource
|
||||
private KeyboardAiChatMessageService chatMessageService;
|
||||
|
||||
@Override
|
||||
public IPage<AiCompanionVO> pageList(Integer pageNum, Integer pageSize) {
|
||||
Page<KeyboardAiCompanion> page = new Page<>(pageNum, pageSize);
|
||||
@@ -147,4 +151,150 @@ public class KeyboardAiCompanionServiceImpl extends ServiceImpl<KeyboardAiCompan
|
||||
}
|
||||
return companion.getSystemPrompt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiCompanionVO> getLikedCompanions(Long userId) {
|
||||
// 获取用户点赞过的所有AI角色ID
|
||||
List<Long> likedCompanionIds = companionLikeService.getAllLikedCompanionIds(userId);
|
||||
if (likedCompanionIds.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// 查询这些AI角色的详细信息(只返回已上线且可见的)
|
||||
LambdaQueryWrapper<KeyboardAiCompanion> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(KeyboardAiCompanion::getId, likedCompanionIds)
|
||||
.eq(KeyboardAiCompanion::getStatus, 1)
|
||||
.eq(KeyboardAiCompanion::getVisibility, 1)
|
||||
.orderByDesc(KeyboardAiCompanion::getSortOrder)
|
||||
.orderByDesc(KeyboardAiCompanion::getPopularityScore);
|
||||
List<KeyboardAiCompanion> companions = this.list(queryWrapper);
|
||||
|
||||
if (companions.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// 获取实际查询到的角色ID
|
||||
List<Long> companionIds = companions.stream()
|
||||
.map(KeyboardAiCompanion::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 批量统计点赞数
|
||||
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()));
|
||||
|
||||
// 转换为VO并填充统计数据
|
||||
return companions.stream().map(entity -> {
|
||||
AiCompanionVO vo = BeanUtil.copyProperties(entity, AiCompanionVO.class);
|
||||
vo.setLikeCount(likeCountMap.getOrDefault(entity.getId(), 0L).intValue());
|
||||
vo.setCommentCount(commentCountMap.getOrDefault(entity.getId(), 0L).intValue());
|
||||
vo.setLiked(true); // 用户已点赞
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiCompanionVO> getChattedCompanions(Long userId) {
|
||||
// 获取用户聊过天的所有AI角色ID
|
||||
List<Long> chattedCompanionIds = chatMessageService.getChattedCompanionIds(userId);
|
||||
if (chattedCompanionIds.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// 查询这些AI角色的详细信息(只返回已上线且可见的)
|
||||
LambdaQueryWrapper<KeyboardAiCompanion> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(KeyboardAiCompanion::getId, chattedCompanionIds)
|
||||
.eq(KeyboardAiCompanion::getStatus, 1)
|
||||
.eq(KeyboardAiCompanion::getVisibility, 1);
|
||||
List<KeyboardAiCompanion> companions = this.list(queryWrapper);
|
||||
|
||||
if (companions.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// 获取实际查询到的角色ID
|
||||
List<Long> companionIds = companions.stream()
|
||||
.map(KeyboardAiCompanion::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 批量统计点赞数
|
||||
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()));
|
||||
|
||||
// 获取当前用户已点赞的角色ID
|
||||
Set<Long> likedCompanionIds = companionLikeService.getLikedCompanionIds(userId, companionIds);
|
||||
|
||||
// 转换为VO并填充统计数据,保持原有顺序(按最近聊天时间)
|
||||
Map<Long, KeyboardAiCompanion> companionMap = companions.stream()
|
||||
.collect(Collectors.toMap(KeyboardAiCompanion::getId, c -> c));
|
||||
|
||||
return chattedCompanionIds.stream()
|
||||
.filter(companionMap::containsKey)
|
||||
.map(id -> {
|
||||
KeyboardAiCompanion entity = companionMap.get(id);
|
||||
AiCompanionVO vo = BeanUtil.copyProperties(entity, AiCompanionVO.class);
|
||||
vo.setLikeCount(likeCountMap.getOrDefault(entity.getId(), 0L).intValue());
|
||||
vo.setCommentCount(commentCountMap.getOrDefault(entity.getId(), 0L).intValue());
|
||||
vo.setLiked(likedCompanionIds.contains(entity.getId()));
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AiCompanionVO getCompanionById(Long userId, Long companionId) {
|
||||
// 查询AI角色
|
||||
KeyboardAiCompanion companion = this.getById(companionId);
|
||||
if (companion == null) {
|
||||
throw new BusinessException(ErrorCode.COMPANION_NOT_FOUND);
|
||||
}
|
||||
if (companion.getStatus() != 1 || companion.getVisibility() != 1) {
|
||||
throw new BusinessException(ErrorCode.COMPANION_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 统计点赞数
|
||||
LambdaQueryWrapper<KeyboardAiCompanionLike> likeWrapper = new LambdaQueryWrapper<>();
|
||||
likeWrapper.eq(KeyboardAiCompanionLike::getCompanionId, companionId)
|
||||
.eq(KeyboardAiCompanionLike::getStatus, (short) 1);
|
||||
long likeCount = companionLikeService.count(likeWrapper);
|
||||
|
||||
// 统计评论数
|
||||
LambdaQueryWrapper<KeyboardAiCompanionComment> commentWrapper = new LambdaQueryWrapper<>();
|
||||
commentWrapper.eq(KeyboardAiCompanionComment::getCompanionId, companionId)
|
||||
.eq(KeyboardAiCompanionComment::getStatus, (short) 1);
|
||||
long commentCount = companionCommentService.count(commentWrapper);
|
||||
|
||||
// 获取当前用户点赞状态
|
||||
boolean liked = companionLikeService.hasLiked(userId, companionId);
|
||||
|
||||
// 转换为VO
|
||||
AiCompanionVO vo = BeanUtil.copyProperties(companion, AiCompanionVO.class);
|
||||
vo.setLikeCount((int) likeCount);
|
||||
vo.setCommentCount((int) commentCount);
|
||||
vo.setLiked(liked);
|
||||
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
|
||||
19
src/main/resources/mapper/KeyboardAiChatSessionMapper.xml
Normal file
19
src/main/resources/mapper/KeyboardAiChatSessionMapper.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.yolo.keyborad.mapper.KeyboardAiChatSessionMapper">
|
||||
<resultMap id="BaseResultMap" type="com.yolo.keyborad.model.entity.KeyboardAiChatSession">
|
||||
<!--@mbg.generated-->
|
||||
<!--@Table keyboard_ai_chat_session-->
|
||||
<id column="id" jdbcType="BIGINT" property="id" />
|
||||
<result column="user_id" jdbcType="BIGINT" property="userId" />
|
||||
<result column="companion_id" jdbcType="BIGINT" property="companionId" />
|
||||
<result column="reset_version" jdbcType="INTEGER" property="resetVersion" />
|
||||
<result column="is_active" jdbcType="BOOLEAN" property="isActive" />
|
||||
<result column="created_at" jdbcType="TIMESTAMP" property="createdAt" />
|
||||
<result column="ended_at" jdbcType="TIMESTAMP" property="endedAt" />
|
||||
</resultMap>
|
||||
<sql id="Base_Column_List">
|
||||
<!--@mbg.generated-->
|
||||
id, user_id, companion_id, reset_version, is_active, created_at, ended_at
|
||||
</sql>
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user