Merge branch 'main' into feature-join-whiteboard-by-code

# Conflicts:
#	dotnet/AipsRT/Hubs/WhiteboardHub.cs
#	dotnet/AipsRT/Services/Interfaces/IMessagingService.cs
#	front/src/stores/whiteboard.ts
This commit is contained in:
2026-03-07 21:40:09 +01:00
18 changed files with 167 additions and 15 deletions

View File

@@ -0,0 +1,6 @@
namespace AipsCore.Application.Abstract;
public interface IWhiteboardAwareContext
{
Guid GetWhiteboardId();
}

View File

@@ -1,6 +1,13 @@
using AipsCore.Application.Abstract;
using AipsCore.Application.Abstract.MessageBroking; using AipsCore.Application.Abstract.MessageBroking;
using AipsCore.Application.Models.Shape.Command.CreateArrow; using AipsCore.Application.Models.Shape.Command.CreateArrow;
namespace AipsCore.Application.Common.Message.AddArrow; namespace AipsCore.Application.Common.Message.AddArrow;
public record AddArrowMessage(CreateArrowCommand Command) : IMessage; public record AddArrowMessage(CreateArrowCommand Command) : IMessage, IWhiteboardAwareContext
{
public Guid GetWhiteboardId()
{
return Guid.Parse(Command.WhiteboardId);
}
}

View File

@@ -1,6 +1,13 @@
using AipsCore.Application.Abstract;
using AipsCore.Application.Abstract.MessageBroking; using AipsCore.Application.Abstract.MessageBroking;
using AipsCore.Application.Models.Shape.Command.CreateLine; using AipsCore.Application.Models.Shape.Command.CreateLine;
namespace AipsCore.Application.Common.Message.AddLine; namespace AipsCore.Application.Common.Message.AddLine;
public record AddLineMessage(CreateLineCommand Command) : IMessage; public record AddLineMessage(CreateLineCommand Command) : IMessage, IWhiteboardAwareContext
{
public Guid GetWhiteboardId()
{
return Guid.Parse(Command.WhiteboardId);
}
}

View File

@@ -1,6 +1,13 @@
using AipsCore.Application.Abstract;
using AipsCore.Application.Abstract.MessageBroking; using AipsCore.Application.Abstract.MessageBroking;
using AipsCore.Application.Models.Shape.Command.CreateRectangle; using AipsCore.Application.Models.Shape.Command.CreateRectangle;
namespace AipsCore.Application.Common.Message.AddRectangle; namespace AipsCore.Application.Common.Message.AddRectangle;
public record AddRectangleMessage(CreateRectangleCommand Command) : IMessage; public record AddRectangleMessage(CreateRectangleCommand Command) : IMessage, IWhiteboardAwareContext
{
public Guid GetWhiteboardId()
{
return Guid.Parse(Command.WhiteboardId);
}
}

View File

@@ -1,6 +1,13 @@
using AipsCore.Application.Abstract;
using AipsCore.Application.Abstract.MessageBroking; using AipsCore.Application.Abstract.MessageBroking;
using AipsCore.Application.Models.Shape.Command.CreateTextShape; using AipsCore.Application.Models.Shape.Command.CreateTextShape;
namespace AipsCore.Application.Common.Message.AddTextShape; namespace AipsCore.Application.Common.Message.AddTextShape;
public record AddTextShapeMessage(CreateTextShapeCommand Command) : IMessage; public record AddTextShapeMessage(CreateTextShapeCommand Command) : IMessage, IWhiteboardAwareContext
{
public Guid GetWhiteboardId()
{
return Guid.Parse(Command.WhiteboardId);
}
}

View File

@@ -0,0 +1,13 @@
using AipsCore.Application.Abstract;
using AipsCore.Application.Abstract.MessageBroking;
using AipsCore.Domain.Common.Validation;
namespace AipsCore.Application.Common.Message.ErrorMessage;
public record ErrorMessage(Guid WhiteboardId, ICollection<ValidationError> Errors) : IMessage, IWhiteboardAwareContext
{
public Guid GetWhiteboardId()
{
return WhiteboardId;
}
}

View File

@@ -0,0 +1,18 @@
using AipsCore.Application.Abstract.MessageBroking;
namespace AipsCore.Application.Common.Message.ErrorMessage;
public class ErrorMessageHandler : IMessageHandler<ErrorMessage>
{
private readonly IErrorMessageHandleStrategy _handleStrategy;
public ErrorMessageHandler(IErrorMessageHandleStrategy handleStrategy)
{
_handleStrategy = handleStrategy;
}
public async Task Handle(ErrorMessage message, CancellationToken cancellationToken)
{
await _handleStrategy.Handle(message, cancellationToken);
}
}

View File

@@ -0,0 +1,6 @@
namespace AipsCore.Application.Common.Message.ErrorMessage;
public interface IErrorMessageHandleStrategy
{
Task Handle(ErrorMessage message, CancellationToken cancellationToken);
}

View File

