bileder
All checks were successful
Branch - test - Build and Push Backend API Docker Image / build-and-push (push) Successful in 28s
All checks were successful
Branch - test - Build and Push Backend API Docker Image / build-and-push (push) Successful in 28s
This commit is contained in:
@@ -38,4 +38,3 @@ namespace Webshop.Application.DTOs.Products
|
|||||||
public uint RowVersion { get; set; }
|
public uint RowVersion { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -111,7 +111,6 @@ namespace Webshop.Application.Services.Admin
|
|||||||
#region Unchanged Methods
|
#region Unchanged Methods
|
||||||
public async Task<ServiceResult> UpdateAdminProductAsync(UpdateAdminProductDto productDto)
|
public async Task<ServiceResult> UpdateAdminProductAsync(UpdateAdminProductDto productDto)
|
||||||
{
|
{
|
||||||
// 1. Produkt aus der DB laden (inklusive der zugeh<65>rigen Bilder und Kategorien)
|
|
||||||
var existingProduct = await _context.Products
|
var existingProduct = await _context.Products
|
||||||
.Include(p => p.Images)
|
.Include(p => p.Images)
|
||||||
.Include(p => p.Productcategories)
|
.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.");
|
return ServiceResult.Fail(ServiceResultType.NotFound, $"Produkt mit ID '{productDto.Id}' nicht gefunden.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Validierung (SKU und Slug auf Einzigartigkeit pr<70>fen)
|
// Validierung
|
||||||
var skuExists = await _context.Products.AnyAsync(p => p.SKU == productDto.SKU && p.Id != productDto.Id);
|
var skuExists = await _context.Products.AnyAsync(p => p.SKU == productDto.SKU && p.Id != productDto.Id);
|
||||||
if (skuExists)
|
if (skuExists) { /* ... */ }
|
||||||
{
|
|
||||||
return ServiceResult.Fail(ServiceResultType.Conflict, $"Ein anderes Produkt mit der SKU '{productDto.SKU}' existiert bereits.");
|
|
||||||
}
|
|
||||||
var slugExists = await _context.Products.AnyAsync(p => p.Slug == productDto.Slug && p.Id != productDto.Id);
|
var slugExists = await _context.Products.AnyAsync(p => p.Slug == productDto.Slug && p.Id != productDto.Id);
|
||||||
if (slugExists)
|
if (slugExists) { /* ... */ }
|
||||||
{
|
|
||||||
return ServiceResult.Fail(ServiceResultType.Conflict, $"Ein anderes Produkt mit dem Slug '{productDto.Slug}' existiert bereits.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. WICHTIG: Den Concurrency Token setzen!
|
// Concurrency Token setzen
|
||||||
// Sagt Entity Framework, welche Version des Produkts der Benutzer urspr<70>nglich hatte.
|
|
||||||
// "xmin" ist der Name der Concurrency-Spalte in PostgreSQL.
|
|
||||||
_context.Entry(existingProduct).Property("xmin").OriginalValue = productDto.RowVersion;
|
_context.Entry(existingProduct).Property("xmin").OriginalValue = productDto.RowVersion;
|
||||||
|
|
||||||
// 4. Bilder-Logik (aufger<65>umt und klarer)
|
// Bilder-Logik
|
||||||
await HandleImageUpdates(productDto, existingProduct);
|
await HandleImageUpdates(productDto, existingProduct);
|
||||||
|
|
||||||
// 5. Produkt-Eigenschaften mappen
|
// Produkt-Eigenschaften mappen
|
||||||
MapDtoToEntity(productDto, existingProduct);
|
MapDtoToEntity(productDto, existingProduct);
|
||||||
|
|
||||||
// 6. Kategorie-Verkn<6B>pfungen aktualisieren
|
// Kategorie-Verkn<6B>pfungen aktualisieren
|
||||||
UpdateProductCategories(productDto, existingProduct);
|
UpdateProductCategories(productDto, existingProduct);
|
||||||
|
|
||||||
// 7. Speichern
|
// Speichern
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _productRepository.UpdateProductAsync(existingProduct);
|
await _productRepository.UpdateProductAsync(existingProduct);
|
||||||
@@ -156,11 +147,67 @@ namespace Webshop.Application.Services.Admin
|
|||||||
}
|
}
|
||||||
catch (DbUpdateConcurrencyException)
|
catch (DbUpdateConcurrencyException)
|
||||||
{
|
{
|
||||||
// Dieser Fehler wird jetzt ausgel<65>st, wenn jemand anderes das Produkt in der Zwischenzeit ge<67>ndert hat.
|
|
||||||
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. 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<75>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<75>tzliche neue Bilder hinzuf<75>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<65>gt wurde
|
||||||
|
{
|
||||||
|
// De-priorisiere alle au<61>er dem neuesten (implizit das letzte hinzugef<65>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)
|
private void MapDtoToEntity(UpdateAdminProductDto dto, Product entity)
|
||||||
{
|
{
|
||||||
entity.Name = dto.Name;
|
entity.Name = dto.Name;
|
||||||
@@ -179,52 +226,6 @@ namespace Webshop.Application.Services.Admin
|
|||||||
entity.FeaturedDisplayOrder = dto.FeaturedDisplayOrder;
|
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<75>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<75>tzliche neue Bilder hinzuf<75>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)
|
private void UpdateProductCategories(UpdateAdminProductDto dto, Product entity)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user