bild update

This commit is contained in:
Tizian.Breuch
2025-08-08 16:00:42 +02:00
parent c2af53c5ce
commit 37dd4f2db1
4 changed files with 65 additions and 34 deletions

View File

@@ -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<ActionResult<AdminProductDto>> CreateAdminProduct([FromBody] CreateAdminProductDto productDto) // << ZUR<55>CK ZU [FromBody] >>
[Consumes("multipart/form-data")]
public async Task<ActionResult<AdminProductDto>> 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<IActionResult> UpdateAdminProduct(Guid id, [FromBody] UpdateAdminProductDto productDto) // << ZUR<55>CK ZU [FromBody] >>
[Consumes("multipart/form-data")]
public async Task<IActionResult> 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 <20>berein.");
if (!ModelState.IsValid) return BadRequest(ModelState);
var success = await _adminProductService.UpdateAdminProductAsync(productDto);
if (!success) return NotFound();
return NoContent();
}

View File

@@ -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<string>? AdditionalImageUrls { get; set; }
// << ZURÜCK ZU IFormFile >>
public IFormFile? MainImageFile { get; set; }
public List<IFormFile>? AdditionalImageFiles { get; set; }
public List<Guid> CategorieIds { get; set; } = new List<Guid>();

View File

@@ -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<string>? AdditionalImageUrls { get; set; }
// << ZURÜCK ZU IFormFile >>
public IFormFile? MainImageFile { get; set; }
public List<IFormFile>? AdditionalImageFiles { get; set; }
public List<Guid>? ImagesToDelete { get; set; }
public List<Guid> CategorieIds { get; set; } = new List<Guid>();

View File

@@ -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<ProductImage>();
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<75>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;
}