This commit is contained in:
Tizian.Breuch
2025-07-22 17:26:04 +02:00
parent dcf6e428ab
commit c8635756f1
8 changed files with 359 additions and 77 deletions

View File

@@ -1,18 +1,15 @@
// src/Webshop.Application/DTOs/ProductDto.cs namespace Webshop.Application.DTOs
namespace Webshop.Application.DTOs
{ {
public class ProductDto public class ProductDto
{ {
public Guid Id { get; set; } // Wird bei Erstellung oft vom Backend gesetzt public Guid Id { get; set; }
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty; // Kann null sein public string Description { get; set; } = string.Empty;
public string SKU { get; set; } = string.Empty; // STELLEN SIE SICHER, DASS DIES DA IST
public decimal Price { get; set; } public decimal Price { get; set; }
public string Sku { get; set; } = string.Empty; public bool IsActive { get; set; }
public string ShortDescription { get; set; } = string.Empty; // Kann null sein, aber hier zur Vollständigkeit public bool IsInStock { get; set; }
public bool IsActive { get; set; } // Muss übergeben werden public int StockQuantity { get; set; }
public bool IsInStock { get; set; } // Muss übergeben werden public string? ImageUrl { get; set; } // STELLEN SIE SICHER, DASS DIES DA IST
public int StockQuantity { get; set; } // Muss übergeben werden
public string Slug { get; set; } = string.Empty; // Muss übergeben werden
} }
} }

View File

