868 lines
32 KiB
C#
868 lines
32 KiB
C#
using System.Text.Json;
|
||
using Hangfire;
|
||
using Microsoft.Extensions.Logging;
|
||
using XiangYi.Application.DTOs.Requests;
|
||
using XiangYi.Application.DTOs.Responses;
|
||
using XiangYi.Application.Interfaces;
|
||
using XiangYi.Application.Jobs;
|
||
using XiangYi.Core.Constants;
|
||
using XiangYi.Core.Entities.Biz;
|
||
using XiangYi.Core.Enums;
|
||
using XiangYi.Core.Exceptions;
|
||
using XiangYi.Core.Interfaces;
|
||
|
||
namespace XiangYi.Application.Services;
|
||
|
||
/// <summary>
|
||
/// 聊天服务实现
|
||
/// </summary>
|
||
public class ChatService : IChatService
|
||
{
|
||
private readonly IRepository<User> _userRepository;
|
||
private readonly IRepository<ChatSession> _sessionRepository;
|
||
private readonly IRepository<ChatMessage> _messageRepository;
|
||
private readonly IRepository<UserProfile> _profileRepository;
|
||
private readonly IRepository<UserPhoto> _photoRepository;
|
||
private readonly INotificationService _notificationService;
|
||
private readonly IBackgroundJobClient _backgroundJobClient;
|
||
private readonly ILogger<ChatService> _logger;
|
||
|
||
public ChatService(
|
||
IRepository<User> userRepository,
|
||
IRepository<ChatSession> sessionRepository,
|
||
IRepository<ChatMessage> messageRepository,
|
||
IRepository<UserProfile> profileRepository,
|
||
IRepository<UserPhoto> photoRepository,
|
||
INotificationService notificationService,
|
||
IBackgroundJobClient backgroundJobClient,
|
||
ILogger<ChatService> logger)
|
||
{
|
||
_userRepository = userRepository;
|
||
_sessionRepository = sessionRepository;
|
||
_messageRepository = messageRepository;
|
||
_profileRepository = profileRepository;
|
||
_photoRepository = photoRepository;
|
||
_notificationService = notificationService;
|
||
_backgroundJobClient = backgroundJobClient;
|
||
_logger = logger;
|
||
}
|
||
|
||
#region 发送消息
|
||
|
||
/// <inheritdoc />
|
||
public async Task<SendMessageResponse> SendMessageAsync(long senderId, SendMessageRequest request)
|
||
{
|
||
// 验证会话存在且用户有权限
|
||
var session = await _sessionRepository.GetByIdAsync(request.SessionId);
|
||
if (session == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.SessionNotFound, "会话不存在");
|
||
}
|
||
|
||
if (!IsUserInSession(senderId, session))
|
||
{
|
||
throw new BusinessException(ErrorCodes.SessionAccessDenied, "无权访问该会话");
|
||
}
|
||
|
||
// 验证接收者是会话的另一方
|
||
var receiverId = GetOtherUserId(senderId, session);
|
||
if (receiverId != request.ReceiverId)
|
||
{
|
||
throw new BusinessException(ErrorCodes.InvalidParameter, "接收者ID不正确");
|
||
}
|
||
|
||
// 验证消息类型
|
||
if (request.MessageType < (int)MessageType.Text || request.MessageType > (int)MessageType.Image)
|
||
{
|
||
throw new BusinessException(ErrorCodes.InvalidMessageType, "无效的消息类型");
|
||
}
|
||
|
||
// 当收到新消息时,自动恢复接收者已删除的会话
|
||
var needUpdateSession = false;
|
||
if (session.User1Id == receiverId && session.User1Deleted)
|
||
{
|
||
session.User1Deleted = false;
|
||
needUpdateSession = true;
|
||
_logger.LogInformation("恢复接收者已删除的会话: SessionId={SessionId}, ReceiverId={ReceiverId}", session.Id, receiverId);
|
||
}
|
||
else if (session.User2Id == receiverId && session.User2Deleted)
|
||
{
|
||
session.User2Deleted = false;
|
||
needUpdateSession = true;
|
||
_logger.LogInformation("恢复接收者已删除的会话: SessionId={SessionId}, ReceiverId={ReceiverId}", session.Id, receiverId);
|
||
}
|
||
|
||
// 检查是否是该会话的首条消息(用于发送首次消息通知)
|
||
var isFirstMessage = !await _messageRepository.ExistsAsync(m =>
|
||
m.SessionId == request.SessionId &&
|
||
m.SenderId == senderId &&
|
||
m.Status == (int)MessageStatus.Normal);
|
||
|
||
// 创建消息
|
||
var message = new ChatMessage
|
||
{
|
||
SessionId = request.SessionId,
|
||
SenderId = senderId,
|
||
ReceiverId = request.ReceiverId,
|
||
MessageType = request.MessageType,
|
||
Content = request.Content,
|
||
VoiceUrl = request.VoiceUrl,
|
||
VoiceDuration = request.VoiceDuration,
|
||
Status = (int)MessageStatus.Normal,
|
||
IsRead = false,
|
||
CreateTime = DateTime.Now,
|
||
UpdateTime = DateTime.Now
|
||
};
|
||
|
||
var createdMessage = await _messageRepository.AddAsync(message);
|
||
|
||
// 更新会话最后消息信息和未读数(如果需要恢复会话,也一并更新)
|
||
await UpdateSessionLastMessageAsync(session, createdMessage, senderId, needUpdateSession);
|
||
|
||
_logger.LogInformation("发送消息成功: MessageId={MessageId}, SessionId={SessionId}, SenderId={SenderId}, ReceiverId={ReceiverId}",
|
||
createdMessage.Id, request.SessionId, senderId, request.ReceiverId);
|
||
|
||
// 如果是首次消息,发送通知给接收者(异步,不阻塞主流程)
|
||
if (isFirstMessage)
|
||
{
|
||
_ = Task.Run(async () =>
|
||
{
|
||
try
|
||
{
|
||
await _notificationService.SendFirstMessageNotificationAsync(request.ReceiverId, senderId);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "发送首次消息通知失败: ReceiverId={ReceiverId}, SenderId={SenderId}", request.ReceiverId, senderId);
|
||
}
|
||
});
|
||
}
|
||
|
||
// 创建5分钟延迟任务,检查接收者是否回复
|
||
try
|
||
{
|
||
_backgroundJobClient.Schedule<NotificationJob>(
|
||
job => job.SendNoReplyReminderAsync(createdMessage.Id, senderId, request.ReceiverId, request.SessionId),
|
||
TimeSpan.FromMinutes(5));
|
||
|
||
_logger.LogInformation("已创建5分钟未回复提醒任务: MessageId={MessageId}", createdMessage.Id);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "创建5分钟未回复提醒任务失败: MessageId={MessageId}", createdMessage.Id);
|
||
}
|
||
|
||
return new SendMessageResponse
|
||
{
|
||
MessageId = createdMessage.Id,
|
||
CreateTime = createdMessage.CreateTime
|
||
};
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 会话管理
|
||
|
||
/// <inheritdoc />
|
||
public async Task<List<ChatSessionResponse>> GetSessionsAsync(long userId)
|
||
{
|
||
_logger.LogInformation("获取会话列表: UserId={UserId}", userId);
|
||
|
||
// 先查询所有相关会话(包括已删除的),用于诊断
|
||
var allSessions = await _sessionRepository.GetListAsync(s =>
|
||
s.User1Id == userId || s.User2Id == userId);
|
||
|
||
_logger.LogInformation("用户相关的所有会话(含已删除): Count={Count}, UserId={UserId}", allSessions.Count, userId);
|
||
|
||
foreach (var s in allSessions)
|
||
{
|
||
_logger.LogInformation("会话详情: SessionId={SessionId}, User1Id={User1Id}, User2Id={User2Id}, User1Deleted={User1Deleted}, User2Deleted={User2Deleted}, LastMessageTime={LastMessageTime}",
|
||
s.Id, s.User1Id, s.User2Id, s.User1Deleted, s.User2Deleted, s.LastMessageTime);
|
||
}
|
||
|
||
// 获取用户参与的所有会话,排除已删除的
|
||
var sessions = await _sessionRepository.GetListAsync(s =>
|
||
(s.User1Id == userId && !s.User1Deleted) || (s.User2Id == userId && !s.User2Deleted));
|
||
|
||
_logger.LogInformation("查询到会话数量(排除已删除): Count={Count}, UserId={UserId}", sessions.Count, userId);
|
||
|
||
// 按最后消息时间排序
|
||
sessions = sessions.OrderByDescending(s => s.LastMessageTime ?? s.CreateTime).ToList();
|
||
|
||
var responses = new List<ChatSessionResponse>();
|
||
foreach (var session in sessions)
|
||
{
|
||
var otherUserId = GetOtherUserId(userId, session);
|
||
var otherUser = await _userRepository.GetByIdAsync(otherUserId);
|
||
|
||
// 获取最后一条消息
|
||
ChatMessage? lastMessage = null;
|
||
if (session.LastMessageId.HasValue)
|
||
{
|
||
lastMessage = await _messageRepository.GetByIdAsync(session.LastMessageId.Value);
|
||
}
|
||
|
||
// 获取未读数
|
||
var unreadCount = session.User1Id == userId ? session.User1UnreadCount : session.User2UnreadCount;
|
||
|
||
responses.Add(new ChatSessionResponse
|
||
{
|
||
SessionId = session.Id,
|
||
OtherUserId = otherUserId,
|
||
OtherNickname = otherUser?.Nickname,
|
||
OtherAvatar = otherUser?.Avatar,
|
||
OtherIsRealName = otherUser?.IsRealName ?? false,
|
||
LastMessageContent = GetMessageDisplayContent(lastMessage),
|
||
LastMessageType = lastMessage?.MessageType,
|
||
LastMessageTime = session.LastMessageTime,
|
||
UnreadCount = unreadCount
|
||
});
|
||
}
|
||
|
||
return responses;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<long> GetOrCreateSessionAsync(long userId1, long userId2)
|
||
{
|
||
// 确保User1Id < User2Id
|
||
var (user1Id, user2Id) = userId1 < userId2 ? (userId1, userId2) : (userId2, userId1);
|
||
|
||
// 查找现有会话
|
||
var existingSessions = await _sessionRepository.GetListAsync(s =>
|
||
s.User1Id == user1Id && s.User2Id == user2Id);
|
||
var existingSession = existingSessions.FirstOrDefault();
|
||
|
||
if (existingSession != null)
|
||
{
|
||
// 如果会话存在但被当前用户删除了,恢复它
|
||
var needUpdate = false;
|
||
if (userId1 == user1Id && existingSession.User1Deleted)
|
||
{
|
||
existingSession.User1Deleted = false;
|
||
needUpdate = true;
|
||
}
|
||
else if (userId1 == user2Id && existingSession.User2Deleted)
|
||
{
|
||
existingSession.User2Deleted = false;
|
||
needUpdate = true;
|
||
}
|
||
|
||
if (needUpdate)
|
||
{
|
||
existingSession.UpdateTime = DateTime.Now;
|
||
await _sessionRepository.UpdateAsync(existingSession);
|
||
_logger.LogInformation("恢复已删除的聊天会话: SessionId={SessionId}, UserId={UserId}",
|
||
existingSession.Id, userId1);
|
||
}
|
||
|
||
return existingSession.Id;
|
||
}
|
||
|
||
// 创建新会话
|
||
var session = new ChatSession
|
||
{
|
||
User1Id = user1Id,
|
||
User2Id = user2Id,
|
||
CreateTime = DateTime.Now,
|
||
UpdateTime = DateTime.Now
|
||
};
|
||
|
||
var createdSession = await _sessionRepository.AddAsync(session);
|
||
|
||
_logger.LogInformation("创建聊天会话: User1Id={User1Id}, User2Id={User2Id}, SessionId={SessionId}",
|
||
user1Id, user2Id, createdSession.Id);
|
||
|
||
return createdSession.Id;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> CanAccessSessionAsync(long userId, long sessionId)
|
||
{
|
||
var session = await _sessionRepository.GetByIdAsync(sessionId);
|
||
if (session == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return IsUserInSession(userId, session);
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task DeleteSessionAsync(long userId, long sessionId)
|
||
{
|
||
var session = await _sessionRepository.GetByIdAsync(sessionId);
|
||
if (session == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.SessionNotFound, "会话不存在");
|
||
}
|
||
|
||
if (!IsUserInSession(userId, session))
|
||
{
|
||
throw new BusinessException(ErrorCodes.SessionAccessDenied, "无权访问该会话");
|
||
}
|
||
|
||
// 软删除:标记该用户已删除此会话
|
||
// 如果是用户1删除,设置User1Deleted = true
|
||
// 如果是用户2删除,设置User2Deleted = true
|
||
if (session.User1Id == userId)
|
||
{
|
||
session.User1Deleted = true;
|
||
}
|
||
else if (session.User2Id == userId)
|
||
{
|
||
session.User2Deleted = true;
|
||
}
|
||
|
||
session.UpdateTime = DateTime.Now;
|
||
await _sessionRepository.UpdateAsync(session);
|
||
|
||
_logger.LogInformation("用户删除会话: UserId={UserId}, SessionId={SessionId}", userId, sessionId);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 消息管理
|
||
|
||
/// <inheritdoc />
|
||
public async Task<PagedResult<ChatMessageResponse>> GetMessagesAsync(long userId, GetMessagesRequest request)
|
||
{
|
||
// 验证会话存在且用户有权限
|
||
var session = await _sessionRepository.GetByIdAsync(request.SessionId);
|
||
if (session == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.SessionNotFound, "会话不存在");
|
||
}
|
||
|
||
if (!IsUserInSession(userId, session))
|
||
{
|
||
throw new BusinessException(ErrorCodes.SessionAccessDenied, "无权访问该会话");
|
||
}
|
||
|
||
// 获取消息列表
|
||
var (messages, total) = await _messageRepository.GetPagedListAsync(
|
||
m => m.SessionId == request.SessionId && m.Status == (int)MessageStatus.Normal,
|
||
request.PageIndex,
|
||
request.PageSize,
|
||
m => m.CreateTime,
|
||
isDescending: true);
|
||
|
||
var responses = messages.Select(m => new ChatMessageResponse
|
||
{
|
||
MessageId = m.Id,
|
||
SessionId = m.SessionId,
|
||
SenderId = m.SenderId,
|
||
ReceiverId = m.ReceiverId,
|
||
MessageType = m.MessageType,
|
||
Content = m.Content,
|
||
VoiceUrl = m.VoiceUrl,
|
||
VoiceDuration = m.VoiceDuration,
|
||
ExtraData = m.ExtraData,
|
||
IsRead = m.IsRead,
|
||
CreateTime = m.CreateTime,
|
||
IsSelf = m.SenderId == userId
|
||
}).ToList();
|
||
|
||
return new PagedResult<ChatMessageResponse>
|
||
{
|
||
Items = responses,
|
||
Total = total,
|
||
PageIndex = request.PageIndex,
|
||
PageSize = request.PageSize
|
||
};
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<int> MarkMessagesAsReadAsync(long userId, long sessionId)
|
||
{
|
||
// 验证会话存在且用户有权限
|
||
var session = await _sessionRepository.GetByIdAsync(sessionId);
|
||
if (session == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.SessionNotFound, "会话不存在");
|
||
}
|
||
|
||
if (!IsUserInSession(userId, session))
|
||
{
|
||
throw new BusinessException(ErrorCodes.SessionAccessDenied, "无权访问该会话");
|
||
}
|
||
|
||
// 获取未读消息(接收者是当前用户的消息)
|
||
var unreadMessages = await _messageRepository.GetListAsync(m =>
|
||
m.SessionId == sessionId &&
|
||
m.ReceiverId == userId &&
|
||
!m.IsRead &&
|
||
m.Status == (int)MessageStatus.Normal);
|
||
|
||
// 标记消息为已读
|
||
foreach (var message in unreadMessages)
|
||
{
|
||
message.IsRead = true;
|
||
message.UpdateTime = DateTime.Now;
|
||
await _messageRepository.UpdateAsync(message);
|
||
}
|
||
|
||
// 始终重置会话未读数(即使没有未读消息,也确保未读数为0)
|
||
var needUpdateSession = false;
|
||
if (session.User1Id == userId && session.User1UnreadCount > 0)
|
||
{
|
||
session.User1UnreadCount = 0;
|
||
needUpdateSession = true;
|
||
}
|
||
else if (session.User2Id == userId && session.User2UnreadCount > 0)
|
||
{
|
||
session.User2UnreadCount = 0;
|
||
needUpdateSession = true;
|
||
}
|
||
|
||
if (needUpdateSession)
|
||
{
|
||
session.UpdateTime = DateTime.Now;
|
||
await _sessionRepository.UpdateAsync(session);
|
||
}
|
||
|
||
_logger.LogInformation("标记消息已读: SessionId={SessionId}, UserId={UserId}, Count={Count}",
|
||
sessionId, userId, unreadMessages.Count);
|
||
|
||
return unreadMessages.Count;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<int> GetUnreadCountAsync(long userId)
|
||
{
|
||
// 获取用户参与的所有会话
|
||
var sessions = await _sessionRepository.GetListAsync(s =>
|
||
s.User1Id == userId || s.User2Id == userId);
|
||
|
||
var totalUnread = 0;
|
||
foreach (var session in sessions)
|
||
{
|
||
totalUnread += session.User1Id == userId ? session.User1UnreadCount : session.User2UnreadCount;
|
||
}
|
||
|
||
return totalUnread;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 交换功能
|
||
|
||
/// <inheritdoc />
|
||
public async Task<ExchangeRequestResponse> ExchangeWeChatAsync(long senderId, ExchangeWeChatRequest request)
|
||
{
|
||
// 验证会话存在且用户有权限
|
||
var session = await _sessionRepository.GetByIdAsync(request.SessionId);
|
||
if (session == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.SessionNotFound, "会话不存在");
|
||
}
|
||
|
||
if (!IsUserInSession(senderId, session))
|
||
{
|
||
throw new BusinessException(ErrorCodes.SessionAccessDenied, "无权访问该会话");
|
||
}
|
||
|
||
// 验证接收者是会话的另一方
|
||
var receiverId = GetOtherUserId(senderId, session);
|
||
if (receiverId != request.ReceiverId)
|
||
{
|
||
throw new BusinessException(ErrorCodes.InvalidParameter, "接收者ID不正确");
|
||
}
|
||
|
||
// 创建交换请求消息
|
||
var extraData = new ExchangeExtraData
|
||
{
|
||
Status = 0 // 待响应
|
||
};
|
||
|
||
var message = new ChatMessage
|
||
{
|
||
SessionId = request.SessionId,
|
||
SenderId = senderId,
|
||
ReceiverId = request.ReceiverId,
|
||
MessageType = (int)MessageType.ExchangeWeChatRequest,
|
||
Content = "请求交换微信",
|
||
ExtraData = JsonSerializer.Serialize(extraData),
|
||
Status = (int)MessageStatus.Normal,
|
||
IsRead = false,
|
||
CreateTime = DateTime.Now,
|
||
UpdateTime = DateTime.Now
|
||
};
|
||
|
||
var createdMessage = await _messageRepository.AddAsync(message);
|
||
|
||
// 更新会话最后消息信息
|
||
await UpdateSessionLastMessageAsync(session, createdMessage, senderId);
|
||
|
||
_logger.LogInformation("发送交换微信请求: MessageId={MessageId}, SessionId={SessionId}, SenderId={SenderId}",
|
||
createdMessage.Id, request.SessionId, senderId);
|
||
|
||
return new ExchangeRequestResponse
|
||
{
|
||
RequestMessageId = createdMessage.Id,
|
||
CreateTime = createdMessage.CreateTime
|
||
};
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<ExchangeRequestResponse> ExchangePhotoAsync(long senderId, ExchangePhotoRequest request)
|
||
{
|
||
// 验证会话存在且用户有权限
|
||
var session = await _sessionRepository.GetByIdAsync(request.SessionId);
|
||
if (session == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.SessionNotFound, "会话不存在");
|
||
}
|
||
|
||
if (!IsUserInSession(senderId, session))
|
||
{
|
||
throw new BusinessException(ErrorCodes.SessionAccessDenied, "无权访问该会话");
|
||
}
|
||
|
||
// 验证接收者是会话的另一方
|
||
var receiverId = GetOtherUserId(senderId, session);
|
||
if (receiverId != request.ReceiverId)
|
||
{
|
||
throw new BusinessException(ErrorCodes.InvalidParameter, "接收者ID不正确");
|
||
}
|
||
|
||
// 创建交换请求消息
|
||
var extraData = new ExchangeExtraData
|
||
{
|
||
Status = 0 // 待响应
|
||
};
|
||
|
||
var message = new ChatMessage
|
||
{
|
||
SessionId = request.SessionId,
|
||
SenderId = senderId,
|
||
ReceiverId = request.ReceiverId,
|
||
MessageType = (int)MessageType.ExchangePhotoRequest,
|
||
Content = "请求交换照片",
|
||
ExtraData = JsonSerializer.Serialize(extraData),
|
||
Status = (int)MessageStatus.Normal,
|
||
IsRead = false,
|
||
CreateTime = DateTime.Now,
|
||
UpdateTime = DateTime.Now
|
||
};
|
||
|
||
var createdMessage = await _messageRepository.AddAsync(message);
|
||
|
||
// 更新会话最后消息信息
|
||
await UpdateSessionLastMessageAsync(session, createdMessage, senderId);
|
||
|
||
_logger.LogInformation("发送交换照片请求: MessageId={MessageId}, SessionId={SessionId}, SenderId={SenderId}",
|
||
createdMessage.Id, request.SessionId, senderId);
|
||
|
||
return new ExchangeRequestResponse
|
||
{
|
||
RequestMessageId = createdMessage.Id,
|
||
CreateTime = createdMessage.CreateTime
|
||
};
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<ExchangeRespondResponse> RespondExchangeAsync(long userId, RespondExchangeRequest request)
|
||
{
|
||
// 获取原始请求消息
|
||
var requestMessage = await _messageRepository.GetByIdAsync(request.RequestMessageId);
|
||
if (requestMessage == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.ExchangeRequestNotFound, "交换请求不存在");
|
||
}
|
||
|
||
// 验证是接收者响应
|
||
if (requestMessage.ReceiverId != userId)
|
||
{
|
||
throw new BusinessException(ErrorCodes.CannotRespondOwnRequest, "只有接收者可以响应交换请求");
|
||
}
|
||
|
||
// 验证消息类型是交换请求
|
||
if (requestMessage.MessageType != (int)MessageType.ExchangeWeChatRequest &&
|
||
requestMessage.MessageType != (int)MessageType.ExchangePhotoRequest)
|
||
{
|
||
throw new BusinessException(ErrorCodes.InvalidMessageType, "该消息不是交换请求");
|
||
}
|
||
|
||
// 解析并验证状态
|
||
var extraData = string.IsNullOrEmpty(requestMessage.ExtraData)
|
||
? new ExchangeExtraData()
|
||
: JsonSerializer.Deserialize<ExchangeExtraData>(requestMessage.ExtraData) ?? new ExchangeExtraData();
|
||
|
||
if (extraData.Status != 0)
|
||
{
|
||
throw new BusinessException(ErrorCodes.ExchangeAlreadyResponded, "该请求已被响应");
|
||
}
|
||
|
||
// 更新请求消息状态
|
||
extraData.Status = request.IsAgreed ? 1 : 2;
|
||
|
||
// 如果同意,获取双方的数据
|
||
string? exchangedData = null;
|
||
if (request.IsAgreed)
|
||
{
|
||
if (requestMessage.MessageType == (int)MessageType.ExchangeWeChatRequest)
|
||
{
|
||
// 获取双方微信号
|
||
var senderProfile = (await _profileRepository.GetListAsync(p => p.UserId == requestMessage.SenderId)).FirstOrDefault();
|
||
var receiverProfile = (await _profileRepository.GetListAsync(p => p.UserId == requestMessage.ReceiverId)).FirstOrDefault();
|
||
|
||
extraData.SenderWeChat = senderProfile?.WeChatNo;
|
||
extraData.ReceiverWeChat = receiverProfile?.WeChatNo;
|
||
|
||
// 包含 RequestMessageId 和 Status 以便前端精确匹配和状态更新
|
||
exchangedData = JsonSerializer.Serialize(new
|
||
{
|
||
RequestMessageId = request.RequestMessageId,
|
||
Status = 1, // 已同意
|
||
SenderWeChat = extraData.SenderWeChat,
|
||
ReceiverWeChat = extraData.ReceiverWeChat
|
||
});
|
||
}
|
||
else
|
||
{
|
||
// 获取双方照片
|
||
var senderPhotos = await _photoRepository.GetListAsync(p => p.UserId == requestMessage.SenderId);
|
||
var receiverPhotos = await _photoRepository.GetListAsync(p => p.UserId == requestMessage.ReceiverId);
|
||
|
||
extraData.SenderPhotos = senderPhotos.OrderBy(p => p.Sort).Select(p => p.PhotoUrl).ToList();
|
||
extraData.ReceiverPhotos = receiverPhotos.OrderBy(p => p.Sort).Select(p => p.PhotoUrl).ToList();
|
||
|
||
// 包含 RequestMessageId 和 Status 以便前端精确匹配和状态更新
|
||
exchangedData = JsonSerializer.Serialize(new
|
||
{
|
||
RequestMessageId = request.RequestMessageId,
|
||
Status = 1, // 已同意
|
||
SenderPhotos = extraData.SenderPhotos,
|
||
ReceiverPhotos = extraData.ReceiverPhotos
|
||
});
|
||
}
|
||
}
|
||
|
||
// 更新请求消息
|
||
requestMessage.ExtraData = JsonSerializer.Serialize(extraData);
|
||
requestMessage.UpdateTime = DateTime.Now;
|
||
await _messageRepository.UpdateAsync(requestMessage);
|
||
|
||
// 创建结果消息
|
||
var resultMessageType = requestMessage.MessageType == (int)MessageType.ExchangeWeChatRequest
|
||
? (int)MessageType.ExchangeWeChatResult
|
||
: (int)MessageType.ExchangePhotoResult;
|
||
|
||
var resultExtraData = new ExchangeExtraData
|
||
{
|
||
RequestMessageId = request.RequestMessageId,
|
||
Status = extraData.Status,
|
||
SenderWeChat = extraData.SenderWeChat,
|
||
ReceiverWeChat = extraData.ReceiverWeChat,
|
||
SenderPhotos = extraData.SenderPhotos,
|
||
ReceiverPhotos = extraData.ReceiverPhotos
|
||
};
|
||
|
||
var resultMessage = new ChatMessage
|
||
{
|
||
SessionId = requestMessage.SessionId,
|
||
SenderId = userId,
|
||
ReceiverId = requestMessage.SenderId,
|
||
MessageType = resultMessageType,
|
||
Content = request.IsAgreed ? "已同意交换" : "已拒绝交换",
|
||
ExtraData = JsonSerializer.Serialize(resultExtraData),
|
||
Status = (int)MessageStatus.Normal,
|
||
IsRead = false,
|
||
CreateTime = DateTime.Now,
|
||
UpdateTime = DateTime.Now
|
||
};
|
||
|
||
var createdResultMessage = await _messageRepository.AddAsync(resultMessage);
|
||
|
||
// 更新会话最后消息信息
|
||
var session = await _sessionRepository.GetByIdAsync(requestMessage.SessionId);
|
||
if (session != null)
|
||
{
|
||
await UpdateSessionLastMessageAsync(session, createdResultMessage, userId);
|
||
}
|
||
|
||
_logger.LogInformation("响应交换请求: RequestMessageId={RequestMessageId}, ResultMessageId={ResultMessageId}, IsAgreed={IsAgreed}",
|
||
request.RequestMessageId, createdResultMessage.Id, request.IsAgreed);
|
||
|
||
return new ExchangeRespondResponse
|
||
{
|
||
ResultMessageId = createdResultMessage.Id,
|
||
IsAgreed = request.IsAgreed,
|
||
ExchangedData = exchangedData,
|
||
SessionId = requestMessage.SessionId,
|
||
RequesterId = requestMessage.SenderId,
|
||
RequestMessageType = requestMessage.MessageType,
|
||
RequestMessageId = request.RequestMessageId
|
||
};
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 私有方法
|
||
|
||
/// <summary>
|
||
/// 检查用户是否在会话中
|
||
/// </summary>
|
||
private static bool IsUserInSession(long userId, ChatSession session)
|
||
{
|
||
return session.User1Id == userId || session.User2Id == userId;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取会话中的另一个用户ID
|
||
/// </summary>
|
||
private static long GetOtherUserId(long userId, ChatSession session)
|
||
{
|
||
return session.User1Id == userId ? session.User2Id : session.User1Id;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取消息显示内容
|
||
/// </summary>
|
||
private static string? GetMessageDisplayContent(ChatMessage? message)
|
||
{
|
||
if (message == null) return null;
|
||
|
||
return message.MessageType switch
|
||
{
|
||
(int)MessageType.Text => message.Content,
|
||
(int)MessageType.Voice => "[语音消息]",
|
||
(int)MessageType.Image => "[图片]",
|
||
(int)MessageType.ExchangeWeChatRequest => "[请求交换微信]",
|
||
(int)MessageType.ExchangeWeChatResult => "[交换微信结果]",
|
||
(int)MessageType.ExchangePhotoRequest => "[请求交换照片]",
|
||
(int)MessageType.ExchangePhotoResult => "[交换照片结果]",
|
||
_ => message.Content
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新会话最后消息信息
|
||
/// </summary>
|
||
/// <param name="session">会话</param>
|
||
/// <param name="message">消息</param>
|
||
/// <param name="senderId">发送者ID</param>
|
||
/// <param name="forceUpdate">是否强制更新(用于恢复已删除会话等场景)</param>
|
||
private async Task UpdateSessionLastMessageAsync(ChatSession session, ChatMessage message, long senderId, bool forceUpdate = false)
|
||
{
|
||
session.LastMessageId = message.Id;
|
||
session.LastMessageTime = message.CreateTime;
|
||
session.UpdateTime = DateTime.Now;
|
||
|
||
// 增加接收者的未读数
|
||
if (session.User1Id == senderId)
|
||
{
|
||
session.User2UnreadCount++;
|
||
}
|
||
else
|
||
{
|
||
session.User1UnreadCount++;
|
||
}
|
||
|
||
await _sessionRepository.UpdateAsync(session);
|
||
}
|
||
|
||
#endregion
|
||
|
||
|
||
#region 静态方法(用于属性测试)
|
||
|
||
/// <summary>
|
||
/// 验证消息是否应该被持久化(静态方法,用于测试)
|
||
/// </summary>
|
||
/// <param name="messageType">消息类型</param>
|
||
/// <param name="content">消息内容</param>
|
||
/// <param name="voiceUrl">语音URL</param>
|
||
/// <returns>是否应该被持久化</returns>
|
||
public static bool ShouldMessageBePersisted(int messageType, string? content, string? voiceUrl)
|
||
{
|
||
// 文本消息需要有内容
|
||
if (messageType == (int)MessageType.Text)
|
||
{
|
||
return !string.IsNullOrWhiteSpace(content);
|
||
}
|
||
|
||
// 语音消息需要有URL
|
||
if (messageType == (int)MessageType.Voice)
|
||
{
|
||
return !string.IsNullOrWhiteSpace(voiceUrl);
|
||
}
|
||
|
||
// 图片消息需要有内容(URL)
|
||
if (messageType == (int)MessageType.Image)
|
||
{
|
||
return !string.IsNullOrWhiteSpace(content);
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证消息持久化后可以被查询到(静态方法,用于测试)
|
||
/// </summary>
|
||
/// <param name="messages">消息列表</param>
|
||
/// <param name="messageId">消息ID</param>
|
||
/// <returns>消息是否存在于列表中</returns>
|
||
public static bool IsMessageInList(List<ChatMessage> messages, long messageId)
|
||
{
|
||
return messages.Any(m => m.Id == messageId && m.Status == (int)MessageStatus.Normal);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算交换请求状态流转(静态方法,用于测试)
|
||
/// </summary>
|
||
/// <param name="currentStatus">当前状态:0待响应 1已同意 2已拒绝</param>
|
||
/// <param name="isAgreed">是否同意</param>
|
||
/// <returns>新状态,如果无法流转返回-1</returns>
|
||
public static int CalculateExchangeStatusTransition(int currentStatus, bool isAgreed)
|
||
{
|
||
// 只有待响应状态可以流转
|
||
if (currentStatus != 0)
|
||
{
|
||
return -1; // 无法流转
|
||
}
|
||
|
||
return isAgreed ? 1 : 2;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证交换请求状态流转是否有效(静态方法,用于测试)
|
||
/// </summary>
|
||
/// <param name="fromStatus">原状态</param>
|
||
/// <param name="toStatus">目标状态</param>
|
||
/// <returns>是否有效</returns>
|
||
public static bool IsValidExchangeStatusTransition(int fromStatus, int toStatus)
|
||
{
|
||
// 只有从待响应(0)可以流转到已同意(1)或已拒绝(2)
|
||
if (fromStatus == 0 && (toStatus == 1 || toStatus == 2))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证交换请求响应后状态是否正确(静态方法,用于测试)
|
||
/// </summary>
|
||
/// <param name="originalStatus">原始状态</param>
|
||
/// <param name="isAgreed">是否同意</param>
|
||
/// <param name="newStatus">新状态</param>
|
||
/// <returns>状态是否正确</returns>
|
||
public static bool IsExchangeResponseStatusCorrect(int originalStatus, bool isAgreed, int newStatus)
|
||
{
|
||
// 原始状态必须是待响应
|
||
if (originalStatus != 0)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// 同意应该变成1,拒绝应该变成2
|
||
var expectedStatus = isAgreed ? 1 : 2;
|
||
return newStatus == expectedStatus;
|
||
}
|
||
|
||
#endregion
|
||
}
|