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]
[ProducesResponseType(typeof(IEnumerable<AddressDto>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetAllAddresses()
public async Task<IActionResult> 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<IActionResult> 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<IActionResult> CreateAddressForCustomer(Guid customerId, [FromBody] CreateAddressDto addressDto)
[HttpPost] // Route vereinfacht, da keine customerId mehr benötigt wird
public async Task<IActionResult> 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<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);
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<IActionResult> DeleteAddress(Guid id)
{
var result = await _adminAddressService.DeleteAddressAsync(id);
return result.Type switch
{
ServiceResultType.Success => NoContent(),
ServiceResultType.Forbidden => Forbid(),
_ => NotFound(new { Message = result.ErrorMessage })
};
}

View File

@@ -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;
}
}

View File

@@ -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<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();
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)
@@ -35,22 +34,23 @@ namespace Webshop.Application.Services.Admin
{
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));
}
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
{
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<ServiceResult> 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<ServiceResult> 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) { /* ... */ }
}
}

View File

@@ -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<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);
// Admins erstellen Adressen typischerweise im Kontext eines Kunden
Task<ServiceResult<AddressDto>> CreateAddressForCustomerAsync(CreateAddressDto addressDto, Guid customerId);
// Erstellt eine neue, "herrenlose" Adresse
Task<ServiceResult<AddressDto>> CreateAddressAsync(CreateAddressDto addressDto);
Task<ServiceResult> UpdateAddressAsync(UpdateAddressDto addressDto);
Task<ServiceResult> DeleteAddressAsync(Guid addressId);
}