@@ -1,12 +1,125 @@
using System; // src/Webshop.Application/Services/Admin/AdminProductService.cs
using System.Collections.Generic; using Webshop.Application.DTOs; // AdminProductDto
using System.Linq; using Webshop.Domain.Entities;
using System.Text; using Webshop.Domain.Interfaces;
using System.Threading.Tasks; using System.Collections.Generic; // Sicherstellen, dass für IEnumerable vorhanden
namespace Webshop.Application.Services.Admin namespace Webshop.Application.Services.Admin
{ {
public class AdminProductService public class AdminProductService
{ {
private readonly IProductRepository _productRepository;
public AdminProductService(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public async Task<IEnumerable<AdminProductDto>> GetAllAdminProductsAsync()
{
var products = await _productRepository.GetAllProductsAsync();
return products.Select(p => new AdminProductDto
{
Id = p.Id,
Name = p.Name,
Description = p.Description,
SKU = p.SKU,
Price = p.Price,
OldPrice = p.OldPrice,
IsActive = p.IsActive,
IsInStock = p.IsInStock,
StockQuantity = p.StockQuantity,
Weight = p.Weight,
ImageUrl = p.ImageUrl,
Slug = p.Slug,
CreatedDate = p.CreatedDate,
LastModifiedDate = p.LastModifiedDate,
SupplierId = p.SupplierId,
PurchasePrice = p.PurchasePrice
}).ToList();
}
public async Task<AdminProductDto?> GetAdminProductByIdAsync(Guid id)
{
var product = await _productRepository.GetProductByIdAsync(id);
if (product == null) return null;
return new AdminProductDto
{
Id = product.Id,
Name = product.Name,
Description = product.Description,
SKU = product.SKU,
Price = product.Price,
OldPrice = product.OldPrice,
IsActive = product.IsActive,
IsInStock = product.IsInStock,
StockQuantity = product.StockQuantity,
Weight = product.Weight,
ImageUrl = product.ImageUrl,
Slug = product.Slug,
CreatedDate = product.CreatedDate,
LastModifiedDate = product.LastModifiedDate,
SupplierId = product.SupplierId,
PurchasePrice = product.PurchasePrice
};
}
public async Task<AdminProductDto> CreateAdminProductAsync(AdminProductDto productDto)
{
var newProduct = new Product
{
Id = Guid.NewGuid(),
Name = productDto.Name,
Description = productDto.Description,
SKU = productDto.SKU,
Price = productDto.Price,
OldPrice = productDto.OldPrice,
IsActive = productDto.IsActive,
IsInStock = productDto.IsInStock,
StockQuantity = productDto.StockQuantity,
Weight = productDto.Weight,
ImageUrl = productDto.ImageUrl,
Slug = productDto.Slug,
CreatedDate = DateTimeOffset.UtcNow,
SupplierId = productDto.SupplierId,
PurchasePrice = productDto.PurchasePrice
};
await _productRepository.AddProductAsync(newProduct);
productDto.Id = newProduct.Id;
return productDto;
}
public async Task<bool> UpdateAdminProductAsync(AdminProductDto productDto)
{
var existingProduct = await _productRepository.GetProductByIdAsync(productDto.Id);
if (existingProduct == null) return false;
existingProduct.Name = productDto.Name;
existingProduct.Description = productDto.Description;
existingProduct.SKU = productDto.SKU;
existingProduct.Price = productDto.Price;
existingProduct.OldPrice = productDto.OldPrice;
existingProduct.IsActive = productDto.IsActive;
existingProduct.IsInStock = productDto.IsInStock;
existingProduct.StockQuantity = productDto.StockQuantity;
existingProduct.Weight = productDto.Weight;
existingProduct.ImageUrl = productDto.ImageUrl;
existingProduct.Slug = productDto.Slug;
existingProduct.LastModifiedDate = DateTimeOffset.UtcNow;
existingProduct.SupplierId = productDto.SupplierId;
existingProduct.PurchasePrice = productDto.PurchasePrice;
await _productRepository.UpdateProductAsync(existingProduct);
return true;
}
public async Task<bool> DeleteAdminProductAsync(Guid id)
{
var product = await _productRepository.GetProductByIdAsync(id);
if (product == null) return false;
await _productRepository.DeleteProductAsync(product.Id); // Verwende product.Id
return true;
}
} }
} }

View File

@@ -1,12 +1,61 @@
using System; // src/Webshop.Application/Services/Admin/AdminUserService.cs
using System.Collections.Generic; using Microsoft.AspNetCore.Identity;
using System.Linq; using Microsoft.EntityFrameworkCore;
using System.Text; using Webshop.Application.DTOs.Users; // UserDto
using System.Threading.Tasks; using System.Collections.Generic; // Sicherstellen, dass für IEnumerable vorhanden
namespace Webshop.Application.Services.Admin namespace Webshop.Application.Services.Admin
{ {
public class AdminUserService public class AdminUserService
{ {
private readonly UserManager<IdentityUser> _userManager;
public AdminUserService(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
public async Task<IEnumerable<UserDto>> GetAllUsersAsync()
{
var users = await _userManager.Users.ToListAsync();
var userDtos = new List<UserDto>();
foreach (var user in users)
{
var roles = await _userManager.GetRolesAsync(user);
userDtos.Add(new UserDto
{
Id = user.Id,
Email = user.Email ?? string.Empty,
UserName = user.UserName ?? string.Empty,
Roles = roles.ToList(),
// LockoutEnd kann als Näherung für CreatedDate verwendet werden,
// da IdentityUser keine direkte CreatedDate-Eigenschaft hat.
// Ideal wäre es, ein CustomUser-Modell zu verwenden, das von IdentityUser erbt.
CreatedDate = user.LockoutEnd ?? DateTimeOffset.MinValue,
EmailConfirmed = user.EmailConfirmed
});
}
return userDtos;
}
public async Task<UserDto?> GetUserByIdAsync(string userId)
{
var user = await _userManager.FindByIdAsync(userId);
if (user == null) return null;
var roles = await _userManager.GetRolesAsync(user);
return new UserDto
{
Id = user.Id,
Email = user.Email ?? string.Empty,
UserName = user.UserName ?? string.Empty,
Roles = roles.ToList(),
CreatedDate = user.LockoutEnd ?? DateTimeOffset.MinValue,
EmailConfirmed = user.EmailConfirmed
};
}
// TODO: Methoden zum Aktualisieren von Rollen, Löschen von Benutzern etc.
} }
} }

View File

@@ -1,12 +1,143 @@
using System; // src/Webshop.Application/Services/Auth/AuthService.cs
using System.Collections.Generic; using Microsoft.AspNetCore.Identity;
using System.Linq; using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text; using System.Text;
using Webshop.Application.DTOs.Auth;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic;
namespace Webshop.Application.Services.Auth namespace Webshop.Application.Services.Auth
{ {
public class AuthService public class AuthService : IAuthService // Sicherstellen, dass IAuthService implementiert wird
{ {
private readonly UserManager<IdentityUser> _userManager;
private readonly SignInManager<IdentityUser> _signInManager;
private readonly IConfiguration _configuration;
private readonly RoleManager<IdentityRole> _roleManager;
public AuthService(
UserManager<IdentityUser> userManager,
SignInManager<IdentityUser> signInManager,
IConfiguration configuration,
RoleManager<IdentityRole> roleManager)
{
_userManager = userManager;
_signInManager = signInManager;
_configuration = configuration;
_roleManager = roleManager;
}
public async Task<AuthResponseDto> RegisterUserAsync(RegisterRequestDto request)
{
var existingUser = await _userManager.FindByEmailAsync(request.Email);
if (existingUser != null)
{
return new AuthResponseDto { IsAuthSuccessful = false, ErrorMessage = "E-Mail ist bereits registriert." };
}
var user = new IdentityUser { Email = request.Email, UserName = request.Email };
var result = await _userManager.CreateAsync(user, request.Password);
if (!result.Succeeded)
{
var errors = string.Join(" ", result.Errors.Select(e => e.Description));
return new AuthResponseDto { IsAuthSuccessful = false, ErrorMessage = errors };
}
if (!await _roleManager.RoleExistsAsync("Customer"))
{
await _roleManager.CreateAsync(new IdentityRole("Customer"));
}
await _userManager.AddToRoleAsync(user, "Customer");
var roles = await _userManager.GetRolesAsync(user);
var token = await GenerateJwtToken(user, roles);
return new AuthResponseDto
{
IsAuthSuccessful = true,
Token = token,
UserId = user.Id,
Email = user.Email,
Roles = roles.ToList()
};
}
public async Task<AuthResponseDto> LoginUserAsync(LoginRequestDto request)
{
var user = await _userManager.FindByEmailAsync(request.Email);
if (user == null)
{
return new AuthResponseDto { IsAuthSuccessful = false, ErrorMessage = "Ungültige Anmeldeinformationen." };
}
var signInResult = await _signInManager.CheckPasswordSignInAsync(user, request.Password, false);
if (!signInResult.Succeeded)
{
return new AuthResponseDto { IsAuthSuccessful = false, ErrorMessage = "Ungültige Anmeldeinformationen." };
}
var roles = await _userManager.GetRolesAsync(user);
var token = await GenerateJwtToken(user, roles);
return new AuthResponseDto
{
IsAuthSuccessful = true,
Token = token,
UserId = user.Id,
Email = user.Email,
Roles = roles.ToList()
};
}
public async Task<AuthResponseDto> LoginAdminAsync(LoginRequestDto request)
{
var authResponse = await LoginUserAsync(request);
if (!authResponse.IsAuthSuccessful)
{
return authResponse;
}
var user = await _userManager.FindByEmailAsync(request.Email);
if (user == null || !await _userManager.IsInRoleAsync(user, "Admin"))
{
return new AuthResponseDto { IsAuthSuccessful = false, ErrorMessage = "Keine Berechtigung." };
}
return authResponse;
}
private async Task<string> GenerateJwtToken(IdentityUser user, IList<string> roles)
{
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, user.Id),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Email, user.Email!)
};
foreach (var role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
var jwtSettings = _configuration.GetSection("JwtSettings");
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["Secret"]!));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var expires = DateTime.UtcNow.AddMinutes(double.Parse(jwtSettings["ExpirationMinutes"]!));
var token = new JwtSecurityToken(
issuer: jwtSettings["Issuer"],
audience: jwtSettings["Audience"],
claims: claims,
expires: expires,
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
} }
} }

