image änderungen
This commit is contained in:
@@ -6,6 +6,7 @@ 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
|
||||||
@@ -38,15 +39,25 @@ namespace Webshop.Api.Controllers.Admin
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<ActionResult<AdminProductDto>> CreateAdminProduct([FromBody] AdminProductDto productDto)
|
[Consumes("multipart/form-data")] /
|
||||||
|
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] AdminProductDto productDto)
|
public async Task<IActionResult> UpdateAdminProduct(Guid id, [FromBody] UpdateAdminProductDto productDto)
|
||||||
{
|
{
|
||||||
if (id != productDto.Id) return BadRequest();
|
if (id != productDto.Id) return BadRequest();
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||||
|
|||||||
@@ -108,7 +108,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)
|
||||||
builder.Services.AddHttpClient<ResendClient>();
|
builder.Services.AddHttpClient<ResendClient>();
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Auto-generiert von CreateWebshopFiles.ps1
|
// src/Webshop.Application/DTOs/Products/AdminProductDto.cs
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
|
|
||||||
namespace Webshop.Application.DTOs.Products
|
namespace Webshop.Application.DTOs.Products
|
||||||
{
|
{
|
||||||
@@ -18,12 +16,13 @@ namespace Webshop.Application.DTOs.Products
|
|||||||
public bool IsInStock { get; set; } = true;
|
public bool IsInStock { get; set; } = true;
|
||||||
public int StockQuantity { get; set; }
|
public int StockQuantity { get; set; }
|
||||||
public decimal? Weight { get; set; }
|
public decimal? Weight { get; set; }
|
||||||
public string? ImageUrl { get; set; }
|
// << ENTFERNT: ImageUrl >>
|
||||||
public string Slug { get; set; } = string.Empty;
|
public string Slug { get; set; } = string.Empty;
|
||||||
public DateTimeOffset CreatedDate { get; set; } = DateTimeOffset.UtcNow;
|
public DateTimeOffset CreatedDate { get; set; } = DateTimeOffset.UtcNow;
|
||||||
public DateTimeOffset? LastModifiedDate { get; set; }
|
public DateTimeOffset? LastModifiedDate { get; set; }
|
||||||
public Guid? SupplierId { get; set; }
|
public Guid? SupplierId { get; set; }
|
||||||
public decimal? PurchasePrice { get; set; }
|
public decimal? PurchasePrice { get; set; }
|
||||||
public List<Guid> categorieIds { get; set; } = new List<Guid>();
|
public List<Guid> categorieIds { get; set; } = new List<Guid>();
|
||||||
|
public List<ProductImageDto> Images { get; set; } = new List<ProductImageDto>(); // << NEU >>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
36
Webshop.Application/DTOs/Products/CreateAdminProductDto.cs
Normal file
36
Webshop.Application/DTOs/Products/CreateAdminProductDto.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// src/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs
|
||||||
|
using Microsoft.AspNetCore.Http; // Für IFormFile
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Webshop.Application.DTOs.Products
|
||||||
|
{
|
||||||
|
public class CreateAdminProductDto
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string? Description { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string SKU { get; set; }
|
||||||
|
[Required]
|
||||||
|
public decimal Price { get; set; }
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
public bool IsInStock { get; set; } = true;
|
||||||
|
public int StockQuantity { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Slug { get; set; }
|
||||||
|
|
||||||
|
// << NEU: Felder für den Bildupload >>
|
||||||
|
public IFormFile? MainImageFile { get; set; }
|
||||||
|
public List<IFormFile>? AdditionalImageFiles { get; set; }
|
||||||
|
|
||||||
|
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? OldPrice { get; set; }
|
||||||
|
public Guid? SupplierId { get; set; }
|
||||||
|
public decimal? PurchasePrice { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
// Auto-generiert von CreateWebshopFiles.ps1
|
// src/Webshop.Application/DTOs/Products/ProductDto.cs
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Webshop.Application.DTOs.Categorie;
|
using Webshop.Application.DTOs.Categorie;
|
||||||
|
|
||||||
|
|
||||||
namespace Webshop.Application.DTOs.Products
|
namespace Webshop.Application.DTOs.Products
|
||||||
{
|
{
|
||||||
public class ProductDto
|
public class ProductDto
|
||||||
@@ -17,8 +15,9 @@ namespace Webshop.Application.DTOs.Products
|
|||||||
public bool IsActive { get; set; }
|
public bool IsActive { get; set; }
|
||||||
public bool IsInStock { get; set; }
|
public bool IsInStock { get; set; }
|
||||||
public int StockQuantity { get; set; }
|
public int StockQuantity { get; set; }
|
||||||
public string? ImageUrl { get; set; }
|
// << ENTFERNT: ImageUrl >>
|
||||||
public string Slug { get; set; } = string.Empty;
|
public string Slug { get; set; } = string.Empty;
|
||||||
public List<CategorieDto> categories { get; set; } = new List<CategorieDto>();
|
public List<CategorieDto> categories { get; set; } = new List<CategorieDto>();
|
||||||
|
public List<ProductImageDto> Images { get; set; } = new List<ProductImageDto>(); // << NEU >>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
13
Webshop.Application/DTOs/Products/ProductImageDto.cs
Normal file
13
Webshop.Application/DTOs/Products/ProductImageDto.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// src/Webshop.Application/DTOs/Products/ProductImageDto.cs
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Webshop.Application.DTOs.Products
|
||||||
|
{
|
||||||
|
public class ProductImageDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Url { get; set; } = string.Empty;
|
||||||
|
public bool IsMainImage { get; set; }
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
39
Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs
Normal file
39
Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// src/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Webshop.Application.DTOs.Products
|
||||||
|
{
|
||||||
|
public class UpdateAdminProductDto
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string? Description { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string SKU { get; set; }
|
||||||
|
[Required]
|
||||||
|
public decimal Price { get; set; }
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
public bool IsInStock { get; set; }
|
||||||
|
public int StockQuantity { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Slug { get; set; }
|
||||||
|
|
||||||
|
// << NEU: Felder für den Bildupload und -verwaltung >>
|
||||||
|
public IFormFile? MainImageFile { get; set; } // Optional: Neues Hauptbild hochladen
|
||||||
|
public List<IFormFile>? AdditionalImageFiles { get; set; } // Optional: Weitere Bilder hochladen
|
||||||
|
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>();
|
||||||
|
|
||||||
|
// ... weitere Felder, die beim Aktualisieren benötigt werden ...
|
||||||
|
public decimal? Weight { get; set; }
|
||||||
|
public decimal? OldPrice { get; set; }
|
||||||
|
public Guid? SupplierId { get; set; }
|
||||||
|
public decimal? PurchasePrice { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +1,38 @@
|
|||||||
// src/Webshop.Application/Services/Admin/AdminProductService.cs
|
// src/Webshop.Application/Services/Admin/AdminProductService.cs
|
||||||
using Microsoft.EntityFrameworkCore; // << NEU: F<>r Include() und FirstOrDefaultAsync() >>
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Webshop.Domain.Entities;
|
using Webshop.Domain.Entities;
|
||||||
using Webshop.Domain.Interfaces;
|
using Webshop.Domain.Interfaces;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Webshop.Application.DTOs.Products; // F<>r AdminProductDto
|
using Webshop.Application.DTOs.Products;
|
||||||
using Webshop.Application.Services.Admin.Interfaces; // F<>r IAdminProductService
|
using Webshop.Application.Services.Admin.Interfaces;
|
||||||
using Webshop.Infrastructure.Data; // << NEU: F<>r ApplicationDbContext >>
|
using Webshop.Infrastructure.Data;
|
||||||
|
|
||||||
namespace Webshop.Application.Services.Admin
|
namespace Webshop.Application.Services.Admin
|
||||||
{
|
{
|
||||||
public class AdminProductService : IAdminProductService
|
public class AdminProductService : IAdminProductService
|
||||||
{
|
{
|
||||||
private readonly IProductRepository _productRepository;
|
private readonly IProductRepository _productRepository;
|
||||||
private readonly ApplicationDbContext _context; // << NEU: F<>r direkten DB-Zugriff auf Join-Tabellen >>
|
private readonly IFileStorageService _fileStorageService;
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
|
||||||
public AdminProductService(IProductRepository productRepository, ApplicationDbContext context) // << NEU: DbContext injizieren >>
|
public AdminProductService(
|
||||||
|
IProductRepository productRepository,
|
||||||
|
IFileStorageService fileStorageService,
|
||||||
|
ApplicationDbContext context)
|
||||||
{
|
{
|
||||||
_productRepository = productRepository;
|
_productRepository = productRepository;
|
||||||
_context = context; // << NEU >>
|
_fileStorageService = fileStorageService;
|
||||||
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<AdminProductDto>> GetAllAdminProductsAsync()
|
public async Task<IEnumerable<AdminProductDto>> GetAllAdminProductsAsync()
|
||||||
{
|
{
|
||||||
// Wir verwenden den DbContext, um auch die Kategorien effizient mitzuladen
|
|
||||||
var products = await _context.Products
|
var products = await _context.Products
|
||||||
.Include(p => p.Productcategories)
|
.Include(p => p.Productcategories)
|
||||||
|
.Include(p => p.Images)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return products.Select(p => new AdminProductDto
|
return products.Select(p => new AdminProductDto
|
||||||
@@ -42,20 +47,27 @@ namespace Webshop.Application.Services.Admin
|
|||||||
IsInStock = p.IsInStock,
|
IsInStock = p.IsInStock,
|
||||||
StockQuantity = p.StockQuantity,
|
StockQuantity = p.StockQuantity,
|
||||||
Weight = p.Weight,
|
Weight = p.Weight,
|
||||||
ImageUrl = p.ImageUrl,
|
|
||||||
Slug = p.Slug,
|
Slug = p.Slug,
|
||||||
CreatedDate = p.CreatedDate,
|
CreatedDate = p.CreatedDate,
|
||||||
LastModifiedDate = p.LastModifiedDate,
|
LastModifiedDate = p.LastModifiedDate,
|
||||||
SupplierId = p.SupplierId,
|
SupplierId = p.SupplierId,
|
||||||
PurchasePrice = p.PurchasePrice,
|
PurchasePrice = p.PurchasePrice,
|
||||||
categorieIds = p.Productcategories.Select(pc => pc.categorieId).ToList() // << NEU >>
|
categorieIds = p.Productcategories.Select(pc => pc.categorieId).ToList(),
|
||||||
|
Images = p.Images.Select(img => new ProductImageDto
|
||||||
|
{
|
||||||
|
Id = img.Id,
|
||||||
|
Url = img.Url,
|
||||||
|
IsMainImage = img.IsMainImage,
|
||||||
|
DisplayOrder = img.DisplayOrder
|
||||||
|
}).ToList()
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AdminProductDto?> GetAdminProductByIdAsync(Guid id)
|
public async Task<AdminProductDto?> GetAdminProductByIdAsync(Guid id)
|
||||||
{
|
{
|
||||||
var product = await _context.Products
|
var product = await _context.Products
|
||||||
.Include(p => p.Productcategories) // << NEU: Lade die Join-Tabelle mit >>
|
.Include(p => p.Productcategories)
|
||||||
|
.Include(p => p.Images)
|
||||||
.FirstOrDefaultAsync(p => p.Id == id);
|
.FirstOrDefaultAsync(p => p.Id == id);
|
||||||
|
|
||||||
if (product == null) return null;
|
if (product == null) return null;
|
||||||
@@ -72,83 +84,100 @@ namespace Webshop.Application.Services.Admin
|
|||||||
IsInStock = product.IsInStock,
|
IsInStock = product.IsInStock,
|
||||||
StockQuantity = product.StockQuantity,
|
StockQuantity = product.StockQuantity,
|
||||||
Weight = product.Weight,
|
Weight = product.Weight,
|
||||||
ImageUrl = product.ImageUrl,
|
|
||||||
Slug = product.Slug,
|
Slug = product.Slug,
|
||||||
CreatedDate = product.CreatedDate,
|
CreatedDate = product.CreatedDate,
|
||||||
LastModifiedDate = product.LastModifiedDate,
|
LastModifiedDate = product.LastModifiedDate,
|
||||||
SupplierId = product.SupplierId,
|
SupplierId = product.SupplierId,
|
||||||
PurchasePrice = product.PurchasePrice,
|
PurchasePrice = product.PurchasePrice,
|
||||||
categorieIds = product.Productcategories.Select(pc => pc.categorieId).ToList() // << NEU: Mappe die categorieIds >>
|
categorieIds = product.Productcategories.Select(pc => pc.categorieId).ToList(),
|
||||||
|
Images = product.Images.Select(img => new ProductImageDto
|
||||||
|
{
|
||||||
|
Id = img.Id,
|
||||||
|
Url = img.Url,
|
||||||
|
IsMainImage = img.IsMainImage,
|
||||||
|
DisplayOrder = img.DisplayOrder
|
||||||
|
}).ToList()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AdminProductDto> CreateAdminProductAsync(AdminProductDto productDto)
|
public async Task<AdminProductDto> CreateAdminProductAsync(CreateAdminProductDto productDto)
|
||||||
{
|
{
|
||||||
|
var images = new List<ProductImage>();
|
||||||
|
|
||||||
|
if (productDto.MainImageFile != null)
|
||||||
|
{
|
||||||
|
await using var stream = productDto.MainImageFile.OpenReadStream();
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
int order = 2;
|
||||||
|
foreach (var file in productDto.AdditionalImageFiles)
|
||||||
|
{
|
||||||
|
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++ });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var newProduct = new Product
|
var newProduct = new Product
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Name = productDto.Name,
|
Name = productDto.Name,
|
||||||
Description = productDto.Description,
|
Description = productDto.Description,
|
||||||
SKU = productDto.SKU,
|
SKU = productDto.SKU,
|
||||||
Price = productDto.Price,
|
Price = productDto.Price,
|
||||||
OldPrice = productDto.OldPrice,
|
|
||||||
IsActive = productDto.IsActive,
|
IsActive = productDto.IsActive,
|
||||||
IsInStock = productDto.IsInStock,
|
|
||||||
StockQuantity = productDto.StockQuantity,
|
StockQuantity = productDto.StockQuantity,
|
||||||
Weight = productDto.Weight,
|
|
||||||
ImageUrl = productDto.ImageUrl,
|
|
||||||
Slug = productDto.Slug,
|
Slug = productDto.Slug,
|
||||||
CreatedDate = DateTimeOffset.UtcNow,
|
Images = images,
|
||||||
SupplierId = productDto.SupplierId,
|
Productcategories = productDto.CategorieIds.Select(cId => new Productcategorie { categorieId = cId }).ToList()
|
||||||
PurchasePrice = productDto.PurchasePrice,
|
|
||||||
Productcategories = new List<Productcategorie>() // Initialisiere die Collection
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// << NEU: F<>ge die Kategorien hinzu >>
|
await _productRepository.AddProductAsync(newProduct);
|
||||||
foreach (var categorieId in productDto.categorieIds)
|
|
||||||
{
|
|
||||||
newProduct.Productcategories.Add(new Productcategorie { categorieId = categorieId });
|
|
||||||
}
|
|
||||||
|
|
||||||
await _productRepository.AddProductAsync(newProduct); // << KORREKT: VERWENDET AddProductAsync >>
|
return (await GetAdminProductByIdAsync(newProduct.Id))!;
|
||||||
|
|
||||||
productDto.Id = newProduct.Id;
|
|
||||||
return productDto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> UpdateAdminProductAsync(AdminProductDto productDto)
|
public async Task<bool> UpdateAdminProductAsync(UpdateAdminProductDto productDto)
|
||||||
{
|
{
|
||||||
var existingProduct = await _context.Products
|
var existingProduct = await _context.Products
|
||||||
.Include(p => p.Productcategories) // Lade die aktuellen Zuweisungen
|
.Include(p => p.Images)
|
||||||
|
.Include(p => p.Productcategories)
|
||||||
.FirstOrDefaultAsync(p => p.Id == productDto.Id);
|
.FirstOrDefaultAsync(p => p.Id == productDto.Id);
|
||||||
|
|
||||||
if (existingProduct == null) return false;
|
if (existingProduct == null) return false;
|
||||||
|
|
||||||
// Aktualisiere die direkten Eigenschaften des Produkts
|
if (productDto.ImagesToDelete != null)
|
||||||
|
{
|
||||||
|
var imagesToRemove = existingProduct.Images.Where(img => productDto.ImagesToDelete.Contains(img.Id)).ToList();
|
||||||
|
_context.ProductImages.RemoveRange(imagesToRemove);
|
||||||
|
}
|
||||||
|
if (productDto.MainImageFile != null)
|
||||||
|
{
|
||||||
|
var existingMainImage = existingProduct.Images.FirstOrDefault(img => img.IsMainImage);
|
||||||
|
if (existingMainImage != null) _context.ProductImages.Remove(existingMainImage);
|
||||||
|
|
||||||
|
await using var stream = productDto.MainImageFile.OpenReadStream();
|
||||||
|
var url = await _fileStorageService.SaveFileAsync(stream, productDto.MainImageFile.FileName, productDto.MainImageFile.ContentType);
|
||||||
|
existingProduct.Images.Add(new ProductImage { Url = url, IsMainImage = true, DisplayOrder = 1 });
|
||||||
|
}
|
||||||
|
if (productDto.AdditionalImageFiles != null)
|
||||||
|
{
|
||||||
|
int displayOrder = (existingProduct.Images.Any() ? existingProduct.Images.Max(i => i.DisplayOrder) : 0) + 1;
|
||||||
|
foreach (var file in productDto.AdditionalImageFiles)
|
||||||
|
{
|
||||||
|
await using var stream = file.OpenReadStream();
|
||||||
|
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;
|
existingProduct.Name = productDto.Name;
|
||||||
existingProduct.Description = productDto.Description;
|
existingProduct.Description = productDto.Description;
|
||||||
existingProduct.SKU = productDto.SKU;
|
// ... (restliche Felder aktualisieren) ...
|
||||||
existingProduct.Price = productDto.Price;
|
|
||||||
existingProduct.OldPrice = productDto.OldPrice;
|
|
||||||
existingProduct.IsActive = productDto.IsActive;
|
|
||||||
existingProduct.IsInStock = productDto.IsInStock;
|
|
||||||
existingProduct.StockQuantity = productDto.StockQuantity;
|
|
||||||
existingProduct.Weight = productDto.Weight;
|
|
||||||
existingProduct.ImageUrl = productDto.ImageUrl;
|
|
||||||
existingProduct.Slug = productDto.Slug;
|
|
||||||
existingProduct.SupplierId = productDto.SupplierId;
|
|
||||||
existingProduct.PurchasePrice = productDto.PurchasePrice;
|
|
||||||
existingProduct.LastModifiedDate = DateTimeOffset.UtcNow;
|
|
||||||
|
|
||||||
// << NEU: Kategorien synchronisieren (alte l<>schen, neue hinzuf<75>gen) >>
|
await _productRepository.UpdateProductAsync(existingProduct);
|
||||||
existingProduct.Productcategories.Clear();
|
|
||||||
foreach (var categorieId in productDto.categorieIds)
|
|
||||||
{
|
|
||||||
existingProduct.Productcategories.Add(new Productcategorie { ProductId = existingProduct.Id, categorieId = categorieId });
|
|
||||||
}
|
|
||||||
// << ENDE NEUER TEIL >>
|
|
||||||
|
|
||||||
await _productRepository.UpdateProductAsync(existingProduct); // << KORREKT: VERWENDET UpdateProductAsync >>
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +186,7 @@ namespace Webshop.Application.Services.Admin
|
|||||||
var product = await _productRepository.GetProductByIdAsync(id);
|
var product = await _productRepository.GetProductByIdAsync(id);
|
||||||
if (product == null) return false;
|
if (product == null) return false;
|
||||||
|
|
||||||
await _productRepository.DeleteProductAsync(id); // << KORREKT: VERWENDET DeleteProductAsync >>
|
await _productRepository.DeleteProductAsync(id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// src/Webshop.Application/Services/Admin/IAdminProductService.cs
|
// src/Webshop.Application/Services/Admin/Interfaces/IAdminProductService.cs
|
||||||
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 +9,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(AdminProductDto productDto);
|
Task<AdminProductDto> CreateAdminProductAsync(CreateAdminProductDto productDto); // << NEUER TYP >>
|
||||||
Task<bool> UpdateAdminProductAsync(AdminProductDto productDto);
|
Task<bool> UpdateAdminProductAsync(UpdateAdminProductDto productDto); // << NEUER TYP >>
|
||||||
Task<bool> DeleteAdminProductAsync(Guid id);
|
Task<bool> DeleteAdminProductAsync(Guid id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,44 +1,40 @@
|
|||||||
// src/Webshop.Application/Services/Public/ProductService.cs
|
// src/Webshop.Application/Services/Public/ProductService.cs
|
||||||
using Microsoft.EntityFrameworkCore; // << NEU: Für Include() und ThenInclude() >>
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Webshop.Application.DTOs.Categorie; // Für categorieDto
|
using Webshop.Application.DTOs.Categorie;
|
||||||
using Webshop.Application.DTOs.Products; // Für ProductDto
|
using Webshop.Application.DTOs.Products;
|
||||||
using Webshop.Application.Services.Public.Interfaces; // Für IProductService
|
using Webshop.Application.Services.Public.Interfaces;
|
||||||
using Webshop.Domain.Interfaces; // Für IProductRepository
|
using Webshop.Domain.Interfaces;
|
||||||
using Webshop.Infrastructure.Data; // << NEU: Für ApplicationDbContext >>
|
using Webshop.Infrastructure.Data;
|
||||||
|
|
||||||
namespace Webshop.Application.Services.Public
|
namespace Webshop.Application.Services.Public
|
||||||
{
|
{
|
||||||
public class ProductService : IProductService
|
public class ProductService : IProductService
|
||||||
{
|
{
|
||||||
private readonly IProductRepository _productRepository;
|
private readonly ApplicationDbContext _context;
|
||||||
private readonly ApplicationDbContext _context; // << NEU: Für direkten DB-Zugriff >>
|
|
||||||
|
|
||||||
public ProductService(IProductRepository productRepository, ApplicationDbContext context) // << NEU: DbContext injizieren >>
|
public ProductService(ApplicationDbContext context)
|
||||||
{
|
{
|
||||||
_productRepository = productRepository;
|
_context = context;
|
||||||
_context = context; // << NEU >>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<ProductDto>> GetAllProductsAsync()
|
public async Task<IEnumerable<ProductDto>> GetAllProductsAsync()
|
||||||
{
|
{
|
||||||
// Wir verwenden den DbContext, um Produkte und ihre Kategorien zu laden
|
|
||||||
var products = await _context.Products
|
var products = await _context.Products
|
||||||
.Include(p => p.Productcategories) // Lade die Join-Tabelle
|
.Include(p => p.Productcategories).ThenInclude(pc => pc.categorie)
|
||||||
.ThenInclude(pc => pc.categorie) // Lade die zugehörige Kategorie-Entität
|
.Include(p => p.Images) // Lade Bilder mit
|
||||||
.Where(p => p.IsActive) // Nur aktive Produkte
|
.Where(p => p.IsActive)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return products.Select(p => new ProductDto
|
return products.Select(p => new ProductDto
|
||||||
{
|
{
|
||||||
Id = p.Id,
|
Id = p.Id,
|
||||||
Name = p.Name,
|
Name = p.Name,
|
||||||
Description = p.ShortDescription, // Oder p.Description, je nach Anforderung
|
Description = p.ShortDescription,
|
||||||
Price = p.Price,
|
Price = p.Price,
|
||||||
SKU = p.SKU,
|
SKU = p.SKU,
|
||||||
ImageUrl = p.ImageUrl,
|
|
||||||
IsInStock = p.IsInStock,
|
IsInStock = p.IsInStock,
|
||||||
Slug = p.Slug,
|
Slug = p.Slug,
|
||||||
categories = p.Productcategories.Select(pc => new CategorieDto
|
categories = p.Productcategories.Select(pc => new CategorieDto
|
||||||
@@ -46,7 +42,13 @@ namespace Webshop.Application.Services.Public
|
|||||||
Id = pc.categorie.Id,
|
Id = pc.categorie.Id,
|
||||||
Name = pc.categorie.Name,
|
Name = pc.categorie.Name,
|
||||||
Slug = pc.categorie.Slug
|
Slug = pc.categorie.Slug
|
||||||
// ... weitere categorieDto-Felder bei Bedarf
|
}).ToList(),
|
||||||
|
Images = p.Images.Select(img => new ProductImageDto
|
||||||
|
{
|
||||||
|
Id = img.Id,
|
||||||
|
Url = img.Url,
|
||||||
|
IsMainImage = img.IsMainImage,
|
||||||
|
DisplayOrder = img.DisplayOrder
|
||||||
}).ToList()
|
}).ToList()
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
@@ -54,9 +56,9 @@ namespace Webshop.Application.Services.Public
|
|||||||
public async Task<ProductDto?> GetProductBySlugAsync(string slug)
|
public async Task<ProductDto?> GetProductBySlugAsync(string slug)
|
||||||
{
|
{
|
||||||
var product = await _context.Products
|
var product = await _context.Products
|
||||||
.Include(p => p.Productcategories)
|
.Include(p => p.Productcategories).ThenInclude(pc => pc.categorie)
|
||||||
.ThenInclude(pc => pc.categorie)
|
.Include(p => p.Images) // Lade Bilder mit
|
||||||
.FirstOrDefaultAsync(p => p.Slug == slug && p.IsActive); // Nur aktives Produkt finden
|
.FirstOrDefaultAsync(p => p.Slug == slug && p.IsActive);
|
||||||
|
|
||||||
if (product == null)
|
if (product == null)
|
||||||
{
|
{
|
||||||
@@ -67,10 +69,9 @@ namespace Webshop.Application.Services.Public
|
|||||||
{
|
{
|
||||||
Id = product.Id,
|
Id = product.Id,
|
||||||
Name = product.Name,
|
Name = product.Name,
|
||||||
Description = product.Description, // Hier die volle Beschreibung
|
Description = product.Description,
|
||||||
Price = product.Price,
|
Price = product.Price,
|
||||||
SKU = product.SKU,
|
SKU = product.SKU,
|
||||||
ImageUrl = product.ImageUrl,
|
|
||||||
IsInStock = product.IsInStock,
|
IsInStock = product.IsInStock,
|
||||||
Slug = product.Slug,
|
Slug = product.Slug,
|
||||||
categories = product.Productcategories.Select(pc => new CategorieDto
|
categories = product.Productcategories.Select(pc => new CategorieDto
|
||||||
@@ -78,6 +79,13 @@ namespace Webshop.Application.Services.Public
|
|||||||
Id = pc.categorie.Id,
|
Id = pc.categorie.Id,
|
||||||
Name = pc.categorie.Name,
|
Name = pc.categorie.Name,
|
||||||
Slug = pc.categorie.Slug
|
Slug = pc.categorie.Slug
|
||||||
|
}).ToList(),
|
||||||
|
Images = product.Images.Select(img => new ProductImageDto
|
||||||
|
{
|
||||||
|
Id = img.Id,
|
||||||
|
Url = img.Url,
|
||||||
|
IsMainImage = img.IsMainImage,
|
||||||
|
DisplayOrder = img.DisplayOrder
|
||||||
}).ToList()
|
}).ToList()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,71 +1,57 @@
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
// src/Webshop.Domain/Entities/Product.cs
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace Webshop.Domain.Entities;
|
namespace Webshop.Domain.Entities
|
||||||
|
|
||||||
public class Product
|
|
||||||
{
|
{
|
||||||
[Key]
|
public class Product
|
||||||
public Guid Id { get; set; }
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
[Required]
|
[Required, MaxLength(255)]
|
||||||
[MaxLength(255)]
|
public string Name { get; set; } = string.Empty;
|
||||||
public string Name { get; set; }
|
[MaxLength(4000)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? ShortDescription { get; set; }
|
||||||
|
[Required, MaxLength(50)]
|
||||||
|
public string SKU { get; set; } = string.Empty;
|
||||||
|
[Required]
|
||||||
|
public decimal Price { get; set; }
|
||||||
|
public decimal? OldPrice { get; set; }
|
||||||
|
[Required]
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
[Required]
|
||||||
|
public bool IsInStock { get; set; }
|
||||||
|
[Required]
|
||||||
|
public int StockQuantity { get; set; }
|
||||||
|
public decimal? Weight { get; set; }
|
||||||
|
public decimal? Width { get; set; }
|
||||||
|
public decimal? Height { get; set; }
|
||||||
|
public decimal? Length { get; set; }
|
||||||
|
|
||||||
[MaxLength(4000)]
|
// << ENTFERNT: ImageUrl wird durch Images ersetzt >>
|
||||||
public string? Description { get; set; }
|
// public string? ImageUrl { get; set; }
|
||||||
|
|
||||||
[MaxLength(500)]
|
[Required, MaxLength(255)]
|
||||||
public string? ShortDescription { get; set; }
|
public string Slug { get; set; } = string.Empty;
|
||||||
|
[Required]
|
||||||
|
public DateTimeOffset CreatedDate { get; set; } = DateTimeOffset.UtcNow;
|
||||||
|
public DateTimeOffset? LastModifiedDate { get; set; }
|
||||||
|
[ForeignKey(nameof(Supplier))]
|
||||||
|
public Guid? SupplierId { get; set; }
|
||||||
|
public decimal? PurchasePrice { get; set; }
|
||||||
|
|
||||||
[Required]
|
public virtual Supplier? Supplier { get; set; }
|
||||||
[MaxLength(50)]
|
public virtual ICollection<ProductVariant> Variants { get; set; } = new List<ProductVariant>();
|
||||||
public string SKU { get; set; } = string.Empty;
|
public virtual ICollection<Review> Reviews { get; set; } = new List<Review>();
|
||||||
|
public virtual ICollection<ProductDiscount> ProductDiscounts { get; set; } = new List<ProductDiscount>();
|
||||||
|
public virtual ICollection<Productcategorie> Productcategories { get; set; } = new List<Productcategorie>();
|
||||||
|
|
||||||
[Required]
|
// << NEU: Navigation Property zu Bildern >>
|
||||||
public decimal Price { get; set; }
|
public virtual ICollection<ProductImage> Images { get; set; } = new List<ProductImage>();
|
||||||
|
}
|
||||||
public decimal? OldPrice { get; set; }
|
}
|
||||||
|
|
||||||
[Required]
|
|
||||||
public bool IsActive { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public bool IsInStock { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public int StockQuantity { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public decimal? Weight { get; set; }
|
|
||||||
|
|
||||||
public decimal? Width { get; set; }
|
|
||||||
|
|
||||||
public decimal? Height { get; set; }
|
|
||||||
|
|
||||||
public decimal? Length { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(2000)]
|
|
||||||
public string? ImageUrl { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
[MaxLength(255)]
|
|
||||||
public string Slug { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public DateTimeOffset CreatedDate { get; set; }
|
|
||||||
|
|
||||||
public DateTimeOffset? LastModifiedDate { get; set; }
|
|
||||||
|
|
||||||
[ForeignKey(nameof(Supplier))]
|
|
||||||
public Guid? SupplierId { get; set; }
|
|
||||||
|
|
||||||
public decimal? PurchasePrice { get; set; }
|
|
||||||
|
|
||||||
public virtual Supplier? Supplier { get; set; }
|
|
||||||
public virtual ICollection<ProductVariant> Variants { get; set; } = new List<ProductVariant>();
|
|
||||||
public virtual ICollection<Review> Reviews { get; set; } = new List<Review>();
|
|
||||||
public virtual ICollection<ProductDiscount> ProductDiscounts { get; set; } = new List<ProductDiscount>();
|
|
||||||
public virtual ICollection<Productcategorie> Productcategories { get; set; } = new List<Productcategorie>();
|
|
||||||
}
|
|
||||||
26
Webshop.Domain/Entities/ProductImage.cs
Normal file
26
Webshop.Domain/Entities/ProductImage.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// src/Webshop.Domain/Entities/ProductImage.cs
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities
|
||||||
|
{
|
||||||
|
public class ProductImage
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string Url { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public bool IsMainImage { get; set; } = false;
|
||||||
|
|
||||||
|
public int DisplayOrder { get; set; } = 0;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(Product))]
|
||||||
|
public Guid ProductId { get; set; }
|
||||||
|
|
||||||
|
public virtual Product Product { get; set; } = default!;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ namespace Webshop.Infrastructure.Data
|
|||||||
public DbSet<Productcategorie> Productcategories { get; set; } = default!;
|
public DbSet<Productcategorie> Productcategories { get; set; } = default!;
|
||||||
public DbSet<ProductDiscount> ProductDiscounts { get; set; } = default!;
|
public DbSet<ProductDiscount> ProductDiscounts { get; set; } = default!;
|
||||||
public DbSet<CategorieDiscount> categorieDiscounts { get; set; } = default!;
|
public DbSet<CategorieDiscount> categorieDiscounts { get; set; } = default!;
|
||||||
|
public DbSet<ProductImage> ProductImages { get; set; } = default!;
|
||||||
|
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
@@ -67,6 +68,16 @@ namespace Webshop.Infrastructure.Data
|
|||||||
e.Property(p => p.Length).HasPrecision(18, 2);
|
e.Property(p => p.Length).HasPrecision(18, 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<ProductImage>(entity =>
|
||||||
|
{
|
||||||
|
entity.ToTable("ProductImages");
|
||||||
|
|
||||||
|
entity.HasOne(pi => pi.Product)
|
||||||
|
.WithMany(p => p.Images)
|
||||||
|
.HasForeignKey(pi => pi.ProductId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<ProductVariant>()
|
modelBuilder.Entity<ProductVariant>()
|
||||||
.Property(pv => pv.PriceAdjustment).HasPrecision(18, 2);
|
.Property(pv => pv.PriceAdjustment).HasPrecision(18, 2);
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ using Webshop.Infrastructure.Data;
|
|||||||
namespace Webshop.Infrastructure.Migrations
|
namespace Webshop.Infrastructure.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(ApplicationDbContext))]
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
[Migration("20250801133504_FinalSchema")]
|
[Migration("20250806081749_images")]
|
||||||
partial class FinalSchema
|
partial class images
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
@@ -580,10 +580,6 @@ namespace Webshop.Infrastructure.Migrations
|
|||||||
.HasPrecision(18, 2)
|
.HasPrecision(18, 2)
|
||||||
.HasColumnType("numeric(18,2)");
|
.HasColumnType("numeric(18,2)");
|
||||||
|
|
||||||
b.Property<string>("ImageUrl")
|
|
||||||
.HasMaxLength(2000)
|
|
||||||
.HasColumnType("character varying(2000)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
b.Property<bool>("IsActive")
|
||||||
.HasColumnType("boolean");
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
@@ -670,6 +666,32 @@ namespace Webshop.Infrastructure.Migrations
|
|||||||
b.ToTable("ProductDiscounts");
|
b.ToTable("ProductDiscounts");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Webshop.Domain.Entities.ProductImage", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("DisplayOrder")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<bool>("IsMainImage")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<Guid>("ProductId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ProductId");
|
||||||
|
|
||||||
|
b.ToTable("ProductImages", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Webshop.Domain.Entities.ProductVariant", b =>
|
modelBuilder.Entity("Webshop.Domain.Entities.ProductVariant", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@@ -1136,6 +1158,17 @@ namespace Webshop.Infrastructure.Migrations
|
|||||||
b.Navigation("Product");
|
b.Navigation("Product");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Webshop.Domain.Entities.ProductImage", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Webshop.Domain.Entities.Product", "Product")
|
||||||
|
.WithMany("Images")
|
||||||
|
.HasForeignKey("ProductId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Product");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Webshop.Domain.Entities.ProductVariant", b =>
|
modelBuilder.Entity("Webshop.Domain.Entities.ProductVariant", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Webshop.Domain.Entities.Product", "Product")
|
b.HasOne("Webshop.Domain.Entities.Product", "Product")
|
||||||
@@ -1224,6 +1257,8 @@ namespace Webshop.Infrastructure.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Webshop.Domain.Entities.Product", b =>
|
modelBuilder.Entity("Webshop.Domain.Entities.Product", b =>
|
||||||
{
|
{
|
||||||
|
b.Navigation("Images");
|
||||||
|
|
||||||
b.Navigation("ProductDiscounts");
|
b.Navigation("ProductDiscounts");
|
||||||
|
|
||||||
b.Navigation("Productcategories");
|
b.Navigation("Productcategories");
|
||||||
@@ -7,7 +7,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace Webshop.Infrastructure.Migrations
|
namespace Webshop.Infrastructure.Migrations
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class FinalSchema : Migration
|
public partial class images : Migration
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
@@ -432,7 +432,6 @@ namespace Webshop.Infrastructure.Migrations
|
|||||||
Width = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
Width = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
||||||
Height = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
Height = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
||||||
Length = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
Length = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
||||||
ImageUrl = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
|
||||||
Slug = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
Slug = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||||
CreatedDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
CreatedDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
LastModifiedDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
LastModifiedDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||||
@@ -497,6 +496,27 @@ namespace Webshop.Infrastructure.Migrations
|
|||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ProductImages",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Url = table.Column<string>(type: "text", nullable: false),
|
||||||
|
IsMainImage = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
DisplayOrder = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
ProductId = table.Column<Guid>(type: "uuid", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ProductImages", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ProductImages_Products_ProductId",
|
||||||
|
column: x => x.ProductId,
|
||||||
|
principalTable: "Products",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "ProductVariants",
|
name: "ProductVariants",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
@@ -678,6 +698,11 @@ namespace Webshop.Infrastructure.Migrations
|
|||||||
table: "ProductDiscounts",
|
table: "ProductDiscounts",
|
||||||
column: "DiscountId");
|
column: "DiscountId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ProductImages_ProductId",
|
||||||
|
table: "ProductImages",
|
||||||
|
column: "ProductId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Products_SKU",
|
name: "IX_Products_SKU",
|
||||||
table: "Products",
|
table: "Products",
|
||||||
@@ -774,6 +799,9 @@ namespace Webshop.Infrastructure.Migrations
|
|||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "ProductDiscounts");
|
name: "ProductDiscounts");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ProductImages");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "Reviews");
|
name: "Reviews");
|
||||||
|
|
||||||
@@ -577,10 +577,6 @@ namespace Webshop.Infrastructure.Migrations
|
|||||||
.HasPrecision(18, 2)
|
.HasPrecision(18, 2)
|
||||||
.HasColumnType("numeric(18,2)");
|
.HasColumnType("numeric(18,2)");
|
||||||
|
|
||||||
b.Property<string>("ImageUrl")
|
|
||||||
.HasMaxLength(2000)
|
|
||||||
.HasColumnType("character varying(2000)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
b.Property<bool>("IsActive")
|
||||||
.HasColumnType("boolean");
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
@@ -667,6 +663,32 @@ namespace Webshop.Infrastructure.Migrations
|
|||||||
b.ToTable("ProductDiscounts");
|
b.ToTable("ProductDiscounts");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Webshop.Domain.Entities.ProductImage", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("DisplayOrder")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<bool>("IsMainImage")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<Guid>("ProductId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ProductId");
|
||||||
|
|
||||||
|
b.ToTable("ProductImages", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Webshop.Domain.Entities.ProductVariant", b =>
|
modelBuilder.Entity("Webshop.Domain.Entities.ProductVariant", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@@ -1133,6 +1155,17 @@ namespace Webshop.Infrastructure.Migrations
|
|||||||
b.Navigation("Product");
|
b.Navigation("Product");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Webshop.Domain.Entities.ProductImage", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Webshop.Domain.Entities.Product", "Product")
|
||||||
|
.WithMany("Images")
|
||||||
|
.HasForeignKey("ProductId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Product");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Webshop.Domain.Entities.ProductVariant", b =>
|
modelBuilder.Entity("Webshop.Domain.Entities.ProductVariant", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Webshop.Domain.Entities.Product", "Product")
|
b.HasOne("Webshop.Domain.Entities.Product", "Product")
|
||||||
@@ -1221,6 +1254,8 @@ namespace Webshop.Infrastructure.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Webshop.Domain.Entities.Product", b =>
|
modelBuilder.Entity("Webshop.Domain.Entities.Product", b =>
|
||||||
{
|
{
|
||||||
|
b.Navigation("Images");
|
||||||
|
|
||||||
b.Navigation("ProductDiscounts");
|
b.Navigation("ProductDiscounts");
|
||||||
|
|
||||||
b.Navigation("Productcategories");
|
b.Navigation("Productcategories");
|
||||||
|
|||||||
Reference in New Issue
Block a user