@@ -1,6 +1,13 @@
using AipsCore.Application.Abstract;
using AipsCore.Application.Abstract.MessageBroking; using AipsCore.Application.Abstract.MessageBroking;
using AipsCore.Application.Models.Shape.Command.MoveShape; using AipsCore.Application.Models.Shape.Command.MoveShape;
namespace AipsCore.Application.Common.Message.MoveShape; namespace AipsCore.Application.Common.Message.MoveShape;
public record MoveShapeMessage(MoveShapeCommand Command) : IMessage; public record MoveShapeMessage(Guid WhiteboardId, MoveShapeCommand Command) : IMessage, IWhiteboardAwareContext
{
public Guid GetWhiteboardId()
{
return WhiteboardId;
}
}

View File

@@ -31,7 +31,7 @@ public class WhiteboardHub : Hub
{ {
if (!_whiteboardManager.WhiteboardExists(whiteboardId)) if (!_whiteboardManager.WhiteboardExists(whiteboardId))
{ {
await _whiteboardManager.AddWhiteboard(whiteboardId); await _whiteboardManager.LoadWhiteboard(whiteboardId);
} }
await Groups.AddToGroupAsync(Context.ConnectionId, whiteboardId.ToString()); await Groups.AddToGroupAsync(Context.ConnectionId, whiteboardId.ToString());
@@ -210,6 +210,6 @@ public class WhiteboardHub : Hub
await MoveShape(moveShape); await MoveShape(moveShape);
await _messagingService.MoveShape(moveShape); await _messagingService.MoveShape(CurrentWhiteboard.WhiteboardId, moveShape);
} }
} }

View File

@@ -14,7 +14,7 @@ public class WhiteboardManager
_scopeFactory = scopeFactory; _scopeFactory = scopeFactory;
} }
public async Task AddWhiteboard(Guid whiteboardId) public async Task LoadWhiteboard(Guid whiteboardId)
{ {
var getWhiteboardService = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService<GetWhiteboardService>(); var getWhiteboardService = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService<GetWhiteboardService>();
var whiteboard = await getWhiteboardService.GetWhiteboard(whiteboardId); var whiteboard = await getWhiteboardService.GetWhiteboard(whiteboardId);

View File

@@ -1,3 +1,4 @@
using AipsCore.Application.Common.Message.ErrorMessage;
using AipsCore.Infrastructure.DI; using AipsCore.Infrastructure.DI;
using AipsRT.Hubs; using AipsRT.Hubs;
using AipsRT.Model.Whiteboard; using AipsRT.Model.Whiteboard;
@@ -15,6 +16,10 @@ builder.Configuration.AddEnvironmentVariables();
builder.Services.AddSignalR(); builder.Services.AddSignalR();
builder.Services.AddAips(builder.Configuration); builder.Services.AddAips(builder.Configuration);
builder.Services.AddAipsMessageHandlers();
builder.Services.AddSingleton<IErrorMessageHandleStrategy, RtErrorHandleStrategy>();
builder.Services.AddHostedService<ErrorSubscriberBackgroundService>();
builder.Services.AddScoped<GetWhiteboardService>(); builder.Services.AddScoped<GetWhiteboardService>();
builder.Services.AddSingleton<WhiteboardManager>(); builder.Services.AddSingleton<WhiteboardManager>();

View File

@@ -0,0 +1,25 @@
using AipsCore.Application.Abstract;
using AipsCore.Application.Abstract.MessageBroking;
using AipsCore.Application.Common.Message.ErrorMessage;
namespace AipsRT.Services;
public class ErrorSubscriberBackgroundService : BackgroundService
{
private readonly IMessageSubscriber _subscriber;
private readonly IDispatcher _dispatcher;
public ErrorSubscriberBackgroundService(IMessageSubscriber subscriber, IDispatcher dispatcher)
{
_subscriber = subscriber;
_dispatcher = dispatcher;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await _subscriber.SubscribeAsync<ErrorMessage>(async (errorMessage, ct) =>
{
await _dispatcher.Execute(errorMessage, ct);
});
}
}

View File

@@ -12,8 +12,8 @@ public interface IMessagingService
Task CreatedArrow(Guid whiteboardId, Arrow arrow); Task CreatedArrow(Guid whiteboardId, Arrow arrow);
Task CreateLine(Guid whiteboardId, Line line); Task CreateLine(Guid whiteboardId, Line line);
Task CreateTextShape(Guid whiteboardId, TextShape textShape); Task CreateTextShape(Guid whiteboardId, TextShape textShape);
Task MoveShape(MoveShapeCommand moveShape); Task MoveShape(Guid whiteboardId, MoveShapeCommand moveShape);
Task AcceptedUser(AcceptUserRequestToJoinCommand command); Task AcceptedUser(AcceptUserRequestToJoinCommand command);
Task RejectedUser(RejectUserRequestToJoinCommand command); Task RejectedUser(RejectUserRequestToJoinCommand command);

View File

@@ -99,9 +99,9 @@ public class MessagingService : IMessagingService
await _messagePublisher.PublishAsync(message); await _messagePublisher.PublishAsync(message);
} }
public async Task MoveShape(MoveShapeCommand moveShape) public async Task MoveShape(Guid whiteboardId, MoveShapeCommand moveShape)
{ {
var message = new MoveShapeMessage(moveShape); var message = new MoveShapeMessage(whiteboardId, moveShape);
await _messagePublisher.PublishAsync(message); await _messagePublisher.PublishAsync(message);
} }