View File

@@ -1,12 +1,14 @@
using System; // src/Webshop.Application/Services/Auth/IAuthService.cs
using System.Collections.Generic; using Webshop.Application.DTOs.Auth;
using System.Linq; using System.Threading.Tasks; // Sicherstellen, dass für Task vorhanden
using System.Text; using System.Collections.Generic; // Sicherstellen, dass für IList<string> vorhanden
using System.Threading.Tasks;
namespace Webshop.Application.Services.Auth namespace Webshop.Application.Services.Auth
{ {
public class IAuthService public interface IAuthService
{ {
Task<AuthResponseDto> RegisterUserAsync(RegisterRequestDto request);
Task<AuthResponseDto> LoginUserAsync(LoginRequestDto request);
Task<AuthResponseDto> LoginAdminAsync(LoginRequestDto request);
} }
} }

View File

@@ -1,11 +1,12 @@
// src/Webshop.Application/Services/ProductService.cs // src/Webshop.Application/Services/Public/ProductService.cs
using Webshop.Application.DTOs; using Webshop.Application.DTOs; // ProductDto
using Webshop.Domain.Entities;
using Webshop.Domain.Interfaces; using Webshop.Domain.Interfaces;
using System.Collections.Generic; // Sicherstellen, dass für IEnumerable vorhanden
using Webshop.Domain.Entities;
namespace Webshop.Application.Services.Public namespace Webshop.Application.Services.Public
{ {
public class ProductService public class ProductService // Sie haben den Namen "ProductService" beibehalten
{ {
private readonly IProductRepository _productRepository; private readonly IProductRepository _productRepository;
@@ -18,62 +19,40 @@ namespace Webshop.Application.Services.Public
{ {
var productsFromDb = await _productRepository.GetAllProductsAsync(); var productsFromDb = await _productRepository.GetAllProductsAsync();
var productDtos = productsFromDb.Select(p => new ProductDto return productsFromDb.Select(p => new ProductDto
{ {
Id = p.Id, Id = p.Id,
Name = p.Name, Name = p.Name,
Description = p.Description, Description = p.Description,
Price = p.Price, Price = p.Price,
Sku = p.SKU SKU = p.SKU,
}); IsActive = p.IsActive,
IsInStock = p.IsInStock,
return productDtos; StockQuantity = p.StockQuantity,
ImageUrl = p.ImageUrl
}).ToList();
} }
// Beispiel: Methode zum Erstellen eines Produkts (wenn PublicService auch Schreiben erlaubt)
// Normalerweise wäre das im AdminProductService
public async Task<ProductDto> CreateProductAsync(ProductDto productDto) public async Task<ProductDto> CreateProductAsync(ProductDto productDto)
{ {
var newProduct = new Product var newProduct = new Product
{ {
// Felder aus DTO Id = Guid.NewGuid(),
Name = productDto.Name, Name = productDto.Name,
Description = productDto.Description, Description = productDto.Description,
SKU = productDto.SKU,
Price = productDto.Price, Price = productDto.Price,
SKU = productDto.Sku, IsActive = true, // Annahme
ShortDescription = productDto.ShortDescription, IsInStock = true, // Annahme
IsActive = productDto.IsActive,
IsInStock = productDto.IsInStock,
StockQuantity = productDto.StockQuantity, StockQuantity = productDto.StockQuantity,
Slug = productDto.Slug, ImageUrl = productDto.ImageUrl,
Id = Guid.NewGuid(),
CreatedDate = DateTimeOffset.UtcNow, CreatedDate = DateTimeOffset.UtcNow,
LastModifiedDate = null,
}; };
if (string.IsNullOrWhiteSpace(newProduct.Slug) && !string.IsNullOrWhiteSpace(newProduct.Name))
{
newProduct.Slug = GenerateSlug(newProduct.Name);
}
await _productRepository.AddProductAsync(newProduct); await _productRepository.AddProductAsync(newProduct);
productDto.Id = newProduct.Id; productDto.Id = newProduct.Id;
return productDto; return productDto;
} }
private string GenerateSlug(string name)
{
if (string.IsNullOrWhiteSpace(name)) return Guid.NewGuid().ToString(); // Fallback
var slug = name.ToLowerInvariant()
.Replace(" ", "-")
.Replace("ä", "ae").Replace("ö", "oe").Replace("ü", "ue")
.Replace("ß", "ss")
.Trim();
// Entferne ungültige Zeichen
slug = System.Text.RegularExpressions.Regex.Replace(slug, @"[^a-z0-9-]", "");
return slug;
}
} }
} }

View File

@@ -8,8 +8,19 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="7.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Webshop.Domain\Webshop.Domain.csproj" /> <ProjectReference Include="..\Webshop.Domain\Webshop.Domain.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -21,7 +21,7 @@ public class Product
[Required] [Required]
[MaxLength(50)] [MaxLength(50)]
public string SKU { get; set; } public string SKU { get; set; } = string.Empty;
[Required] [Required]
public decimal Price { get; set; } public decimal Price { get; set; }