From cf6f9b8b7c2f38add571331d9f31543c8af23f57 Mon Sep 17 00:00:00 2001 From: KilLze Date: Thu, 8 Jan 2026 01:20:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=B6=88=E6=81=AF=E6=92=A4?= =?UTF-8?q?=E5=9B=9E=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../websocket/ChatWebSocketHandler.java | 66 +++++++++++++++++-- .../bao/dating/mapper/ChatRecordsMapper.java | 16 +++++ .../bao/dating/pojo/dto/ChatRecallDTO.java | 13 ++++ .../com/bao/dating/service/ChatService.java | 8 +++ .../dating/service/impl/ChatServiceImpl.java | 45 +++++++++++++ .../bao/dating/mapper/ChatRecordsMapper.xml | 25 +++++++ .../bao/dating/mapper/ChatSessionsMapper.xml | 1 - 7 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/bao/dating/pojo/dto/ChatRecallDTO.java diff --git a/src/main/java/com/bao/dating/controller/websocket/ChatWebSocketHandler.java b/src/main/java/com/bao/dating/controller/websocket/ChatWebSocketHandler.java index e8afa10..ed8cc43 100644 --- a/src/main/java/com/bao/dating/controller/websocket/ChatWebSocketHandler.java +++ b/src/main/java/com/bao/dating/controller/websocket/ChatWebSocketHandler.java @@ -1,11 +1,13 @@ package com.bao.dating.controller.websocket; import com.bao.dating.message.WsMessage; +import com.bao.dating.pojo.dto.ChatRecallDTO; import com.bao.dating.pojo.dto.ChatRecordSendDTO; import com.bao.dating.pojo.vo.ChatRecordsVO; import com.bao.dating.service.ChatService; import com.bao.dating.session.WsSessionManager; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -57,14 +59,23 @@ public class ChatWebSocketHandler extends TextWebSocketHandler { log.error("WebSocket session 中未找到 userId"); return; } - // 解析消息 - WsMessage wsMessage = - objectMapper.readValue(message.getPayload(), - new TypeReference>(){}); - // 处理私聊消息 - if ("chat".equals(wsMessage.getType())) { - handlePrivateChat(session, senderUserId, wsMessage.getData()); + JsonNode node = objectMapper.readTree(message.getPayload()); + String type = node.get("type").asText(); + // 根据消息类型解析消息 + WsMessage wsMessage = objectMapper.readValue(message.getPayload(), WsMessage.class); + + // 先获取消息类型,再根据类型进行相应处理和转换 + if ("chat".equals(type)) { + // 处理私聊消息 + WsMessage chatWsMessage = + objectMapper.convertValue(node, new TypeReference>(){}); + handlePrivateChat(session, senderUserId, chatWsMessage.getData()); + } else if ("recall".equals(type)) { + // 处理撤回消息 + WsMessage recallWsMessage = + objectMapper.convertValue(node, new TypeReference>(){}); + handleRecallMessage(session, senderUserId, recallWsMessage.getData()); } } @@ -79,6 +90,7 @@ public class ChatWebSocketHandler extends TextWebSocketHandler { WsMessage errorMsg = new WsMessage<>(); errorMsg.setType("error"); errorMsg.setData("会话已删除,无法发送消息"); + // 返回错误信息 session.sendMessage(new TextMessage(objectMapper.writeValueAsString(errorMsg))); return; } @@ -97,6 +109,46 @@ public class ChatWebSocketHandler extends TextWebSocketHandler { } } + /** + * 消息撤回处理 + */ + private void handleRecallMessage(WebSocketSession session, Long senderUserId, Object data) throws Exception { + + // 转 DTO + ChatRecallDTO dto = objectMapper.convertValue(data, ChatRecallDTO.class); + + // 撤回逻辑 + boolean success = chatService.recallMessage(senderUserId, dto.getChatId()); + // 如果返回false,说明消息撤回失败 + if (!success) { + WsMessage errorMsg = new WsMessage<>(); + errorMsg.setType("error"); + errorMsg.setData("撤回失败,消息可能无法撤回或不存在"); + // 返回错误信息 + session.sendMessage(new TextMessage(objectMapper.writeValueAsString(errorMsg))); + return; + } + + // 创建撤回通知消息 + WsMessage pushMsg = new WsMessage<>(); + pushMsg.setType("recall"); + pushMsg.setData(dto); + + // 通知自己 + if (session.isOpen()) { + session.sendMessage(new TextMessage(objectMapper.writeValueAsString(pushMsg))); + } + + // 通知对方 + WebSocketSession receiverSession = + sessionManager.getSession(dto.getReceiverUserId()); + + if (receiverSession != null && receiverSession.isOpen()) { + receiverSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(pushMsg)) + ); + } + } + /** * 用户断开连接(下线) diff --git a/src/main/java/com/bao/dating/mapper/ChatRecordsMapper.java b/src/main/java/com/bao/dating/mapper/ChatRecordsMapper.java index e3a37b0..630b5a6 100644 --- a/src/main/java/com/bao/dating/mapper/ChatRecordsMapper.java +++ b/src/main/java/com/bao/dating/mapper/ChatRecordsMapper.java @@ -32,4 +32,20 @@ public interface ChatRecordsMapper { * @return 影响行数 */ int markMessagesAsRead(ChatMarkReadDTO markReadDTO); + + /** + * 根据ID查询聊天记录 + * @param chatId 聊天记录ID + * @return 聊天记录 + */ + ChatRecords selectById(@Param("chatId") Long chatId); + + /** + * 撤回聊天记录 + * @param chatId 聊天记录ID + * @param senderUserId 发送者ID + * @return 影响行数 + */ + int recallMessage(@Param("chatId") Long chatId, + @Param("senderUserId") Long senderUserId); } diff --git a/src/main/java/com/bao/dating/pojo/dto/ChatRecallDTO.java b/src/main/java/com/bao/dating/pojo/dto/ChatRecallDTO.java new file mode 100644 index 0000000..12cfb1d --- /dev/null +++ b/src/main/java/com/bao/dating/pojo/dto/ChatRecallDTO.java @@ -0,0 +1,13 @@ +package com.bao.dating.pojo.dto; + +import lombok.Data; + +/** + * 聊天记录撤回参数 + * @author lenovo + */ +@Data +public class ChatRecallDTO { + private Long chatId; + private Long receiverUserId; +} diff --git a/src/main/java/com/bao/dating/service/ChatService.java b/src/main/java/com/bao/dating/service/ChatService.java index 5a7dc28..8cc4cf1 100644 --- a/src/main/java/com/bao/dating/service/ChatService.java +++ b/src/main/java/com/bao/dating/service/ChatService.java @@ -71,4 +71,12 @@ public interface ChatService { * @param dto 免打扰参数 */ void updateMuteStatus(Long userId, ChatSessionMuteDTO dto); + + /** + * 撤回消息 + * @param senderUserId 发送方ID + * @param chatId 聊天记录ID + * @return 撤回结果 + */ + boolean recallMessage(Long senderUserId, Long chatId); } diff --git a/src/main/java/com/bao/dating/service/impl/ChatServiceImpl.java b/src/main/java/com/bao/dating/service/impl/ChatServiceImpl.java index bdfa589..517eb9c 100644 --- a/src/main/java/com/bao/dating/service/impl/ChatServiceImpl.java +++ b/src/main/java/com/bao/dating/service/impl/ChatServiceImpl.java @@ -21,6 +21,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -193,6 +194,9 @@ public class ChatServiceImpl implements ChatService { }).collect(Collectors.toList()); } + /** + * 标记聊天消息为已读 + */ @Override @Transactional(rollbackFor = Exception.class) public void markChatMessagesAsRead(Long currentUserId, Long targetUserId) { @@ -277,4 +281,45 @@ public class ChatServiceImpl implements ChatService { dto.getMuteStatus() ); } + + + /** + * 撤回消息 + * @param senderUserId 发送者用户ID + * @param chatId 聊天记录ID + * @return 是否成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean recallMessage(Long senderUserId, Long chatId) { + + // 查询聊天记录 + ChatRecords record = chatRecordsMapper.selectById(chatId); + // 消息不存在 + if (record == null) { + log.info("消息不存在,chatId: {}", chatId); + return false; + } + + // 只能撤回自己发的 + if (!record.getSenderUserId().equals(senderUserId)) { + log.info("不能撤回别人发的消息,chatId: {},当前用户: {},消息发送者: {}", chatId, senderUserId, record.getSenderUserId()); + return false; + } + + // 已撤回或已删除 + if (record.getMessageStatus() != 1) { + log.info("消息已撤回或已删除,chatId: {},当前状态: {}", chatId, record.getMessageStatus()); + return false; + } + + // 时间限制(2 分钟) + Duration duration = Duration.between(record.getSendTime(), LocalDateTime.now()); + if (duration.toMinutes() > 2) { + log.info("消息已超过 2 分钟,不能撤回,chatId: {},发送时间: {}", chatId, record.getSendTime()); + return false; + } + + return chatRecordsMapper.recallMessage(chatId, senderUserId) > 0; + } } diff --git a/src/main/resources/com/bao/dating/mapper/ChatRecordsMapper.xml b/src/main/resources/com/bao/dating/mapper/ChatRecordsMapper.xml index 0238dd8..5d3ddda 100644 --- a/src/main/resources/com/bao/dating/mapper/ChatRecordsMapper.xml +++ b/src/main/resources/com/bao/dating/mapper/ChatRecordsMapper.xml @@ -64,4 +64,29 @@ AND read_status = 0 AND message_status = 1 + + + + + + + UPDATE chat_records + SET + message_status = 2, + updated_at = NOW() + WHERE + chat_id = #{chatId} + AND sender_user_id = #{senderUserId} + AND message_status = 1 + + \ No newline at end of file diff --git a/src/main/resources/com/bao/dating/mapper/ChatSessionsMapper.xml b/src/main/resources/com/bao/dating/mapper/ChatSessionsMapper.xml index 79bb6a9..1688893 100644 --- a/src/main/resources/com/bao/dating/mapper/ChatSessionsMapper.xml +++ b/src/main/resources/com/bao/dating/mapper/ChatSessionsMapper.xml @@ -57,7 +57,6 @@ last_message_time, unread_count, top_status, mute_status FROM chat_sessions WHERE user_id = #{userId} - AND session_status = 1 AND session_status in (1,2) ORDER BY top_status DESC, last_message_time DESC