Projektdateien hinzufügen.
This commit is contained in:
30
.dockerignore
Normal file
30
.dockerignore
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
|
!**/.gitignore
|
||||||
|
!.git/HEAD
|
||||||
|
!.git/config
|
||||||
|
!.git/packed-refs
|
||||||
|
!.git/refs/heads/**
|
||||||
43
ShopSolution.sln
Normal file
43
ShopSolution.sln
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.10.35122.118
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Webshop.Api", "Webshop.Api\Webshop.Api.csproj", "{CFC6D39C-5455-4091-8831-A07E34E1EF5F}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Webshop.Domain", "Webshop.Domain\Webshop.Domain.csproj", "{959C9FF8-13B2-43AD-9795-878D497EB975}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Webshop.Application", "Webshop.Application\Webshop.Application.csproj", "{48E0F59D-F70D-4AA7-895C-671AD939A4B9}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Webshop.Infrastructure", "Webshop.Infrastructure\Webshop.Infrastructure.csproj", "{546165FB-7B73-40A5-B774-15CEDFEBB9E8}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{CFC6D39C-5455-4091-8831-A07E34E1EF5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CFC6D39C-5455-4091-8831-A07E34E1EF5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CFC6D39C-5455-4091-8831-A07E34E1EF5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CFC6D39C-5455-4091-8831-A07E34E1EF5F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{959C9FF8-13B2-43AD-9795-878D497EB975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{959C9FF8-13B2-43AD-9795-878D497EB975}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{959C9FF8-13B2-43AD-9795-878D497EB975}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{959C9FF8-13B2-43AD-9795-878D497EB975}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{48E0F59D-F70D-4AA7-895C-671AD939A4B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{48E0F59D-F70D-4AA7-895C-671AD939A4B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{48E0F59D-F70D-4AA7-895C-671AD939A4B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{48E0F59D-F70D-4AA7-895C-671AD939A4B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{546165FB-7B73-40A5-B774-15CEDFEBB9E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{546165FB-7B73-40A5-B774-15CEDFEBB9E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{546165FB-7B73-40A5-B774-15CEDFEBB9E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{546165FB-7B73-40A5-B774-15CEDFEBB9E8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {A1FA3277-2297-416D-90D0-6DAE385449BE}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
26
Webshop.Api/Controllers/ProductsController.cs
Normal file
26
Webshop.Api/Controllers/ProductsController.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// src/Webshop.Api/Controllers/ProductsController.cs
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Webshop.Application.DTOs;
|
||||||
|
using Webshop.Application.Services;
|
||||||
|
|
||||||
|
namespace Webshop.Api.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class ProductsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ProductService _productService;
|
||||||
|
|
||||||
|
public ProductsController(ProductService productService)
|
||||||
|
{
|
||||||
|
_productService = productService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<IEnumerable<ProductDto>>> GetAllProducts()
|
||||||
|
{
|
||||||
|
var products = await _productService.GetAllProductsAsync();
|
||||||
|
return Ok(products);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Webshop.Api/Dockerfile
Normal file
25
Webshop.Api/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||||
|
USER app
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 8080
|
||||||
|
EXPOSE 8081
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["Webshop.Api/Webshop.Api.csproj", "Webshop.Api/"]
|
||||||
|
RUN dotnet restore "./Webshop.Api/Webshop.Api.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/Webshop.Api"
|
||||||
|
RUN dotnet build "./Webshop.Api.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
RUN dotnet publish "./Webshop.Api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "Webshop.Api.dll"]
|
||||||
87
Webshop.Api/Program.cs
Normal file
87
Webshop.Api/Program.cs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System.Text;
|
||||||
|
using Webshop.Application.Services;
|
||||||
|
using Webshop.Domain.Interfaces;
|
||||||
|
using Webshop.Infrastructure.Data;
|
||||||
|
using Webshop.Infrastructure.Repositories;
|
||||||
|
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// --- START: DIENSTE ZUM CONTAINER HINZUF<55>GEN ---
|
||||||
|
|
||||||
|
// 1. Datenbank-Kontext (DbContext) registrieren
|
||||||
|
// Sagt der Anwendung, wie sie sich mit der PostgreSQL-Datenbank verbinden soll.
|
||||||
|
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
|
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||||
|
|
||||||
|
// 2. ASP.NET Core Identity f<>r Benutzerverwaltung registrieren
|
||||||
|
// Verwendet unseren DbContext, um Benutzer- und Rollen-Daten zu speichern.
|
||||||
|
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
|
||||||
|
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||||
|
.AddDefaultTokenProviders();
|
||||||
|
|
||||||
|
// 3. JWT-Authentifizierung konfigurieren
|
||||||
|
// Richtet ein, wie die API die von Benutzern gesendeten Tokens validieren soll.
|
||||||
|
var jwtSettings = builder.Configuration.GetSection("JwtSettings");
|
||||||
|
var secretKey = jwtSettings["Secret"] ?? throw new InvalidOperationException("JWT Secret not found");
|
||||||
|
|
||||||
|
builder.Services.AddAuthentication(options =>
|
||||||
|
{
|
||||||
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
})
|
||||||
|
.AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
ValidIssuer = jwtSettings["Issuer"],
|
||||||
|
ValidAudience = jwtSettings["Audience"],
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. Unsere eigenen Interfaces und Klassen registrieren (Dependency Injection)
|
||||||
|
// Sagt: "Immer wenn jemand nach 'IProductRepository' fragt, gib ihm eine neue 'ProductRepository'-Instanz."
|
||||||
|
builder.Services.AddScoped<IProductRepository, ProductRepository>();
|
||||||
|
|
||||||
|
# region Services
|
||||||
|
builder.Services.AddScoped<ProductService>();
|
||||||
|
# endregion
|
||||||
|
// 5. Controller und Swagger/OpenAPI hinzuf<75>gen
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
|
// --- ENDE: DIENSTE ZUM CONTAINER HINZUF<55>GEN ---
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// --- START: HTTP REQUEST PIPELINE KONFIGURIEREN ---
|
||||||
|
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
// WICHTIG: Die Reihenfolge ist entscheidend!
|
||||||
|
// Zuerst pr<70>fen, wer der Benutzer ist (Authentifizierung)...
|
||||||
|
app.UseAuthentication();
|
||||||
|
// ...dann pr<70>fen, was der Benutzer darf (Autorisierung).
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
// --- ENDE: HTTP REQUEST PIPELINE KONFIGURIEREN ---
|
||||||
|
|
||||||
|
app.Run();
|
||||||
52
Webshop.Api/Properties/launchSettings.json
Normal file
52
Webshop.Api/Properties/launchSettings.json
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "http://localhost:5176"
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "https://localhost:7270;http://localhost:5176"
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Container (Dockerfile)": {
|
||||||
|
"commandName": "Docker",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_HTTPS_PORTS": "8081",
|
||||||
|
"ASPNETCORE_HTTP_PORTS": "8080"
|
||||||
|
},
|
||||||
|
"publishAllPorts": true,
|
||||||
|
"useSSL": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:60457",
|
||||||
|
"sslPort": 44372
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
Webshop.Api/Webshop.Api.csproj
Normal file
26
Webshop.Api/Webshop.Api.csproj
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UserSecretsId>461c56b2-b056-42a9-8ce5-ccc441ef8a37</UserSecretsId>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.18" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Webshop.Application\Webshop.Application.csproj" />
|
||||||
|
<ProjectReference Include="..\Webshop.Infrastructure\Webshop.Infrastructure.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
6
Webshop.Api/Webshop.Api.http
Normal file
6
Webshop.Api/Webshop.Api.http
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@Webshop.Api_HostAddress = http://localhost:5176
|
||||||
|
|
||||||
|
GET {{Webshop.Api_HostAddress}}/weatherforecast/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
8
Webshop.Api/appsettings.Development.json
Normal file
8
Webshop.Api/appsettings.Development.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Webshop.Api/appsettings.json
Normal file
18
Webshop.Api/appsettings.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"DefaultConnection": "Host=postgres-db;Port=5432;Database=webshopdb;Username=postgres;Password=MEIN_DEBUG_PASSWORT"
|
||||||
|
},
|
||||||
|
"JwtSettings": {
|
||||||
|
"Secret": "MEIN_DEBUG_PASSWORT",
|
||||||
|
"Issuer": "https://dein-webshop.com",
|
||||||
|
"Audience": "webshop-users",
|
||||||
|
"ExpirationMinutes": 120
|
||||||
|
}
|
||||||
|
}
|
||||||
7
Webshop.Application/Class1.cs
Normal file
7
Webshop.Application/Class1.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Webshop.Application
|
||||||
|
{
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
12
Webshop.Application/DTOs/ProductDto.cs
Normal file
12
Webshop.Application/DTOs/ProductDto.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// src/Webshop.Application/DTOs/ProductDto.cs
|
||||||
|
namespace Webshop.Application.DTOs
|
||||||
|
{
|
||||||
|
public class ProductDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string Description { get; set; } = string.Empty;
|
||||||
|
public decimal Price { get; set; }
|
||||||
|
public string Sku { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Webshop.Application/Services/ProductService.cs
Normal file
32
Webshop.Application/Services/ProductService.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// src/Webshop.Application/Services/ProductService.cs
|
||||||
|
using Webshop.Application.DTOs;
|
||||||
|
using Webshop.Domain.Interfaces;
|
||||||
|
|
||||||
|
namespace Webshop.Application.Services
|
||||||
|
{
|
||||||
|
public class ProductService
|
||||||
|
{
|
||||||
|
private readonly IProductRepository _productRepository;
|
||||||
|
|
||||||
|
public ProductService(IProductRepository productRepository)
|
||||||
|
{
|
||||||
|
_productRepository = productRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<ProductDto>> GetAllProductsAsync()
|
||||||
|
{
|
||||||
|
var productsFromDb = await _productRepository.GetAllProductsAsync();
|
||||||
|
|
||||||
|
var productDtos = productsFromDb.Select(p => new ProductDto
|
||||||
|
{
|
||||||
|
Id = p.Id,
|
||||||
|
Name = p.Name,
|
||||||
|
Description = p.Description,
|
||||||
|
Price = p.Price,
|
||||||
|
Sku = p.SKU
|
||||||
|
});
|
||||||
|
|
||||||
|
return productDtos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Webshop.Application/Webshop.Application.csproj
Normal file
17
Webshop.Application/Webshop.Application.csproj
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Webshop.Domain\Webshop.Domain.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
7
Webshop.Domain/Class1.cs
Normal file
7
Webshop.Domain/Class1.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Webshop.Domain
|
||||||
|
{
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
56
Webshop.Domain/Entities/Adress.cs
Normal file
56
Webshop.Domain/Entities/Adress.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verwendet für Kundenadressen, Rechnungs- und Lieferadressen von Bestellungen oder Lieferanten.
|
||||||
|
/// </summary>
|
||||||
|
public class Address
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(Customer))]
|
||||||
|
public Guid? CustomerId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string? AddressType { get; set; } // z.B. "Billing", "Shipping"
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string Street { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string? Street2 { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string City { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string? State { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(20)]
|
||||||
|
public string PostalCode { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string Country { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string? CompanyName { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string LastName { get; set; }
|
||||||
|
|
||||||
|
// Navigation Property
|
||||||
|
public virtual Customer? Customer { get; set; }
|
||||||
|
}
|
||||||
45
Webshop.Domain/Entities/Category.cs
Normal file
45
Webshop.Domain/Entities/Category.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Zum Gruppieren und Organisieren von Produkten.
|
||||||
|
/// </summary>
|
||||||
|
public class Category
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(1000)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
// Unique-Constraint wird typischerweise via Fluent API konfiguriert
|
||||||
|
[Required]
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string Slug { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(ParentCategory))]
|
||||||
|
public Guid? ParentCategoryId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(2000)]
|
||||||
|
public string? ImageUrl { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
|
||||||
|
// Navigation Properties
|
||||||
|
public virtual Category? ParentCategory { get; set; }
|
||||||
|
public virtual ICollection<Category> SubCategories { get; set; } = new List<Category>();
|
||||||
|
public virtual ICollection<ProductCategory> ProductCategories { get; set; } = new List<ProductCategory>();
|
||||||
|
public virtual ICollection<CategoryDiscount> CategoryDiscounts { get; set; } = new List<CategoryDiscount>();
|
||||||
|
}
|
||||||
23
Webshop.Domain/Entities/CategoryDiscount.cs
Normal file
23
Webshop.Domain/Entities/CategoryDiscount.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verknüpfungstabelle für die Viele-zu-Viele-Beziehung zwischen Category und Discount.
|
||||||
|
/// </summary>
|
||||||
|
public class CategoryDiscount
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(Category))]
|
||||||
|
public Guid CategoryId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(Discount))]
|
||||||
|
public Guid DiscountId { get; set; }
|
||||||
|
|
||||||
|
// Navigation Properties
|
||||||
|
public virtual Category Category { get; set; }
|
||||||
|
public virtual Discount Discount { get; set; }
|
||||||
|
}
|
||||||
50
Webshop.Domain/Entities/Customer.cs
Normal file
50
Webshop.Domain/Entities/Customer.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Für registrierte Nutzer des Webshops. Dies ist die Schnittstelle zu ASP.NET Core Identity.
|
||||||
|
/// </summary>
|
||||||
|
public class Customer
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
// Unique-Constraint und Foreign Key werden via Fluent API konfiguriert
|
||||||
|
[Required]
|
||||||
|
[MaxLength(450)]
|
||||||
|
public string AspNetUserId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string LastName { get; set; }
|
||||||
|
|
||||||
|
// Unique-Constraint wird von Identity verwaltet
|
||||||
|
[Required]
|
||||||
|
[MaxLength(256)]
|
||||||
|
[EmailAddress]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(20)]
|
||||||
|
[Phone]
|
||||||
|
public string? PhoneNumber { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public DateTimeOffset CreatedDate { get; set; }
|
||||||
|
|
||||||
|
public DateTimeOffset? LastLoginDate { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
// Navigation Properties
|
||||||
|
public virtual ICollection<Address> Addresses { get; set; } = new List<Address>();
|
||||||
|
public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
|
||||||
|
public virtual ICollection<Review> Reviews { get; set; } = new List<Review>();
|
||||||
|
}
|
||||||
55
Webshop.Domain/Entities/Discount.cs
Normal file
55
Webshop.Domain/Entities/Discount.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verwaltung von Rabatten auf Produkte oder Kategorien.
|
||||||
|
/// </summary>
|
||||||
|
public class Discount
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string DiscountType { get; set; } // "Percentage", "FixedAmount"
|
||||||
|
|
||||||
|
// Precision wird via Fluent API konfiguriert
|
||||||
|
[Required]
|
||||||
|
public decimal DiscountValue { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public DateTimeOffset StartDate { get; set; }
|
||||||
|
|
||||||
|
public DateTimeOffset? EndDate { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool RequiresCouponCode { get; set; }
|
||||||
|
|
||||||
|
// Unique-Constraint wird via Fluent API konfiguriert
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string? CouponCode { get; set; }
|
||||||
|
|
||||||
|
public decimal? MinimumOrderAmount { get; set; }
|
||||||
|
|
||||||
|
public int? MaximumUsageCount { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int CurrentUsageCount { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(1000)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
// Navigation Properties
|
||||||
|
public virtual ICollection<ProductDiscount> ProductDiscounts { get; set; } = new List<ProductDiscount>();
|
||||||
|
public virtual ICollection<CategoryDiscount> CategoryDiscounts { get; set; } = new List<CategoryDiscount>();
|
||||||
|
}
|
||||||
90
Webshop.Domain/Entities/Order.cs
Normal file
90
Webshop.Domain/Entities/Order.cs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Die Details jeder Kundenbestellung, inklusive Gastbestellungen.
|
||||||
|
/// </summary>
|
||||||
|
public class Order
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
// Unique-Constraint wird via Fluent API konfiguriert
|
||||||
|
[Required]
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string OrderNumber { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(Customer))]
|
||||||
|
public Guid? CustomerId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(256)]
|
||||||
|
[EmailAddress]
|
||||||
|
public string? GuestEmail { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(20)]
|
||||||
|
[Phone]
|
||||||
|
public string? GuestPhoneNumber { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public DateTimeOffset OrderDate { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string OrderStatus { get; set; }
|
||||||
|
|
||||||
|
// Precision wird via Fluent API konfiguriert
|
||||||
|
[Required]
|
||||||
|
public decimal OrderTotal { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public decimal ShippingCost { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public decimal TaxAmount { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public decimal DiscountAmount { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string PaymentStatus { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string PaymentMethod { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(PaymentMethodInfo))]
|
||||||
|
public Guid? PaymentMethodId { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(ShippingMethodInfo))]
|
||||||
|
public Guid? ShippingMethodId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string? TransactionId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(BillingAddress))]
|
||||||
|
public Guid BillingAddressId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(ShippingAddress))]
|
||||||
|
public Guid ShippingAddressId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(1000)]
|
||||||
|
public string? CustomerNotes { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(1000)]
|
||||||
|
public string? AdminNotes { get; set; }
|
||||||
|
|
||||||
|
// Navigation Properties
|
||||||
|
public virtual Customer? Customer { get; set; }
|
||||||
|
public virtual Address BillingAddress { get; set; }
|
||||||
|
public virtual Address ShippingAddress { get; set; }
|
||||||
|
public virtual PaymentMethod? PaymentMethodInfo { get; set; }
|
||||||
|
public virtual ShippingMethod? ShippingMethodInfo { get; set; }
|
||||||
|
public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
|
||||||
|
}
|
||||||
47
Webshop.Domain/Entities/OrderItem.cs
Normal file
47
Webshop.Domain/Entities/OrderItem.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Die einzelnen Artikel innerhalb einer Bestellung.
|
||||||
|
/// </summary>
|
||||||
|
public class OrderItem
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(Order))]
|
||||||
|
public Guid OrderId { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(Product))]
|
||||||
|
public Guid? ProductId { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(ProductVariant))]
|
||||||
|
public Guid? ProductVariantId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string ProductName { get; set; } // Snapshot
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string ProductSKU { get; set; } // Snapshot
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int Quantity { get; set; }
|
||||||
|
|
||||||
|
// Precision wird via Fluent API konfiguriert
|
||||||
|
[Required]
|
||||||
|
public decimal UnitPrice { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public decimal TotalPrice { get; set; }
|
||||||
|
|
||||||
|
// Navigation Properties
|
||||||
|
public virtual Order Order { get; set; }
|
||||||
|
public virtual Product? Product { get; set; }
|
||||||
|
public virtual ProductVariant? ProductVariant { get; set; }
|
||||||
|
}
|
||||||
29
Webshop.Domain/Entities/PaymentMethod.cs
Normal file
29
Webshop.Domain/Entities/PaymentMethod.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Konfigurierbare Zahlungsoptionen für den Checkout.
|
||||||
|
/// </summary>
|
||||||
|
public class PaymentMethod
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string PaymentGatewayType { get; set; } // "Stripe", "PayPal", etc.
|
||||||
|
|
||||||
|
public decimal? ProcessingFee { get; set; }
|
||||||
|
}
|
||||||
86
Webshop.Domain/Entities/Product.cs
Normal file
86
Webshop.Domain/Entities/Product.cs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 1. Das zentrale Modell für deine Verkaufsartikel.
|
||||||
|
/// </summary>
|
||||||
|
public class Product
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(4000)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? ShortDescription { get; set; }
|
||||||
|
|
||||||
|
// Unique-Constraint wird typischerweise via Fluent API konfiguriert:
|
||||||
|
// modelBuilder.Entity<Product>().HasIndex(p => p.SKU).IsUnique();
|
||||||
|
[Required]
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string SKU { get; set; }
|
||||||
|
|
||||||
|
// Precision wird typischerweise via Fluent API konfiguriert:
|
||||||
|
// modelBuilder.Entity<Product>().Property(p => p.Price).HasPrecision(18, 2);
|
||||||
|
[Required]
|
||||||
|
public decimal Price { get; set; }
|
||||||
|
|
||||||
|
// modelBuilder.Entity<Product>().Property(p => p.OldPrice).HasPrecision(18, 2);
|
||||||
|
public decimal? OldPrice { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool IsInStock { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int StockQuantity { get; set; }
|
||||||
|
|
||||||
|
// modelBuilder.Entity<Product>().Property(p => p.Weight).HasPrecision(18, 3);
|
||||||
|
public decimal? Weight { get; set; }
|
||||||
|
|
||||||
|
// modelBuilder.Entity<Product>().Property(p => p.Width).HasPrecision(18, 2);
|
||||||
|
public decimal? Width { get; set; }
|
||||||
|
|
||||||
|
// modelBuilder.Entity<Product>().Property(p => p.Height).HasPrecision(18, 2);
|
||||||
|
public decimal? Height { get; set; }
|
||||||
|
|
||||||
|
// modelBuilder.Entity<Product>().Property(p => p.Length).HasPrecision(18, 2);
|
||||||
|
public decimal? Length { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(2000)]
|
||||||
|
public string? ImageUrl { get; set; }
|
||||||
|
|
||||||
|
// Unique-Constraint wird typischerweise via Fluent API konfiguriert:
|
||||||
|
// modelBuilder.Entity<Product>().HasIndex(p => p.Slug).IsUnique();
|
||||||
|
[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; }
|
||||||
|
|
||||||
|
// modelBuilder.Entity<Product>().Property(p => p.PurchasePrice).HasPrecision(18, 2);
|
||||||
|
public decimal? PurchasePrice { get; set; }
|
||||||
|
|
||||||
|
// Navigation Properties
|
||||||
|
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<ProductCategory> ProductCategories { get; set; } = new List<ProductCategory>();
|
||||||
|
}
|
||||||
24
Webshop.Domain/Entities/ProductCategory.cs
Normal file
24
Webshop.Domain/Entities/ProductCategory.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verknüpfungstabelle für die Viele-zu-Viele-Beziehung zwischen Category und Discount.
|
||||||
|
/// </summary>
|
||||||
|
public class ProductCategory
|
||||||
|
{
|
||||||
|
// Composite Primary Key wird via Fluent API in Ihrem DbContext konfiguriert:
|
||||||
|
// modelBuilder.Entity<ProductCategory>().HasKey(pc => new { pc.ProductId, pc.CategoryId });
|
||||||
|
|
||||||
|
[ForeignKey(nameof(Product))]
|
||||||
|
public Guid ProductId { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(Category))]
|
||||||
|
public Guid CategoryId { get; set; }
|
||||||
|
|
||||||
|
// Navigation Properties
|
||||||
|
public virtual Product Product { get; set; }
|
||||||
|
public virtual Category Category { get; set; }
|
||||||
|
}
|
||||||
23
Webshop.Domain/Entities/ProductDiscount.cs
Normal file
23
Webshop.Domain/Entities/ProductDiscount.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verknüpfungstabelle für die Viele-zu-Viele-Beziehung zwischen Product und Discount.
|
||||||
|
/// </summary>
|
||||||
|
public class ProductDiscount
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(Product))]
|
||||||
|
public Guid ProductId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(Discount))]
|
||||||
|
public Guid DiscountId { get; set; }
|
||||||
|
|
||||||
|
// Navigation Properties
|
||||||
|
public virtual Product Product { get; set; }
|
||||||
|
public virtual Discount Discount { get; set; }
|
||||||
|
}
|
||||||
46
Webshop.Domain/Entities/ProductVariant.cs
Normal file
46
Webshop.Domain/Entities/ProductVariant.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Für Produkte mit unterschiedlichen Attributen (z.B. Größe, Farbe) und eigenem Lagerbestand.
|
||||||
|
/// </summary>
|
||||||
|
public class ProductVariant
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(Product))]
|
||||||
|
public Guid ProductId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string Name { get; set; } // z.B. "Farbe", "Größe"
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string Value { get; set; } // z.B. "Rot", "XL"
|
||||||
|
|
||||||
|
// Unique-Constraint wird typischerweise via Fluent API konfiguriert
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string? SKU { get; set; }
|
||||||
|
|
||||||
|
// Precision wird via Fluent API konfiguriert
|
||||||
|
[Required]
|
||||||
|
public decimal PriceAdjustment { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int StockQuantity { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(2000)]
|
||||||
|
public string? ImageUrl { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
// Navigation Property
|
||||||
|
public virtual Product Product { get; set; }
|
||||||
|
}
|
||||||
41
Webshop.Domain/Entities/Review.cs
Normal file
41
Webshop.Domain/Entities/Review.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Kundenbewertungen für Produkte.
|
||||||
|
/// </summary>
|
||||||
|
public class Review
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(Product))]
|
||||||
|
public Guid ProductId { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(Customer))]
|
||||||
|
public Guid? CustomerId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[Range(1, 5)]
|
||||||
|
public int Rating { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string? Title { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(2000)]
|
||||||
|
public string? Comment { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public DateTimeOffset ReviewDate { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool IsApproved { get; set; }
|
||||||
|
|
||||||
|
// Navigation Properties
|
||||||
|
public virtual Product Product { get; set; }
|
||||||
|
public virtual Customer? Customer { get; set; }
|
||||||
|
}
|
||||||
30
Webshop.Domain/Entities/Setting.cs
Normal file
30
Webshop.Domain/Entities/Setting.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generische Tabelle für flexible Shop-Einstellungen.
|
||||||
|
/// </summary>
|
||||||
|
public class Setting
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
// Unique-Constraint wird via Fluent API konfiguriert
|
||||||
|
[Required]
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string Key { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(2000)]
|
||||||
|
public string? Value { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string? Group { get; set; } // "Shipping", "Order Processing", etc.
|
||||||
|
}
|
||||||
35
Webshop.Domain/Entities/ShippingMethod.cs
Normal file
35
Webshop.Domain/Entities/ShippingMethod.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Konfigurierbare Versandoptionen für den Checkout.
|
||||||
|
/// </summary>
|
||||||
|
public class ShippingMethod
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
// Precision wird via Fluent API konfiguriert
|
||||||
|
[Required]
|
||||||
|
public decimal BaseCost { get; set; }
|
||||||
|
|
||||||
|
public decimal? MinimumOrderAmount { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string? EstimatedDeliveryTime { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool RequiresTracking { get; set; }
|
||||||
|
}
|
||||||
40
Webshop.Domain/Entities/Supplier.cs
Normal file
40
Webshop.Domain/Entities/Supplier.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Details zu den Produktlieferanten.
|
||||||
|
/// </summary>
|
||||||
|
public class Supplier
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string? ContactPerson { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(256)]
|
||||||
|
[EmailAddress]
|
||||||
|
public string? Email { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
[Phone]
|
||||||
|
public string? PhoneNumber { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(Address))]
|
||||||
|
public Guid? AddressId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(1000)]
|
||||||
|
public string? Notes { get; set; }
|
||||||
|
|
||||||
|
// Navigation Properties
|
||||||
|
public virtual Address? Address { get; set; }
|
||||||
|
public virtual ICollection<Product> Products { get; set; } = new List<Product>();
|
||||||
|
}
|
||||||
6
Webshop.Domain/Enums/AddressType.cs
Normal file
6
Webshop.Domain/Enums/AddressType.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
public enum AddressType
|
||||||
|
{
|
||||||
|
Billing,
|
||||||
|
Shipping,
|
||||||
|
CustomerDefault
|
||||||
|
}
|
||||||
1
Webshop.Domain/Enums/DiscountType.cs
Normal file
1
Webshop.Domain/Enums/DiscountType.cs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
public enum DiscountType { Percentage, FixedAmount }
|
||||||
1
Webshop.Domain/Enums/OrderStatus.cs
Normal file
1
Webshop.Domain/Enums/OrderStatus.cs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
public enum OrderStatus { Pending, Processing, Shipped, Delivered, Cancelled, Refunded }
|
||||||
7
Webshop.Domain/Enums/PaymentGatewayType.cs
Normal file
7
Webshop.Domain/Enums/PaymentGatewayType.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
public enum PaymentGatewayType
|
||||||
|
{
|
||||||
|
Stripe,
|
||||||
|
PayPal,
|
||||||
|
Klarna,
|
||||||
|
Manual // z.B. Vorkasse
|
||||||
|
}
|
||||||
1
Webshop.Domain/Enums/PaymentStatus.cs
Normal file
1
Webshop.Domain/Enums/PaymentStatus.cs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
public enum PaymentStatus { Pending, Paid, Failed, Refunded }
|
||||||
14
Webshop.Domain/Interfaces/IProductRepository.cs
Normal file
14
Webshop.Domain/Interfaces/IProductRepository.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// src/Webshop.Domain/Interfaces/IProductRepository.cs
|
||||||
|
using Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
namespace Webshop.Domain.Interfaces
|
||||||
|
{
|
||||||
|
public interface IProductRepository
|
||||||
|
{
|
||||||
|
Task<Product?> GetProductByIdAsync(Guid id);
|
||||||
|
Task<IEnumerable<Product>> GetAllProductsAsync();
|
||||||
|
Task AddProductAsync(Product product);
|
||||||
|
Task UpdateProductAsync(Product product);
|
||||||
|
Task DeleteProductAsync(Guid id);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Webshop.Domain/Webshop.Domain.csproj
Normal file
9
Webshop.Domain/Webshop.Domain.csproj
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
127
Webshop.Infrastructure/Data/ApplicationDbContext.cs
Normal file
127
Webshop.Infrastructure/Data/ApplicationDbContext.cs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Webshop.Domain.Entities;
|
||||||
|
|
||||||
|
namespace Webshop.Infrastructure.Data
|
||||||
|
{
|
||||||
|
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
|
||||||
|
{
|
||||||
|
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbSet<Product> Products { get; set; } = default!;
|
||||||
|
public DbSet<ProductVariant> ProductVariants { get; set; } = default!;
|
||||||
|
public DbSet<Category> Categories { get; set; } = default!;
|
||||||
|
public DbSet<Customer> Customers { get; set; } = default!;
|
||||||
|
public DbSet<Address> Addresses { get; set; } = default!;
|
||||||
|
public DbSet<Order> Orders { get; set; } = default!;
|
||||||
|
public DbSet<OrderItem> OrderItems { get; set; } = default!;
|
||||||
|
public DbSet<Review> Reviews { get; set; } = default!;
|
||||||
|
public DbSet<Discount> Discounts { get; set; } = default!;
|
||||||
|
public DbSet<Supplier> Suppliers { get; set; } = default!;
|
||||||
|
public DbSet<ShippingMethod> ShippingMethods { get; set; } = default!;
|
||||||
|
public DbSet<PaymentMethod> PaymentMethods { get; set; } = default!;
|
||||||
|
public DbSet<Setting> Settings { get; set; } = default!;
|
||||||
|
|
||||||
|
public DbSet<ProductCategory> ProductCategories { get; set; } = default!;
|
||||||
|
public DbSet<ProductDiscount> ProductDiscounts { get; set; } = default!;
|
||||||
|
public DbSet<CategoryDiscount> CategoryDiscounts { get; set; } = default!;
|
||||||
|
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
|
foreach (var entity in modelBuilder.Model.GetEntityTypes())
|
||||||
|
{
|
||||||
|
var tableName = entity.GetTableName();
|
||||||
|
|
||||||
|
if (tableName != null && tableName.StartsWith("AspNet"))
|
||||||
|
{
|
||||||
|
entity.SetTableName(tableName.Substring(6));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modelBuilder.Entity<ProductCategory>().HasKey(pc => new { pc.ProductId, pc.CategoryId });
|
||||||
|
modelBuilder.Entity<ProductDiscount>().HasKey(pd => new { pd.ProductId, pd.DiscountId });
|
||||||
|
modelBuilder.Entity<CategoryDiscount>().HasKey(cd => new { cd.CategoryId, cd.DiscountId });
|
||||||
|
|
||||||
|
modelBuilder.Entity<Product>().HasIndex(p => p.SKU).IsUnique();
|
||||||
|
modelBuilder.Entity<Product>().HasIndex(p => p.Slug).IsUnique();
|
||||||
|
modelBuilder.Entity<Category>().HasIndex(c => c.Slug).IsUnique();
|
||||||
|
modelBuilder.Entity<Discount>().HasIndex(d => d.CouponCode).IsUnique().HasFilter("\"CouponCode\" IS NOT NULL");
|
||||||
|
modelBuilder.Entity<Setting>().HasIndex(s => s.Key).IsUnique();
|
||||||
|
modelBuilder.Entity<Order>().HasIndex(o => o.OrderNumber).IsUnique();
|
||||||
|
|
||||||
|
modelBuilder.Entity<Product>(e => {
|
||||||
|
e.Property(p => p.Price).HasPrecision(18, 2);
|
||||||
|
e.Property(p => p.OldPrice).HasPrecision(18, 2);
|
||||||
|
e.Property(p => p.PurchasePrice).HasPrecision(18, 2);
|
||||||
|
e.Property(p => p.Weight).HasPrecision(18, 3);
|
||||||
|
e.Property(p => p.Width).HasPrecision(18, 2);
|
||||||
|
e.Property(p => p.Height).HasPrecision(18, 2);
|
||||||
|
e.Property(p => p.Length).HasPrecision(18, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<ProductVariant>()
|
||||||
|
.Property(pv => pv.PriceAdjustment).HasPrecision(18, 2);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Order>(e => {
|
||||||
|
e.Property(o => o.OrderTotal).HasPrecision(18, 2);
|
||||||
|
e.Property(o => o.ShippingCost).HasPrecision(18, 2);
|
||||||
|
e.Property(o => o.TaxAmount).HasPrecision(18, 2);
|
||||||
|
e.Property(o => o.DiscountAmount).HasPrecision(18, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<OrderItem>(e => {
|
||||||
|
e.Property(oi => oi.UnitPrice).HasPrecision(18, 2);
|
||||||
|
e.Property(oi => oi.TotalPrice).HasPrecision(18, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<Discount>(e => {
|
||||||
|
e.Property(d => d.DiscountValue).HasPrecision(18, 2);
|
||||||
|
e.Property(d => d.MinimumOrderAmount).HasPrecision(18, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<ShippingMethod>(e => {
|
||||||
|
e.Property(sm => sm.BaseCost).HasPrecision(18, 2);
|
||||||
|
e.Property(sm => sm.MinimumOrderAmount).HasPrecision(18, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<PaymentMethod>()
|
||||||
|
.Property(pm => pm.ProcessingFee).HasPrecision(18, 2);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Category>()
|
||||||
|
.HasOne(c => c.ParentCategory)
|
||||||
|
.WithMany(c => c.SubCategories)
|
||||||
|
.HasForeignKey(c => c.ParentCategoryId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
modelBuilder.Entity<OrderItem>()
|
||||||
|
.HasOne(oi => oi.Product)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(oi => oi.ProductId)
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
modelBuilder.Entity<OrderItem>()
|
||||||
|
.HasOne(oi => oi.ProductVariant)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(oi => oi.ProductVariantId)
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Order>()
|
||||||
|
.HasOne(o => o.BillingAddress)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(o => o.BillingAddressId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Order>()
|
||||||
|
.HasOne(o => o.ShippingAddress)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(o => o.ShippingAddressId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1220
Webshop.Infrastructure/Migrations/20250721175118_InitialCreate.Designer.cs
generated
Normal file
1220
Webshop.Infrastructure/Migrations/20250721175118_InitialCreate.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,818 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Webshop.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class InitialCreate : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Categories",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||||
|
Slug = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||||
|
ParentCategoryId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
ImageUrl = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||||
|
IsActive = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
DisplayOrder = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Categories", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Categories_Categories_ParentCategoryId",
|
||||||
|
column: x => x.ParentCategoryId,
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Customers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
AspNetUserId = table.Column<string>(type: "character varying(450)", maxLength: 450, nullable: false),
|
||||||
|
FirstName = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
LastName = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
|
PhoneNumber = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
|
||||||
|
CreatedDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
LastLoginDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||||
|
IsActive = table.Column<bool>(type: "boolean", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Customers", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Discounts",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||||
|
DiscountType = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
DiscountValue = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
StartDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
EndDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||||
|
IsActive = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
RequiresCouponCode = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
CouponCode = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
|
||||||
|
MinimumOrderAmount = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
||||||
|
MaximumUsageCount = table.Column<int>(type: "integer", nullable: true),
|
||||||
|
CurrentUsageCount = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Discounts", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PaymentMethods",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||||
|
IsActive = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
PaymentGatewayType = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
ProcessingFee = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PaymentMethods", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Roles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Roles", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Settings",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Key = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||||
|
Value = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||||
|
Description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||||
|
IsActive = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
Group = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Settings", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ShippingMethods",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||||
|
BaseCost = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
MinimumOrderAmount = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
||||||
|
IsActive = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
EstimatedDeliveryTime = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||||
|
RequiresTracking = table.Column<bool>(type: "boolean", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ShippingMethods", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Users",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "text", nullable: false),
|
||||||
|
UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
NormalizedEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
EmailConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
PasswordHash = table.Column<string>(type: "text", nullable: true),
|
||||||
|
SecurityStamp = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
|
||||||
|
PhoneNumber = table.Column<string>(type: "text", nullable: true),
|
||||||
|
PhoneNumberConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
TwoFactorEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
LockoutEnd = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||||
|
LockoutEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
AccessFailedCount = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Users", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Addresses",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
CustomerId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
AddressType = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
|
||||||
|
Street = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||||
|
Street2 = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||||
|
City = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
State = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||||
|
PostalCode = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
Country = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
CompanyName = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||||
|
FirstName = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
LastName = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Addresses", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Addresses_Customers_CustomerId",
|
||||||
|
column: x => x.CustomerId,
|
||||||
|
principalTable: "Customers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "CategoryDiscounts",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
CategoryId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
DiscountId = table.Column<Guid>(type: "uuid", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_CategoryDiscounts", x => new { x.CategoryId, x.DiscountId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_CategoryDiscounts_Categories_CategoryId",
|
||||||
|
column: x => x.CategoryId,
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_CategoryDiscounts_Discounts_DiscountId",
|
||||||
|
column: x => x.DiscountId,
|
||||||
|
principalTable: "Discounts",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "RoleClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
RoleId = table.Column<string>(type: "text", nullable: false),
|
||||||
|
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ClaimValue = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_RoleClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_RoleClaims_Roles_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "Roles",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
UserId = table.Column<string>(type: "text", nullable: false),
|
||||||
|
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ClaimValue = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserClaims_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserLogins",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
LoginProvider = table.Column<string>(type: "text", nullable: false),
|
||||||
|
ProviderKey = table.Column<string>(type: "text", nullable: false),
|
||||||
|
ProviderDisplayName = table.Column<string>(type: "text", nullable: true),
|
||||||
|
UserId = table.Column<string>(type: "text", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserLogins_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<string>(type: "text", nullable: false),
|
||||||
|
RoleId = table.Column<string>(type: "text", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserRoles", x => new { x.UserId, x.RoleId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserRoles_Roles_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "Roles",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserRoles_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserTokens",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<string>(type: "text", nullable: false),
|
||||||
|
LoginProvider = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Value = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserTokens_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Orders",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
OrderNumber = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
CustomerId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
GuestEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
GuestPhoneNumber = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
|
||||||
|
OrderDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
OrderStatus = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
OrderTotal = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
ShippingCost = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
TaxAmount = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
DiscountAmount = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
PaymentStatus = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
PaymentMethod = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
PaymentMethodId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
ShippingMethodId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
TransactionId = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||||
|
BillingAddressId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
ShippingAddressId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
CustomerNotes = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||||
|
AdminNotes = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Orders", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Orders_Addresses_BillingAddressId",
|
||||||
|
column: x => x.BillingAddressId,
|
||||||
|
principalTable: "Addresses",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Orders_Addresses_ShippingAddressId",
|
||||||
|
column: x => x.ShippingAddressId,
|
||||||
|
principalTable: "Addresses",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Orders_Customers_CustomerId",
|
||||||
|
column: x => x.CustomerId,
|
||||||
|
principalTable: "Customers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Orders_PaymentMethods_PaymentMethodId",
|
||||||
|
column: x => x.PaymentMethodId,
|
||||||
|
principalTable: "PaymentMethods",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Orders_ShippingMethods_ShippingMethodId",
|
||||||
|
column: x => x.ShippingMethodId,
|
||||||
|
principalTable: "ShippingMethods",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Suppliers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||||
|
ContactPerson = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||||
|
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
PhoneNumber = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
|
||||||
|
AddressId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
Notes = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Suppliers", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Suppliers_Addresses_AddressId",
|
||||||
|
column: x => x.AddressId,
|
||||||
|
principalTable: "Addresses",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Products",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(4000)", maxLength: 4000, nullable: true),
|
||||||
|
ShortDescription = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||||
|
SKU = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
Price = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
OldPrice = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
||||||
|
IsActive = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
IsInStock = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
StockQuantity = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
Weight = table.Column<decimal>(type: "numeric(18,3)", precision: 18, scale: 3, 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),
|
||||||
|
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),
|
||||||
|
CreatedDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
LastModifiedDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||||
|
SupplierId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
PurchasePrice = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Products", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Products_Suppliers_SupplierId",
|
||||||
|
column: x => x.SupplierId,
|
||||||
|
principalTable: "Suppliers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ProductCategories",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
ProductId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
CategoryId = table.Column<Guid>(type: "uuid", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ProductCategories", x => new { x.ProductId, x.CategoryId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ProductCategories_Categories_CategoryId",
|
||||||
|
column: x => x.CategoryId,
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ProductCategories_Products_ProductId",
|
||||||
|
column: x => x.ProductId,
|
||||||
|
principalTable: "Products",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ProductDiscounts",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
ProductId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
DiscountId = table.Column<Guid>(type: "uuid", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ProductDiscounts", x => new { x.ProductId, x.DiscountId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ProductDiscounts_Discounts_DiscountId",
|
||||||
|
column: x => x.DiscountId,
|
||||||
|
principalTable: "Discounts",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ProductDiscounts_Products_ProductId",
|
||||||
|
column: x => x.ProductId,
|
||||||
|
principalTable: "Products",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ProductVariants",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
ProductId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
Value = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
SKU = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
|
||||||
|
PriceAdjustment = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
StockQuantity = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
ImageUrl = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||||
|
IsActive = table.Column<bool>(type: "boolean", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ProductVariants", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ProductVariants_Products_ProductId",
|
||||||
|
column: x => x.ProductId,
|
||||||
|
principalTable: "Products",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Reviews",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
ProductId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
CustomerId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
Rating = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
Title = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||||
|
Comment = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||||
|
ReviewDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
IsApproved = table.Column<bool>(type: "boolean", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Reviews", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Reviews_Customers_CustomerId",
|
||||||
|
column: x => x.CustomerId,
|
||||||
|
principalTable: "Customers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Reviews_Products_ProductId",
|
||||||
|
column: x => x.ProductId,
|
||||||
|
principalTable: "Products",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "OrderItems",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
OrderId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
ProductId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
ProductVariantId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
ProductName = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||||
|
ProductSKU = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
Quantity = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
UnitPrice = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||||
|
TotalPrice = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_OrderItems", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_OrderItems_Orders_OrderId",
|
||||||
|
column: x => x.OrderId,
|
||||||
|
principalTable: "Orders",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_OrderItems_ProductVariants_ProductVariantId",
|
||||||
|
column: x => x.ProductVariantId,
|
||||||
|
principalTable: "ProductVariants",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.SetNull);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_OrderItems_Products_ProductId",
|
||||||
|
column: x => x.ProductId,
|
||||||
|
principalTable: "Products",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.SetNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Addresses_CustomerId",
|
||||||
|
table: "Addresses",
|
||||||
|
column: "CustomerId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Categories_ParentCategoryId",
|
||||||
|
table: "Categories",
|
||||||
|
column: "ParentCategoryId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Categories_Slug",
|
||||||
|
table: "Categories",
|
||||||
|
column: "Slug",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_CategoryDiscounts_DiscountId",
|
||||||
|
table: "CategoryDiscounts",
|
||||||
|
column: "DiscountId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Discounts_CouponCode",
|
||||||
|
table: "Discounts",
|
||||||
|
column: "CouponCode",
|
||||||
|
unique: true,
|
||||||
|
filter: "\"CouponCode\" IS NOT NULL");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_OrderItems_OrderId",
|
||||||
|
table: "OrderItems",
|
||||||
|
column: "OrderId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_OrderItems_ProductId",
|
||||||
|
table: "OrderItems",
|
||||||
|
column: "ProductId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_OrderItems_ProductVariantId",
|
||||||
|
table: "OrderItems",
|
||||||
|
column: "ProductVariantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Orders_BillingAddressId",
|
||||||
|
table: "Orders",
|
||||||
|
column: "BillingAddressId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Orders_CustomerId",
|
||||||
|
table: "Orders",
|
||||||
|
column: "CustomerId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Orders_OrderNumber",
|
||||||
|
table: "Orders",
|
||||||
|
column: "OrderNumber",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Orders_PaymentMethodId",
|
||||||
|
table: "Orders",
|
||||||
|
column: "PaymentMethodId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Orders_ShippingAddressId",
|
||||||
|
table: "Orders",
|
||||||
|
column: "ShippingAddressId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Orders_ShippingMethodId",
|
||||||
|
table: "Orders",
|
||||||
|
column: "ShippingMethodId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ProductCategories_CategoryId",
|
||||||
|
table: "ProductCategories",
|
||||||
|
column: "CategoryId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ProductDiscounts_DiscountId",
|
||||||
|
table: "ProductDiscounts",
|
||||||
|
column: "DiscountId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Products_SKU",
|
||||||
|
table: "Products",
|
||||||
|
column: "SKU",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Products_Slug",
|
||||||
|
table: "Products",
|
||||||
|
column: "Slug",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Products_SupplierId",
|
||||||
|
table: "Products",
|
||||||
|
column: "SupplierId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ProductVariants_ProductId",
|
||||||
|
table: "ProductVariants",
|
||||||
|
column: "ProductId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Reviews_CustomerId",
|
||||||
|
table: "Reviews",
|
||||||
|
column: "CustomerId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Reviews_ProductId",
|
||||||
|
table: "Reviews",
|
||||||
|
column: "ProductId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_RoleClaims_RoleId",
|
||||||
|
table: "RoleClaims",
|
||||||
|
column: "RoleId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "RoleNameIndex",
|
||||||
|
table: "Roles",
|
||||||
|
column: "NormalizedName",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Settings_Key",
|
||||||
|
table: "Settings",
|
||||||
|
column: "Key",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Suppliers_AddressId",
|
||||||
|
table: "Suppliers",
|
||||||
|
column: "AddressId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserClaims_UserId",
|
||||||
|
table: "UserClaims",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserLogins_UserId",
|
||||||
|
table: "UserLogins",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserRoles_RoleId",
|
||||||
|
table: "UserRoles",
|
||||||
|
column: "RoleId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "EmailIndex",
|
||||||
|
table: "Users",
|
||||||
|
column: "NormalizedEmail");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "UserNameIndex",
|
||||||
|
table: "Users",
|
||||||
|
column: "NormalizedUserName",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "CategoryDiscounts");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "OrderItems");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ProductCategories");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ProductDiscounts");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Reviews");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "RoleClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Settings");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserLogins");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserRoles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserTokens");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Orders");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ProductVariants");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Categories");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Discounts");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Roles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Users");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PaymentMethods");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ShippingMethods");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Products");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Suppliers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Addresses");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Customers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
51
Webshop.Infrastructure/Repositories/ProductRepository.cs
Normal file
51
Webshop.Infrastructure/Repositories/ProductRepository.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// src/Webshop.Infrastructure/Repositories/ProductRepository.cs
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Webshop.Domain.Entities;
|
||||||
|
using Webshop.Domain.Interfaces;
|
||||||
|
using Webshop.Infrastructure.Data;
|
||||||
|
|
||||||
|
namespace Webshop.Infrastructure.Repositories
|
||||||
|
{
|
||||||
|
public class ProductRepository : IProductRepository
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
|
||||||
|
// Wir lassen uns den DbContext per Dependency Injection geben
|
||||||
|
public ProductRepository(ApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Product>> GetAllProductsAsync()
|
||||||
|
{
|
||||||
|
return await _context.Products.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Product?> GetProductByIdAsync(Guid id)
|
||||||
|
{
|
||||||
|
return await _context.Products.FindAsync(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddProductAsync(Product product)
|
||||||
|
{
|
||||||
|
_context.Products.Add(product);
|
||||||
|
await _context.SaveChangesAsync(); // Wichtig: Änderungen speichern
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateProductAsync(Product product)
|
||||||
|
{
|
||||||
|
_context.Products.Update(product);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteProductAsync(Guid id)
|
||||||
|
{
|
||||||
|
var product = await _context.Products.FindAsync(id);
|
||||||
|
if (product != null)
|
||||||
|
{
|
||||||
|
_context.Products.Remove(product);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Webshop.Infrastructure/Webshop.Infrastructure.csproj
Normal file
25
Webshop.Infrastructure/Webshop.Infrastructure.csproj
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.18" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.18" />
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Webshop.Domain\Webshop.Domain.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user