refactoring project split

This commit is contained in:
beo3000 2025-12-16 21:15:20 +01:00
parent eb86fd1cfb
commit ab0c5d053a
11 changed files with 347 additions and 39 deletions

View File

@ -1,7 +0,0 @@
namespace Koogle.Application
{
public class Class1
{
}
}

View File

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Koogle.Domain.Enums;
namespace Koogle.Application.DTOs
{
public record DayDto
{
public int Id { get; set; }
public DateTime PostDate { get; set; }
public DayStatus Status { get; set; }
}
public record DaySummaryDto
{
public int Id { get; set; }
public DateTime PostDate { get; set; }
public DayStatus Status { get; set; }
}
public record DayFilterDto
{
public int Year { get; set; }
///// <summary>
///// Sortierfeld.
///// </summary>
//public GoalSortField SortBy { get; init; } = GoalSortField.Name;
///// <summary>
///// Sortierrichtung.
///// </summary>
//public SortDirection SortDirection { get; init; } = SortDirection.Ascending;
// Paginierung
/// <summary>
/// Seitennummer (1-basiert).
/// </summary>
public int Page { get; init; } = 1;
/// <summary>
/// Anzahl der Einträge pro Seite.
/// </summary>
public int PageSize { get; init; } = 20;
/// <summary>
/// Berechnet den Skip-Wert für die Paginierung.
/// </summary>
public int Skip => (Page - 1) * PageSize;
}
}

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Koogle.Application.DTOs
{
/// <summary>
/// Ergebnis einer paginierten Abfrage.
/// </summary>
/// <typeparam name="T">Typ der Ergebnisse.</typeparam>
public record PagedResultDto<T>
{
/// <summary>
/// Die Ergebnisse der aktuellen Seite.
/// </summary>
public IReadOnlyList<T> Items { get; init; } = [];
/// <summary>
/// Gesamtanzahl der Ergebnisse (über alle Seiten).
/// </summary>
public int TotalCount { get; init; }
/// <summary>
/// Aktuelle Seitennummer.
/// </summary>
public int Page { get; init; }
/// <summary>
/// Anzahl der Einträge pro Seite.
/// </summary>
public int PageSize { get; init; }
/// <summary>
/// Gesamtanzahl der Seiten.
/// </summary>
public int TotalPages => PageSize > 0 ? (int)Math.Ceiling((double)TotalCount / PageSize) : 0;
/// <summary>
/// Gibt an, ob es eine vorherige Seite gibt.
/// </summary>
public bool HasPreviousPage => Page > 1;
/// <summary>
/// Gibt an, ob es eine nächste Seite gibt.
/// </summary>
public bool HasNextPage => Page < TotalPages;
}
}

View File

@ -0,0 +1,36 @@
using Koogle.Application.Interfaces;
using Koogle.Application.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Koogle.Application
{
public static class DependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services)
{
// AutoMapper
services.AddAutoMapper(cfg => cfg.LicenseKey = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikx1Y2t5UGVubnlTb2Z0d2FyZUxpY2Vuc2VLZXkvYmJiMTNhY2I1OTkwNGQ4OWI0Y2IxYzg1ZjA4OGNjZjkiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2x1Y2t5cGVubnlzb2Z0d2FyZS5jb20iLCJhdWQiOiJMdWNreVBlbm55U29mdHdhcmUiLCJleHAiOiIxNzk3MjkyODAwIiwiaWF0IjoiMTc2NTgyNzkzMyIsImFjY291bnRfaWQiOiIwMTliMjM4YjYxZGM3MWYwOTAyYmY5OGU0NzNmOTY5ZSIsImN1c3RvbWVyX2lkIjoiY3RtXzAxa2NocnF3dHNkczM4cjJnOTBncmZ5ajA1Iiwic3ViX2lkIjoiLSIsImVkaXRpb24iOiIwIiwidHlwZSI6IjIifQ.ehnUm61bFyXv2s0RScHd3vV2wIRivFN-phslB65UxztWBsk1EAtqTgPT55ONQ6-k7zi7G1vpLUUz9NL4EfpMRwgl1obeCTrs1pzvIRkednzrSdcPKAOmil-xiCS1TlrvaLzLnBWCr0JroGrAmtMYjsfUpYayRx9x8BzjUGBPUvb6eUR6wSbEtPzZbycgsp4Oj4Wwi23o56UGWHdNz7R8ofKy9EyzrgiG1uYfJZUDB_B5uWtdWi5M2bfoYHcLj-7VbSJlVlW2RoETEYuylBjG0Bwg_ZtSoVsCuqi_qV_GuyBOKbPpjFK4NFcInYFkAxAyzV_uTaFQpCFukPpCMZpFtA",
Assembly.GetExecutingAssembly());
// FluentValidation
//services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
// Application Services
services.AddScoped<IDayService, DayService>();
//services.AddScoped<IYearService, YearService>();
//services.AddScoped<ICompanyService, CompanyService>();
//services.AddScoped<IUserService, UserService>();
//services.AddScoped<ITagService, TagService>();
//services.AddScoped<IAuthorizationService, AuthorizationService>();
return services;
}
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Koogle.Application.DTOs;
namespace Koogle.Application.Interfaces
{
public interface IDayService
{
Task<PagedResultDto<DaySummaryDto>> GetAllAsync(DayFilterDto filter, CancellationToken cancellationToken = default);
}
}

