bileder
Branch - test - Build and Push Backend API Docker Image / build-and-push (push) Successful in 28s

This commit is contained in:
Tizian.Breuch
2025-11-07 12:16:48 +01:00
parent 2839eccf71
commit 1ad7fd4b6d
2 changed files with 65 additions and 65 deletions
@@ -38,4 +38,3 @@ namespace Webshop.Application.DTOs.Products
public uint RowVersion { get; set; }
}
}
}
@@ -111,7 +111,6 @@ namespace Webshop.Application.Services.Admin
#region Unchanged Methods
public async Task<ServiceResult> UpdateAdminProductAsync(UpdateAdminProductDto productDto)
{
// 1. Produkt aus der DB laden (inklusive der zugehörigen Bilder und Kategorien)
var existingProduct = await _context.Products
.Include(p => p.Images)
.Include(p => p.Productcategories)
@@ -122,33 +121,25 @@ namespace Webshop.Application.Services.Admin
return ServiceResult.Fail(ServiceResultType.NotFound, $"Produkt mit ID '{productDto.Id}' nicht gefunden.");
}
// 2. Validierung (SKU und Slug auf Einzigartigkeit prüfen)
// Validierung
var skuExists = await _context.Products.AnyAsync(p => p.SKU == productDto.SKU && p.Id != productDto.Id);
if (skuExists)
{
return ServiceResult.Fail(ServiceResultType.Conflict, $"Ein anderes Produkt mit der SKU '{productDto.SKU}' existiert bereits.");
}
if (skuExists) { /* ... */ }
var slugExists = await _context.Products.AnyAsync(p => p.Slug == productDto.Slug && p.Id != productDto.Id);
if (slugExists)
{
return ServiceResult.Fail(ServiceResultType.Conflict, $"Ein anderes Produkt mit dem Slug '{productDto.Slug}' existiert bereits.");
}
if (slugExists) { /* ... */ }
// 3. WICHTIG: Den Concurrency Token setzen!
// Sagt Entity Framework, welche Version des Produkts der Benutzer ursprünglich hatte.
// "xmin" ist der Name der Concurrency-Spalte in PostgreSQL.
// Concurrency Token setzen
_context.Entry(existingProduct).Property("xmin").OriginalValue = productDto.RowVersion;
// 4. Bilder-Logik (aufgeräumt und klarer)
// Bilder-Logik
await HandleImageUpdates(productDto, existingProduct);
// 5. Produkt-Eigenschaften mappen
// Produkt-Eigenschaften mappen
MapDtoToEntity(productDto, existingProduct);
// 6. Kategorie-Verknüpfungen aktualisieren
// Kategorie-Verknüpfungen aktualisieren
UpdateProductCategories(productDto, existingProduct);
// 7. Speichern
// Speichern
try
{
await _productRepository.UpdateProductAsync(existingProduct);
@@ -156,11 +147,67 @@ namespace Webshop.Application.Services.Admin
}
catch (DbUpdateConcurrencyException)
{
// Dieser Fehler wird jetzt ausgelöst, wenn jemand anderes das Produkt in der Zwischenzeit geändert hat.
return ServiceResult.Fail(ServiceResultType.Conflict, "Die Produktdaten wurden von einem anderen Benutzer geändert. Bitte laden Sie die Seite neu.");
}
}
private async Task HandleImageUpdates(UpdateAdminProductDto dto, Product entity)
{
// 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)
{
// 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 });
}
// Zusätzliche neue Bilder hinzufügen
if (dto.AdditionalImageFiles != null && dto.AdditionalImageFiles.Any())
{
foreach (var file in dto.AdditionalImageFiles)
{
await using var stream = file.OpenReadStream();
var url = await _fileStorageService.SaveFileAsync(stream, file.FileName, file.ContentType);
entity.Images.Add(new ProductImage { Url = url, IsMainImage = false });
}
}
// 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
{
// De-priorisiere alle außer dem neuesten (implizit das letzte hinzugefügte)
for (int i = 0; i < mainImages.Count - 1; i++)
{
mainImages[i].IsMainImage = false;
}
}
// Wenn nach dem Löschen gar kein Hauptbild mehr da ist, setze das erste Bild als Hauptbild
if (!entity.Images.Any(i => i.IsMainImage) && entity.Images.Any())
{
entity.Images.First().IsMainImage = true;
}
// DisplayOrder neu berechnen
int displayOrder = 1;
var orderedImages = entity.Images.OrderByDescending(i => i.IsMainImage).ThenBy(i => i.Id).ToList(); // Sortiere nach ID als Fallback
orderedImages.ForEach(i => i.DisplayOrder = displayOrder++);
}
private void MapDtoToEntity(UpdateAdminProductDto dto, Product entity)
{
entity.Name = dto.Name;
@@ -179,52 +226,6 @@ namespace Webshop.Application.Services.Admin
entity.FeaturedDisplayOrder = dto.FeaturedDisplayOrder;
}
private async Task HandleImageUpdates(UpdateAdminProductDto dto, Product entity)
{
// Bilder zum Löschen entfernen
if (dto.ImagesToDelete != null && dto.ImagesToDelete.Any())
{
var imagesToRemove = entity.Images.Where(img => dto.ImagesToDelete.Contains(img.Id)).ToList();
foreach (var image in imagesToRemove)
{
// Optional: Datei auch aus dem Storage löschen
// await _fileStorageService.DeleteFileAsync(image.Url);
}
_context.ProductImages.RemoveRange(imagesToRemove);
}
// Hauptbild aktualisieren/hinzufügen
if (dto.MainImageFile != null)
{
// Altes Hauptbild als "nicht Hauptbild" markieren oder löschen
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 });
}
else if (dto.MainImageId.HasValue) // Wenn ein existierendes Bild zum Hauptbild gemacht wird
{
entity.Images.ToList().ForEach(i => i.IsMainImage = (i.Id == dto.MainImageId.Value));
}
// Zusätzliche neue Bilder hinzufügen
if (dto.AdditionalImageFiles != null && dto.AdditionalImageFiles.Any())
{
foreach (var file in dto.AdditionalImageFiles)
{
await using var stream = file.OpenReadStream();
var url = await _fileStorageService.SaveFileAsync(stream, file.FileName, file.ContentType);
entity.Images.Add(new ProductImage { Url = url, IsMainImage = false });
}
}
// DisplayOrder neu berechnen
int displayOrder = 1;
var orderedImages = entity.Images.OrderByDescending(i => i.IsMainImage).ThenBy(i => i.CreatedDate).ToList();
orderedImages.ForEach(i => i.DisplayOrder = displayOrder++);
}
private void UpdateProductCategories(UpdateAdminProductDto dto, Product entity)
{