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:
@@ -202,6 +202,118 @@ public class RealtimeNotificationService : IRealtimeNotificationService
|
||||
});
|
||||
}
|
||||
|
||||
// Sprint notifications
|
||||
public async Task NotifySprintCreated(Guid tenantId, Guid projectId, Guid sprintId, string sprintName)
|
||||
{
|
||||
var projectGroupName = $"project-{projectId}";
|
||||
var tenantGroupName = $"tenant-{tenantId}";
|
||||
|
||||
_logger.LogInformation("Notifying sprint {SprintId} created in project {ProjectId}", sprintId, projectId);
|
||||
|
||||
await _projectHubContext.Clients.Group(projectGroupName).SendAsync("SprintCreated", new
|
||||
{
|
||||
SprintId = sprintId,
|
||||
SprintName = sprintName,
|
||||
ProjectId = projectId,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await _projectHubContext.Clients.Group(tenantGroupName).SendAsync("SprintCreated", new
|
||||
{
|
||||
SprintId = sprintId,
|
||||
SprintName = sprintName,
|
||||
ProjectId = projectId,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
public async Task NotifySprintUpdated(Guid tenantId, Guid projectId, Guid sprintId, string sprintName)
|
||||
{
|
||||
var projectGroupName = $"project-{projectId}";
|
||||
|
||||
_logger.LogInformation("Notifying sprint {SprintId} updated", sprintId);
|
||||
|
||||
await _projectHubContext.Clients.Group(projectGroupName).SendAsync("SprintUpdated", new
|
||||
{
|
||||
SprintId = sprintId,
|
||||
SprintName = sprintName,
|
||||
ProjectId = projectId,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
public async Task NotifySprintStarted(Guid tenantId, Guid projectId, Guid sprintId, string sprintName)
|
||||
{
|
||||
var projectGroupName = $"project-{projectId}";
|
||||
var tenantGroupName = $"tenant-{tenantId}";
|
||||
|
||||
_logger.LogInformation("Notifying sprint {SprintId} started", sprintId);
|
||||
|
||||
await _projectHubContext.Clients.Group(projectGroupName).SendAsync("SprintStarted", new
|
||||
{
|
||||
SprintId = sprintId,
|
||||
SprintName = sprintName,
|
||||
ProjectId = projectId,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await _projectHubContext.Clients.Group(tenantGroupName).SendAsync("SprintStarted", new
|
||||
{
|
||||
SprintId = sprintId,
|
||||
SprintName = sprintName,
|
||||
ProjectId = projectId,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
public async Task NotifySprintCompleted(Guid tenantId, Guid projectId, Guid sprintId, string sprintName)
|
||||
{
|
||||
var projectGroupName = $"project-{projectId}";
|
||||
var tenantGroupName = $"tenant-{tenantId}";
|
||||
|
||||
_logger.LogInformation("Notifying sprint {SprintId} completed", sprintId);
|
||||
|
||||
await _projectHubContext.Clients.Group(projectGroupName).SendAsync("SprintCompleted", new
|
||||
{
|
||||
SprintId = sprintId,
|
||||
SprintName = sprintName,
|
||||
ProjectId = projectId,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await _projectHubContext.Clients.Group(tenantGroupName).SendAsync("SprintCompleted", new
|
||||
{
|
||||
SprintId = sprintId,
|
||||
SprintName = sprintName,
|
||||
ProjectId = projectId,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
public async Task NotifySprintDeleted(Guid tenantId, Guid projectId, Guid sprintId, string sprintName)
|
||||
{
|
||||
var projectGroupName = $"project-{projectId}";
|
||||
var tenantGroupName = $"tenant-{tenantId}";
|
||||
|
||||
_logger.LogInformation("Notifying sprint {SprintId} deleted", sprintId);
|
||||
|
||||
await _projectHubContext.Clients.Group(projectGroupName).SendAsync("SprintDeleted", new
|
||||
{
|
||||
SprintId = sprintId,
|
||||
SprintName = sprintName,
|
||||
ProjectId = projectId,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await _projectHubContext.Clients.Group(tenantGroupName).SendAsync("SprintDeleted", new
|
||||
{
|
||||
SprintId = sprintId,
|
||||
SprintName = sprintName,
|
||||
ProjectId = projectId,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
public async Task NotifyUser(Guid userId, string message, string type = "info")
|
||||
{
|
||||
var userConnectionId = $"user-{userId}";
|
||||
|
||||
Reference in New Issue
Block a user