feat(backend): Create Sprint 2 backend Stories and Tasks

Created detailed implementation plans for Sprint 2 backend work:

Story 1: Audit Log Foundation (Phase 1)
- Task 1: Design AuditLog database schema and create migration
- Task 2: Create AuditLog entity and Repository
- Task 3: Implement EF Core SaveChangesInterceptor
- Task 4: Write unit tests for audit logging
- Task 5: Integrate with ProjectManagement Module

Story 2: Audit Log Core Features (Phase 2)
- Task 1: Implement Changed Fields Detection (JSON Diff)
- Task 2: Integrate User Context Tracking
- Task 3: Add Multi-Tenant Isolation
- Task 4: Implement Audit Query API
- Task 5: Write Integration Tests

Story 3: Sprint Management Module
- Task 1: Create Sprint Aggregate Root and Domain Events
- Task 2: Implement Sprint Repository and EF Core Configuration
- Task 3: Create CQRS Commands and Queries
- Task 4: Implement Burndown Chart Calculation
- Task 5: Add SignalR Real-Time Notifications
- Task 6: Write Integration Tests

Total: 3 Stories, 16 Tasks, 24 Story Points (8+8+8)
Estimated Duration: 10-12 days

All tasks include:
- Detailed technical implementation guidance
- Code examples and file paths
- Testing requirements (>= 90% coverage)
- Performance benchmarks (< 5ms audit overhead)
- Multi-tenant security validation

🤖 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-04 22:56:31 +01:00
parent d6cf86a4da
commit ebb56cc9f8
19 changed files with 4030 additions and 0 deletions

View File

@@ -0,0 +1,162 @@
---
task_id: sprint_2_story_2_task_2
story: sprint_2_story_2
status: not_started
estimated_hours: 3
created_date: 2025-11-05
assignee: Backend Team
---
# Task 2: Integrate User Context Tracking
**Story**: Story 2 - Audit Log Core Features (Phase 2)
**Estimated**: 3 hours
## Description
Enhance audit logging to automatically capture the current user (UserId) from HTTP context for every operation. This provides accountability and traceability.
## Acceptance Criteria
- [ ] UserId automatically captured from JWT token
- [ ] System operations (null user) handled correctly
- [ ] User information enriched in audit logs
- [ ] Integration tests verify user tracking
- [ ] Performance not impacted
## Implementation Details
**Already Implemented in Story 1!**
The `AuditLogInterceptor` already captures UserId from HTTP context:
```csharp
private Guid? GetCurrentUserId()
{
var userIdClaim = _httpContextAccessor.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier);
return userIdClaim != null ? Guid.Parse(userIdClaim.Value) : null;
}
```
**This Task: Add User Information Enrichment**
1. **Add User Navigation Property**: `colaflow-api/src/ColaFlow.Domain/Entities/AuditLog.cs`
```csharp
public class AuditLog
{
// ... existing properties ...
public Guid? UserId { get; set; }
// Navigation property
public User? User { get; set; }
}
```
2. **Update EF Configuration**: `colaflow-api/src/ColaFlow.Infrastructure/Data/Configurations/AuditLogConfiguration.cs`
```csharp
public void Configure(EntityTypeBuilder<AuditLog> builder)
{
// ... existing configuration ...
// User relationship (optional foreign key)
builder.HasOne(a => a.User)
.WithMany()
.HasForeignKey(a => a.UserId)
.OnDelete(DeleteBehavior.SetNull); // Don't delete audit logs when user is deleted
}
```
3. **Enrich Query Results**: `colaflow-api/src/ColaFlow.Infrastructure/Repositories/AuditLogRepository.cs`
```csharp
public async Task<List<AuditLog>> GetByEntityAsync(string entityType, Guid entityId)
{
var tenantId = _tenantContext.TenantId;
return await _context.AuditLogs
.Include(a => a.User) // Include user info
.Where(a => a.TenantId == tenantId && a.EntityType == entityType && a.EntityId == entityId)
.OrderByDescending(a => a.Timestamp)
.ToListAsync();
}
```
4. **Handle System Operations**: Update `AuditLogInterceptor` to handle null users gracefully:
```csharp
private Guid? GetCurrentUserId()
{
try
{
var userIdClaim = _httpContextAccessor.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier);
if (userIdClaim != null && Guid.TryParse(userIdClaim.Value, out var userId))
{
return userId;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to get current user ID for audit log");
}
return null; // System operation or anonymous
}
```
**Example Audit Log with User Info**:
```json
{
"Id": "abc-123",
"EntityType": "Project",
"Action": "Update",
"UserId": "user-456",
"User": {
"Id": "user-456",
"UserName": "john.doe@example.com",
"DisplayName": "John Doe"
},
"Timestamp": "2025-11-05T10:30:00Z",
"ChangedFields": {
"Title": { "OldValue": "Old", "NewValue": "New" }
}
}
```
## Technical Notes
- Use `OnDelete(DeleteBehavior.SetNull)` to preserve audit logs when users are deleted
- Handle null users gracefully (system operations, background jobs)
- Use `Include(a => a.User)` for enriched query results
- Consider caching user info for performance (future optimization)
## Testing
**Integration Tests**:
```csharp
[Fact]
public async Task CreateProject_ShouldCaptureCurrentUser()
{
// Arrange
var userId = Guid.NewGuid();
SetCurrentUser(userId); // Helper to set HTTP context user
// Act
var projectId = await Mediator.Send(new CreateProjectCommand { /* ... */ });
// Assert
var auditLog = await Context.AuditLogs
.Include(a => a.User)
.FirstAsync(a => a.EntityId == projectId);
Assert.Equal(userId, auditLog.UserId);
Assert.NotNull(auditLog.User);
}
[Fact]
public async Task SystemOperation_ShouldHaveNullUser()
{
// Test background job or system operation
// ...
}
```
---
**Created**: 2025-11-05 by Backend Agent