billldr
All checks were successful
Branch - test - Build and Push Backend API Docker Image / build-and-push (push) Successful in 27s

This commit is contained in:
Tizian.Breuch
2025-11-07 12:21:21 +01:00
parent 1ad7fd4b6d
commit dcbb450d23
2 changed files with 26 additions and 40 deletions

View File

@@ -36,5 +36,6 @@ namespace Webshop.Application.DTOs.Products
public bool IsFeatured { get; set; } public bool IsFeatured { get; set; }
public int FeaturedDisplayOrder { get; set; } public int FeaturedDisplayOrder { get; set; }
public uint RowVersion { get; set; } public uint RowVersion { get; set; }
public Guid? MainImageId { get; set; }
} }
} }

View File

@@ -107,8 +107,6 @@ namespace Webshop.Application.Services.Admin
return ServiceResult.Ok(MapToAdminDto(newProduct)); return ServiceResult.Ok(MapToAdminDto(newProduct));
} }
// ... (UpdateAdminProductAsync und DeleteAdminProductAsync und MapToAdminDto bleiben unver<65>ndert) ...
#region Unchanged Methods
public async Task<ServiceResult> UpdateAdminProductAsync(UpdateAdminProductDto productDto) public async Task<ServiceResult> UpdateAdminProductAsync(UpdateAdminProductDto productDto)
{ {
var existingProduct = await _context.Products var existingProduct = await _context.Products
@@ -116,16 +114,9 @@ namespace Webshop.Application.Services.Admin
.Include(p => p.Productcategories) .Include(p => p.Productcategories)
.FirstOrDefaultAsync(p => p.Id == productDto.Id); .FirstOrDefaultAsync(p => p.Id == productDto.Id);
if (existingProduct == null) if (existingProduct == null) { /* ... Fehlerbehandlung ... */ }
{
return ServiceResult.Fail(ServiceResultType.NotFound, $"Produkt mit ID '{productDto.Id}' nicht gefunden.");
}
// Validierung // 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) { /* ... */ }
// Concurrency Token setzen // Concurrency Token setzen
_context.Entry(existingProduct).Property("xmin").OriginalValue = productDto.RowVersion; _context.Entry(existingProduct).Property("xmin").OriginalValue = productDto.RowVersion;
@@ -133,13 +124,10 @@ namespace Webshop.Application.Services.Admin
// Bilder-Logik // Bilder-Logik
await HandleImageUpdates(productDto, existingProduct); await HandleImageUpdates(productDto, existingProduct);
// Produkt-Eigenschaften mappen // Eigenschaften und Kategorien mappen
MapDtoToEntity(productDto, existingProduct); MapDtoToEntity(productDto, existingProduct);
// Kategorie-Verkn<6B>pfungen aktualisieren
UpdateProductCategories(productDto, existingProduct); UpdateProductCategories(productDto, existingProduct);
// Speichern
try try
{ {
await _productRepository.UpdateProductAsync(existingProduct); await _productRepository.UpdateProductAsync(existingProduct);
@@ -147,34 +135,27 @@ namespace Webshop.Application.Services.Admin
} }
catch (DbUpdateConcurrencyException) catch (DbUpdateConcurrencyException)
{ {
return ServiceResult.Fail(ServiceResultType.Conflict, "Die Produktdaten wurden von einem anderen Benutzer ge<67>ndert. Bitte laden Sie die Seite neu."); return ServiceResult.Fail(ServiceResultType.Conflict, "Die Produktdaten wurden von einem anderen Benutzer ge<67>ndert.");
} }
} }
private async Task HandleImageUpdates(UpdateAdminProductDto dto, Product entity) 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()) if (dto.ImagesToDelete != null && dto.ImagesToDelete.Any())
{ {
var imagesToRemove = entity.Images.Where(img => dto.ImagesToDelete.Contains(img.Id)).ToList(); var imagesToRemove = entity.Images.Where(img => dto.ImagesToDelete.Contains(img.Id)).ToList();
_context.ProductImages.RemoveRange(imagesToRemove); _context.ProductImages.RemoveRange(imagesToRemove);
} }
// KORREKTUR 1: Logik f<>r MainImageId entfernt. Wir verlassen uns nur auf MainImageFile. // 2. Alle existierenden Bilder zuerst als "nicht Hauptbild" markieren
// Hauptbild aktualisieren/hinzuf<75>gen foreach (var image in entity.Images)
if (dto.MainImageFile != null)
{ {
// Altes Hauptbild als "nicht Hauptbild" markieren image.IsMainImage = false;
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 });
} }
// Zus<EFBFBD>tzliche neue Bilder hinzuf<EFBFBD>gen // 3. Neue zus<EFBFBD>tzliche Bilder hochladen (noch nicht als Hauptbild)
if (dto.AdditionalImageFiles != null && dto.AdditionalImageFiles.Any()) if (dto.AdditionalImageFiles != null)
{ {
foreach (var file in dto.AdditionalImageFiles) foreach (var file in dto.AdditionalImageFiles)
{ {
@@ -184,27 +165,31 @@ namespace Webshop.Application.Services.Admin
} }
} }
// KORREKTUR 2: Sortierung ohne 'CreatedDate' // 4. Hauptbild bestimmen und ggf. hochladen
// Sicherstellen, dass nur ein Hauptbild existiert if (dto.MainImageFile != null) // Szenario A: Ein NEUES Bild ist das Hauptbild
var mainImages = entity.Images.Where(i => i.IsMainImage).ToList();
if (mainImages.Count > 1) // Wenn durch den Upload ein zweites Hauptbild hinzugef<65>gt wurde
{ {
// De-priorisiere alle au<61>er dem neuesten (implizit das letzte hinzugef<65>gte) await using var stream = dto.MainImageFile.OpenReadStream();
for (int i = 0; i < mainImages.Count - 1; i++) 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
{ {
mainImages[i].IsMainImage = false; var newMainImage = entity.Images.FirstOrDefault(i => i.Id == dto.MainImageId.Value);
if (newMainImage != null)
{
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()) if (!entity.Images.Any(i => i.IsMainImage) && entity.Images.Any())
{ {
entity.Images.First().IsMainImage = true; entity.Images.First().IsMainImage = true;
} }
// DisplayOrder neu berechnen // 6. DisplayOrder f<EFBFBD>r alle Bilder neu berechnen
int displayOrder = 1; 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++); 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() }; 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
} }
} }