View File

@ -6,4 +6,13 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="15.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Koogle.Domain\Koogle.Domain.csproj" />
<ProjectReference Include="..\Koogle.Infrastrcuture\Koogle.Infrastrcuture.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,135 @@
using Koogle.Application.DTOs;
using Koogle.Application.Interfaces;
using Koogle.Domain.Interfaces;
using KoogleApp.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using KoogleApp.Entities;
namespace Koogle.Application.Services
{
public class DayService : IDayService
{
private readonly IDbContextFactory<AppDbContext> _contextFactory;
private readonly IDayRepository _goalRepository;
private readonly ICurrentUserService _currentUserService;
private readonly IAuthorizationService _authorizationService;
private readonly IMapper _mapper;
public DayService(IDbContextFactory<AppDbContext> contextFactory,
IDayRepository goalRepository,
IMapper mapper)
{
}
public async Task<PagedResultDto<DaySummaryDto>> GetAllAsync(DayFilterDto filter, CancellationToken cancellationToken = default)
{
await using var context = await _contextFactory.CreateDbContextAsync(cancellationToken);
var query = context.Days
.Include(g => g.PostDate)
.Include(g => g.Status)
//.Include(g => g.ActualValues.Where(av => !av.IsDeleted))
.AsQueryable();
// Filter anwenden
query = ApplyFilters(query, filter);
// Gesamtanzahl ermitteln
var totalCount = await query.CountAsync(cancellationToken);
// Sortierung anwenden
//query = ApplySorting(query, filter);
// Paginierung anwenden
var goals = await query
.Skip(filter.Skip)
.Take(filter.PageSize)
.ToListAsync(cancellationToken);
var items = _mapper.Map<List<DaySummaryDto>>(goals);
return new PagedResultDto<DaySummaryDto>
{
Items = items,
TotalCount = totalCount,
Page = filter.Page,
PageSize = filter.PageSize
};
}
/// <summary>
/// Wendet Filter auf die Abfrage an.
/// </summary>
private static IQueryable<Day> ApplyFilters(IQueryable<Day> query, DayFilterDto filter)
{
//if (!string.IsNullOrWhiteSpace(filter.SearchTerm))
//{
// var searchTerm = filter.SearchTerm.ToLower();
// query = query.Where(g =>
// g.Name.ToLower().Contains(searchTerm) ||
// (g.Description != null && g.Description.ToLower().Contains(searchTerm)));
//}
//if (filter.CompanyId.HasValue)
//{
// query = query.Where(g => g.CompanyId == filter.CompanyId.Value);
//}
//if (filter.CompanyIds?.Count > 0)
//{
// query = query.Where(g => filter.CompanyIds.Contains(g.CompanyId));
//}
//if (filter.YearId.HasValue)
//{
// query = query.Where(g => g.YearId == filter.YearId.Value);
//}
//if (filter.Year.HasValue)
//{
// query = query.Where(g => g.Year.YearNumber == filter.Year.Value);
//}
//if (filter.GoalType.HasValue)
//{
// query = query.Where(g => g.GoalType == filter.GoalType.Value);
//}
//if (filter.TagId.HasValue)
//{
// query = query.Where(g => g.Tags.Any(t => t.Id == filter.TagId.Value));
//}
//if (filter.TagIds?.Count > 0)
//{
// query = query.Where(g => g.Tags.Any(t => filter.TagIds.Contains(t.Id)));
//}
//if (filter.OwnerId.HasValue)
//{
// query = query.Where(g => g.GoalOwners.Any(go => go.UserId == filter.OwnerId.Value));
//}
//if (filter.IsInherited.HasValue)
//{
// query = filter.IsInherited.Value
// ? query.Where(g => g.ParentGoalId != null)
// : query.Where(g => g.ParentGoalId == null);
//}
//if (filter.IsRootGoal == true)
//{
// query = query.Where(g => g.ParentGoalId == null);
//}
return query;
}
}
}

