package com.xuqm.im.service; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.xuqm.common.exception.BusinessException; import com.xuqm.im.cluster.ImClusterPublisher; import com.xuqm.im.entity.ImFriendRequestEntity; import com.xuqm.im.entity.ImMessageEntity; import com.xuqm.im.repository.ImFriendRequestRepository; import com.xuqm.im.repository.ImFriendRepository; import com.xuqm.im.repository.ImMessageRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.List; import java.util.UUID; @Service public class FriendRequestService { private final ImFriendRequestRepository requestRepository; private final ImFriendRepository friendRepository; private final ImMessageRepository messageRepository; private final ImClusterPublisher clusterPublisher; private final ObjectMapper objectMapper; public FriendRequestService(ImFriendRequestRepository requestRepository, ImFriendRepository friendRepository, ImMessageRepository messageRepository, ImClusterPublisher clusterPublisher, ObjectMapper objectMapper) { this.requestRepository = requestRepository; this.friendRepository = friendRepository; this.messageRepository = messageRepository; this.clusterPublisher = clusterPublisher; this.objectMapper = objectMapper; } @Transactional public ImFriendRequestEntity send(String appId, String fromUserId, String toUserId, String remark) { ImFriendRequestEntity saved = requestRepository.findByAppIdAndFromUserIdAndToUserId(appId, fromUserId, toUserId) .orElseGet(() -> { ImFriendRequestEntity entity = new ImFriendRequestEntity(); entity.setId(UUID.randomUUID().toString()); entity.setAppId(appId); entity.setFromUserId(fromUserId); entity.setToUserId(toUserId); entity.setRemark(remark); entity.setStatus(ImFriendRequestEntity.Status.PENDING.name()); entity.setCreatedAt(LocalDateTime.now()); return requestRepository.save(entity); }); if (!ImFriendRequestEntity.Status.PENDING.name().equals(saved.getStatus())) { return saved; } publishNotification( saved, saved.getFromUserId(), saved.getToUserId(), "FRIEND_REQUEST", "好友申请", buildDescription("好友申请", saved.getRemark()) ); return saved; } @Transactional public ImFriendRequestEntity accept(String appId, String requestId, String operatorId) { ImFriendRequestEntity request = getRequest(appId, requestId, operatorId); request.setStatus(ImFriendRequestEntity.Status.ACCEPTED.name()); request.setReviewedAt(LocalDateTime.now()); requestRepository.save(request); friendRepository .findByAppIdAndUserIdAndFriendId(appId, request.getFromUserId(), request.getToUserId()) .orElseGet(() -> friendEntity(appId, request.getFromUserId(), request.getToUserId())); friendRepository .findByAppIdAndUserIdAndFriendId(appId, request.getToUserId(), request.getFromUserId()) .orElseGet(() -> friendEntity(appId, request.getToUserId(), request.getFromUserId())); publishNotification( request, request.getToUserId(), request.getFromUserId(), "FRIEND_REQUEST_STATUS", "好友申请已通过", buildDescription("好友申请已通过", request.getRemark()) ); return request; } @Transactional public ImFriendRequestEntity reject(String appId, String requestId, String operatorId) { ImFriendRequestEntity request = getRequest(appId, requestId, operatorId); request.setStatus(ImFriendRequestEntity.Status.REJECTED.name()); request.setReviewedAt(LocalDateTime.now()); ImFriendRequestEntity saved = requestRepository.save(request); publishNotification( saved, saved.getToUserId(), saved.getFromUserId(), "FRIEND_REQUEST_STATUS", "好友申请已拒绝", buildDescription("好友申请已拒绝", saved.getRemark()) ); return saved; } public List incoming(String appId, String userId) { return requestRepository.findByAppIdAndToUserId(appId, userId).stream() .filter(request -> ImFriendRequestEntity.Status.PENDING.name().equals(request.getStatus())) .toList(); } public List outgoing(String appId, String userId) { return requestRepository.findByAppIdAndFromUserId(appId, userId); } private ImFriendRequestEntity getRequest(String appId, String requestId, String operatorId) { ImFriendRequestEntity request = requestRepository.findById(requestId) .orElseThrow(() -> new BusinessException(404, "好友申请不存在")); if (!request.getAppId().equals(appId) || !request.getToUserId().equals(operatorId)) { throw new BusinessException(403, "无权操作"); } return request; } private com.xuqm.im.entity.ImFriendEntity friendEntity(String appId, String userId, String friendId) { com.xuqm.im.entity.ImFriendEntity entity = new com.xuqm.im.entity.ImFriendEntity(); entity.setAppId(appId); entity.setUserId(userId); entity.setFriendId(friendId); return friendRepository.save(entity); } private void publishNotification( ImFriendRequestEntity request, String fromUserId, String toUserId, String type, String title, String content ) { ImMessageEntity message = new ImMessageEntity(); message.setId(UUID.randomUUID().toString()); message.setAppId(request.getAppId()); message.setFromUserId(fromUserId); message.setToId(toUserId); message.setChatType(ImMessageEntity.ChatType.SINGLE); message.setMsgType(ImMessageEntity.MsgType.NOTIFY); message.setContent(buildNotificationContent(type, title, content, request)); message.setStatus(ImMessageEntity.MsgStatus.SENT); message.setCreatedAt(LocalDateTime.now()); ImMessageEntity saved = messageRepository.save(message); clusterPublisher.publish("/user/" + toUserId + "/queue/messages", saved); } private String buildNotificationContent( String type, String title, String content, ImFriendRequestEntity request ) { ObjectNode node = objectMapper.createObjectNode(); node.put("type", type); node.put("title", title); node.put("content", content); node.put("requestId", request.getId()); node.put("fromUserId", request.getFromUserId()); node.put("toUserId", request.getToUserId()); node.put("status", request.getStatus()); if (request.getRemark() != null) { node.put("remark", request.getRemark()); } return node.toString(); } private String buildDescription(String prefix, String remark) { if (remark == null || remark.isBlank()) { return prefix; } return prefix + ":" + remark; } }