View File

@@ -0,0 +1,29 @@
using AipsCore.Application.Common.Message.ErrorMessage;
using AipsRT.Hubs;
using AipsRT.Model.Whiteboard;
using Microsoft.AspNetCore.SignalR;
namespace AipsRT.Services;
public class RtErrorHandleStrategy : IErrorMessageHandleStrategy
{
private readonly IHubContext<WhiteboardHub> _hubContext;
private readonly WhiteboardManager _whiteboardManager;
public RtErrorHandleStrategy(IHubContext<WhiteboardHub> hubContext, WhiteboardManager whiteboardManager)
{
_hubContext = hubContext;
_whiteboardManager = whiteboardManager;
}
public async Task Handle(ErrorMessage message, CancellationToken cancellationToken)
{
await _whiteboardManager.LoadWhiteboard(message.WhiteboardId);
var whiteboard = _whiteboardManager.GetWhiteboard(message.WhiteboardId)!;
await _hubContext.Clients
.Group(whiteboard.WhiteboardId.ToString())
.SendAsync("InitWhiteboard", whiteboard, cancellationToken);
}
}

View File

@@ -1,6 +1,7 @@
using System.Reflection; using System.Reflection;
using AipsCore.Application.Abstract; using AipsCore.Application.Abstract;
using AipsCore.Application.Abstract.MessageBroking; using AipsCore.Application.Abstract.MessageBroking;
using AipsCore.Application.Common.Message.ErrorMessage;
using AipsCore.Application.Common.Message.TestMessage; using AipsCore.Application.Common.Message.TestMessage;
using AipsCore.Domain.Common.Validation; using AipsCore.Domain.Common.Validation;
using AipsWorker.Utilities; using AipsWorker.Utilities;
@@ -12,12 +13,14 @@ public class WorkerService : BackgroundService
{ {
private readonly IDispatcher _dispatcher; private readonly IDispatcher _dispatcher;
private readonly IMessageTypesProvider _messageTypesProvider; private readonly IMessageTypesProvider _messageTypesProvider;
private readonly IMessagePublisher _publisher;
private readonly SubscribeMethodUtility _subscribeMethodUtility; private readonly SubscribeMethodUtility _subscribeMethodUtility;
public WorkerService(IMessageSubscriber subscriber, IDispatcher dispatcher, IMessageTypesProvider messageTypesProvider) public WorkerService(IMessageSubscriber subscriber, IDispatcher dispatcher, IMessageTypesProvider messageTypesProvider, IMessagePublisher publisher)
{ {
_dispatcher = dispatcher; _dispatcher = dispatcher;
_messageTypesProvider = messageTypesProvider; _messageTypesProvider = messageTypesProvider;
_publisher = publisher;
_subscribeMethodUtility = new SubscribeMethodUtility(subscriber); _subscribeMethodUtility = new SubscribeMethodUtility(subscriber);
} }
@@ -37,15 +40,26 @@ public class WorkerService : BackgroundService
private async Task HandleMessage<T>(T message, CancellationToken ct) where T : IMessage private async Task HandleMessage<T>(T message, CancellationToken ct) where T : IMessage
{ {
Console.WriteLine($"*--------{message.GetType().Name}--------*");
try try
{ {
await _dispatcher.Execute(message, ct); await _dispatcher.Execute(message, ct);
Console.WriteLine($"OK: {message.GetType().Name}"); Console.WriteLine("OK!");
} }
catch (ValidationException validationException) catch (ValidationException validationException)
{ {
Console.WriteLine("===Validation Exception: "); if (message is IWhiteboardAwareContext)
{
var whiteboardId = ((IWhiteboardAwareContext)message).GetWhiteboardId();
var errorMessage = new ErrorMessage(whiteboardId, validationException.ValidationErrors);
await _publisher.PublishAsync(errorMessage, ct);
}
Console.WriteLine("Validation Exception: ");
foreach (var error in validationException.ValidationErrors) foreach (var error in validationException.ValidationErrors)
{ {
Console.WriteLine(" * Code: " + error.Code); Console.WriteLine(" * Code: " + error.Code);

View File

@@ -50,6 +50,7 @@ export const useWhiteboardStore = defineStore('whiteboard', () => {
function registerHubEvents() { function registerHubEvents() {
whiteboardHubService.onInitWhiteboard((wb) => { whiteboardHubService.onInitWhiteboard((wb) => {
deselectShape()
whiteboard.value = wb whiteboard.value = wb
isLoading.value = false isLoading.value = false
}) })