test bilder erneut
This commit is contained in:
@@ -1,12 +1,11 @@
|
|||||||
// Auto-generiert von CreateWebshopFiles.ps1
|
// src/Webshop.Api/Controllers/Admin/AdminProductsController.cs
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http; // F<>r IFormFile
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Webshop.Application.DTOs.Products;
|
using Webshop.Application.DTOs.Products;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Webshop.Application.Services.Admin.Interfaces;
|
using Webshop.Application.Services.Admin.Interfaces;
|
||||||
|
|
||||||
namespace Webshop.Api.Controllers.Admin
|
namespace Webshop.Api.Controllers.Admin
|
||||||
@@ -39,25 +38,15 @@ namespace Webshop.Api.Controllers.Admin
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Consumes("multipart/form-data")]
|
public async Task<ActionResult<AdminProductDto>> CreateAdminProduct([FromBody] CreateAdminProductDto productDto) // << ZUR<55>CK ZU [FromBody] >>
|
||||||
public async Task<ActionResult<AdminProductDto>> CreateAdminProduct([FromForm] CreateAdminProductDto productDto) // << NEU: [FromForm] und CreateAdminProductDto >>
|
|
||||||
{
|
{
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||||
|
|
||||||
var createdProduct = await _adminProductService.CreateAdminProductAsync(productDto);
|
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);
|
return CreatedAtAction(nameof(GetAdminProduct), new { id = createdProduct.Id }, createdProduct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpPut("{id}")]
|
[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 (id != productDto.Id) return BadRequest();
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||||
@@ -66,6 +55,7 @@ namespace Webshop.Api.Controllers.Admin
|
|||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
public async Task<IActionResult> DeleteAdminProduct(Guid id)
|
public async Task<IActionResult> DeleteAdminProduct(Guid id)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ using Webshop.Domain.Identity;
|
|||||||
using Webshop.Domain.Interfaces;
|
using Webshop.Domain.Interfaces;
|
||||||
using Webshop.Infrastructure.Data;
|
using Webshop.Infrastructure.Data;
|
||||||
using Webshop.Infrastructure.Repositories;
|
using Webshop.Infrastructure.Repositories;
|
||||||
|
using Webshop.Infrastructure.Services;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@@ -108,6 +109,7 @@ builder.Services.AddScoped<IOrderService, OrderService>();
|
|||||||
builder.Services.AddScoped<IAddressService, AddressService>();
|
builder.Services.AddScoped<IAddressService, AddressService>();
|
||||||
builder.Services.AddScoped<ICheckoutService, CheckoutService>();
|
builder.Services.AddScoped<ICheckoutService, CheckoutService>();
|
||||||
builder.Services.AddScoped<IReviewService, ReviewService>();
|
builder.Services.AddScoped<IReviewService, ReviewService>();
|
||||||
|
builder.Services.AddScoped<IFileStorageService, LocalFileStorageService>();
|
||||||
|
|
||||||
|
|
||||||
// Externe Dienste (Resend)
|
// Externe Dienste (Resend)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// src/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs
|
// src/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs
|
||||||
using Microsoft.AspNetCore.Http; // Für IFormFile
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
@@ -21,13 +20,12 @@ namespace Webshop.Application.DTOs.Products
|
|||||||
[Required]
|
[Required]
|
||||||
public string Slug { get; set; }
|
public string Slug { get; set; }
|
||||||
|
|
||||||
// << NEU: Felder für den Bildupload >>
|
// << KORREKTUR: Felder für Bild-URLs, keine IFormFile >>
|
||||||
public IFormFile? MainImageFile { get; set; }
|
public string? MainImageUrl { get; set; }
|
||||||
public List<IFormFile>? AdditionalImageFiles { get; set; }
|
public List<string>? AdditionalImageUrls { get; set; }
|
||||||
|
|
||||||
public List<Guid> CategorieIds { get; set; } = new List<Guid>();
|
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? Weight { get; set; }
|
||||||
public decimal? OldPrice { get; set; }
|
public decimal? OldPrice { get; set; }
|
||||||
public Guid? SupplierId { get; set; }
|
public Guid? SupplierId { get; set; }
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// src/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs
|
// src/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
@@ -23,14 +22,12 @@ namespace Webshop.Application.DTOs.Products
|
|||||||
[Required]
|
[Required]
|
||||||
public string Slug { get; set; }
|
public string Slug { get; set; }
|
||||||
|
|
||||||
// << NEU: Felder für den Bildupload und -verwaltung >>
|
// << KORREKTUR: Felder für Bild-URLs >>
|
||||||
public IFormFile? MainImageFile { get; set; } // Optional: Neues Hauptbild hochladen
|
public string? MainImageUrl { get; set; }
|
||||||
public List<IFormFile>? AdditionalImageFiles { get; set; } // Optional: Weitere Bilder hochladen
|
public List<string>? AdditionalImageUrls { get; set; }
|
||||||
public List<Guid>? ImagesToDelete { get; set; } // Liste der IDs von Bildern, die gelöscht werden sollen
|
|
||||||
|
|
||||||
public List<Guid> CategorieIds { get; set; } = new List<Guid>();
|
public List<Guid> CategorieIds { get; set; } = new List<Guid>();
|
||||||
|
|
||||||
// ... weitere Felder, die beim Aktualisieren benötigt werden ...
|
|
||||||
public decimal? Weight { get; set; }
|
public decimal? Weight { get; set; }
|
||||||
public decimal? OldPrice { get; set; }
|
public decimal? OldPrice { get; set; }
|
||||||
public Guid? SupplierId { get; set; }
|
public Guid? SupplierId { get; set; }
|
||||||
|
|||||||
@@ -15,16 +15,13 @@ namespace Webshop.Application.Services.Admin
|
|||||||
public class AdminProductService : IAdminProductService
|
public class AdminProductService : IAdminProductService
|
||||||
{
|
{
|
||||||
private readonly IProductRepository _productRepository;
|
private readonly IProductRepository _productRepository;
|
||||||
private readonly IFileStorageService _fileStorageService;
|
|
||||||
private readonly ApplicationDbContext _context;
|
private readonly ApplicationDbContext _context;
|
||||||
|
|
||||||
public AdminProductService(
|
public AdminProductService(
|
||||||
IProductRepository productRepository,
|
IProductRepository productRepository,
|
||||||
IFileStorageService fileStorageService,
|
|
||||||
ApplicationDbContext context)
|
ApplicationDbContext context)
|
||||||
{
|
{
|
||||||
_productRepository = productRepository;
|
_productRepository = productRepository;
|
||||||
_fileStorageService = fileStorageService;
|
|
||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +30,7 @@ namespace Webshop.Application.Services.Admin
|
|||||||
var products = await _context.Products
|
var products = await _context.Products
|
||||||
.Include(p => p.Productcategories)
|
.Include(p => p.Productcategories)
|
||||||
.Include(p => p.Images)
|
.Include(p => p.Images)
|
||||||
|
.OrderBy(p => p.Name)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return products.Select(p => new AdminProductDto
|
return products.Select(p => new AdminProductDto
|
||||||
@@ -53,7 +51,7 @@ namespace Webshop.Application.Services.Admin
|
|||||||
SupplierId = p.SupplierId,
|
SupplierId = p.SupplierId,
|
||||||
PurchasePrice = p.PurchasePrice,
|
PurchasePrice = p.PurchasePrice,
|
||||||
categorieIds = p.Productcategories.Select(pc => pc.categorieId).ToList(),
|
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,
|
Id = img.Id,
|
||||||
Url = img.Url,
|
Url = img.Url,
|
||||||
@@ -90,7 +88,7 @@ namespace Webshop.Application.Services.Admin
|
|||||||
SupplierId = product.SupplierId,
|
SupplierId = product.SupplierId,
|
||||||
PurchasePrice = product.PurchasePrice,
|
PurchasePrice = product.PurchasePrice,
|
||||||
categorieIds = product.Productcategories.Select(pc => pc.categorieId).ToList(),
|
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,
|
Id = img.Id,
|
||||||
Url = img.Url,
|
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>();
|
var images = new List<ProductImage>();
|
||||||
|
|
||||||
if (productDto.MainImageFile != null)
|
if (!string.IsNullOrEmpty(productDto.MainImageUrl))
|
||||||
{
|
{
|
||||||
await using var stream = productDto.MainImageFile.OpenReadStream();
|
images.Add(new ProductImage { Url = productDto.MainImageUrl, IsMainImage = true, DisplayOrder = 1 });
|
||||||
var url = await _fileStorageService.SaveFileAsync(stream, productDto.MainImageFile.FileName, productDto.MainImageFile.ContentType);
|
|
||||||
images.Add(new ProductImage { Url = url, IsMainImage = true, DisplayOrder = 1 });
|
|
||||||
}
|
}
|
||||||
if (productDto.AdditionalImageFiles != null)
|
if (productDto.AdditionalImageUrls != null)
|
||||||
{
|
{
|
||||||
int order = 2;
|
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++ });
|
images.Add(new ProductImage { Url = url, IsMainImage = false, DisplayOrder = order++ });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,13 +124,17 @@ namespace Webshop.Application.Services.Admin
|
|||||||
IsActive = productDto.IsActive,
|
IsActive = productDto.IsActive,
|
||||||
StockQuantity = productDto.StockQuantity,
|
StockQuantity = productDto.StockQuantity,
|
||||||
Slug = productDto.Slug,
|
Slug = productDto.Slug,
|
||||||
|
Weight = productDto.Weight,
|
||||||
|
OldPrice = productDto.OldPrice,
|
||||||
|
SupplierId = productDto.SupplierId,
|
||||||
|
PurchasePrice = productDto.PurchasePrice,
|
||||||
Images = images,
|
Images = images,
|
||||||
Productcategories = productDto.CategorieIds.Select(cId => new Productcategorie { categorieId = cId }).ToList()
|
Productcategories = productDto.CategorieIds.Select(cId => new Productcategorie { categorieId = cId }).ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
await _productRepository.AddProductAsync(newProduct);
|
await _productRepository.AddProductAsync(newProduct);
|
||||||
|
|
||||||
return (await GetAdminProductByIdAsync(newProduct.Id))!;
|
return await GetAdminProductByIdAsync(newProduct.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> UpdateAdminProductAsync(UpdateAdminProductDto productDto)
|
public async Task<bool> UpdateAdminProductAsync(UpdateAdminProductDto productDto)
|
||||||
@@ -148,34 +146,44 @@ namespace Webshop.Application.Services.Admin
|
|||||||
|
|
||||||
if (existingProduct == null) return false;
|
if (existingProduct == null) return false;
|
||||||
|
|
||||||
if (productDto.ImagesToDelete != null)
|
// Basisdaten aktualisieren
|
||||||
{
|
existingProduct.Name = productDto.Name;
|
||||||
var imagesToRemove = existingProduct.Images.Where(img => productDto.ImagesToDelete.Contains(img.Id)).ToList();
|
existingProduct.Description = productDto.Description;
|
||||||
_context.ProductImages.RemoveRange(imagesToRemove);
|
existingProduct.SKU = productDto.SKU;
|
||||||
}
|
existingProduct.Price = productDto.Price;
|
||||||
if (productDto.MainImageFile != null)
|
existingProduct.IsActive = productDto.IsActive;
|
||||||
{
|
existingProduct.StockQuantity = productDto.StockQuantity;
|
||||||
var existingMainImage = existingProduct.Images.FirstOrDefault(img => img.IsMainImage);
|
existingProduct.Slug = productDto.Slug;
|
||||||
if (existingMainImage != null) _context.ProductImages.Remove(existingMainImage);
|
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();
|
// Kategorien synchronisieren
|
||||||
var url = await _fileStorageService.SaveFileAsync(stream, productDto.MainImageFile.FileName, productDto.MainImageFile.ContentType);
|
existingProduct.Productcategories.Clear();
|
||||||
existingProduct.Images.Add(new ProductImage { Url = url, IsMainImage = true, DisplayOrder = 1 });
|
if (productDto.CategorieIds != null)
|
||||||
}
|
|
||||||
if (productDto.AdditionalImageFiles != null)
|
|
||||||
{
|
{
|
||||||
int displayOrder = (existingProduct.Images.Any() ? existingProduct.Images.Max(i => i.DisplayOrder) : 0) + 1;
|
foreach (var categorieId in productDto.CategorieIds)
|
||||||
foreach (var file in productDto.AdditionalImageFiles)
|
|
||||||
{
|
{
|
||||||
await using var stream = file.OpenReadStream();
|
existingProduct.Productcategories.Add(new Productcategorie { categorieId = categorieId });
|
||||||
var url = await _fileStorageService.SaveFileAsync(stream, file.FileName, file.ContentType);
|
|
||||||
existingProduct.Images.Add(new ProductImage { Url = url, IsMainImage = false, DisplayOrder = displayOrder++ });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
existingProduct.Name = productDto.Name;
|
// Bilder synchronisieren
|
||||||
existingProduct.Description = productDto.Description;
|
existingProduct.Images.Clear();
|
||||||
// ... (restliche Felder aktualisieren) ...
|
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);
|
await _productRepository.UpdateProductAsync(existingProduct);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -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.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Webshop.Application.DTOs.Products;
|
using Webshop.Application.DTOs.Products;
|
||||||
@@ -9,8 +10,8 @@ namespace Webshop.Application.Services.Admin.Interfaces
|
|||||||
{
|
{
|
||||||
Task<IEnumerable<AdminProductDto>> GetAllAdminProductsAsync();
|
Task<IEnumerable<AdminProductDto>> GetAllAdminProductsAsync();
|
||||||
Task<AdminProductDto?> GetAdminProductByIdAsync(Guid id);
|
Task<AdminProductDto?> GetAdminProductByIdAsync(Guid id);
|
||||||
Task<AdminProductDto> CreateAdminProductAsync(CreateAdminProductDto productDto); // << NEUER TYP >>
|
Task<AdminProductDto?> CreateAdminProductAsync(CreateAdminProductDto productDto); // << DTO-TYP GEÄNDERT >>
|
||||||
Task<bool> UpdateAdminProductAsync(UpdateAdminProductDto productDto); // << NEUER TYP >>
|
Task<bool> UpdateAdminProductAsync(UpdateAdminProductDto productDto); // << DTO-TYP GEÄNDERT >>
|
||||||
Task<bool> DeleteAdminProductAsync(Guid id);
|
Task<bool> DeleteAdminProductAsync(Guid id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user