View File

@ -1,12 +1,40 @@
using System;
using KoogleApp.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Koogle.Infrastrcuture
{
internal class DependencyInjection
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services,
IConfiguration configuration)
{
var connectionString = configuration.GetConnectionString("AppDb") ?? throw new InvalidOperationException("Connection string 'AppDb' not found.");
//services.AddDbContext<AppDbContext>(options =>
// options.UseSqlServer(connectionString));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<AppDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
services.AddDbContextFactory<AppDbContext>(options =>
options.UseSqlServer(connectionString)
);
return services;
}
}
}

View File

@ -17,7 +17,7 @@ namespace Koogle.Tests.ReducerTests
{
// Arrange
var initialState = new ThrowPanelState();
var action = new StartStopAction(initialState, new TrainingSetupState());
var action = new StartStopAction(new TrainingSetupState());
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
@ -31,7 +31,7 @@ namespace Koogle.Tests.ReducerTests
{
// Arrange
var initialState = new ThrowPanelState();
var action = new StartStopAction(initialState, null);
var action = new StartStopAction(null);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
@ -469,7 +469,7 @@ namespace Koogle.Tests.ReducerTests
// Arrange
var initialState = new ThrowPanelState();
var setupState = new TrainingSetupState() { ThrowMode = ThrowMode.Decrease };
var action = new StartStopAction(initialState, setupState);
var action = new StartStopAction(setupState);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
@ -484,7 +484,7 @@ namespace Koogle.Tests.ReducerTests
// Arrange
var initialState = new ThrowPanelState();
var setupState = new TrainingSetupState() { ThrowsPerRound = 5 };
var action = new StartStopAction(initialState, setupState);
var action = new StartStopAction(setupState);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
@ -499,7 +499,7 @@ namespace Koogle.Tests.ReducerTests
// Arrange
var initialState = new ThrowPanelState();
var setupState = new TrainingSetupState() { DayId = 123 };
var action = new StartStopAction(initialState, setupState);
var action = new StartStopAction(setupState);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
@ -514,7 +514,7 @@ namespace Koogle.Tests.ReducerTests
// Arrange
var initialState = new ThrowPanelState();
var setupState = new TrainingSetupState();
var action = new StartStopAction(initialState, setupState);
var action = new StartStopAction(setupState);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
@ -528,7 +528,7 @@ namespace Koogle.Tests.ReducerTests
{
// Arrange
var initialState = new ThrowPanelState() with { IsStated = true };
var action = new StartStopAction(initialState, null);
var action = new StartStopAction(null);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);

View File

@ -1,3 +1,5 @@
using Koogle.Application;
using Koogle.Infrastrcuture;
using KoogleApp.Components;
using KoogleApp.Components.Account;
using KoogleApp.Data;
@ -39,8 +41,12 @@ builder.Services.AddAuthentication(options =>
})
.AddIdentityCookies();
// Infrastructure Layer (DbContext, Repositories, CurrentUserService)
builder.Services.AddInfrastructure(builder.Configuration);
// Application Layer (Services, AutoMapper, Validators)
builder.Services.AddApplication();
builder.Services.AddDbServices(builder.Configuration);
builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();

View File

@ -14,28 +14,6 @@ namespace KoogleApp.Services
{
public static class ServiceExtension
{
public static IServiceCollection AddDbServices(this IServiceCollection services, IConfiguration configuration)
{
var connectionString = configuration.GetConnectionString("AppDb") ?? throw new InvalidOperationException("Connection string 'AppDb' not found.");
//services.AddDbContext<AppDbContext>(options =>
// options.UseSqlServer(connectionString));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<AppDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
services.AddDbContextFactory<AppDbContext>(options =>
options.UseSqlServer(connectionString)
);
return services;
}
public static IServiceCollection AddAppServices(this IServiceCollection services)
{
services.AddFluxor(x =>