feat(backend): Add SignalR real-time notifications for Sprint events - Sprint 2 Story 3 Task 5

Implemented comprehensive SignalR notifications for Sprint lifecycle events.

Features:
- Extended IRealtimeNotificationService with 5 Sprint notification methods
- Implemented Sprint notification service methods in RealtimeNotificationService
- Created SprintEventHandlers to handle all 5 Sprint domain events
- Updated UpdateSprintCommandHandler to publish SprintUpdatedEvent
- SignalR events broadcast to both project and tenant groups

Sprint Events Implemented:
1. SprintCreated - New sprint created
2. SprintUpdated - Sprint details modified
3. SprintStarted - Sprint transitioned to Active status
4. SprintCompleted - Sprint transitioned to Completed status
5. SprintDeleted - Sprint removed

Technical Details:
- Event handlers catch and log errors (fire-and-forget pattern)
- Notifications include SprintId, SprintName, ProjectId, and Timestamp
- Multi-tenant isolation via tenant groups
- Project-level targeting via project groups

Frontend Integration:
- Frontend can listen to 'SprintCreated', 'SprintUpdated', 'SprintStarted', 'SprintCompleted', 'SprintDeleted' events
- Real-time UI updates for sprint changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2025-11-05 00:35:33 +01:00
parent 252674b508
commit 96fed691ab
4 changed files with 265 additions and 1 deletions

View File

@@ -4,6 +4,7 @@ using ColaFlow.Modules.ProjectManagement.Application.Common.Interfaces;
using ColaFlow.Modules.ProjectManagement.Domain.Repositories;
using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects;
using ColaFlow.Modules.ProjectManagement.Domain.Exceptions;
using ColaFlow.Modules.ProjectManagement.Domain.Events;
namespace ColaFlow.Modules.ProjectManagement.Application.Commands.UpdateSprint;
@@ -12,11 +13,13 @@ namespace ColaFlow.Modules.ProjectManagement.Application.Commands.UpdateSprint;
/// </summary>
public sealed class UpdateSprintCommandHandler(
IApplicationDbContext context,
IUnitOfWork unitOfWork)
IUnitOfWork unitOfWork,
IMediator mediator)
: IRequestHandler<UpdateSprintCommand, Unit>
{
private readonly IApplicationDbContext _context = context ?? throw new ArgumentNullException(nameof(context));
private readonly IUnitOfWork _unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
private readonly IMediator _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
public async Task<Unit> Handle(UpdateSprintCommand request, CancellationToken cancellationToken)
{
@@ -39,6 +42,9 @@ public sealed class UpdateSprintCommandHandler(
// Save changes
await _unitOfWork.SaveChangesAsync(cancellationToken);
// Publish domain event
await _mediator.Publish(new SprintUpdatedEvent(sprint.Id.Value, sprint.Name, sprint.ProjectId.Value), cancellationToken);
return Unit.Value;
}
}