feat: 完成情绪博物馆项目重构和功能增强 - 新增日记评论和帖子功能 - 重构前端架构,优化用户体验 - 完善WebSocket通信机制 - 更新项目文档和部署配置
This commit is contained in:
@@ -262,26 +262,8 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
userId
|
||||
);
|
||||
|
||||
// 构建AI回复消息(不分割,保持完整性)
|
||||
WebSocketMessage aiMessage = WebSocketMessage.builder()
|
||||
.messageId(UUID.randomUUID().toString())
|
||||
.conversationId(conversationId)
|
||||
.type(WebSocketMessage.MessageType.TEXT)
|
||||
.content(aiReply)
|
||||
.senderId("ai")
|
||||
.senderType(WebSocketMessage.SenderType.AI)
|
||||
.status(WebSocketMessage.MessageStatus.SENT)
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
// AI回复已经在sendChatMessageForWebSocket中保存了,这里不需要重复保存
|
||||
|
||||
// 发送AI回复
|
||||
messagingTemplate.convertAndSendToUser(userId, "/queue/messages", aiMessage);
|
||||
|
||||
if (conversationId != null) {
|
||||
messagingTemplate.convertAndSend("/topic/conversation/" + conversationId, aiMessage);
|
||||
}
|
||||
// 根据换行符分割AI回复并按顺序发送多条消息
|
||||
sendAiReplyInParts(userId, conversationId, aiReply);
|
||||
|
||||
// 更新会话的最后活跃时间和消息数量
|
||||
updateConversationActivity(conversationId);
|
||||
@@ -398,4 +380,158 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
log.error("更新会话活跃状态失败: conversationId={}", conversationId, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据换行符分割AI回复并按顺序发送多条消息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param conversationId 会话ID
|
||||
* @param aiReply AI回复内容
|
||||
*/
|
||||
private void sendAiReplyInParts(String userId, String conversationId, String aiReply) {
|
||||
try {
|
||||
log.info("开始处理AI回复消息: userId={}, conversationId={}, aiReply长度={}",
|
||||
userId, conversationId, aiReply != null ? aiReply.length() : 0);
|
||||
|
||||
if (aiReply == null || aiReply.trim().isEmpty()) {
|
||||
log.warn("AI回复内容为空,跳过发送");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否需要分割
|
||||
boolean needsSplit = aiReply.contains("\n\n") || aiReply.contains("\n");
|
||||
|
||||
if (!needsSplit) {
|
||||
// 不需要分割,直接发送完整消息
|
||||
log.info("AI回复无换行符,发送完整消息");
|
||||
sendSingleAiMessage(userId, conversationId, aiReply.trim());
|
||||
return;
|
||||
}
|
||||
|
||||
// 需要分割,按换行符分割并发送多条消息
|
||||
log.info("AI回复包含换行符,开始分割发送");
|
||||
String[] replyParts = splitAiReply(aiReply);
|
||||
|
||||
log.info("AI回复分割完成,共{}个部分", replyParts.length);
|
||||
|
||||
// 按顺序发送每个部分
|
||||
int sentCount = 0;
|
||||
for (int i = 0; i < replyParts.length; i++) {
|
||||
String part = replyParts[i].trim();
|
||||
|
||||
// 跳过空白部分
|
||||
if (part.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 发送消息部分
|
||||
sendSingleAiMessage(userId, conversationId, part);
|
||||
sentCount++;
|
||||
|
||||
log.info("发送AI回复部分 {}/{}: 内容长度={}", sentCount, replyParts.length, part.length());
|
||||
|
||||
// 在多个部分之间添加短暂延迟,模拟自然对话节奏
|
||||
if (i < replyParts.length - 1) {
|
||||
try {
|
||||
Thread.sleep(500); // 延迟500毫秒
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("发送AI回复时被中断");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("AI回复发送完成: userId={}, conversationId={}, 实际发送{}条消息",
|
||||
userId, conversationId, sentCount);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("分割发送AI回复失败: userId={}, conversationId={}", userId, conversationId, e);
|
||||
|
||||
// 发送错误时,尝试发送完整的原始回复
|
||||
try {
|
||||
WebSocketMessage fallbackMessage = WebSocketMessage.builder()
|
||||
.messageId(UUID.randomUUID().toString())
|
||||
.conversationId(conversationId)
|
||||
.type(WebSocketMessage.MessageType.TEXT)
|
||||
.content(aiReply)
|
||||
.senderId("ai")
|
||||
.senderType(WebSocketMessage.SenderType.AI)
|
||||
.status(WebSocketMessage.MessageStatus.SENT)
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
messagingTemplate.convertAndSendToUser(userId, "/queue/messages", fallbackMessage);
|
||||
|
||||
if (conversationId != null) {
|
||||
messagingTemplate.convertAndSend("/topic/conversation/" + conversationId, fallbackMessage);
|
||||
}
|
||||
|
||||
log.info("已发送完整AI回复作为备用方案");
|
||||
|
||||
} catch (Exception fallbackError) {
|
||||
log.error("发送备用AI回复也失败", fallbackError);
|
||||
sendErrorMessage(userId, "AI回复发送失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送单条AI消息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param conversationId 会话ID
|
||||
* @param content 消息内容
|
||||
*/
|
||||
private void sendSingleAiMessage(String userId, String conversationId, String content) {
|
||||
// 构建AI回复消息
|
||||
WebSocketMessage aiMessage = WebSocketMessage.builder()
|
||||
.messageId(UUID.randomUUID().toString())
|
||||
.conversationId(conversationId)
|
||||
.type(WebSocketMessage.MessageType.TEXT)
|
||||
.content(content)
|
||||
.senderId("ai")
|
||||
.senderType(WebSocketMessage.SenderType.AI)
|
||||
.status(WebSocketMessage.MessageStatus.SENT)
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
// 发送给用户私有队列
|
||||
messagingTemplate.convertAndSendToUser(userId, "/queue/messages", aiMessage);
|
||||
|
||||
// 发送到会话公共频道
|
||||
if (conversationId != null) {
|
||||
messagingTemplate.convertAndSend("/topic/conversation/" + conversationId, aiMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能分割AI回复内容
|
||||
*
|
||||
* @param aiReply AI回复内容
|
||||
* @return 分割后的内容数组
|
||||
*/
|
||||
private String[] splitAiReply(String aiReply) {
|
||||
if (aiReply == null || aiReply.trim().isEmpty()) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
// 首先尝试按双换行符分割(段落分割)
|
||||
if (aiReply.contains("\n\n")) {
|
||||
String[] parts = aiReply.split("\n\n");
|
||||
log.debug("按双换行符分割,得到{}个部分", parts.length);
|
||||
return parts;
|
||||
}
|
||||
|
||||
// 如果没有双换行符,按单换行符分割(行分割)
|
||||
if (aiReply.contains("\n")) {
|
||||
String[] parts = aiReply.split("\n");
|
||||
log.debug("按单换行符分割,得到{}个部分", parts.length);
|
||||
return parts;
|
||||
}
|
||||
|
||||
// 如果没有换行符,返回原始内容(这种情况不应该到达这里)
|
||||
log.debug("没有换行符,返回原始内容");
|
||||
return new String[]{aiReply};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user