From dcbb450d23ee66fe4c2839a3cb95b652bb1b2c03 Mon Sep 17 00:00:00 2001 From: "Tizian.Breuch" Date: Fri, 7 Nov 2025 12:21:21 +0100 Subject: [PATCH] billldr --- .../DTOs/Products/UpdateAdminProductDto.cs | 1 + .../Services/Admin/AdminProductService.cs | 65 +++++++------------ 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs b/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs index d0d480b..cd534e8 100644 --- a/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs +++ b/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs @@ -36,5 +36,6 @@ namespace Webshop.Application.DTOs.Products public bool IsFeatured { get; set; } public int FeaturedDisplayOrder { get; set; } public uint RowVersion { get; set; } + public Guid? MainImageId { get; set; } } } diff --git a/Webshop.Application/Services/Admin/AdminProductService.cs b/Webshop.Application/Services/Admin/AdminProductService.cs index 2f1ac34..fa8a3d9 100644 --- a/Webshop.Application/Services/Admin/AdminProductService.cs +++ b/Webshop.Application/Services/Admin/AdminProductService.cs @@ -107,8 +107,6 @@ namespace Webshop.Application.Services.Admin return ServiceResult.Ok(MapToAdminDto(newProduct)); } - // ... (UpdateAdminProductAsync und DeleteAdminProductAsync und MapToAdminDto bleiben unverändert) ... - #region Unchanged Methods public async Task UpdateAdminProductAsync(UpdateAdminProductDto productDto) { var existingProduct = await _context.Products @@ -116,16 +114,9 @@ namespace Webshop.Application.Services.Admin .Include(p => p.Productcategories) .FirstOrDefaultAsync(p => p.Id == productDto.Id); - if (existingProduct == null) - { - return ServiceResult.Fail(ServiceResultType.NotFound, $"Produkt mit ID '{productDto.Id}' nicht gefunden."); - } + if (existingProduct == null) { /* ... Fehlerbehandlung ... */ } - // Validierung - var skuExists = await _context.Products.AnyAsync(p => p.SKU == productDto.SKU && p.Id != productDto.Id); - if (skuExists) { /* ... */ } - var slugExists = await _context.Products.AnyAsync(p => p.Slug == productDto.Slug && p.Id != productDto.Id); - if (slugExists) { /* ... */ } + // Validierung ... // Concurrency Token setzen _context.Entry(existingProduct).Property("xmin").OriginalValue = productDto.RowVersion; @@ -133,13 +124,10 @@ namespace Webshop.Application.Services.Admin // Bilder-Logik await HandleImageUpdates(productDto, existingProduct); - // Produkt-Eigenschaften mappen + // Eigenschaften und Kategorien mappen MapDtoToEntity(productDto, existingProduct); - - // Kategorie-Verknüpfungen aktualisieren UpdateProductCategories(productDto, existingProduct); - // Speichern try { await _productRepository.UpdateProductAsync(existingProduct); @@ -147,34 +135,27 @@ namespace Webshop.Application.Services.Admin } catch (DbUpdateConcurrencyException) { - return ServiceResult.Fail(ServiceResultType.Conflict, "Die Produktdaten wurden von einem anderen Benutzer geändert. Bitte laden Sie die Seite neu."); + return ServiceResult.Fail(ServiceResultType.Conflict, "Die Produktdaten wurden von einem anderen Benutzer geändert."); } } private async Task HandleImageUpdates(UpdateAdminProductDto dto, Product entity) { - // Bilder zum Löschen entfernen + // 1. Bilder zum Löschen entfernen if (dto.ImagesToDelete != null && dto.ImagesToDelete.Any()) { var imagesToRemove = entity.Images.Where(img => dto.ImagesToDelete.Contains(img.Id)).ToList(); _context.ProductImages.RemoveRange(imagesToRemove); } - // KORREKTUR 1: Logik für MainImageId entfernt. Wir verlassen uns nur auf MainImageFile. - // Hauptbild aktualisieren/hinzufügen - if (dto.MainImageFile != null) + // 2. Alle existierenden Bilder zuerst als "nicht Hauptbild" markieren + foreach (var image in entity.Images) { - // Altes Hauptbild als "nicht Hauptbild" markieren - var oldMainImage = entity.Images.FirstOrDefault(i => i.IsMainImage); - if (oldMainImage != null) oldMainImage.IsMainImage = false; - - await using var stream = dto.MainImageFile.OpenReadStream(); - var url = await _fileStorageService.SaveFileAsync(stream, dto.MainImageFile.FileName, dto.MainImageFile.ContentType); - entity.Images.Add(new ProductImage { Url = url, IsMainImage = true }); + image.IsMainImage = false; } - // Zusätzliche neue Bilder hinzufügen - if (dto.AdditionalImageFiles != null && dto.AdditionalImageFiles.Any()) + // 3. Neue zusätzliche Bilder hochladen (noch nicht als Hauptbild) + if (dto.AdditionalImageFiles != null) { foreach (var file in dto.AdditionalImageFiles) { @@ -184,27 +165,31 @@ namespace Webshop.Application.Services.Admin } } - // KORREKTUR 2: Sortierung ohne 'CreatedDate' - // Sicherstellen, dass nur ein Hauptbild existiert - var mainImages = entity.Images.Where(i => i.IsMainImage).ToList(); - if (mainImages.Count > 1) // Wenn durch den Upload ein zweites Hauptbild hinzugefügt wurde + // 4. Hauptbild bestimmen und ggf. hochladen + if (dto.MainImageFile != null) // Szenario A: Ein NEUES Bild ist das Hauptbild { - // De-priorisiere alle außer dem neuesten (implizit das letzte hinzugefügte) - for (int i = 0; i < mainImages.Count - 1; i++) + await using var stream = dto.MainImageFile.OpenReadStream(); + var url = await _fileStorageService.SaveFileAsync(stream, dto.MainImageFile.FileName, dto.MainImageFile.ContentType); + entity.Images.Add(new ProductImage { Url = url, IsMainImage = true }); + } + else if (dto.MainImageId.HasValue) // Szenario B: Ein EXISTIERENDES Bild ist das Hauptbild + { + var newMainImage = entity.Images.FirstOrDefault(i => i.Id == dto.MainImageId.Value); + if (newMainImage != null) { - mainImages[i].IsMainImage = false; + newMainImage.IsMainImage = true; } } - // Wenn nach dem Löschen gar kein Hauptbild mehr da ist, setze das erste Bild als Hauptbild + // 5. Fallback: Wenn nach allen Operationen kein Hauptbild gesetzt ist, das erste nehmen if (!entity.Images.Any(i => i.IsMainImage) && entity.Images.Any()) { entity.Images.First().IsMainImage = true; } - // DisplayOrder neu berechnen + // 6. DisplayOrder für alle Bilder neu berechnen int displayOrder = 1; - var orderedImages = entity.Images.OrderByDescending(i => i.IsMainImage).ThenBy(i => i.Id).ToList(); // Sortiere nach ID als Fallback + var orderedImages = entity.Images.OrderByDescending(i => i.IsMainImage).ThenBy(i => i.Id).ToList(); orderedImages.ForEach(i => i.DisplayOrder = displayOrder++); } @@ -250,6 +235,6 @@ namespace Webshop.Application.Services.Admin { return new AdminProductDto { Id = product.Id, Name = product.Name, Description = product.Description, SKU = product.SKU, Price = product.Price, OldPrice = product.OldPrice, IsActive = product.IsActive, IsInStock = product.IsInStock, StockQuantity = product.StockQuantity, Weight = product.Weight, Slug = product.Slug, CreatedDate = product.CreatedDate, LastModifiedDate = product.LastModifiedDate, SupplierId = product.SupplierId, PurchasePrice = product.PurchasePrice, IsFeatured = product.IsFeatured, FeaturedDisplayOrder = product.FeaturedDisplayOrder, categorieIds = product.Productcategories.Select(pc => pc.categorieId).ToList(), Images = product.Images.OrderBy(i => i.DisplayOrder).Select(img => new ProductImageDto { Id = img.Id, Url = img.Url, IsMainImage = img.IsMainImage, DisplayOrder = img.DisplayOrder }).ToList() }; } - #endregion + } } \ No newline at end of file