欢迎光临
我们一直在努力

客服消息保存与处理代码修改

看了您的代码和需求,我将为您修改相关代码,确保客服飞书上发送的消息(包括图片、视频、文件)和客户发送的媒体消息(转人工后)都保存到 CsMessage 表中。

1. 首先修改 EnhancedMessageService.java

添加保存客户媒体消息到数据库的方法:

package com.alatus.customerService.manager;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.alatus.customerService.domain.CsMessage;
import com.alatus.customerService.domain.CustomerServiceSession;
import com.alatus.customerService.dto.CustomerMessageDTO;
import com.alatus.customerService.mapper.CsMessageMapper;
import com.alatus.customerService.mapper.CustomerServiceSessionMapper;
import com.alatus.customerService.service.ICustomerInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.socket.WebSocketSession;
import java.util.Date;
import com.alatus.customerService.alatusEnum.Enum;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.socket.TextMessage;

@Service
@Slf4j
public class EnhancedMessageService {

// … 已有的代码保持不变 …

/**
* 🔥 新增:保存客户图片消息到CsMessage表(转人工后)
*/
@Transactional(rollbackFor = Exception.class)
public void saveCustomerImageMessage(String customerId, String customerName, String imageUrl,
String imageName, String sessionId) {
try {
CsMessage csMessage = new CsMessage();
csMessage.setMessageId("cust_img_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 1000));
csMessage.setSessionId(sessionId);
csMessage.setSenderType(Enum.SenderType.CUSTOMER.toString());
csMessage.setSenderId(customerId);
csMessage.setSenderName(customerName);
csMessage.setContent("[图片] " + imageName);
csMessage.setMessageType(Enum.MessageType.IMAGE);
csMessage.setSendTime(new Date());
csMessage.setDelivered(true);
csMessage.setMediaUrl(imageUrl);
csMessage.setFileName(imageName);

csMessageMapper.insert(csMessage);
log.info("✅ 客户图片消息已保存到CsMessage表: {} – {}", customerId, imageName);
} catch (Exception e) {
log.error("保存客户图片消息到数据库失败: {}", customerId, e);
}
}

/**
* 🔥 新增:保存客户文件消息到CsMessage表(转人工后)
*/
@Transactional(rollbackFor = Exception.class)
public void saveCustomerFileMessage(String customerId, String customerName, String fileUrl,
String fileName, String sessionId) {
try {
CsMessage csMessage = new CsMessage();
csMessage.setMessageId("cust_file_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 1000));
csMessage.setSessionId(sessionId);
csMessage.setSenderType(Enum.SenderType.CUSTOMER.toString());
csMessage.setSenderId(customerId);
csMessage.setSenderName(customerName);
csMessage.setContent("[文件] " + fileName);
csMessage.setMessageType(Enum.MessageType.FILE);
csMessage.setSendTime(new Date());
csMessage.setDelivered(true);
csMessage.setMediaUrl(fileUrl);
csMessage.setFileName(fileName);

csMessageMapper.insert(csMessage);
log.info("✅ 客户文件消息已保存到CsMessage表: {} – {}", customerId, fileName);
} catch (Exception e) {
log.error("保存客户文件消息到数据库失败: {}", customerId, e);
}
}

/**
* 🔥 新增:保存客户媒体消息到CsMessage表(转人工后)
*/
@Transactional(rollbackFor = Exception.class)
public void saveCustomerMediaMessage(String customerId, String customerName, String mediaUrl,
String fileName, String sessionId, String mediaType) {
try {
CsMessage csMessage = new CsMessage();
csMessage.setMessageId("cust_media_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 1000));
csMessage.setSessionId(sessionId);
csMessage.setSenderType(Enum.SenderType.CUSTOMER.toString());
csMessage.setSenderId(customerId);
csMessage.setSenderName(customerName);
csMessage.setContent("[媒体] " + fileName);
csMessage.setMessageType(Enum.MessageType.MEDIA);
csMessage.setSendTime(new Date());
csMessage.setDelivered(true);
csMessage.setMediaUrl(mediaUrl);
csMessage.setFileName(fileName);
csMessage.setMessageType(mediaType.equals("VIDEO") ? Enum.MessageType.VIDEO :
mediaType.equals("AUDIO") ? Enum.MessageType.AUDIO :
Enum.MessageType.MEDIA);

csMessageMapper.insert(csMessage);
log.info("✅ 客户媒体消息已保存到CsMessage表: {} – {} – {}", customerId, fileName, mediaType);
} catch (Exception e) {
log.error("保存客户媒体消息到数据库失败: {}", customerId, e);
}
}

/**
* 🔥 修改:处理客服媒体消息 – 添加数据库保存
*/
@Transactional(rollbackFor = Exception.class)
public void handleAgentMediaMessage(String feishuChatId, String agentName, String mediaUrl,
String fileName, String feishuMessageId) {
// 🔥 关键修复:只在方法开始时检查去重,不立即标记
if (isFeishuMessageProcessed(feishuMessageId)) {
log.warn("🚫 飞书媒体消息已处理过,强制跳过: {}", feishuMessageId);
return;
}

Object messageLock = messageSendLocks.computeIfAbsent(feishuMessageId, k -> new Object());

synchronized (messageLock) {
try {
log.info("处理客服媒体消息: {} – {} – 飞书消息ID: {}", agentName, fileName, feishuMessageId);

// 🔥 双重检查:在锁内再次检查去重
if (isFeishuMessageProcessed(feishuMessageId)) {
log.warn("🚫 飞书媒体消息已处理过(锁内检查),强制跳过: {}", feishuMessageId);
return;
}

String customerId = findCustomerIdByFeishuChat(feishuChatId);
if (customerId == null) {
log.warn("未找到客户会话: {}", feishuChatId);
return;
}

// 🔥 关键修复:检查客户状态,如果是AI状态则自动转回人工
checkAndAutoTransferToHuman(customerId, agentName, "[媒体消息] " + fileName);

String sessionId = getSessionIdByCustomerId(customerId);
if (sessionId == null) {
log.error("未找到客户活跃会话: {}", customerId);
return;
}

// 🔥 修复:生成固定的消息ID
String messageId = generateStableMediaMessageId(feishuMessageId);

// 🔥 新增:先保存到CsMessage数据库
saveAgentMediaMessageToDB(sessionId, agentName, mediaUrl, fileName, messageId, feishuMessageId);

// 🔥 关键修复:直接构建完整消息发送到MQ,不存数据库!
CustomerMessageDTO message = new CustomerMessageDTO();
message.setMessageId(messageId);
message.setCustomerId(customerId);
message.setSessionId(sessionId);
message.setContent(mediaUrl);
message.setMessageType("MEDIA");
message.setSenderType("AGENT");
message.setSenderName(agentName);
message.setStatus("PENDING");
message.setCreateTime(System.currentTimeMillis());
message.setRetryCount(0);
message.setMaxRetryCount(3);

// 🔥 关键修复:构建完整的附加数据
Map<String, Object> additionalData = new HashMap<>();
additionalData.put("mediaUrl", mediaUrl);
additionalData.put("fileName", fileName);
additionalData.put("agentName", agentName);
additionalData.put("originalFeishuMessageId", feishuMessageId);
additionalData.put("messageType", "MEDIA");
additionalData.put("timestamp", System.currentTimeMillis());

String fileExtension = getFileExtension(fileName);
additionalData.put("mediaType", getMediaType(fileExtension));
additionalData.put("fileExtension", fileExtension);

message.setAdditionalData(additionalData);

// 🔥 关键修复:直接发送完整消息到可靠消息服务
reliableMessageService.sendReliableMessage(message);

// 🔥 关键修复:在处理成功后标记为已处理
markFeishuMessageAsProcessed(feishuMessageId);

log.info("✅ 客服媒体消息处理完成(直接MQ): {} -> {}", agentName, customerId);

} catch (DuplicateKeyException e) {
log.warn("飞书媒体消息ID重复: {}", feishuMessageId);
} catch (Exception e) {
log.error("处理客服媒体消息失败: {}", feishuChatId, e);
// 🔥 修复:处理失败时移除去重标记,允许重试
removeFeishuMessageFromProcessed(feishuMessageId);
} finally {
messageSendLocks.remove(feishuMessageId);
}
}
}

/**
* 🔥 新增:保存客服媒体消息到CsMessage表
*/
private void saveAgentMediaMessageToDB(String sessionId, String agentName, String mediaUrl,
String fileName, String messageId, String feishuMessageId) {
try {
CsMessage csMessage = new CsMessage();
csMessage.setMessageId(messageId);
csMessage.setSessionId(sessionId);
csMessage.setSenderType(Enum.SenderType.AGENT.toString());
csMessage.setSenderId("agent_" + agentName);
csMessage.setSenderName(agentName);
csMessage.setContent("[媒体] " + fileName);
csMessage.setMessageType(Enum.MessageType.MEDIA);
csMessage.setSendTime(new Date());
csMessage.setDelivered(false);
csMessage.setFeishuMessageId(feishuMessageId);
csMessage.setMediaUrl(mediaUrl);
csMessage.setFileName(fileName);

// 设置媒体类型
String fileExtension = getFileExtension(fileName);
String mediaType = getMediaType(fileExtension);
csMessage.setMessageType(mediaType.equals("VIDEO") ? Enum.MessageType.VIDEO :
mediaType.equals("AUDIO") ? Enum.MessageType.AUDIO :
Enum.MessageType.MEDIA);

csMessageMapper.insert(csMessage);
log.info("✅ 客服媒体消息已保存到CsMessage表: {} – {}", agentName, fileName);
} catch (Exception e) {
log.error("保存客服媒体消息到数据库失败: {}", messageId, e);
}
}

/**
* 🔥 修改:处理客服图片消息 – 添加数据库保存
*/
@Transactional(rollbackFor = Exception.class)
public void handleAgentImageMessage(String feishuChatId, String agentName, String imageUrl,
String imageKey, String feishuMessageId) {
Object messageLock = messageSendLocks.computeIfAbsent(feishuMessageId, k -> new Object());

synchronized (messageLock) {
try {
log.info("处理客服图片消息: {} – {} – 飞书消息ID: {}", agentName, imageKey, feishuMessageId);

if (isFeishuMessageProcessed(feishuMessageId)) {
log.warn("🚫 飞书图片消息已处理过,强制跳过: {}", feishuMessageId);
return;
}

String customerId = findCustomerIdByFeishuChat(feishuChatId);
if (customerId == null) {
log.warn("未找到客户会话: {}", feishuChatId);
return;
}

// 🔥 关键修复:检查客户状态,如果是AI状态则自动转回人工
checkAndAutoTransferToHuman(customerId, agentName, "[图片消息]");

String sessionId = getSessionIdByCustomerId(customerId);
if (sessionId == null) {
log.error("未找到客户活跃会话: {}", customerId);
return;
}

String messageId = generateImageMessageIdFromFeishu(feishuMessageId);

// 🔥 新增:先保存到CsMessage数据库
saveAgentImageMessageToDB(sessionId, agentName, imageUrl, imageKey, messageId, feishuMessageId);

// 🔥 关键修复:直接构建完整消息发送到MQ
CustomerMessageDTO message = new CustomerMessageDTO();
message.setMessageId(messageId);
message.setCustomerId(customerId);
message.setSessionId(sessionId);
message.setContent(imageUrl);
message.setMessageType("IMAGE");
message.setSenderType("AGENT");
message.setSenderName(agentName);
message.setStatus("PENDING");
message.setCreateTime(System.currentTimeMillis());
message.setRetryCount(0);
message.setMaxRetryCount(3);

// 🔥 关键修复:构建完整的附加数据
Map<String, Object> additionalData = new HashMap<>();
additionalData.put("imageUrl", imageUrl);
additionalData.put("imageKey", imageKey);
additionalData.put("agentName", agentName);
additionalData.put("originalFeishuMessageId", feishuMessageId);
additionalData.put("messageType", "IMAGE");
additionalData.put("timestamp", System.currentTimeMillis());

message.setAdditionalData(additionalData);

// 🔥 直接发送到可靠消息服务
reliableMessageService.sendReliableMessage(message);

markFeishuMessageAsProcessed(feishuMessageId);

log.info("✅ 客服图片消息处理完成(直接MQ): {} -> {}", agentName, customerId);

} catch (Exception e) {
log.error("处理客服图片消息失败: {}", feishuChatId, e);
} finally {
messageSendLocks.remove(feishuMessageId);
}
}
}

/**
* 🔥 新增:保存客服图片消息到CsMessage表
*/
private void saveAgentImageMessageToDB(String sessionId, String agentName, String imageUrl,
String imageKey, String messageId, String feishuMessageId) {
try {
CsMessage csMessage = new CsMessage();
csMessage.setMessageId(messageId);
csMessage.setSessionId(sessionId);
csMessage.setSenderType(Enum.SenderType.AGENT.toString());
csMessage.setSenderId("agent_" + agentName);
csMessage.setSenderName(agentName);
csMessage.setContent("[图片]");
csMessage.setMessageType(Enum.MessageType.IMAGE);
csMessage.setSendTime(new Date());
csMessage.setDelivered(false);
csMessage.setFeishuMessageId(feishuMessageId);
csMessage.setMediaUrl(imageUrl);
csMessage.setFileName("image_" + imageKey + ".jpg");

csMessageMapper.insert(csMessage);
log.info("✅ 客服图片消息已保存到CsMessage表: {} – {}", agentName, imageKey);
} catch (Exception e) {
log.error("保存客服图片消息到数据库失败: {}", messageId, e);
}
}

/**
* 🔥 修改:处理客服文件消息 – 添加数据库保存
*/
@Transactional(rollbackFor = Exception.class)
public void handleAgentFileMessage(String feishuChatId, String agentName, String fileUrl,
String fileName, String feishuMessageId) {
Object messageLock = messageSendLocks.computeIfAbsent(feishuMessageId, k -> new Object());

synchronized (messageLock) {
try {
log.info("处理客服文件消息: {} – {} – 飞书消息ID: {}", agentName, fileName, feishuMessageId);

if (isFeishuMessageProcessed(feishuMessageId)) {
log.warn("🚫 飞书文件消息已处理过,强制跳过: {}", feishuMessageId);
return;
}

String customerId = findCustomerIdByFeishuChat(feishuChatId);
if (customerId == null) {
log.warn("未找到客户会话: {}", feishuChatId);
return;
}

// 🔥 关键修复:检查客户状态,如果是AI状态则自动转回人工
checkAndAutoTransferToHuman(customerId, agentName, "[文件消息] " + fileName);

String sessionId = getSessionIdByCustomerId(customerId);
if (sessionId == null) {
log.error("未找到客户活跃会话: {}", customerId);
return;
}

String messageId = generateFileMessageIdFromFeishu(feishuMessageId);

// 🔥 新增:先保存到CsMessage数据库
saveAgentFileMessageToDB(sessionId, agentName, fileUrl, fileName, messageId, feishuMessageId);

// 🔥 关键修复:直接构建完整消息发送到MQ
CustomerMessageDTO message = new CustomerMessageDTO();
message.setMessageId(messageId);
message.setCustomerId(customerId);
message.setSessionId(sessionId);
message.setContent(fileUrl);
message.setMessageType("FILE");
message.setSenderType("AGENT");
message.setSenderName(agentName);
message.setStatus("PENDING");
message.setCreateTime(System.currentTimeMillis());
message.setRetryCount(0);
message.setMaxRetryCount(3);

// 🔥 关键修复:构建完整的附加数据
Map<String, Object> additionalData = new HashMap<>();
additionalData.put("fileUrl", fileUrl);
additionalData.put("fileName", fileName);
additionalData.put("agentName", agentName);
additionalData.put("originalFeishuMessageId", feishuMessageId);
additionalData.put("messageType", "FILE");
additionalData.put("timestamp", System.currentTimeMillis());
additionalData.put("fileType", getFileTypeCategory(getFileExtension(fileName)));

message.setAdditionalData(additionalData);

// 🔥 直接发送到可靠消息服务
reliableMessageService.sendReliableMessage(message);

markFeishuMessageAsProcessed(feishuMessageId);

log.info("✅ 客服文件消息处理完成(直接MQ): {} -> {}", agentName, customerId);

} catch (Exception e) {
log.error("处理客服文件消息失败: {}", feishuChatId, e);
} finally {
messageSendLocks.remove(feishuMessageId);
}
}
}

/**
* 🔥 新增:保存客服文件消息到CsMessage表
*/
private void saveAgentFileMessageToDB(String sessionId, String agentName, String fileUrl,
String fileName, String messageId, String feishuMessageId) {
try {
CsMessage csMessage = new CsMessage();
csMessage.setMessageId(messageId);
csMessage.setSessionId(sessionId);
csMessage.setSenderType(Enum.SenderType.AGENT.toString());
csMessage.setSenderId("agent_" + agentName);
csMessage.setSenderName(agentName);
csMessage.setContent("[文件] " + fileName);
csMessage.setMessageType(Enum.MessageType.FILE);
csMessage.setSendTime(new Date());
csMessage.setDelivered(false);
csMessage.setFeishuMessageId(feishuMessageId);
csMessage.setMediaUrl(fileUrl);
csMessage.setFileName(fileName);

csMessageMapper.insert(csMessage);
log.info("✅ 客服文件消息已保存到CsMessage表: {} – {}", agentName, fileName);
} catch (Exception e) {
log.error("保存客服文件消息到数据库失败: {}", messageId, e);
}
}

/**
* 🔥 修改:保存客服文本消息(包含飞书消息ID)- 增强版
*/
private CsMessage saveAgentMessageWithFeishuId(String sessionId, String agentName, String content,
String messageId, String feishuMessageId) {
CsMessage csMessage = new CsMessage();
csMessage.setMessageId(messageId);
csMessage.setSessionId(sessionId);
csMessage.setSenderType(Enum.SenderType.AGENT.toString());
csMessage.setSenderId("agent_" + agentName);
csMessage.setSenderName(agentName);
csMessage.setContent(content);
csMessage.setMessageType(Enum.MessageType.TEXT);
csMessage.setSendTime(new Date());
csMessage.setDelivered(false);
csMessage.setFeishuMessageId(feishuMessageId);

csMessageMapper.insert(csMessage);
log.info("✅ 客服文本消息已保存到CsMessage表: {} – {}", agentName,
content.length() > 50 ? content.substring(0, 50) + "…" : content);
return csMessage;
}

// … 其他已有代码保持不变 …
}

2. 修改 CustomerWebSocketHandler.java

添加客户发送媒体消息时的数据库保存:

package com.alatus.customerService.config;

// … 已有导入 …

@Component
@Slf4j
public class CustomerWebSocketHandler extends TextWebSocketHandler {

// … 已有代码 …

@Autowired
private EnhancedMessageService enhancedMessageService;

// … 已有代码 …

/**
* 🔥 修改:处理客户消息 – 添加媒体消息支持
*/
private void handleCustomerMessageOptimized(String customerId, String customerName, String content, WebSocketSession session) {
// 🔥 关键修复:收到客户消息时也更新心跳时间
updateHeartbeatOnActivity(customerId);

// 检查消息类型
try {
Map<String, Object> messageData = objectMapper.readValue(content, Map.class);
String messageType = (String) messageData.get("type");

// 如果是媒体消息,特殊处理
if ("CUSTOMER_MEDIA".equals(messageType) && isTransferredToHuman(customerId)) {
handleCustomerMediaMessage(customerId, customerName, messageData, session);
return;
}

// 如果是文本消息,继续原有流程
if (messageData.containsKey("content")) {
content = (String) messageData.get("content");
}
} catch (Exception e) {
// 如果不是JSON,继续当作文本处理
log.debug("消息不是JSON格式,按文本处理: {}", content);
}

// 1. 检查是否已经是人工状态
if (isTransferredToHuman(customerId)) {
log.info("客户 {} 在人工状态,消息直接转发: {}", customerId, content);

// 🔥 如果是媒体消息,需要先保存到数据库
if (content.startsWith("[图片]") || content.startsWith("[文件]") || content.startsWith("[媒体]")) {
saveCustomerMediaToDatabase(customerId, customerName, content);
}

messageService.handleCustomerTextMessage(customerId, customerName, content);
return;
}

// 2. 检查是否需要转人工
CustomerAIChatState state = customerAIChatStates.get(customerId);
if (shouldAutoTransferToHuman(content, state)) {
log.info("客户 {} 触发转人工条件: {}", customerId, content);

// 🔥 修复:使用统一的转人工处理方法(包含分布式锁)
handleTransferToHumanUnified(customerId, customerName, content, session);
return;
}

// 3. 否则交给AI处理
CompletableFuture<OptimizedAIChatAgentService.AIChatResult> future =
CompletableFuture.supplyAsync(() -> {
try {
sendSystemMessageToSession(session, "智能客服正在思考,可能需要几秒钟");
return optimizedAIChatAgentService.handleCustomerMessage(customerId, customerName, content);
} catch (Exception e) {
log.error("AI处理异常: {}", customerId, e);
return OptimizedAIChatAgentService.AIChatResult.continueWithAI(
"抱歉,AI服务暂时不可用。请稍后重试或回复\\"转人工\\"联系客服。"
);
}
});

// 设置超时时间
try {
OptimizedAIChatAgentService.AIChatResult aiResult = future.get(30, TimeUnit.SECONDS);
handleOptimizedAIChatResult(aiResult, customerId, customerName, content, session);
} catch (TimeoutException e) {
log.warn("AI处理超时(30秒): {}", customerId);
future.cancel(true);
handleOptimizedAITimeout(customerId, customerName, content, session);
} catch (Exception e) {
log.error("AI处理失败: {}", customerId, e);
handleOptimizedAIError(customerId, session);
}
}

/**
* 🔥 新增:处理客户媒体消息
*/
private void handleCustomerMediaMessage(String customerId, String customerName,
Map<String, Object> messageData, WebSocketSession session) {
try {
String mediaType = (String) messageData.get("mediaType");
String mediaUrl = (String) messageData.get("mediaUrl");
String fileName = (String) messageData.get("fileName");

log.info("处理客户媒体消息: {} – {} – {} – {}", customerId, mediaType, fileName, mediaUrl);

// 获取会话ID
CustomerServiceSession dbSession = messageService.findSessionByCustomerId(customerId);
if (dbSession == null) {
log.error("未找到客户会话: {}", customerId);
return;
}

String sessionId = dbSession.getSessionId();

// 根据媒体类型保存到数据库
switch (mediaType) {
case "IMAGE":
enhancedMessageService.saveCustomerImageMessage(customerId, customerName, mediaUrl, fileName, sessionId);
break;
case "FILE":
enhancedMessageService.saveCustomerFileMessage(customerId, customerName, mediaUrl, fileName, sessionId);
break;
case "VIDEO":
case "AUDIO":
enhancedMessageService.saveCustomerMediaMessage(customerId, customerName, mediaUrl, fileName, sessionId, mediaType);
break;
}

// 转发到飞书
String feishuChatId = dbSession.getFeishuChatId();
if (feishuChatId != null) {
switch (mediaType) {
case "IMAGE":
feishuService.sendImageMessage(feishuChatId, mediaUrl, "chat_id");
break;
case "FILE":
case "VIDEO":
case "AUDIO":
// 发送文件通知到飞书
String notification = String.format("客户 %s 发送了%s: %s",
customerName,
mediaType.equals("VIDEO") ? "视频" : mediaType.equals("AUDIO") ? "音频" : "文件",
fileName);
feishuService.sendTextMessageToFeishu(feishuChatId, notification, customerName);
break;
}
}

log.info("✅ 客户媒体消息处理完成: {} – {}", customerId, fileName);

} catch (Exception e) {
log.error("处理客户媒体消息失败: {}", customerId, e);
}
}

/**
* 🔥 新增:保存客户媒体消息到数据库
*/
private void saveCustomerMediaToDatabase(String customerId, String customerName, String content) {
try {
// 解析内容,提取媒体信息
if (content.startsWith("[图片]") && content.contains("URL:")) {
// 格式:[图片] 文件名 URL:http://…
String[] parts = content.split(" URL:");
if (parts.length >= 2) {
String fileName = parts[0].replace("[图片] ", "").trim();
String imageUrl = parts[1].trim();

CustomerServiceSession dbSession = messageService.findSessionByCustomerId(customerId);
if (dbSession != null) {
enhancedMessageService.saveCustomerImageMessage(customerId, customerName,
imageUrl, fileName, dbSession.getSessionId());
}
}
} else if (content.startsWith("[文件]") && content.contains("URL:")) {
// 格式:[文件] 文件名 URL:http://…
String[] parts = content.split(" URL:");
if (parts.length >= 2) {
String fileName = parts[0].replace("[文件] ", "").trim();
String fileUrl = parts[1].trim();

CustomerServiceSession dbSession = messageService.findSessionByCustomerId(customerId);
if (dbSession != null) {
enhancedMessageService.saveCustomerFileMessage(customerId, customerName,
fileUrl, fileName, dbSession.getSessionId());
}
}
} else if ((content.startsWith("[视频]") || content.startsWith("[音频]")) && content.contains("URL:")) {
// 格式:[视频/音频] 文件名 URL:http://…
String[] parts = content.split(" URL:");
if (parts.length >= 2) {
String fileName = parts[0].replace("[视频] ", "").replace("[音频] ", "").trim();
String mediaUrl = parts[1].trim();
String mediaType = content.startsWith("[视频]") ? "VIDEO" : "AUDIO";

CustomerServiceSession dbSession = messageService.findSessionByCustomerId(customerId);
if (dbSession != null) {
enhancedMessageService.saveCustomerMediaMessage(customerId, customerName,
mediaUrl, fileName, dbSession.getSessionId(), mediaType);
}
}
}
} catch (Exception e) {
log.error("保存客户媒体消息到数据库失败: {}", customerId, e);
}
}

// … 其他代码保持不变 …
}

3. 修改 CsMessage 实体(如果还没有相关字段)

确保您的 CsMessage 实体有以下字段:

package com.alatus.customerService.domain;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;

@Data
@TableName("cs_message")
public class CsMessage implements Serializable {

@TableId(value = "id", type = IdType.AUTO)
private Long id;

@TableField("message_id")
private String messageId;

@TableField("session_id")
private String sessionId;

@TableField("sender_type")
private String senderType;

@TableField("sender_id")
private String senderId;

@TableField("sender_name")
private String senderName;

@TableField("content")
private String content;

@TableField("message_type")
private String messageType;

@TableField("send_time")
private Date sendTime;

@TableField("delivered")
private Boolean delivered;

@TableField("feishu_message_id")
private String feishuMessageId;

// 🔥 新增:媒体文件URL
@TableField("media_url")
private String mediaUrl;

// 🔥 新增:文件名
@TableField("file_name")
private String fileName;

@TableField("created_at")
private Date createdAt;

@TableField("updated_at")
private Date updatedAt;
}

4. 如果需要,更新数据库表结构

在数据库中执行以下SQL语句,添加缺失的字段:

— 添加媒体文件URL字段
ALTER TABLE cs_message ADD COLUMN IF NOT EXISTS media_url VARCHAR(500);

— 添加文件名字段
ALTER TABLE cs_message ADD COLUMN IF NOT EXISTS file_name VARCHAR(255);

— 更新现有数据(可选)
UPDATE cs_message SET media_url = content
WHERE (message_type = 'IMAGE' OR message_type = 'FILE' OR message_type = 'MEDIA')
AND content LIKE 'http%';

关键修改总结:

  • EnhancedMessageService 中添加了保存客服和客户媒体消息的方法:

    • saveCustomerImageMessage() – 保存客户图片消息

    • saveCustomerFileMessage() – 保存客户文件消息

    • saveCustomerMediaMessage() – 保存客户媒体消息

    • saveAgentImageMessageToDB() – 保存客服图片消息

    • saveAgentFileMessageToDB() – 保存客服文件消息

    • saveAgentMediaMessageToDB() – 保存客服媒体消息

  • CustomerWebSocketHandler 中添加了客户媒体消息处理:

    • 在 handleCustomerMessageOptimized() 中识别媒体消息

    • 新增 handleCustomerMediaMessage() 处理媒体消息

    • 新增 saveCustomerMediaToDatabase() 保存到数据库

  • CsMessage 实体添加了媒体相关字段:

    • media_url – 存储媒体文件URL

    • file_name – 存储文件名

  • 这样修改后,系统将能够:

    • 客服通过飞书发送的文本、图片、文件、视频、音频消息都会保存到 CsMessage 表

    • 客户在转人工后发送的媒体消息也会保存到 CsMessage 表

    • 所有媒体文件的URL都会正确存储在数据库中

    我们有几个文件,其中关键的是EnhancedMessageService,它处理消息的保存和转发到飞书。但是,根据要求,我们需要确保以下几点:

  • 客服(飞书)发过来的消息需要保存到CsMessage表。

  • 图片、视频、文件需要保存URL到数据库。

  • 客户发送的图片、视频、文件也需要保存到CsMessage表(转人工以后)。

  • 目前,从飞书回调过来的消息(包括文本、图片、文件、媒体)已经在FeishuCallbackController中处理,并调用了EnhancedMessageService中的相应方法。
    例如:handleAgentReply(文本)、handleAgentImageMessage(图片)、handleAgentFileMessage(文件)、handleAgentMediaMessage(媒体)。

    在EnhancedMessageService中,这些方法目前是直接将消息通过可靠消息服务发送给客户,并且没有将消息保存到CsMessage表。

    因此,我们需要修改EnhancedMessageService,在接收到飞书消息(即客服回复)时,将消息保存到CsMessage表。

    具体修改点:

  • 对于文本消息:在handleAgentReply方法中,已经保存了消息到CsMessage表(通过saveAgentMessageWithFeishuId方法),但是注意,这里保存的是文本消息。

  • 对于图片、文件、媒体消息:目前没有保存到CsMessage表,需要增加保存逻辑。

  • 但是,注意:在handleAgentImageMessage、handleAgentFileMessage、handleAgentMediaMessage方法中,目前是直接构建CustomerMessageDTO然后通过可靠消息服务发送。我们需要先将这些消息保存到CsMessage表。

    然而,查看代码发现,在handleAgentReply方法中,我们保存了文本消息,然后通过deliverMessageViaReliableService发送。而对于图片、文件、媒体消息,我们并没有保存。

    因此,我们需要在handleAgentImageMessage、handleAgentFileMessage、handleAgentMediaMessage方法中也添加保存到CsMessage表的逻辑。

    但是,注意:这些消息的类型不同,我们需要在CsMessage表中记录消息类型(message_type)和内容(content,对于媒体消息,内容可以是URL,也可以是其他信息)。

    另外,CsMessage表中有以下字段可能用于媒体消息:

    • message_type: 枚举,包括TEXT, IMAGE, FILE, MEDIA等。

    • content: 对于文本消息,就是文本内容;对于媒体消息,可以存储URL。

    • 此外,可能有其他字段存储额外的信息,比如文件大小、文件名等,但我们的CsMessage表可能没有这些字段。如果有需要,可能需要扩展。

    但是,根据现有的CsMessage实体,我们只有content字段可以存储文本或URL。对于媒体消息,我们可能需要将URL存储到content中。

    因此,我们可以这样做:

    对于图片消息:

    • 将图片的URL存储到content字段。

    • 将message_type设置为IMAGE。

    对于文件消息:

    • 将文件的URL存储到content字段。

    • 将message_type设置为FILE。

    对于媒体消息(视频、音频):

    • 将媒体文件的URL存储到content字段。

    • 将message_type设置为MEDIA。

    但是,注意:在CsMessage表中,我们还有一个sender_type字段,表示发送者是客服还是客户。这里我们保存的是客服的消息,所以sender_type应该是AGENT。

    另外,我们还需要记录飞书消息ID(feishu_message_id)用于去重。

    所以,我们需要修改以下方法,在发送消息之前,先将消息保存到CsMessage表:

  • handleAgentImageMessage

  • handleAgentFileMessage

  • handleAgentMediaMessage

  • 但是,注意:这些方法中,我们已经有了sessionId(通过getSessionIdByCustomerId获取),并且有了消息内容(图片URL、文件URL、媒体URL)和飞书消息ID。

    我们可以创建一个保存媒体消息到CsMessage表的方法,然后在这些方法中调用。

    然而,观察文本消息的处理,我们使用saveAgentMessageWithFeishuId方法保存文本消息。我们可以类似地创建保存图片、文件、媒体消息的方法。

    但是,由于消息类型不同,我们可以使用同一个方法,通过参数区分。或者,为每种类型写一个保存方法。为了代码清晰,我们可以写一个通用的保存方法。

    但是,注意:在文本消息的保存中,我们使用了generateMessageIdFromFeishu来生成消息ID,并且将飞书消息ID保存到feishu_message_id字段。

    对于媒体消息,我们也应该使用类似的逻辑生成消息ID,并保存飞书消息ID。

    因此,我们可以修改EnhancedMessageService,在handleAgentImageMessage、handleAgentFileMessage、handleAgentMediaMessage方法中,在发送消息之前,先保存消息到CsMessage表。

    但是,注意:在handleAgentImageMessage等方法中,我们已经构建了CustomerMessageDTO,然后通过可靠消息服务发送。现在我们需要先保存到数据库,然后再发送。

    另外,注意:在保存媒体消息时,我们需要将URL存储到content字段。但是,在构建CustomerMessageDTO时,我们可能还需要额外的数据(如图片URL、文件URL等),这些数据存储在additionalData中。

    但是,对于CsMessage表,我们只需要存储URL(content)和消息类型(message_type)即可。

    因此,我们可以这样做:

    在handleAgentImageMessage方法中,在构建CustomerMessageDTO之前,先保存图片消息到CsMessage表。

    步骤:

  • 获取sessionId、customerId、agentName、imageUrl、messageId(飞书消息ID)。

  • 生成一个消息ID(可以使用generateImageMessageIdFromFeishu方法,但注意,这个方法已经存在,我们用它来生成消息ID)。

  • 创建一个CsMessage对象,设置相应的字段(包括sessionId、senderType为AGENT、senderId为agentName、senderName为agentName、content为imageUrl、messageType为IMAGE、feishuMessageId为飞书消息ID等)。

  • 插入到CsMessage表。

  • 然后,再构建CustomerMessageDTO并发送。

    同理,处理文件和媒体消息。

    但是,注意:在handleAgentImageMessage等方法中,我们已经有了messageId(飞书消息ID),我们需要用这个飞书消息ID来生成我们自己的消息ID(用于CsMessage表的主键),同时也要将这个飞书消息ID保存到feishu_message_id字段。

    但是,注意:在文本消息的处理中,我们使用generateMessageIdFromFeishu来生成消息ID,这个方法基于飞书消息ID生成一个固定的消息ID。对于图片、文件、媒体消息,我们也应该使用类似的方法,但是注意,我们已经有了generateImageMessageIdFromFeishu、generateFileMessageIdFromFeishu、generateStableMediaMessageId方法,我们可以直接使用。

    但是,注意:在handleAgentImageMessage中,我们调用generateImageMessageIdFromFeishu来生成消息ID,这个生成的消息ID应该作为CsMessage表的主键(message_id)。同时,在构建CustomerMessageDTO时,也使用同一个消息ID。

    因此,我们可以这样修改:

    以handleAgentImageMessage为例:

    修改前:
    String messageId = generateImageMessageIdFromFeishu(feishuMessageId);
    // 然后构建CustomerMessageDTO,并设置messageId为这个值

    修改后:
    String messageId = generateImageMessageIdFromFeishu(feishuMessageId);
    // 保存到CsMessage表
    saveAgentImageMessage(sessionId, agentName, imageUrl, messageId, feishuMessageId);
    // 然后构建CustomerMessageDTO,并设置messageId为这个值

    这样,我们就将图片消息保存到了CsMessage表。

    同理,处理文件和媒体消息。

    但是,注意:在handleAgentImageMessage中,我们已经有了imageUrl,这个imageUrl是图片下载到本地(或OSS)后的URL,我们将其保存到CsMessage表的content字段。

    另外,对于文件和媒体消息,我们也有相应的URL。

    因此,我们需要在EnhancedMessageService中添加三个保存方法:

    private void saveAgentImageMessage(String sessionId, String agentName, String imageUrl, String messageId, String feishuMessageId)
    private void saveAgentFileMessage(String sessionId, String agentName, String fileUrl, String fileName, String messageId, String feishuMessageId)
    private void saveAgentMediaMessage(String sessionId, String agentName, String mediaUrl, String fileName, String messageId, String feishuMessageId)

    但是,注意:CsMessage表可能没有fileName字段,所以对于文件消息,我们可能需要将文件名也保存到content字段,或者单独用一个字段。但是,根据现有的CsMessage实体,我们只有content字段。所以,我们可以将文件名和URL一起保存在content字段,或者只保存URL。

    但是,考虑到前端显示可能需要文件名,我们可以将文件名和URL用某种格式拼接,比如:"文件名|URL",或者将URL作为内容,文件名在另外的字段中。但是,我们的CsMessage表没有专门的fileName字段。

    因此,我们可能需要修改CsMessage实体,增加fileName字段。但是,如果时间紧迫,我们可以暂时将文件名和URL一起放到content字段,用分隔符分开。或者,只保存URL,文件名从URL中提取。

    但是,根据要求,我们需要保存URL到数据库。所以,我们可以只保存URL,文件名可以额外存储在一个字段中,但如果没有这个字段,那就只能存URL。

    但是,观察CsMessage实体,我们发现有content字段,可以存储字符串。所以,我们可以将URL存储到content字段。对于文件名,我们可能无法保存。

    但是,如果CsMessage表没有fileName字段,那么我们就只能保存URL。前端显示时,可以从URL中提取文件名。

    因此,我们暂时只保存URL到content字段。

    所以,对于文件消息,我们保存的content就是fileUrl。

    但是,对于媒体消息,同样只保存URL。

    那么,我们就按照这个思路修改。

    另外,注意:在handleAgentImageMessage等方法中,我们已经有sessionId,这个sessionId是通过getSessionIdByCustomerId获取的,它表示当前客户会话的ID。

    但是,在保存消息时,我们还需要设置sender_type为AGENT,sender_id和sender_name为agentName。

    现在,我们开始修改EnhancedMessageService:

    首先,添加一个保存图片消息到CsMessage表的方法。

    但是,为了减少重复代码,我们可以创建一个通用的保存消息的方法,根据不同的消息类型设置不同的字段。

    由于时间关系,我们分别创建三个方法,因为每种消息类型的处理略有不同(比如,文本消息有文本内容,图片消息有图片URL等)。

    但是,注意:在文本消息的保存中,我们使用了saveAgentMessageWithFeishuId方法,它已经保存了文本消息。我们可以参考它。

    所以,我们可以在EnhancedMessageService中添加以下方法:

  • 保存图片消息到CsMessage表

  • 保存文件消息到CsMessage表

  • 保存媒体消息到CsMessage表

  • 但是,注意:在CsMessage表中,有一个message_type字段,它使用的是枚举Enum.MessageType。所以,我们需要在保存时设置正确的枚举值。

    由于Enum.MessageType中已经定义了TEXT, IMAGE, FILE, MEDIA等,我们可以直接使用。

    另外,注意:在CsMessage表中,还有一个feishu_message_id字段,我们用来存储飞书消息ID,用于去重。

    现在,我们修改handleAgentImageMessage方法,在构建CustomerMessageDTO之前,先保存图片消息到CsMessage表。

    但是,注意:在handleAgentImageMessage方法中,我们已经有飞书消息ID(feishuMessageId)、图片URL(imageUrl)、会话ID(sessionId)、客服名称(agentName)等。

    所以,我们可以在handleAgentImageMessage方法中添加保存逻辑。

    同理,修改handleAgentFileMessage和handleAgentMediaMessage。

    由于修改涉及多个方法,我们逐个修改。

    但是,注意:在handleAgentImageMessage等方法中,我们已经在事务中(@Transactional),所以保存消息到数据库会在同一个事务中。

    另外,注意:在保存消息时,我们使用csMessageMapper.insert方法,如果出现异常,事务会回滚。

    现在,我们开始修改EnhancedMessageService,在handleAgentImageMessage、handleAgentFileMessage、handleAgentMediaMessage方法中添加保存消息到CsMessage表的逻辑。

    由于代码较长,我们只修改关键部分。

    由于时间关系,我们只修改handleAgentImageMessage方法作为示例,其他两个方法类似。

    但是,由于我们已经有文本消息的保存示例,我们可以参考。

    修改后的handleAgentImageMessage方法:

    步骤:

  • 生成消息ID(已经存在)

  • 保存图片消息到CsMessage表

  • 构建CustomerMessageDTO并发送

  • 但是,注意:在保存图片消息时,我们需要创建一个CsMessage对象,并设置相关字段。

    由于CsMessage的sender_id和sender_name都是字符串,我们可以将agentName同时设置为sender_id和sender_name。

    另外,注意:CsMessage的send_time字段,我们可以设置为当前时间。

    由于我们已经在方法中,我们可以直接使用new Date()。

    现在,我们修改EnhancedMessageService,在handleAgentImageMessage方法中添加保存逻辑。

    但是,注意:在handleAgentImageMessage方法中,我们已经有sessionId,但是sessionId可能为null。如果为null,我们需要记录错误并返回。

    另外,注意:在保存消息时,我们还需要设置delivered字段为false,因为消息刚保存,还没有发送给客户。

    但是,实际上,我们通过可靠消息服务发送,发送成功后会更新delivered字段。但是,在保存时,我们设置为false。

    所以,我们修改handleAgentImageMessage方法,添加以下代码:

    在生成messageId之后,构建CustomerMessageDTO之前:

    // 保存图片消息到数据库
    saveAgentImageMessage(sessionId, agentName, imageUrl, messageId, feishuMessageId);

    然后,我们实现saveAgentImageMessage方法:

    private void saveAgentImageMessage(String sessionId, String agentName, String imageUrl, String messageId, String feishuMessageId) {
    try {
    CsMessage csMessage = new CsMessage();
    csMessage.setMessageId(messageId);
    csMessage.setSessionId(sessionId);
    csMessage.setSenderType(Enum.SenderType.AGENT.toString());
    csMessage.setSenderId(agentName);
    csMessage.setSenderName(agentName);
    csMessage.setContent(imageUrl);
    csMessage.setMessageType(Enum.MessageType.IMAGE);
    csMessage.setSendTime(new Date());
    csMessage.setDelivered(false);
    csMessage.setFeishuMessageId(feishuMessageId);

    csMessageMapper.insert(csMessage);
    log.info("图片消息已保存到数据库: {}", messageId);
    } catch (Exception e) {
    log.error("保存图片消息到数据库失败: {}", messageId, e);
    }
    }

    同理,对于文件消息和媒体消息,我们也可以类似地实现。

    但是,注意:在文件消息和媒体消息中,我们除了URL,还有文件名。但是,我们的CsMessage表没有fileName字段,所以只能保存URL。

    所以,对于文件消息,我们保存的content是fileUrl;对于媒体消息,保存的content是mediaUrl。

    这样,我们就将客服发送的图片、文件、媒体消息保存到了CsMessage表。

    但是,注意:在handleAgentMediaMessage方法中,我们已经有mediaUrl和fileName,我们只保存mediaUrl。

    另外,对于消息类型,文件消息使用Enum.MessageType.FILE,媒体消息使用Enum.MessageType.MEDIA。

    现在,我们修改EnhancedMessageService,添加三个保存方法,并在对应的消息处理方法中调用。

    由于代码量较大,我们只修改关键部分。

    但是,注意:在handleAgentImageMessage等方法中,我们已经有了messageId(生成的消息ID),所以我们可以直接使用。

    另外,注意:在handleAgentMediaMessage方法中,我们生成的messageId是使用generateStableMediaMessageId方法,这个生成的消息ID应该作为CsMessage表的主键。

    所以,我们修改这三个方法,在构建CustomerMessageDTO之前,先保存消息到CsMessage表。

    最后,注意:在保存消息时,如果保存失败,我们不应该中断流程,而是记录错误,然后继续发送消息。因为保存消息是为了记录,而发送消息是主要业务。

    所以,我们使用try-catch包裹保存消息的逻辑,确保即使保存失败,也不影响消息发送。

    现在,我们开始修改EnhancedMessageService。

    由于修改涉及多个方法,我们只给出修改示例,具体修改请根据实际代码调整。

    另外,注意:在EnhancedMessageService中,我们已经有saveAgentMessageWithFeishuId方法,我们可以参考它。

    由于时间关系,我们只修改handleAgentImageMessage方法,其他两个方法类似。

    修改后的handleAgentImageMessage方法(部分代码):

    // … 之前的代码

    String messageId = generateImageMessageIdFromFeishu(feishuMessageId);

    // 🔥 保存图片消息到数据库
    saveAgentImageMessage(sessionId, agentName, imageUrl, messageId, feishuMessageId);

    // 🔥 关键修复:直接构建完整消息发送到MQ
    CustomerMessageDTO message = new CustomerMessageDTO();
    // … 设置消息属性

    // … 之后的代码

    然后,我们实现saveAgentImageMessage方法。

    同理,在handleAgentFileMessage和handleAgentMediaMessage方法中,我们也添加类似的保存逻辑。

    最后,我们还需要注意:客户发送的图片、视频、文件(转人工以后)也需要保存到CsMessage表。这部分是在FeishuCallbackController中处理用户发送的媒体消息时,调用了messageService的相应方法,但是目前这些方法(handleAgentImageMessage等)是处理客服发送的消息,而用户发送的消息并没有保存到CsMessage表。

    但是,根据要求,客户发送的图片、视频、文件(转人工以后)也需要保存到CsMessage表。所以,我们还需要修改FeishuCallbackController中处理用户发送的媒体消息的逻辑,将用户发送的媒体消息也保存到CsMessage表。

    但是,注意:在FeishuCallbackController中,我们处理用户发送的媒体消息时,调用的是messageService的handleAgentImageMessage等方法,这些方法目前是处理客服消息的,现在用户消息也调用这些方法,可能会导致混淆。

    但是,实际上,这些方法内部会根据sender_type(客服还是客户)来区分。但是,在handleAgentImageMessage等方法中,我们设置sender_type为AGENT,因为这是客服消息。对于用户消息,我们应该设置sender_type为CUSTOMER。

    但是,在FeishuCallbackController中,我们收到的是用户发送的消息,所以我们应该调用一个处理用户消息的方法,而不是处理客服消息的方法。

    所以,我们需要在EnhancedMessageService中增加处理用户发送的媒体消息的方法,或者修改现有方法,使其能够区分是客服消息还是用户消息。

    但是,考虑到时间,我们可以这样修改:

    在FeishuCallbackController中,我们处理用户发送的媒体消息时,调用一个新的方法,比如handleCustomerImageMessage,这个方法会保存消息到CsMessage表,并且sender_type为CUSTOMER。

    但是,我们也可以修改现有的handleAgentImageMessage等方法,通过参数区分是客服还是用户。但是,为了减少改动,我们可以在EnhancedMessageService中增加处理用户媒体消息的方法。

    但是,由于时间关系,我们暂时不修改,因为要求是转人工以后客户发送的消息才需要保存。而转人工以后,客户的消息是通过WebSocket发送的,并不是通过飞书回调。

    所以,实际上,在飞书回调中,我们只处理客服发送的消息。客户发送的消息是通过WebSocket发送,然后由EnhancedMessageService的handleCustomerTextMessage方法处理,但是这个方法只处理文本消息,不处理媒体消息。

    所以,对于客户发送的媒体消息(转人工以后),目前是通过WebSocket发送,然后由EnhancedMessageService处理吗?目前代码中没有看到。

    因此,我们可能需要先完成客服发送的媒体消息的保存,而客户发送的媒体消息的保存可能需要另外处理。

    但是,根据要求,客户发送的图片、视频、文件(转人工以后)也需要保存到CsMessage表。这部分可能需要在前端通过WebSocket发送媒体消息时,由后端保存。

    由于时间关系,我们暂时只处理客服发送的媒体消息的保存。客户发送的媒体消息的保存可能需要另外的设计。

    所以,我们本次修改只确保客服(飞书)发过来的消息保存到CsMessage表。

    总结修改:

  • 在EnhancedMessageService中,添加保存图片、文件、媒体消息到CsMessage表的方法。

  • 在handleAgentImageMessage、handleAgentFileMessage、handleAgentMediaMessage方法中,调用相应的保存方法,将消息保存到CsMessage表。

  • 由于代码较长,我们只给出修改示例,具体修改请根据实际代码调整。

    注意:在保存消息时,使用Enum.MessageType来设置消息类型,使用Enum.SenderType来设置发送者类型(AGENT)。

    另外,注意:在保存消息时,需要设置feishu_message_id字段,用于去重。

    现在,我们开始修改EnhancedMessageService。

    由于代码量较大,我们只修改关键部分。

    修改示例:

    在EnhancedMessageService中添加以下方法:

    private void saveAgentImageMessage(String sessionId, String agentName, String imageUrl, String messageId, String feishuMessageId) {
    try {
    CsMessage csMessage = new CsMessage();
    csMessage.setMessageId(messageId);
    csMessage.setSessionId(sessionId);
    csMessage.setSenderType(Enum.SenderType.AGENT.toString());
    csMessage.setSenderId(agentName);
    csMessage.setSenderName(agentName);
    csMessage.setContent(imageUrl);
    csMessage.setMessageType(Enum.MessageType.IMAGE);
    csMessage.setSendTime(new Date());
    csMessage.setDelivered(false);
    csMessage.setFeishuMessageId(feishuMessageId);

    csMessageMapper.insert(csMessage);
    log.info("图片消息已保存到数据库: {}", messageId);
    } catch (Exception e) {
    log.error("保存图片消息到数据库失败: {}", messageId, e);
    }
    }

    private void saveAgentFileMessage(String sessionId, String agentName, String fileUrl, String fileName, String messageId, String feishuMessageId) {
    try {
    CsMessage csMessage = new CsMessage();
    csMessage.setMessageId(messageId);
    csMessage.setSessionId(sessionId);
    csMessage.setSenderType(Enum.SenderType.AGENT.toString());
    csMessage.setSenderId(agentName);
    csMessage.setSenderName(agentName);
    csMessage.setContent(fileUrl);
    csMessage.setMessageType(Enum.MessageType.FILE);
    csMessage.setSendTime(new Date());
    csMessage.setDelivered(false);
    csMessage.setFeishuMessageId(feishuMessageId);

    csMessageMapper.insert(csMessage);
    log.info("文件消息已保存到数据库: {}", messageId);
    } catch (Exception e) {
    log.error("保存文件消息到数据库失败: {}", messageId, e);
    }
    }

    private void saveAgentMediaMessage(String sessionId, String agentName, String mediaUrl, String fileName, String messageId, String feishuMessageId) {
    try {
    CsMessage csMessage = new CsMessage();
    csMessage.setMessageId(messageId);
    csMessage.setSessionId(sessionId);
    csMessage.setSenderType(Enum.SenderType.AGENT.toString());
    csMessage.setSenderId(agentName);
    csMessage.setSenderName(agentName);
    csMessage.setContent(mediaUrl);
    csMessage.setMessageType(Enum.MessageType.MEDIA);
    csMessage.setSendTime(new Date());
    csMessage.setDelivered(false);
    csMessage.setFeishuMessageId(feishuMessageId);

    csMessageMapper.insert(csMessage);
    log.info("媒体消息已保存到数据库: {}", messageId);
    } catch (Exception e) {
    log.error("保存媒体消息到数据库失败: {}", messageId, e);
    }
    }

    然后,在对应的handleAgentImageMessage、handleAgentFileMessage、handleAgentMediaMessage方法中,在构建CustomerMessageDTO之前,调用这些保存方法。

    注意:在handleAgentMediaMessage方法中,我们使用generateStableMediaMessageId生成消息ID,然后调用saveAgentMediaMessage方法。

    这样,我们就将客服发送的媒体消息保存到了CsMessage表。

    由于时间关系,我们只修改了EnhancedMessageService,确保客服发送的媒体消息被保存。对于客户发送的媒体消息,可能需要另外处理。

    另外,注意:在文本消息的处理中,我们已经在handleAgentReply方法中保存了文本消息,所以文本消息已经保存。

    因此,本次修改完成。

    由于代码较长,我们只给出了修改示例,具体修改请根据实际代码调整。

    赞(0)
    未经允许不得转载:171主机测评 » 客服消息保存与处理代码修改
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址