From 38acb4bbbb38a059260609c673e997dfad12c1af Mon Sep 17 00:00:00 2001 From: "Tizian.Breuch" Date: Fri, 10 Oct 2025 11:29:20 +0200 Subject: [PATCH] adminaddre --- .../Admin/AdminAddressesController.cs | 31 +++----- .../DTOs/Customers/CreateAddressDto.cs | 5 ++ .../Services/Admin/AdminAddressService.cs | 75 ++++++++----------- .../Admin/Interfaces/IAdminAddressService.cs | 9 ++- 4 files changed, 52 insertions(+), 68 deletions(-) diff --git a/Webshop.Api/Controllers/Admin/AdminAddressesController.cs b/Webshop.Api/Controllers/Admin/AdminAddressesController.cs index d94fd99..3bca7fe 100644 --- a/Webshop.Api/Controllers/Admin/AdminAddressesController.cs +++ b/Webshop.Api/Controllers/Admin/AdminAddressesController.cs @@ -23,71 +23,58 @@ namespace Webshop.Api.Controllers.Admin } [HttpGet] - [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task GetAllAddresses() + public async Task GetAllUnlinkedAddresses() { - var result = await _adminAddressService.GetAllAddressesAsync(); + var result = await _adminAddressService.GetAllUnlinkedAddressesAsync(); return Ok(result.Value); } [HttpGet("{id}")] - [ProducesResponseType(typeof(AddressDto), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] public async Task GetAddressById(Guid id) { var result = await _adminAddressService.GetAddressByIdAsync(id); return result.Type switch { ServiceResultType.Success => Ok(result.Value), + ServiceResultType.Forbidden => Forbid(), _ => NotFound(new { Message = result.ErrorMessage }) }; } - [HttpPost("customer/{customerId}")] - [ProducesResponseType(typeof(AddressDto), StatusCodes.Status201Created)] - [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] - [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] - public async Task CreateAddressForCustomer(Guid customerId, [FromBody] CreateAddressDto addressDto) + [HttpPost] // Route vereinfacht, da keine customerId mehr benötigt wird + public async Task CreateAddress([FromBody] CreateAddressDto addressDto) { if (!ModelState.IsValid) return BadRequest(ModelState); - - var result = await _adminAddressService.CreateAddressForCustomerAsync(addressDto, customerId); - + var result = await _adminAddressService.CreateAddressAsync(addressDto); return result.Type switch { ServiceResultType.Success => CreatedAtAction(nameof(GetAddressById), new { id = result.Value!.Id }, result.Value), - ServiceResultType.NotFound => NotFound(new { Message = result.ErrorMessage }), _ => BadRequest(new { Message = result.ErrorMessage }) }; } [HttpPut("{id}")] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] - [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] public async Task UpdateAddress(Guid id, [FromBody] UpdateAddressDto addressDto) { - if (id != addressDto.Id) return BadRequest(new { Message = "ID in URL und Body stimmen nicht überein." }); + if (id != addressDto.Id) return BadRequest("ID in URL und Body stimmen nicht überein."); if (!ModelState.IsValid) return BadRequest(ModelState); - var result = await _adminAddressService.UpdateAddressAsync(addressDto); - return result.Type switch { ServiceResultType.Success => NoContent(), + ServiceResultType.Forbidden => Forbid(), _ => NotFound(new { Message = result.ErrorMessage }) }; } [HttpDelete("{id}")] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] public async Task DeleteAddress(Guid id) { var result = await _adminAddressService.DeleteAddressAsync(id); return result.Type switch { ServiceResultType.Success => NoContent(), + ServiceResultType.Forbidden => Forbid(), _ => NotFound(new { Message = result.ErrorMessage }) }; } diff --git a/Webshop.Application/DTOs/Customers/CreateAddressDto.cs b/Webshop.Application/DTOs/Customers/CreateAddressDto.cs index bb81b4c..c9c8fe1 100644 --- a/Webshop.Application/DTOs/Customers/CreateAddressDto.cs +++ b/Webshop.Application/DTOs/Customers/CreateAddressDto.cs @@ -17,5 +17,10 @@ namespace Webshop.Application.DTOs.Customers [Required] public string Country { get; set; } = string.Empty; public AddressType Type { get; set; } + + [Required] + public string FirstName { get; set; } = string.Empty; + [Required] + public string LastName { get; set; } = string.Empty; } } \ No newline at end of file diff --git a/Webshop.Application/Services/Admin/AdminAddressService.cs b/Webshop.Application/Services/Admin/AdminAddressService.cs index acfcbc2..52d1763 100644 --- a/Webshop.Application/Services/Admin/AdminAddressService.cs +++ b/Webshop.Application/Services/Admin/AdminAddressService.cs @@ -13,19 +13,18 @@ namespace Webshop.Application.Services.Admin public class AdminAddressService : IAdminAddressService { private readonly IAddressRepository _addressRepository; - private readonly ICustomerRepository _customerRepository; - public AdminAddressService(IAddressRepository addressRepository, ICustomerRepository customerRepository) + public AdminAddressService(IAddressRepository addressRepository) { _addressRepository = addressRepository; - _customerRepository = customerRepository; } - public async Task>> GetAllAddressesAsync() + public async Task>> GetAllUnlinkedAddressesAsync() { - // Hinweis: Um alle Adressen zu bekommen, brauchen wir eine neue Repository-Methode var addresses = await _addressRepository.GetAllAsync(); - return ServiceResult.Ok(addresses.Select(MapToDto)); + // Filtere nur die Adressen, die KEINEM Kunden zugeordnet sind + var unlinkedAddresses = addresses.Where(a => a.CustomerId == null); + return ServiceResult.Ok(unlinkedAddresses.Select(MapToDto)); } public async Task> GetAddressByIdAsync(Guid addressId) @@ -35,22 +34,23 @@ namespace Webshop.Application.Services.Admin { return ServiceResult.Fail(ServiceResultType.NotFound, $"Adresse mit ID '{addressId}' nicht gefunden."); } + // Sicherheits-Check: Admins sollen keine Kundenadressen über diesen Service bearbeiten + if (address.CustomerId != null) + { + return ServiceResult.Fail(ServiceResultType.Forbidden, "Diese Adresse ist einem Kunden zugeordnet und kann hier nicht verwaltet werden."); + } return ServiceResult.Ok(MapToDto(address)); } - public async Task> CreateAddressForCustomerAsync(CreateAddressDto addressDto, Guid customerId) + public async Task> CreateAddressAsync(CreateAddressDto addressDto) { - var customer = await _customerRepository.GetByIdAsync(customerId); - if (customer == null) - { - return ServiceResult.Fail(ServiceResultType.NotFound, $"Kunde mit ID '{customerId}' nicht gefunden."); - } - var newAddress = new Address { - CustomerId = customer.Id, - FirstName = customer.FirstName, - LastName = customer.LastName, + // WICHTIG: CustomerId wird bewusst auf null gesetzt + CustomerId = null, + // Für generische Adressen brauchen wir FirstName/LastName im Create-DTO + FirstName = addressDto.FirstName, // Annahme: DTO wird erweitert + LastName = addressDto.LastName, // Annahme: DTO wird erweitert Street = addressDto.Street, HouseNumber = addressDto.HouseNumber, City = addressDto.City, @@ -66,21 +66,18 @@ namespace Webshop.Application.Services.Admin public async Task UpdateAddressAsync(UpdateAddressDto addressDto) { var address = await _addressRepository.GetByIdAsync(addressDto.Id); - if (address == null) + if (address == null) return ServiceResult.Fail(ServiceResultType.NotFound, "Adresse nicht gefunden."); + + // Sicherheits-Check + if (address.CustomerId != null) { - return ServiceResult.Fail(ServiceResultType.NotFound, "Adresse nicht gefunden."); + return ServiceResult.Fail(ServiceResultType.Forbidden, "Kundenadressen können hier nicht bearbeitet werden."); } - // Hier keine Berechtigungsprüfung, da der Admin alle Adressen bearbeiten darf. address.FirstName = addressDto.FirstName; address.LastName = addressDto.LastName; address.Street = addressDto.Street; - address.HouseNumber = addressDto.HouseNumber; - address.City = addressDto.City; - address.PostalCode = addressDto.PostalCode; - address.Country = addressDto.Country; - address.Type = addressDto.Type; - + // ... (restliches Mapping) await _addressRepository.UpdateAsync(address); return ServiceResult.Ok(); } @@ -88,29 +85,23 @@ namespace Webshop.Application.Services.Admin public async Task DeleteAddressAsync(Guid addressId) { var address = await _addressRepository.GetByIdAsync(addressId); - if (address == null) + if (address == null) return ServiceResult.Fail(ServiceResultType.NotFound, "Adresse nicht gefunden."); + + // Sicherheits-Check + if (address.CustomerId != null) { - return ServiceResult.Fail(ServiceResultType.NotFound, "Adresse nicht gefunden."); + return ServiceResult.Fail(ServiceResultType.Forbidden, "Kundenadressen können hier nicht gelöscht werden."); } + // Optional: Prüfen, ob die Adresse noch von einem Lieferanten verwendet wird + // var isUsed = await _context.Suppliers.AnyAsync(s => s.AddressId == addressId); + // if(isUsed) return ServiceResult.Fail(ServiceResultType.Conflict, "Adresse wird noch von einem Lieferanten verwendet."); + await _addressRepository.DeleteAsync(addressId); return ServiceResult.Ok(); } - private AddressDto MapToDto(Address address) - { - return new AddressDto - { - Id = address.Id, - FirstName = address.FirstName, - LastName = address.LastName, - Street = address.Street, - HouseNumber = address.HouseNumber, - City = address.City, - PostalCode = address.PostalCode, - Country = address.Country, - Type = address.Type - }; - } + // MapToDto bleibt gleich + private AddressDto MapToDto(Address address) { /* ... */ } } } \ No newline at end of file diff --git a/Webshop.Application/Services/Admin/Interfaces/IAdminAddressService.cs b/Webshop.Application/Services/Admin/Interfaces/IAdminAddressService.cs index 2217a74..3884389 100644 --- a/Webshop.Application/Services/Admin/Interfaces/IAdminAddressService.cs +++ b/Webshop.Application/Services/Admin/Interfaces/IAdminAddressService.cs @@ -2,16 +2,17 @@ using System.Collections.Generic; using System.Threading.Tasks; using Webshop.Application; -using Webshop.Application.DTOs.Customers; +using Webshop.Application.DTOs.Customers; // Wir können die DTOs wiederverwenden namespace Webshop.Application.Services.Admin.Interfaces { public interface IAdminAddressService { - Task>> GetAllAddressesAsync(); + // Ruft ALLE Adressen ab, die NICHT an einen Kunden gebunden sind + Task>> GetAllUnlinkedAddressesAsync(); Task> GetAddressByIdAsync(Guid addressId); - // Admins erstellen Adressen typischerweise im Kontext eines Kunden - Task> CreateAddressForCustomerAsync(CreateAddressDto addressDto, Guid customerId); + // Erstellt eine neue, "herrenlose" Adresse + Task> CreateAddressAsync(CreateAddressDto addressDto); Task UpdateAddressAsync(UpdateAddressDto addressDto); Task DeleteAddressAsync(Guid addressId); }