feat:增加单聊消息模块

This commit is contained in:
2026-06-18 14:21:49 +08:00
parent 0bf484522c
commit f9084cfb23
7 changed files with 93 additions and 28 deletions

View File

@@ -13,13 +13,13 @@ using System.Threading.Tasks;
namespace SweetChatService.Handlers
{
internal class WebSocketMessageHandler(ILogger<WebSocketMessageHandler> logger,
WebSocketSessionManager sessionManager,
UserService userService)
internal class WsMsgHandler(ILogger<WsMsgHandler> logger,
WsSessionManager sessionManager,
PrivateChatService privateChatService)
{
private readonly ILogger<WebSocketMessageHandler> _logger = logger;
private readonly WebSocketSessionManager _sessionManager = sessionManager;
private readonly UserService _userService = userService;
private readonly ILogger<WsMsgHandler> _logger = logger;
private readonly WsSessionManager _sessionManager = sessionManager;
private readonly PrivateChatService _privateChatService = privateChatService;
public async ValueTask HandleAsync(WebSocketSession session, WebSocketPackage package)
{
@@ -55,20 +55,17 @@ namespace SweetChatService.Handlers
{
switch (msg.Type)
{
case ImMessageType.CHAT:
if (_sessionManager.TryGet(msg.To, out var toSession))
{
await toSession.SendAsync(msg.Content);
}
case ImMessageType.CHAT_PRIVATE:
await _privateChatService.HandleAsync(msg);
break;
case ImMessageType.ONLINE:
_sessionManager.Add(msg.From, session);
await session.SendAsync("ok");
await session.SendAsync(ImMessage.SystemMessage(msg.From, "OK").ToJson());
break;
case ImMessageType.HEARTBEAT:
await session.SendAsync("ok");
await session.SendAsync(ImMessage.SystemMessage(msg.From, "OK").ToJson());
break;
}
}

View File

@@ -3,7 +3,7 @@ using System.Collections.Concurrent;
namespace SweetChatService.Handlers
{
internal class WebSocketSessionManager
internal class WsSessionManager
{
private readonly ConcurrentDictionary<string, WebSocketSession> _sessions = new();
@@ -17,6 +17,19 @@ namespace SweetChatService.Handlers
_sessions.TryRemove(userId, out _);
}
public void RemoveBySession(string sessionId)
{
var keys = _sessions
.Where(x => ReferenceEquals(x.Value.SessionID, sessionId))
.Select(x => x.Key)
.ToList();
foreach (var key in keys)
{
_sessions.TryRemove(key, out _);
}
}
public bool TryGet(string userId, out WebSocketSession? session)
{
return _sessions.TryGetValue(userId, out session);

View File

@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SweetChatService.Models
{
@@ -15,5 +16,28 @@ namespace SweetChatService.Models
[JsonPropertyName("content")]
public string Content { get; set; } = default!;
public static ImMessage SystemMessage(string to, string content)
{
return new ImMessage
{
Type = ImMessageType.SYSTEM,
From = "SYSTEM",
To = to,
Content = content
};
}
/// <summary>
/// 转为 JSON 字符串
/// </summary>
public string ToJson()
{
return JsonSerializer.Serialize(this, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
});
}
}
}

View File

@@ -5,7 +5,8 @@ namespace SweetChatService.Models
[JsonConverter(typeof(JsonStringEnumConverter))]
internal enum ImMessageType
{
CHAT,
CHAT_PRIVATE,
CHAT_GROUP,
ONLINE,
HEARTBEAT,
SYSTEM,

View File

@@ -12,8 +12,10 @@ var host = Host.CreateDefaultBuilder(args)
})
.ConfigureServices((context, services) =>
{
services.AddSingleton<WebSocketMessageHandler>();
services.AddSingleton<WebSocketSessionManager>();
services.AddSingleton<WsMsgHandler>();
services.AddSingleton<WsSessionManager>();
services.AddSingleton<PrivateChatService>();
services.AddSingleton(FreeSqlSetup.CreateFreeSql(context.Configuration["ConnectionStrings:Default"]));
services.AddSingleton<UserService>();
@@ -22,18 +24,20 @@ var host = Host.CreateDefaultBuilder(args)
.AddWebSocketServer(builder =>
{
builder
//.UseSessionHandler(async (session) =>
//{
// Console.WriteLine($"Client connected: {session.SessionID}");
//},
//async (session, error) =>
//{
// Console.WriteLine($"Client disconnected: {session.SessionID}");
//})
.UseSessionHandler(async (session) =>
{
},
async (session, error) =>
{
using var scope = session.Server.ServiceProvider.CreateScope();
var manager = scope.ServiceProvider.GetRequiredService<WsSessionManager>();
manager.RemoveBySession(session.SessionID);
})
.UseWebSocketMessageHandler(async (session, package) =>
{
using var scope = session.Server.ServiceProvider.CreateScope();
var handler = scope.ServiceProvider.GetRequiredService<WebSocketMessageHandler>();
var handler = scope.ServiceProvider.GetRequiredService<WsMsgHandler>();
await handler.HandleAsync(session, package);
})

View File

@@ -0,0 +1,26 @@
using SweetChatService.Handlers;
using SweetChatService.Models;
namespace SweetChatService.Services
{
internal class PrivateChatService(WsSessionManager wsSessionManager)
{
private readonly WsSessionManager _wsSessionManager = wsSessionManager;
public async Task HandleAsync(ImMessage msg)
{
// 推送给对方
if (_wsSessionManager.TryGet(msg.To, out var toSession))
{
await toSession.SendAsync(msg.ToJson());
}
// 推送给自己
if (_wsSessionManager.TryGet(msg.From, out var fromSession))
{
await fromSession.SendAsync(msg.ToJson());
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"ConnectionStrings": {
"Default": "Server=localhost;Port=3306;Database=sweet_chat;Uid=root;Pwd=123456;Charset=utf8mb4;"
"Default": "Server=localhost;Port=3306;Database=sweet_chat;Uid=root;Pwd=estun@medical;Charset=utf8mb4;"
},
"serverOptions": {
"WebSocketServer": {