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

View File

@@ -3,7 +3,7 @@ using System.Collections.Concurrent;
namespace SweetChatService.Handlers namespace SweetChatService.Handlers
{ {
internal class WebSocketSessionManager internal class WsSessionManager
{ {
private readonly ConcurrentDictionary<string, WebSocketSession> _sessions = new(); private readonly ConcurrentDictionary<string, WebSocketSession> _sessions = new();
@@ -17,6 +17,19 @@ namespace SweetChatService.Handlers
_sessions.TryRemove(userId, out _); _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) public bool TryGet(string userId, out WebSocketSession? session)
{ {
return _sessions.TryGetValue(userId, out 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 namespace SweetChatService.Models
{ {
@@ -15,5 +16,28 @@ namespace SweetChatService.Models
[JsonPropertyName("content")] [JsonPropertyName("content")]
public string Content { get; set; } = default!; 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))] [JsonConverter(typeof(JsonStringEnumConverter))]
internal enum ImMessageType internal enum ImMessageType
{ {
CHAT, CHAT_PRIVATE,
CHAT_GROUP,
ONLINE, ONLINE,
HEARTBEAT, HEARTBEAT,
SYSTEM, SYSTEM,

View File

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