DeutschLernen/GermanApp/Infrastructure/Data/Repositories/StoryProgressRepository.cs
Lasse Rune Hansen 5a514c5a2d feat(backend/story-integration): Phase 1 - Database & Models
Implement story integration foundation:
- Create Domain/Entities/StorySegment.cs with full CRUD methods
- Create Domain/Entities/StoryProgress.cs for user story progress tracking
- Create Application/DTOs/StorySegmentDto.cs with multiple DTO types
- Create Domain/Interfaces/IStoryRepository.cs
- Create Domain/Interfaces/IStoryProgressRepository.cs
- Create Infrastructure/Data/Repositories/StoryRepository.cs
- Create Infrastructure/Data/Repositories/StoryProgressRepository.cs
- Add DbSets and entity configurations to AppDbContext
- Add navigation properties to Level, Lesson, and User entities

Next: Phase 2 - Backend Services (StoryService, StoryGenerationService)

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-13 13:23:12 +02:00

127 lines
4.2 KiB
C#

using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using GermanApp.Domain.Entities;
using GermanApp.Domain.Interfaces;
using GermanApp.Infrastructure.Data.DbContext;
using Microsoft.EntityFrameworkCore;
namespace GermanApp.Infrastructure.Data.Repositories;
/// <summary>
/// Entity Framework Core implementation of IStoryProgressRepository.
/// This is part of the Infrastructure layer.
/// </summary>
public class StoryProgressRepository : IStoryProgressRepository
{
private readonly AppDbContext _context;
public StoryProgressRepository(AppDbContext context)
{
_context = context;
}
public async Task<IReadOnlyList<StoryProgress>> GetByUserAndLevelAsync(
int userId,
int levelId,
CancellationToken cancellationToken = default)
{
return await _context.StoryProgress
.Where(p => p.UserId == userId && p.LevelId == levelId)
.Include(p => p.StorySegment)
.OrderBy(p => p.StorySegment != null ? p.StorySegment.Order : 0)
.AsNoTracking()
.ToListAsync(cancellationToken);
}
public async Task<StoryProgress?> GetByUserAndSegmentAsync(
int userId,
int storySegmentId,
CancellationToken cancellationToken = default)
{
return await _context.StoryProgress
.FirstOrDefaultAsync(
p => p.UserId == userId && p.StorySegmentId == storySegmentId,
cancellationToken);
}
public async Task<int> GetHighestUnlockedOrderAsync(
int userId,
int levelId,
CancellationToken cancellationToken = default)
{
var highestOrder = await _context.StoryProgress
.Where(p => p.UserId == userId && p.LevelId == levelId)
.Include(p => p.StorySegment)
.Select(p => p.StorySegment != null ? p.StorySegment.Order : 0)
.MaxAsync(cancellationToken);
return highestOrder == null ? 0 : highestOrder;
}
public async Task<bool> IsSegmentUnlockedAsync(
int userId,
int storySegmentId,
CancellationToken cancellationToken = default)
{
return await _context.StoryProgress
.AnyAsync(
p => p.UserId == userId && p.StorySegmentId == storySegmentId,
cancellationToken);
}
public async Task<bool> IsSegmentCompletedAsync(
int userId,
int storySegmentId,
CancellationToken cancellationToken = default)
{
return await _context.StoryProgress
.AnyAsync(
p => p.UserId == userId
&& p.StorySegmentId == storySegmentId
&& p.IsCompleted,
cancellationToken);
}
public async Task<StoryProgress> AddAsync(StoryProgress progress, CancellationToken cancellationToken = default)
{
await _context.StoryProgress.AddAsync(progress, cancellationToken);
await _context.SaveChangesAsync(cancellationToken);
return progress;
}
public async Task UpdateAsync(StoryProgress progress, CancellationToken cancellationToken = default)
{
_context.StoryProgress.Update(progress);
await _context.SaveChangesAsync(cancellationToken);
}
public async Task<int> GetTotalSegmentsForLevelAsync(int levelId, CancellationToken cancellationToken = default)
{
return await _context.StorySegments
.CountAsync(s => s.LevelId == levelId && s.IsActive, cancellationToken);
}
public async Task<int> GetUnlockedCountAsync(
int userId,
int levelId,
CancellationToken cancellationToken = default)
{
return await _context.StoryProgress
.CountAsync(
p => p.UserId == userId && p.LevelId == levelId,
cancellationToken);
}
public async Task<int> GetCompletedCountAsync(
int userId,
int levelId,
CancellationToken cancellationToken = default)
{
return await _context.StoryProgress
.CountAsync(
p => p.UserId == userId && p.LevelId == levelId && p.IsCompleted,
cancellationToken);
}
}