From 37dd4f2db17ea5bb9fe2c4b4475553be709b3632 Mon Sep 17 00:00:00 2001 From: "Tizian.Breuch" Date: Fri, 8 Aug 2025 16:00:42 +0200 Subject: [PATCH] bild update --- .../Admin/AdminProductsController.cs | 18 ++++-- .../DTOs/Products/CreateAdminProductDto.cs | 8 +-- .../DTOs/Products/UpdateAdminProductDto.cs | 9 +-- .../Services/Admin/AdminProductService.cs | 64 +++++++++++++------ 4 files changed, 65 insertions(+), 34 deletions(-) diff --git a/Webshop.Api/Controllers/Admin/AdminProductsController.cs b/Webshop.Api/Controllers/Admin/AdminProductsController.cs index d2514d9..894ece5 100644 --- a/Webshop.Api/Controllers/Admin/AdminProductsController.cs +++ b/Webshop.Api/Controllers/Admin/AdminProductsController.cs @@ -1,13 +1,12 @@ // src/Webshop.Api/Controllers/Admin/AdminProductsController.cs using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; // Für IFormFile +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Threading.Tasks; using Webshop.Application.DTOs.Products; using Webshop.Application.Services.Admin.Interfaces; - namespace Webshop.Api.Controllers.Admin { [ApiController] @@ -38,20 +37,29 @@ namespace Webshop.Api.Controllers.Admin } [HttpPost] - public async Task> CreateAdminProduct([FromBody] CreateAdminProductDto productDto) // << ZURÜCK ZU [FromBody] >> + [Consumes("multipart/form-data")] + public async Task> CreateAdminProduct([FromForm] CreateAdminProductDto productDto) { if (!ModelState.IsValid) return BadRequest(ModelState); + var createdProduct = await _adminProductService.CreateAdminProductAsync(productDto); + if (createdProduct == null) return BadRequest("Produkt konnte nicht erstellt werden."); + return CreatedAtAction(nameof(GetAdminProduct), new { id = createdProduct.Id }, createdProduct); } + [HttpPut("{id}")] - public async Task UpdateAdminProduct(Guid id, [FromBody] UpdateAdminProductDto productDto) // << ZURÜCK ZU [FromBody] >> + [Consumes("multipart/form-data")] + public async Task UpdateAdminProduct(Guid id, [FromForm] UpdateAdminProductDto productDto) { - if (id != productDto.Id) return BadRequest(); + if (id != productDto.Id) return BadRequest("ID in URL und Body stimmen nicht überein."); if (!ModelState.IsValid) return BadRequest(ModelState); + var success = await _adminProductService.UpdateAdminProductAsync(productDto); + if (!success) return NotFound(); + return NoContent(); } diff --git a/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs b/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs index e51460a..8949a8e 100644 --- a/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs +++ b/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs @@ -1,4 +1,5 @@ // src/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs +using Microsoft.AspNetCore.Http; // FÜR IFormFile using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -15,14 +16,13 @@ namespace Webshop.Application.DTOs.Products [Required] public decimal Price { get; set; } public bool IsActive { get; set; } = true; - public bool IsInStock { get; set; } = true; public int StockQuantity { get; set; } [Required] public string Slug { get; set; } - // << KORREKTUR: Felder für Bild-URLs, keine IFormFile >> - public string? MainImageUrl { get; set; } - public List? AdditionalImageUrls { get; set; } + // << ZURÜCK ZU IFormFile >> + public IFormFile? MainImageFile { get; set; } + public List? AdditionalImageFiles { get; set; } public List CategorieIds { get; set; } = new List(); diff --git a/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs b/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs index d56f18d..51c7cba 100644 --- a/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs +++ b/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs @@ -1,4 +1,5 @@ // src/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs +using Microsoft.AspNetCore.Http; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -17,14 +18,14 @@ namespace Webshop.Application.DTOs.Products [Required] public decimal Price { get; set; } public bool IsActive { get; set; } - public bool IsInStock { get; set; } public int StockQuantity { get; set; } [Required] public string Slug { get; set; } - // << KORREKTUR: Felder für Bild-URLs >> - public string? MainImageUrl { get; set; } - public List? AdditionalImageUrls { get; set; } + // << ZURÜCK ZU IFormFile >> + public IFormFile? MainImageFile { get; set; } + public List? AdditionalImageFiles { get; set; } + public List? ImagesToDelete { get; set; } public List CategorieIds { get; set; } = new List(); diff --git a/Webshop.Application/Services/Admin/AdminProductService.cs b/Webshop.Application/Services/Admin/AdminProductService.cs index 56bc618..0764b89 100644 --- a/Webshop.Application/Services/Admin/AdminProductService.cs +++ b/Webshop.Application/Services/Admin/AdminProductService.cs @@ -15,13 +15,16 @@ namespace Webshop.Application.Services.Admin public class AdminProductService : IAdminProductService { private readonly IProductRepository _productRepository; + private readonly IFileStorageService _fileStorageService; private readonly ApplicationDbContext _context; public AdminProductService( - IProductRepository productRepository, - ApplicationDbContext context) + IProductRepository productRepository, + IFileStorageService fileStorageService, + ApplicationDbContext context) { _productRepository = productRepository; + _fileStorageService = fileStorageService; _context = context; } @@ -102,15 +105,21 @@ namespace Webshop.Application.Services.Admin { var images = new List(); - if (!string.IsNullOrEmpty(productDto.MainImageUrl)) + // Hauptbild hochladen + if (productDto.MainImageFile != null) { - images.Add(new ProductImage { Url = productDto.MainImageUrl, IsMainImage = true, DisplayOrder = 1 }); + await using var stream = productDto.MainImageFile.OpenReadStream(); + var url = await _fileStorageService.SaveFileAsync(stream, productDto.MainImageFile.FileName, productDto.MainImageFile.ContentType); + images.Add(new ProductImage { Url = url, IsMainImage = true, DisplayOrder = 1 }); } - if (productDto.AdditionalImageUrls != null) + // Weitere Bilder hochladen + if (productDto.AdditionalImageFiles != null) { int order = 2; - foreach (var url in productDto.AdditionalImageUrls) + foreach (var file in productDto.AdditionalImageFiles) { + await using var stream = file.OpenReadStream(); + var url = await _fileStorageService.SaveFileAsync(stream, file.FileName, file.ContentType); images.Add(new ProductImage { Url = url, IsMainImage = false, DisplayOrder = order++ }); } } @@ -146,6 +155,34 @@ namespace Webshop.Application.Services.Admin if (existingProduct == null) return false; + // Bilder löschen + if (productDto.ImagesToDelete != null && productDto.ImagesToDelete.Any()) + { + var imagesToRemove = existingProduct.Images.Where(img => productDto.ImagesToDelete.Contains(img.Id)).ToList(); + _context.ProductImages.RemoveRange(imagesToRemove); + } + // Hauptbild aktualisieren/hochladen + if (productDto.MainImageFile != null) + { + var existingMainImage = existingProduct.Images.FirstOrDefault(img => img.IsMainImage); + if (existingMainImage != null) _context.ProductImages.Remove(existingMainImage); + + await using var stream = productDto.MainImageFile.OpenReadStream(); + var url = await _fileStorageService.SaveFileAsync(stream, productDto.MainImageFile.FileName, productDto.MainImageFile.ContentType); + existingProduct.Images.Add(new ProductImage { Url = url, IsMainImage = true, DisplayOrder = 1 }); + } + // Weitere Bilder hinzufügen + if (productDto.AdditionalImageFiles != null && productDto.AdditionalImageFiles.Any()) + { + int displayOrder = (existingProduct.Images.Any() ? existingProduct.Images.Max(i => i.DisplayOrder) : 0) + 1; + foreach (var file in productDto.AdditionalImageFiles) + { + await using var stream = file.OpenReadStream(); + var url = await _fileStorageService.SaveFileAsync(stream, file.FileName, file.ContentType); + existingProduct.Images.Add(new ProductImage { Url = url, IsMainImage = false, DisplayOrder = displayOrder++ }); + } + } + // Basisdaten aktualisieren existingProduct.Name = productDto.Name; existingProduct.Description = productDto.Description; @@ -170,21 +207,6 @@ namespace Webshop.Application.Services.Admin } } - // Bilder synchronisieren - existingProduct.Images.Clear(); - if (!string.IsNullOrEmpty(productDto.MainImageUrl)) - { - existingProduct.Images.Add(new ProductImage { Url = productDto.MainImageUrl, IsMainImage = true, DisplayOrder = 1 }); - } - if (productDto.AdditionalImageUrls != null) - { - int order = 2; - foreach (var url in productDto.AdditionalImageUrls) - { - existingProduct.Images.Add(new ProductImage { Url = url, IsMainImage = false, DisplayOrder = order++ }); - } - } - await _productRepository.UpdateProductAsync(existingProduct); return true; }