From 007da919da20b4d5e5b3f65521194811821f8058 Mon Sep 17 00:00:00 2001 From: "Tizian.Breuch" Date: Thu, 25 Sep 2025 13:41:40 +0200 Subject: [PATCH] AdminCategorie --- .../Admin/AdminCategorieController.cs | 71 ++++++++++++++----- .../Services/Admin/AdminCategorieService.cs | 48 +++++++++---- .../Interfaces/IAdminCategorieService.cs | 10 +-- 3 files changed, 90 insertions(+), 39 deletions(-) diff --git a/Webshop.Api/Controllers/Admin/AdminCategorieController.cs b/Webshop.Api/Controllers/Admin/AdminCategorieController.cs index 646b403..8982e50 100644 --- a/Webshop.Api/Controllers/Admin/AdminCategorieController.cs +++ b/Webshop.Api/Controllers/Admin/AdminCategorieController.cs @@ -1,5 +1,6 @@ -// src/Webshop.Api/Controllers/Admin/AdmincategoriesController.cs +// src/Webshop.Api/Controllers/Admin/AdminCategoriesController.cs using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; @@ -23,42 +24,70 @@ namespace Webshop.Api.Controllers.Admin } [HttpGet] - public async Task>> GetAllcategories() + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetAllcategories() { - var categories = await _adminCategorieService.GetAllAsync(); - return Ok(categories); + var result = await _adminCategorieService.GetAllAsync(); + // Ein 'GetAll' schlägt im Normalfall nicht fehl (gibt leere Liste zurück), + // daher wird hier nur der Erfolgsfall mit dem Wert zurückgegeben. + return Ok(result.Value); } [HttpGet("{id}")] - public async Task> GetcategorieById(Guid id) + [ProducesResponseType(typeof(CategorieDto), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task GetcategorieById(Guid id) { - var categorie = await _adminCategorieService.GetByIdAsync(id); - if (categorie == null) return NotFound(); - return Ok(categorie); + var result = await _adminCategorieService.GetByIdAsync(id); + + return result.Type switch + { + ServiceResultType.Success => Ok(result.Value), + ServiceResultType.NotFound => NotFound(new { Message = result.ErrorMessage }), + _ => StatusCode(StatusCodes.Status500InternalServerError, new { Message = result.ErrorMessage ?? "Ein unerwarteter Fehler ist aufgetreten." }) + }; } [HttpPost] [Consumes("multipart/form-data")] - public async Task> CreateCategorie([FromForm] CreatecategorieDto categorieDto) + [ProducesResponseType(typeof(CategorieDto), StatusCodes.Status201Created)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)] + public async Task CreateCategorie([FromForm] CreatecategorieDto categorieDto) { - if (!ModelState.IsValid) return BadRequest(ModelState); + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } var result = await _adminCategorieService.CreateAsync(categorieDto); - if (result.Type == ServiceResultType.Success) + return result.Type switch { - return CreatedAtAction(nameof(GetcategorieById), new { id = result.Value!.Id }, result.Value); - } - - return BadRequest(new { Message = result.ErrorMessage }); + ServiceResultType.Success => CreatedAtAction(nameof(GetcategorieById), new { id = result.Value!.Id }, result.Value), + ServiceResultType.Conflict => Conflict(new { Message = result.ErrorMessage }), + ServiceResultType.InvalidInput => BadRequest(new { Message = result.ErrorMessage }), + _ => StatusCode(StatusCodes.Status500InternalServerError, new { Message = result.ErrorMessage ?? "Ein unerwarteter Fehler ist aufgetreten." }) + }; } [HttpPut("{id}")] [Consumes("multipart/form-data")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)] public async Task UpdateCategorie(Guid id, [FromForm] UpdatecategorieDto categorieDto) { - if (id != categorieDto.Id) return BadRequest("ID in URL und Body stimmen nicht überein."); - if (!ModelState.IsValid) return BadRequest(ModelState); + if (id != categorieDto.Id) + { + return BadRequest(new { Message = "ID in der URL und im Body stimmen nicht überein." }); + } + + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } var result = await _adminCategorieService.UpdateAsync(categorieDto); @@ -66,12 +95,16 @@ namespace Webshop.Api.Controllers.Admin { ServiceResultType.Success => NoContent(), ServiceResultType.NotFound => NotFound(new { Message = result.ErrorMessage }), + ServiceResultType.Conflict => Conflict(new { Message = result.ErrorMessage }), ServiceResultType.InvalidInput => BadRequest(new { Message = result.ErrorMessage }), - _ => StatusCode(500, "Ein unerwarteter Fehler ist aufgetreten.") + _ => StatusCode(StatusCodes.Status500InternalServerError, new { Message = result.ErrorMessage ?? "Ein unerwarteter Fehler ist aufgetreten." }) }; } [HttpDelete("{id}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)] public async Task DeleteCategorie(Guid id) { var result = await _adminCategorieService.DeleteAsync(id); @@ -81,7 +114,7 @@ namespace Webshop.Api.Controllers.Admin ServiceResultType.Success => NoContent(), ServiceResultType.NotFound => NotFound(new { Message = result.ErrorMessage }), ServiceResultType.Conflict => Conflict(new { Message = result.ErrorMessage }), - _ => StatusCode(500, "Ein unerwarteter Fehler ist aufgetreten.") + _ => StatusCode(StatusCodes.Status500InternalServerError, new { Message = result.ErrorMessage ?? "Ein unerwarteter Fehler ist aufgetreten." }) }; } } diff --git a/Webshop.Application/Services/Admin/AdminCategorieService.cs b/Webshop.Application/Services/Admin/AdminCategorieService.cs index 02d774e..6df6602 100644 --- a/Webshop.Application/Services/Admin/AdminCategorieService.cs +++ b/Webshop.Application/Services/Admin/AdminCategorieService.cs @@ -1,4 +1,4 @@ -// src/Webshop.Application/Services/Admin/Admincategorieservice.cs +// src/Webshop.Application/Services/Admin/AdminCategorieService.cs using System; using System.Collections.Generic; using System.Linq; @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Webshop.Application.DTOs.Categorie; using Webshop.Domain.Entities; using Webshop.Domain.Interfaces; -using Webshop.Application; // << NEU: Für ServiceResult >> +using Webshop.Application; namespace Webshop.Application.Services.Admin { @@ -21,16 +21,21 @@ namespace Webshop.Application.Services.Admin _fileStorageService = fileStorageService; } - public async Task> GetAllAsync() + public async Task>> GetAllAsync() { var categories = await _categorieRepository.GetAllAsync(); - return categories.Select(MapToDto).ToList(); + var dtos = categories.Select(MapToDto).ToList(); + return ServiceResult.Ok>(dtos); } - public async Task GetByIdAsync(Guid id) + public async Task> GetByIdAsync(Guid id) { var categorie = await _categorieRepository.GetByIdAsync(id); - return categorie != null ? MapToDto(categorie) : null; + if (categorie == null) + { + return ServiceResult.Fail(ServiceResultType.NotFound, $"Kategorie mit ID '{id}' nicht gefunden."); + } + return ServiceResult.Ok(MapToDto(categorie)); } public async Task> CreateAsync(CreatecategorieDto categorieDto) @@ -38,7 +43,7 @@ namespace Webshop.Application.Services.Admin var existing = await _categorieRepository.GetBySlugAsync(categorieDto.Slug); if (existing != null) { - return ServiceResult.Fail(ServiceResultType.InvalidInput, "Eine Kategorie mit diesem Slug existiert bereits."); + return ServiceResult.Fail(ServiceResultType.Conflict, "Eine Kategorie mit diesem Slug existiert bereits."); } string? imageUrl = null; @@ -62,19 +67,21 @@ namespace Webshop.Application.Services.Admin await _categorieRepository.AddAsync(categorie); - var createdDto = MapToDto(categorie); - return ServiceResult.Ok(createdDto); + return ServiceResult.Ok(MapToDto(categorie)); } public async Task UpdateAsync(UpdatecategorieDto categorieDto) { var existing = await _categorieRepository.GetByIdAsync(categorieDto.Id); - if (existing == null) return ServiceResult.Fail(ServiceResultType.NotFound, "Kategorie nicht gefunden."); + if (existing == null) + { + return ServiceResult.Fail(ServiceResultType.NotFound, $"Kategorie mit ID '{categorieDto.Id}' nicht gefunden."); + } var slugExists = await _categorieRepository.GetBySlugAsync(categorieDto.Slug); if (slugExists != null && slugExists.Id != categorieDto.Id) { - return ServiceResult.Fail(ServiceResultType.InvalidInput, "Eine andere Kategorie mit diesem Slug existiert bereits."); + return ServiceResult.Fail(ServiceResultType.Conflict, "Eine andere Kategorie mit diesem Slug existiert bereits."); } string? imageUrl = existing.ImageUrl; @@ -83,12 +90,12 @@ namespace Webshop.Application.Services.Admin await using var stream = categorieDto.ImageFile.OpenReadStream(); imageUrl = await _fileStorageService.SaveFileAsync(stream, categorieDto.ImageFile.FileName, categorieDto.ImageFile.ContentType); } - else if (string.IsNullOrEmpty(categorieDto.ImageUrl)) + else if (string.IsNullOrEmpty(categorieDto.ImageUrl) && !string.IsNullOrEmpty(existing.ImageUrl)) { + // Hier könnte Logik zum Löschen der alten Datei stehen, falls gewünscht imageUrl = null; } - existing.Name = categorieDto.Name; existing.Slug = categorieDto.Slug; existing.Description = categorieDto.Description; @@ -105,13 +112,24 @@ namespace Webshop.Application.Services.Admin public async Task DeleteAsync(Guid id) { var categorie = await _categorieRepository.GetByIdAsync(id); - if (categorie == null) return ServiceResult.Fail(ServiceResultType.NotFound, "Kategorie nicht gefunden."); + if (categorie == null) + { + return ServiceResult.Fail(ServiceResultType.NotFound, $"Kategorie mit ID '{id}' nicht gefunden."); + } + + // Prüfung auf Konflikt: Hat diese Kategorie untergeordnete Kategorien? + var allCategories = await _categorieRepository.GetAllAsync(); + if (allCategories.Any(c => c.ParentcategorieId == id)) + { + return ServiceResult.Fail(ServiceResultType.Conflict, "Kategorie kann nicht gelöscht werden, da sie als übergeordnete Kategorie für andere Kategorien dient."); + } + + // Hier könnte man auch prüfen, ob Produkte dieser Kategorie zugeordnet sind. await _categorieRepository.DeleteAsync(id); return ServiceResult.Ok(); } - // Private Helper-Methode für konsistentes Mapping private CategorieDto MapToDto(Categorie c) { return new CategorieDto diff --git a/Webshop.Application/Services/Admin/Interfaces/IAdminCategorieService.cs b/Webshop.Application/Services/Admin/Interfaces/IAdminCategorieService.cs index 88dc602..81213da 100644 --- a/Webshop.Application/Services/Admin/Interfaces/IAdminCategorieService.cs +++ b/Webshop.Application/Services/Admin/Interfaces/IAdminCategorieService.cs @@ -1,18 +1,18 @@ -// src/Webshop.Application/Services/Admin/IAdmincategorieservice.cs +// src/Webshop.Application/Services/Admin/IAdminCategorieService.cs using System; using System.Collections.Generic; using System.Threading.Tasks; -using Webshop.Application; // << HINZUFÜGEN >> +using Webshop.Application; using Webshop.Application.DTOs.Categorie; namespace Webshop.Application.Services.Admin { public interface IAdminCategorieService { - Task> GetAllAsync(); - Task GetByIdAsync(Guid id); + Task>> GetAllAsync(); + Task> GetByIdAsync(Guid id); Task> CreateAsync(CreatecategorieDto categorieDto); - Task UpdateAsync(UpdatecategorieDto categorieDto); // << DTO-TYP GEÄNDERT >> + Task UpdateAsync(UpdatecategorieDto categorieDto); Task DeleteAsync(Guid id); } } \ No newline at end of file