From ab0c5d053ab6bffd4602989efc16879f3b1d2de5 Mon Sep 17 00:00:00 2001 From: beo3000 Date: Tue, 16 Dec 2025 21:15:20 +0100 Subject: [PATCH] refactoring project split --- Koogle.Application/Class1.cs | 7 - Koogle.Application/DTOs/DayDto.cs | 59 ++++++++ Koogle.Application/DTOs/PagedResultDto.cs | 50 +++++++ Koogle.Application/DependencyInjection.cs | 36 +++++ Koogle.Application/Interfaces/IDayService.cs | 14 ++ Koogle.Application/Koogle.Application.csproj | 9 ++ Koogle.Application/Services/DayService.cs | 135 ++++++++++++++++++ Koogle.Infrastrcuture/DependencyInjection.cs | 32 ++++- .../ReducerTests/ThrowPanelStateTests.cs | 14 +- KoogleApp/Program.cs | 8 +- KoogleApp/Services/ServiceExtension.cs | 22 --- 11 files changed, 347 insertions(+), 39 deletions(-) delete mode 100644 Koogle.Application/Class1.cs create mode 100644 Koogle.Application/DTOs/DayDto.cs create mode 100644 Koogle.Application/DTOs/PagedResultDto.cs create mode 100644 Koogle.Application/DependencyInjection.cs create mode 100644 Koogle.Application/Interfaces/IDayService.cs create mode 100644 Koogle.Application/Services/DayService.cs diff --git a/Koogle.Application/Class1.cs b/Koogle.Application/Class1.cs deleted file mode 100644 index ebefdfc..0000000 --- a/Koogle.Application/Class1.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Koogle.Application -{ - public class Class1 - { - - } -} diff --git a/Koogle.Application/DTOs/DayDto.cs b/Koogle.Application/DTOs/DayDto.cs new file mode 100644 index 0000000..883fa30 --- /dev/null +++ b/Koogle.Application/DTOs/DayDto.cs @@ -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; } + + ///// + ///// Sortierfeld. + ///// + //public GoalSortField SortBy { get; init; } = GoalSortField.Name; + + ///// + ///// Sortierrichtung. + ///// + //public SortDirection SortDirection { get; init; } = SortDirection.Ascending; + + // Paginierung + + /// + /// Seitennummer (1-basiert). + /// + public int Page { get; init; } = 1; + + /// + /// Anzahl der Einträge pro Seite. + /// + public int PageSize { get; init; } = 20; + + /// + /// Berechnet den Skip-Wert für die Paginierung. + /// + public int Skip => (Page - 1) * PageSize; + } +} diff --git a/Koogle.Application/DTOs/PagedResultDto.cs b/Koogle.Application/DTOs/PagedResultDto.cs new file mode 100644 index 0000000..5003d01 --- /dev/null +++ b/Koogle.Application/DTOs/PagedResultDto.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Koogle.Application.DTOs +{ + /// + /// Ergebnis einer paginierten Abfrage. + /// + /// Typ der Ergebnisse. + public record PagedResultDto + { + /// + /// Die Ergebnisse der aktuellen Seite. + /// + public IReadOnlyList Items { get; init; } = []; + + /// + /// Gesamtanzahl der Ergebnisse (über alle Seiten). + /// + public int TotalCount { get; init; } + + /// + /// Aktuelle Seitennummer. + /// + public int Page { get; init; } + + /// + /// Anzahl der Einträge pro Seite. + /// + public int PageSize { get; init; } + + /// + /// Gesamtanzahl der Seiten. + /// + public int TotalPages => PageSize > 0 ? (int)Math.Ceiling((double)TotalCount / PageSize) : 0; + + /// + /// Gibt an, ob es eine vorherige Seite gibt. + /// + public bool HasPreviousPage => Page > 1; + + /// + /// Gibt an, ob es eine nächste Seite gibt. + /// + public bool HasNextPage => Page < TotalPages; + } +} diff --git a/Koogle.Application/DependencyInjection.cs b/Koogle.Application/DependencyInjection.cs new file mode 100644 index 0000000..93da18e --- /dev/null +++ b/Koogle.Application/DependencyInjection.cs @@ -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(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + //services.AddScoped(); + + return services; + } + } +} diff --git a/Koogle.Application/Interfaces/IDayService.cs b/Koogle.Application/Interfaces/IDayService.cs new file mode 100644 index 0000000..2300572 --- /dev/null +++ b/Koogle.Application/Interfaces/IDayService.cs @@ -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> GetAllAsync(DayFilterDto filter, CancellationToken cancellationToken = default); + } +} diff --git a/Koogle.Application/Koogle.Application.csproj b/Koogle.Application/Koogle.Application.csproj index 125f4c9..5a2d68f 100644 --- a/Koogle.Application/Koogle.Application.csproj +++ b/Koogle.Application/Koogle.Application.csproj @@ -6,4 +6,13 @@ enable + + + + + + + + + diff --git a/Koogle.Application/Services/DayService.cs b/Koogle.Application/Services/DayService.cs new file mode 100644 index 0000000..a43a6f3 --- /dev/null +++ b/Koogle.Application/Services/DayService.cs @@ -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 _contextFactory; + private readonly IDayRepository _goalRepository; + private readonly ICurrentUserService _currentUserService; + private readonly IAuthorizationService _authorizationService; + private readonly IMapper _mapper; + + public DayService(IDbContextFactory contextFactory, + IDayRepository goalRepository, + IMapper mapper) + { + + } + public async Task> 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>(goals); + + return new PagedResultDto + { + Items = items, + TotalCount = totalCount, + Page = filter.Page, + PageSize = filter.PageSize + }; + } + + /// + /// Wendet Filter auf die Abfrage an. + /// + private static IQueryable ApplyFilters(IQueryable 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; + } + } +} diff --git a/Koogle.Infrastrcuture/DependencyInjection.cs b/Koogle.Infrastrcuture/DependencyInjection.cs index e6c91f1..0e9077f 100644 --- a/Koogle.Infrastrcuture/DependencyInjection.cs +++ b/Koogle.Infrastrcuture/DependencyInjection.cs @@ -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(options => + // options.UseSqlServer(connectionString)); + + services.AddDatabaseDeveloperPageExceptionFilter(); + + services.AddIdentityCore(options => options.SignIn.RequireConfirmedAccount = true) + .AddEntityFrameworkStores() + .AddSignInManager() + .AddDefaultTokenProviders(); + + services.AddDbContextFactory(options => + options.UseSqlServer(connectionString) + ); + + return services; + } } } diff --git a/Koogle.Tests/ReducerTests/ThrowPanelStateTests.cs b/Koogle.Tests/ReducerTests/ThrowPanelStateTests.cs index b29293b..d499131 100644 --- a/Koogle.Tests/ReducerTests/ThrowPanelStateTests.cs +++ b/Koogle.Tests/ReducerTests/ThrowPanelStateTests.cs @@ -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); diff --git a/KoogleApp/Program.cs b/KoogleApp/Program.cs index e6a93a3..cacf5f4 100644 --- a/KoogleApp/Program.cs +++ b/KoogleApp/Program.cs @@ -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, IdentityNoOpEmailSender>(); diff --git a/KoogleApp/Services/ServiceExtension.cs b/KoogleApp/Services/ServiceExtension.cs index e57737f..4d8c500 100644 --- a/KoogleApp/Services/ServiceExtension.cs +++ b/KoogleApp/Services/ServiceExtension.cs @@ -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(options => - // options.UseSqlServer(connectionString)); - - services.AddDatabaseDeveloperPageExceptionFilter(); - - services.AddIdentityCore(options => options.SignIn.RequireConfirmedAccount = true) - .AddEntityFrameworkStores() - .AddSignInManager() - .AddDefaultTokenProviders(); - - services.AddDbContextFactory(options => - options.UseSqlServer(connectionString) - ); - - - return services; - } - public static IServiceCollection AddAppServices(this IServiceCollection services) { services.AddFluxor(x =>