test bilder erneut

This commit is contained in:
Tizian.Breuch
2025-08-08 15:50:01 +02:00
parent 562d720c4a
commit c2af53c5ce
7 changed files with 65 additions and 69 deletions

View File

@@ -1,12 +1,11 @@
// Auto-generiert von CreateWebshopFiles.ps1
using Microsoft.AspNetCore.Mvc;
// src/Webshop.Api/Controllers/Admin/AdminProductsController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; // F<>r IFormFile
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Webshop.Application.DTOs.Products;
using Microsoft.AspNetCore.Http;
using Webshop.Application.Services.Admin.Interfaces;
namespace Webshop.Api.Controllers.Admin
@@ -39,25 +38,15 @@ namespace Webshop.Api.Controllers.Admin
}
[HttpPost]
[Consumes("multipart/form-data")]
public async Task<ActionResult<AdminProductDto>> CreateAdminProduct([FromForm] CreateAdminProductDto productDto) // << NEU: [FromForm] und CreateAdminProductDto >>
public async Task<ActionResult<AdminProductDto>> CreateAdminProduct([FromBody] CreateAdminProductDto productDto) // << ZUR<55>CK ZU [FromBody] >>
{
if (!ModelState.IsValid) return BadRequest(ModelState);
var createdProduct = await _adminProductService.CreateAdminProductAsync(productDto);
if (createdProduct == null)
{
// Hier k<>nnte eine spezifischere Fehlermeldung vom Service kommen
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)
public async Task<IActionResult> UpdateAdminProduct(Guid id, [FromBody] UpdateAdminProductDto productDto) // << ZUR<55>CK ZU [FromBody] >>
{
if (id != productDto.Id) return BadRequest();
if (!ModelState.IsValid) return BadRequest(ModelState);
@@ -66,6 +55,7 @@ namespace Webshop.Api.Controllers.Admin
return NoContent();
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteAdminProduct(Guid id)
{

View File

@@ -23,6 +23,7 @@ using Webshop.Domain.Identity;
using Webshop.Domain.Interfaces;
using Webshop.Infrastructure.Data;
using Webshop.Infrastructure.Repositories;
using Webshop.Infrastructure.Services;
var builder = WebApplication.CreateBuilder(args);
@@ -108,6 +109,7 @@ builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.AddScoped<IAddressService, AddressService>();
builder.Services.AddScoped<ICheckoutService, CheckoutService>();
builder.Services.AddScoped<IReviewService, ReviewService>();
builder.Services.AddScoped<IFileStorageService, LocalFileStorageService>();
// Externe Dienste (Resend)

View File

@@ -1,5 +1,4 @@
// src/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs
using Microsoft.AspNetCore.Http; // Für IFormFile
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -21,13 +20,12 @@ namespace Webshop.Application.DTOs.Products
[Required]
public string Slug { get; set; }
// << NEU: Felder für den Bildupload >>
public IFormFile? MainImageFile { get; set; }
public List<IFormFile>? AdditionalImageFiles { get; set; }
// << KORREKTUR: Felder für Bild-URLs, keine IFormFile >>
public string? MainImageUrl { get; set; }
public List<string>? AdditionalImageUrls { get; set; }
public List<Guid> CategorieIds { get; set; } = new List<Guid>();
// ... weitere Felder, die beim Erstellen benötigt werden (z.B. SupplierId, PurchasePrice etc.) ...
public decimal? Weight { get; set; }
public decimal? OldPrice { get; set; }
public Guid? SupplierId { get; set; }

View File

@@ -1,5 +1,4 @@
// src/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -23,14 +22,12 @@ namespace Webshop.Application.DTOs.Products
[Required]
public string Slug { get; set; }
// << NEU: Felder für den Bildupload und -verwaltung >>
public IFormFile? MainImageFile { get; set; } // Optional: Neues Hauptbild hochladen
public List<IFormFile>? AdditionalImageFiles { get; set; } // Optional: Weitere Bilder hochladen
public List<Guid>? ImagesToDelete { get; set; } // Liste der IDs von Bildern, die gelöscht werden sollen
// << KORREKTUR: Felder für Bild-URLs >>
public string? MainImageUrl { get; set; }
public List<string>? AdditionalImageUrls { get; set; }
public List<Guid> CategorieIds { get; set; } = new List<Guid>();
// ... weitere Felder, die beim Aktualisieren benötigt werden ...
public decimal? Weight { get; set; }
public decimal? OldPrice { get; set; }
public Guid? SupplierId { get; set; }

View File

@@ -15,16 +15,13 @@ 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,
IFileStorageService fileStorageService,
ApplicationDbContext context)
{
_productRepository = productRepository;
_fileStorageService = fileStorageService;
_context = context;
}
@@ -33,6 +30,7 @@ namespace Webshop.Application.Services.Admin
var products = await _context.Products
.Include(p => p.Productcategories)
.Include(p => p.Images)
.OrderBy(p => p.Name)
.ToListAsync();
return products.Select(p => new AdminProductDto
@@ -53,7 +51,7 @@ namespace Webshop.Application.Services.Admin
SupplierId = p.SupplierId,
PurchasePrice = p.PurchasePrice,
categorieIds = p.Productcategories.Select(pc => pc.categorieId).ToList(),
Images = p.Images.Select(img => new ProductImageDto
Images = p.Images.OrderBy(i => i.DisplayOrder).Select(img => new ProductImageDto
{
Id = img.Id,
Url = img.Url,
@@ -90,7 +88,7 @@ namespace Webshop.Application.Services.Admin
SupplierId = product.SupplierId,
PurchasePrice = product.PurchasePrice,
categorieIds = product.Productcategories.Select(pc => pc.categorieId).ToList(),
Images = product.Images.Select(img => new ProductImageDto
Images = product.Images.OrderBy(i => i.DisplayOrder).Select(img => new ProductImageDto
{
Id = img.Id,
Url = img.Url,
@@ -100,23 +98,19 @@ namespace Webshop.Application.Services.Admin
};
}
public async Task<AdminProductDto> CreateAdminProductAsync(CreateAdminProductDto productDto)
public async Task<AdminProductDto?> CreateAdminProductAsync(CreateAdminProductDto productDto)
{
var images = new List<ProductImage>();
if (productDto.MainImageFile != null)
if (!string.IsNullOrEmpty(productDto.MainImageUrl))
{
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 });
images.Add(new ProductImage { Url = productDto.MainImageUrl, IsMainImage = true, DisplayOrder = 1 });
}
if (productDto.AdditionalImageFiles != null)
if (productDto.AdditionalImageUrls != null)
{
int order = 2;
foreach (var file in productDto.AdditionalImageFiles)
foreach (var url in productDto.AdditionalImageUrls)
{
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++ });
}
}
@@ -130,13 +124,17 @@ namespace Webshop.Application.Services.Admin
IsActive = productDto.IsActive,
StockQuantity = productDto.StockQuantity,
Slug = productDto.Slug,
Weight = productDto.Weight,
OldPrice = productDto.OldPrice,
SupplierId = productDto.SupplierId,
PurchasePrice = productDto.PurchasePrice,
Images = images,
Productcategories = productDto.CategorieIds.Select(cId => new Productcategorie { categorieId = cId }).ToList()
};
await _productRepository.AddProductAsync(newProduct);
return (await GetAdminProductByIdAsync(newProduct.Id))!;
return await GetAdminProductByIdAsync(newProduct.Id);
}
public async Task<bool> UpdateAdminProductAsync(UpdateAdminProductDto productDto)
@@ -148,34 +146,44 @@ namespace Webshop.Application.Services.Admin
if (existingProduct == null) return false;
if (productDto.ImagesToDelete != null)
{
var imagesToRemove = existingProduct.Images.Where(img => productDto.ImagesToDelete.Contains(img.Id)).ToList();
_context.ProductImages.RemoveRange(imagesToRemove);
}
if (productDto.MainImageFile != null)
{
var existingMainImage = existingProduct.Images.FirstOrDefault(img => img.IsMainImage);
if (existingMainImage != null) _context.ProductImages.Remove(existingMainImage);
// Basisdaten aktualisieren
existingProduct.Name = productDto.Name;
existingProduct.Description = productDto.Description;
existingProduct.SKU = productDto.SKU;
existingProduct.Price = productDto.Price;
existingProduct.IsActive = productDto.IsActive;
existingProduct.StockQuantity = productDto.StockQuantity;
existingProduct.Slug = productDto.Slug;
existingProduct.Weight = productDto.Weight;
existingProduct.OldPrice = productDto.OldPrice;
existingProduct.SupplierId = productDto.SupplierId;
existingProduct.PurchasePrice = productDto.PurchasePrice;
existingProduct.LastModifiedDate = DateTimeOffset.UtcNow;
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 });
}
if (productDto.AdditionalImageFiles != null)
// Kategorien synchronisieren
existingProduct.Productcategories.Clear();
if (productDto.CategorieIds != null)
{
int displayOrder = (existingProduct.Images.Any() ? existingProduct.Images.Max(i => i.DisplayOrder) : 0) + 1;
foreach (var file in productDto.AdditionalImageFiles)
foreach (var categorieId in productDto.CategorieIds)
{
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++ });
existingProduct.Productcategories.Add(new Productcategorie { categorieId = categorieId });
}
}
existingProduct.Name = productDto.Name;
existingProduct.Description = productDto.Description;
// ... (restliche Felder aktualisieren) ...
// 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;

View File

@@ -1,4 +1,5 @@
// src/Webshop.Application/Services/Admin/Interfaces/IAdminProductService.cs
// src/Webshop.Application/Services/Admin/IAdminProductService.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Webshop.Application.DTOs.Products;
@@ -9,8 +10,8 @@ namespace Webshop.Application.Services.Admin.Interfaces
{
Task<IEnumerable<AdminProductDto>> GetAllAdminProductsAsync();
Task<AdminProductDto?> GetAdminProductByIdAsync(Guid id);
Task<AdminProductDto> CreateAdminProductAsync(CreateAdminProductDto productDto); // << NEUER TYP >>
Task<bool> UpdateAdminProductAsync(UpdateAdminProductDto productDto); // << NEUER TYP >>
Task<AdminProductDto?> CreateAdminProductAsync(CreateAdminProductDto productDto); // << DTO-TYP GEÄNDERT >>
Task<bool> UpdateAdminProductAsync(UpdateAdminProductDto productDto); // << DTO-TYP GEÄNDERT >>
Task<bool> DeleteAdminProductAsync(Guid id);
}
}