adminaddre

This commit is contained in:
Tizian.Breuch
2025-10-10 11:29:20 +02:00
parent 48617f983a
commit 38acb4bbbb
4 changed files with 52 additions and 68 deletions

View File

@@ -23,71 +23,58 @@ namespace Webshop.Api.Controllers.Admin
} }
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(IEnumerable<AddressDto>), StatusCodes.Status200OK)] public async Task<IActionResult> GetAllUnlinkedAddresses()
public async Task<IActionResult> GetAllAddresses()
{ {
var result = await _adminAddressService.GetAllAddressesAsync(); var result = await _adminAddressService.GetAllUnlinkedAddressesAsync();
return Ok(result.Value); return Ok(result.Value);
} }
[HttpGet("{id}")] [HttpGet("{id}")]
[ProducesResponseType(typeof(AddressDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetAddressById(Guid id) public async Task<IActionResult> GetAddressById(Guid id)
{ {
var result = await _adminAddressService.GetAddressByIdAsync(id); var result = await _adminAddressService.GetAddressByIdAsync(id);
return result.Type switch return result.Type switch
{ {
ServiceResultType.Success => Ok(result.Value), ServiceResultType.Success => Ok(result.Value),
ServiceResultType.Forbidden => Forbid(),
_ => NotFound(new { Message = result.ErrorMessage }) _ => NotFound(new { Message = result.ErrorMessage })
}; };
} }
[HttpPost("customer/{customerId}")] [HttpPost] // Route vereinfacht, da keine customerId mehr benötigt wird
[ProducesResponseType(typeof(AddressDto), StatusCodes.Status201Created)] public async Task<IActionResult> CreateAddress([FromBody] CreateAddressDto addressDto)
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> CreateAddressForCustomer(Guid customerId, [FromBody] CreateAddressDto addressDto)
{ {
if (!ModelState.IsValid) return BadRequest(ModelState); if (!ModelState.IsValid) return BadRequest(ModelState);
var result = await _adminAddressService.CreateAddressAsync(addressDto);
var result = await _adminAddressService.CreateAddressForCustomerAsync(addressDto, customerId);
return result.Type switch return result.Type switch
{ {
ServiceResultType.Success => CreatedAtAction(nameof(GetAddressById), new { id = result.Value!.Id }, result.Value), ServiceResultType.Success => CreatedAtAction(nameof(GetAddressById), new { id = result.Value!.Id }, result.Value),
ServiceResultType.NotFound => NotFound(new { Message = result.ErrorMessage }),
_ => BadRequest(new { Message = result.ErrorMessage }) _ => BadRequest(new { Message = result.ErrorMessage })
}; };
} }
[HttpPut("{id}")] [HttpPut("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> UpdateAddress(Guid id, [FromBody] UpdateAddressDto addressDto) public async Task<IActionResult> 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); if (!ModelState.IsValid) return BadRequest(ModelState);
var result = await _adminAddressService.UpdateAddressAsync(addressDto); var result = await _adminAddressService.UpdateAddressAsync(addressDto);
return result.Type switch return result.Type switch
{ {
ServiceResultType.Success => NoContent(), ServiceResultType.Success => NoContent(),
ServiceResultType.Forbidden => Forbid(),
_ => NotFound(new { Message = result.ErrorMessage }) _ => NotFound(new { Message = result.ErrorMessage })
}; };
} }
[HttpDelete("{id}")] [HttpDelete("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> DeleteAddress(Guid id) public async Task<IActionResult> DeleteAddress(Guid id)
{ {
var result = await _adminAddressService.DeleteAddressAsync(id); var result = await _adminAddressService.DeleteAddressAsync(id);
return result.Type switch return result.Type switch
{ {
ServiceResultType.Success => NoContent(), ServiceResultType.Success => NoContent(),
ServiceResultType.Forbidden => Forbid(),
_ => NotFound(new { Message = result.ErrorMessage }) _ => NotFound(new { Message = result.ErrorMessage })
}; };
} }

View File

@@ -17,5 +17,10 @@ namespace Webshop.Application.DTOs.Customers
[Required] [Required]
public string Country { get; set; } = string.Empty; public string Country { get; set; } = string.Empty;
public AddressType Type { get; set; } public AddressType Type { get; set; }
[Required]
public string FirstName { get; set; } = string.Empty;
[Required]
public string LastName { get; set; } = string.Empty;
} }
} }

View File

@@ -13,19 +13,18 @@ namespace Webshop.Application.Services.Admin
public class AdminAddressService : IAdminAddressService public class AdminAddressService : IAdminAddressService
{ {
private readonly IAddressRepository _addressRepository; private readonly IAddressRepository _addressRepository;
private readonly ICustomerRepository _customerRepository;
public AdminAddressService(IAddressRepository addressRepository, ICustomerRepository customerRepository) public AdminAddressService(IAddressRepository addressRepository)
{ {
_addressRepository = addressRepository; _addressRepository = addressRepository;
_customerRepository = customerRepository;
} }
public async Task<ServiceResult<IEnumerable<AddressDto>>> GetAllAddressesAsync() public async Task<ServiceResult<IEnumerable<AddressDto>>> GetAllUnlinkedAddressesAsync()
{ {
// Hinweis: Um alle Adressen zu bekommen, brauchen wir eine neue Repository-Methode
var addresses = await _addressRepository.GetAllAsync(); 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<ServiceResult<AddressDto>> GetAddressByIdAsync(Guid addressId) public async Task<ServiceResult<AddressDto>> GetAddressByIdAsync(Guid addressId)
@@ -35,22 +34,23 @@ namespace Webshop.Application.Services.Admin
{ {
return ServiceResult.Fail<AddressDto>(ServiceResultType.NotFound, $"Adresse mit ID '{addressId}' nicht gefunden."); return ServiceResult.Fail<AddressDto>(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<AddressDto>(ServiceResultType.Forbidden, "Diese Adresse ist einem Kunden zugeordnet und kann hier nicht verwaltet werden.");
}
return ServiceResult.Ok(MapToDto(address)); return ServiceResult.Ok(MapToDto(address));
} }
public async Task<ServiceResult<AddressDto>> CreateAddressForCustomerAsync(CreateAddressDto addressDto, Guid customerId) public async Task<ServiceResult<AddressDto>> CreateAddressAsync(CreateAddressDto addressDto)
{ {
var customer = await _customerRepository.GetByIdAsync(customerId);
if (customer == null)
{
return ServiceResult.Fail<AddressDto>(ServiceResultType.NotFound, $"Kunde mit ID '{customerId}' nicht gefunden.");
}
var newAddress = new Address var newAddress = new Address
{ {
CustomerId = customer.Id, // WICHTIG: CustomerId wird bewusst auf null gesetzt
FirstName = customer.FirstName, CustomerId = null,
LastName = customer.LastName, // 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, Street = addressDto.Street,
HouseNumber = addressDto.HouseNumber, HouseNumber = addressDto.HouseNumber,
City = addressDto.City, City = addressDto.City,
@@ -66,21 +66,18 @@ namespace Webshop.Application.Services.Admin
public async Task<ServiceResult> UpdateAddressAsync(UpdateAddressDto addressDto) public async Task<ServiceResult> UpdateAddressAsync(UpdateAddressDto addressDto)
{ {
var address = await _addressRepository.GetByIdAsync(addressDto.Id); 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.FirstName = addressDto.FirstName;
address.LastName = addressDto.LastName; address.LastName = addressDto.LastName;
address.Street = addressDto.Street; address.Street = addressDto.Street;
address.HouseNumber = addressDto.HouseNumber; // ... (restliches Mapping)
address.City = addressDto.City;
address.PostalCode = addressDto.PostalCode;
address.Country = addressDto.Country;
address.Type = addressDto.Type;
await _addressRepository.UpdateAsync(address); await _addressRepository.UpdateAsync(address);
return ServiceResult.Ok(); return ServiceResult.Ok();
} }
@@ -88,29 +85,23 @@ namespace Webshop.Application.Services.Admin
public async Task<ServiceResult> DeleteAddressAsync(Guid addressId) public async Task<ServiceResult> DeleteAddressAsync(Guid addressId)
{ {
var address = await _addressRepository.GetByIdAsync(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); await _addressRepository.DeleteAsync(addressId);
return ServiceResult.Ok(); return ServiceResult.Ok();
} }
private AddressDto MapToDto(Address address) // MapToDto bleibt gleich
{ 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
};
}
} }
} }

View File

@@ -2,16 +2,17 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Webshop.Application; 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 namespace Webshop.Application.Services.Admin.Interfaces
{ {
public interface IAdminAddressService public interface IAdminAddressService
{ {
Task<ServiceResult<IEnumerable<AddressDto>>> GetAllAddressesAsync(); // Ruft ALLE Adressen ab, die NICHT an einen Kunden gebunden sind
Task<ServiceResult<IEnumerable<AddressDto>>> GetAllUnlinkedAddressesAsync();
Task<ServiceResult<AddressDto>> GetAddressByIdAsync(Guid addressId); Task<ServiceResult<AddressDto>> GetAddressByIdAsync(Guid addressId);
// Admins erstellen Adressen typischerweise im Kontext eines Kunden // Erstellt eine neue, "herrenlose" Adresse
Task<ServiceResult<AddressDto>> CreateAddressForCustomerAsync(CreateAddressDto addressDto, Guid customerId); Task<ServiceResult<AddressDto>> CreateAddressAsync(CreateAddressDto addressDto);
Task<ServiceResult> UpdateAddressAsync(UpdateAddressDto addressDto); Task<ServiceResult> UpdateAddressAsync(UpdateAddressDto addressDto);
Task<ServiceResult> DeleteAddressAsync(Guid addressId); Task<ServiceResult> DeleteAddressAsync(Guid addressId);
} }