This commit is contained in:
Tizian.Breuch
2025-07-29 14:04:35 +02:00
parent 2d6eeb7a50
commit c1ee56c81c
19 changed files with 339 additions and 125 deletions

View File

@@ -1,15 +1,19 @@
// src/Webshop.Api/Controllers/Customer/ProfileController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using Webshop.Application.DTOs; // CustomerDto
using Webshop.Application.DTOs.Auth; // ChangePasswordRequestDto
using Webshop.Application.DTOs.Customers; // UpdateCustomerProfileDto
using Webshop.Application.Services;
using System.Threading.Tasks;
using Webshop.Application.DTOs.Customers;
using Webshop.Application.Services.Customers.Interfaces;
using Webshop.Application.Services.Customers;
namespace Webshop.Api.Controllers.Customers
namespace Webshop.Api.Controllers.Customer
{
[ApiController]
[Route("api/v1/customer/profile")] // Eindeutige Route f<>r das Profil
[Authorize(Roles = "Customer")] // Nur f<>r eingeloggte Kunden!
[Route("api/v1/customer/[controller]")] // z.B. /api/v1/customer/profile
[Authorize(Roles = "Customer")]
public class ProfileController : ControllerBase
{
private readonly ICustomerService _customerService;
@@ -19,33 +23,49 @@ namespace Webshop.Api.Controllers.Customers
_customerService = customerService;
}
// Hilfsmethode, um die ID des eingeloggten Benutzers aus dem Token zu holen
private string GetUserId() => User.FindFirstValue(ClaimTypes.NameIdentifier)!;
[HttpGet("me")] // GET /api/v1/customer/profile/me
[HttpGet("me")] // /api/v1/customer/profile/me
public async Task<ActionResult<CustomerDto>> GetMyProfile()
{
var userId = GetUserId();
var profile = await _customerService.GetMyProfileAsync(userId);
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (string.IsNullOrEmpty(userId)) return Unauthorized(new { Message = "Benutzer-ID nicht im Token gefunden." });
if (profile == null)
{
return NotFound("Kundenprofil nicht gefunden.");
}
return Ok(profile);
var customerProfile = await _customerService.GetMyProfileAsync(userId);
if (customerProfile == null) return NotFound(new { Message = "Kundenprofil nicht gefunden. Bitte erstellen Sie es." });
return Ok(customerProfile);
}
[HttpPut("me")] // PUT /api/v1/customer/profile/me
public async Task<IActionResult> UpdateMyProfile([FromBody] UpdateCustomerProfileDto profileDto)
[HttpPost("change-password")] // /api/v1/customer/profile/change-password
public async Task<IActionResult> ChangePassword([FromBody] ChangePasswordRequestDto request)
{
var userId = GetUserId();
var success = await _customerService.UpdateMyProfileAsync(userId, profileDto);
if (!ModelState.IsValid) return BadRequest(ModelState);
if (!success)
{
return NotFound("Kundenprofil nicht gefunden.");
}
return NoContent(); // Standardantwort f<>r ein erfolgreiches Update
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (string.IsNullOrEmpty(userId)) return Unauthorized(new { Message = "Benutzer-ID nicht im Token gefunden." });
var (success, errorMessage) = await _customerService.ChangePasswordAsync(userId, request);
if (!success) return BadRequest(new { Message = errorMessage });
return Ok(new { Message = "Passwort erfolgreich ge<67>ndert. Bitte melden Sie sich mit dem neuen Passwort an." });
}
// << NEUER/AKTUALISIERTER ENDPUNKT F<>R PROFIL-AKTUALISIERUNG >>
[HttpPost("update-profile")] // /api/v1/customer/profile/update-profile
public async Task<IActionResult> UpdateProfile([FromBody] UpdateCustomerProfileDto request)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (string.IsNullOrEmpty(userId)) return Unauthorized(new { Message = "Benutzer-ID nicht im Token gefunden." });
var (success, errorMessage) = await _customerService.UpdateMyProfileAsync(userId, request);
if (!success) return BadRequest(new { Message = errorMessage });
return Ok(new { Message = "Profil erfolgreich aktualisiert." });
}
// << ENTFERNT: UpdateContactInfo Endpoint >>
}
}

View File

@@ -18,6 +18,7 @@ using Webshop.Application.Services.Admin.Interfaces;
using Webshop.Application.Services.Public.Interfaces;
using Webshop.Application.Services.Customers.Interfaces;
using Webshop.Application.Services.Customers;
using Webshop.Domain.Identity;
var builder = WebApplication.CreateBuilder(args);
@@ -136,6 +137,7 @@ builder.Services.AddSwaggerGen(c =>
var app = builder.Build(); // <-- Hier wird die App gebaut
// OPTIONALE BL<42>CKE F<>R MIGRATION UND BENUTZERINITIALISIERUNG - DIESER CODE WIRD VOR APP.RUN() AUSGEF<45>HRT
// OPTIONALE BL<42>CKE F<>R MIGRATION UND BENUTZERINITIALISIERUNG - DIESER CODE WIRD VOR APP.RUN() AUSGEF<45>HRT
using (var scope = app.Services.CreateScope())
{
@@ -150,7 +152,7 @@ using (var scope = app.Services.CreateScope())
// Dieser Block erstellt Rollen und initiale Benutzer, falls sie noch nicht existieren.
// BITTE ENTFERNEN ODER KOMMENTIEREN SIE DIESEN BLOCK AUS, NACHDEM SIE IHRE ERSTEN BENUTZER ERSTELLT HABEN!
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
var userManager = services.GetRequiredService<UserManager<ApplicationUser>>(); // << KORREKT: UserManager f<>r ApplicationUser >>
string[] roleNames = { "Admin", "Customer" };
@@ -162,23 +164,39 @@ using (var scope = app.Services.CreateScope())
}
}
// Erstelle einen initialen Admin-Benutzer
var adminUser = await userManager.FindByEmailAsync("admin@yourwebshop.com");
// Erstelle einen initialen Admin-Benutzer und sein Customer-Profil
var adminUser = await userManager.FindByEmailAsync("admin@yourwebshop.com"); // << ANPASSEN >>
if (adminUser == null)
{
// Erstellen Sie hier eine Instanz von ApplicationUser
adminUser = new ApplicationUser
adminUser = new ApplicationUser // << KORREKT: ERSTELLT ApplicationUser >>
{
UserName = "admin@yourwebshop.com",
Email = "admin@yourwebshop.com",
UserName = "admin@yourwebshop.com", // << ANPASSEN >>
Email = "admin@yourwebshop.com", // << ANPASSEN >>
EmailConfirmed = true,
CreatedDate = DateTimeOffset.UtcNow // Setzen Sie Ihr neues Feld!
CreatedDate = DateTimeOffset.UtcNow, // Custom Property auf ApplicationUser
LastActive = DateTimeOffset.UtcNow // Custom Property auf ApplicationUser
};
var createAdmin = await userManager.CreateAsync(adminUser, "SecureAdminPass123!");
var createAdmin = await userManager.CreateAsync(adminUser, "SecureAdminPass123!"); // << ANPASSEN >>
if (createAdmin.Succeeded)
{
await userManager.AddToRoleAsync(adminUser, "Admin");
Console.WriteLine("Admin user created.");
// Erstelle Customer-Profil f<>r Admin (falls Admins auch Kundenprofile haben sollen)
var adminCustomerProfile = await context.Customers.FirstOrDefaultAsync(c => c.AspNetUserId == adminUser.Id); // << KORREKT: SUCHT NACH AspNetUserId >>
if (adminCustomerProfile == null)
{
adminCustomerProfile = new Webshop.Domain.Entities.Customer
{
Id = Guid.NewGuid(),
AspNetUserId = adminUser.Id, // << KORREKT: VERKN<4B>PFUNG <20>BER AspNetUserId >>
FirstName = "Admin",
LastName = "User"
};
context.Customers.Add(adminCustomerProfile);
await context.SaveChangesAsync();
Console.WriteLine("Admin's Customer profile created.");
}
}
else
{
@@ -186,29 +204,46 @@ using (var scope = app.Services.CreateScope())
}
}
// Erstelle einen initialen Kunden-Benutzer
var customerUser = await userManager.FindByEmailAsync("customer@yourwebshop.com");
// Erstelle einen initialen Kunden-Benutzer und sein Customer-Profil (KOMBINIERT)
var customerUser = await userManager.FindByEmailAsync("customer@yourwebshop.com"); // << ANPASSEN >>
if (customerUser == null)
{
// Erstellen Sie auch hier eine Instanz von ApplicationUser
customerUser = new ApplicationUser
customerUser = new ApplicationUser // << KORREKT: ERSTELLT ApplicationUser >>
{
UserName = "customer@yourwebshop.com",
Email = "customer@yourwebshop.com",
UserName = "customer@yourwebshop.com", // << ANPASSEN >>
Email = "customer@yourwebshop.com", // << ANPASSEN >>
EmailConfirmed = true,
CreatedDate = DateTimeOffset.UtcNow // Setzen Sie Ihr neues Feld!
CreatedDate = DateTimeOffset.UtcNow, // Custom Property auf ApplicationUser
LastActive = DateTimeOffset.UtcNow // Custom Property auf ApplicationUser
};
var createCustomer = await userManager.CreateAsync(customerUser, "SecureCustomerPass123!");
var createCustomer = await userManager.CreateAsync(customerUser, "SecureCustomerPass123!"); // << ANPASSEN >>
if (createCustomer.Succeeded)
{
await userManager.AddToRoleAsync(customerUser, "Customer");
Console.WriteLine("Customer user created.");
// Kombinierter Teil: Customer-Profil erstellen, direkt nach IdentityUser-Erstellung
var customerProfile = await context.Customers.FirstOrDefaultAsync(c => c.AspNetUserId == customerUser.Id); // << KORREKT: SUCHT NACH AspNetUserId >>
if (customerProfile == null)
{
customerProfile = new Webshop.Domain.Entities.Customer
{
Id = Guid.NewGuid(),
AspNetUserId = customerUser.Id,
FirstName = "Test",
LastName = "Kunde"
};
context.Customers.Add(customerProfile);
await context.SaveChangesAsync();
Console.WriteLine("Customer profile created for new customer user.");
}
}
else
{
Console.WriteLine($"Error creating customer user: {string.Join(", ", createCustomer.Errors.Select(e => e.Description))}");
}
}
// --- ENDE DES TEMPOR<4F>REN SETUP-BLOCKS ---
}
catch (Exception ex)
{

View File

@@ -14,7 +14,7 @@ using Webshop.Application.DTOs.Products;
using Webshop.Application.DTOs.Payments;
using Webshop.Application.DTOs.Orders;
using Webshop.Application.DTOs.Discounts;
using Webshop.Application.DTOs.Categorys; // Für Guid.NewGuid()
using Webshop.Application.DTOs.Categorys;
namespace Webshop.Api.SwaggerFilters
{

View File

@@ -0,0 +1,19 @@
// src/Webshop.Application/DTOs/Auth/ChangePasswordRequestDto.cs
using System.ComponentModel.DataAnnotations;
namespace Webshop.Application.DTOs.Auth
{
public class ChangePasswordRequestDto
{
[Required(ErrorMessage = "Altes Passwort ist erforderlich.")]
public string OldPassword { get; set; } = string.Empty;
[Required(ErrorMessage = "Neues Passwort ist erforderlich.")]
[MinLength(6, ErrorMessage = "Passwort muss mindestens 6 Zeichen lang sein.")]
public string NewPassword { get; set; } = string.Empty;
[Required(ErrorMessage = "Passwortbestätigung ist erforderlich.")]
[Compare("NewPassword", ErrorMessage = "Neues Passwort und Bestätigung stimmen nicht überein.")]
public string ConfirmNewPassword { get; set; } = string.Empty;
}
}

View File

@@ -1,16 +1,26 @@
using System.ComponentModel.DataAnnotations;
// src/Webshop.Application/DTOs/Customers/UpdateCustomerProfileDto.cs
using System.ComponentModel.DataAnnotations;
namespace Webshop.Application.DTOs.Customers
{
public class UpdateCustomerProfileDto
{
[Required(ErrorMessage = "Vorname ist erforderlich.")]
[MaxLength(100)]
[StringLength(100)]
public string FirstName { get; set; } = string.Empty;
[Required(ErrorMessage = "Nachname ist erforderlich.")]
[MaxLength(100)]
[StringLength(100)]
public string LastName { get; set; } = string.Empty;
[Phone(ErrorMessage = "Ungültiges Telefonnummernformat.")]
public string? PhoneNumber { get; set; } // Telefonnummer des Benutzers
[EmailAddress(ErrorMessage = "Ungültiges E-Mail-Format.")]
public string? Email { get; set; } // E-Mail des Benutzers
// Optional, aber gute Sicherheitspraxis: Aktuelles Passwort zur Bestätigung sensibler Änderungen
[Required(ErrorMessage = "Aktuelles Passwort ist zur Bestätigung erforderlich.")]
public string CurrentPassword { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,19 @@
// src/Webshop.Application/DTOs/Users/AdminResetPasswordRequestDto.cs
using System.ComponentModel.DataAnnotations;
namespace Webshop.Application.DTOs.Users
{
public class AdminResetPasswordRequestDto
{
[Required(ErrorMessage = "Benutzer-ID ist erforderlich.")]
public string UserId { get; set; } = string.Empty;
[Required(ErrorMessage = "Neues Passwort ist erforderlich.")]
[MinLength(6, ErrorMessage = "Passwort muss mindestens 6 Zeichen lang sein.")]
public string NewPassword { get; set; } = string.Empty;
[Required(ErrorMessage = "Passwortbestätigung ist erforderlich.")]
[Compare("NewPassword", ErrorMessage = "Neues Passwort und Bestätigung stimmen nicht überein.")]
public string ConfirmNewPassword { get; set; } = string.Empty;
}
}

View File

@@ -1,18 +1,25 @@
// src/Webshop.Application/DTOs/Users/UserDto.cs
using System;
using System.Collections.Generic;
namespace Webshop.Application.DTOs.Users
{
public class UserDto
{
public string Id { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string UserName { get; set; } = string.Empty;
public List<string> Roles { get; set; } = new List<string>();
public DateTimeOffset CreatedDate { get; set; }
public bool EmailConfirmed { get; set; }
public string Id { get; set; } = string.Empty; // Vom ApplicationUser.Id
public string Email { get; set; } = string.Empty; // Vom ApplicationUser.Email
public string UserName { get; set; } = string.Empty; // Vom ApplicationUser.UserName
public List<string> Roles { get; set; } = new List<string>(); // Aus Identity-System
public DateTimeOffset CreatedDate { get; set; } // Vom ApplicationUser.CreatedDate
public bool EmailConfirmed { get; set; } // Vom ApplicationUser.EmailConfirmed
// Hinzugef<65>gte Felder
public DateTimeOffset? LastActive { get; set; }
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public DateTimeOffset? LastActive { get; set; } // Vom ApplicationUser.LastActive
public string? PhoneNumber { get; set; } // Vom ApplicationUser.PhoneNumber
// << NEU: Customer-Felder, die NICHT in ApplicationUser sind >>
public string FirstName { get; set; } = string.Empty; // Vom Customer.FirstName
public string LastName { get; set; } = string.Empty; // Vom Customer.LastName
public Guid? DefaultShippingAddressId { get; set; } // Vom Customer
public Guid? DefaultBillingAddressId { get; set; } // Vom Customer
}
}

View File

@@ -8,6 +8,7 @@ using System.Linq;
using Webshop.Application.DTOs.Products;
using Webshop.Application.Services.Admin.Interfaces; // F<>r Select
namespace Webshop.Application.Services.Admin
{
public class AdminProductService : IAdminProductService // Sicherstellen, dass IAdminProductService implementiert wird

View File

@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading.Tasks;
using Webshop.Application.DTOs.Suppliers;
using Webshop.Application.Services.Admin.Interfaces;
using Webshop.Domain.Identity;
namespace Webshop.Application.Services.Admin
{

View File

@@ -8,6 +8,7 @@ using Webshop.Application.DTOs.Users;
using Webshop.Application.Services.Admin.Interfaces;
using Webshop.Domain.Entities;
using Webshop.Infrastructure.Data; // WICHTIG: Stellt sicher, dass ApplicationDbContext gefunden wird.
using Webshop.Domain.Identity;
namespace Webshop.Application.Services.Admin
{

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
// src/Webshop.Application/Services/Admin/Interfaces/IAdminUserService.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using Webshop.Application.DTOs.Users;

View File

@@ -1,52 +1,122 @@
// src/Webshop.Application/Services/Customers/CustomerService.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;
using Webshop.Application.DTOs.Customers;
using Webshop.Application.Services.Customers.Interfaces;
using Webshop.Domain.Interfaces; // Wichtig für ICustomerRepository
using Webshop.Application.DTOs; // CustomerDto
using Webshop.Application.DTOs.Auth; // ChangePasswordRequestDto
using Webshop.Application.DTOs.Customers; // UpdateCustomerProfileDto
using Webshop.Domain.Entities; // Customer Entity
using Webshop.Domain.Interfaces; // ICustomerRepository
using Webshop.Domain.Identity; // Für ApplicationUser
using System.Linq; // Für Select
using System.Collections.Generic; // Für IEnumerable
namespace Webshop.Application.Services.Customers
{
public class CustomerService : ICustomerService
{
private readonly ICustomerRepository _customerRepository;
private readonly UserManager<ApplicationUser> _userManager;
public CustomerService(ICustomerRepository customerRepository)
public CustomerService(ICustomerRepository customerRepository, UserManager<ApplicationUser> userManager)
{
_customerRepository = customerRepository;
_userManager = userManager;
}
public async Task<CustomerDto?> GetMyProfileAsync(string userId)
{
var customer = await _customerRepository.GetByUserIdAsync(userId);
if (customer == null)
{
return null;
}
if (customer == null) return null;
var identityUser = await _userManager.FindByIdAsync(userId);
if (identityUser == null) return null;
// Mappe die Entity auf das CustomerDto
return new CustomerDto
{
Id = customer.Id,
UserId = customer.AspNetUserId,
FirstName = customer.FirstName,
LastName = customer.LastName,
// Fügen Sie hier weitere Felder hinzu, die der Kunde sehen soll (Email, Phone etc.)
Email = identityUser.Email ?? string.Empty, // E-Mail vom ApplicationUser
PhoneNumber = identityUser.PhoneNumber, // Telefonnummer vom ApplicationUser
DefaultShippingAddressId = customer.DefaultShippingAddressId,
DefaultBillingAddressId = customer.DefaultBillingAddressId
};
}
public async Task<bool> UpdateMyProfileAsync(string userId, UpdateCustomerProfileDto profileDto)
public async Task<(bool Success, string ErrorMessage)> ChangePasswordAsync(string userId, ChangePasswordRequestDto request)
{
var customer = await _customerRepository.GetByUserIdAsync(userId);
if (customer == null)
var user = await _userManager.FindByIdAsync(userId);
if (user == null) return (false, "Benutzer nicht gefunden.");
var result = await _userManager.ChangePasswordAsync(user, request.OldPassword, request.NewPassword);
if (!result.Succeeded)
{
return false; // Kunde nicht gefunden
var errors = string.Join(" ", result.Errors.Select(e => e.Description));
return (false, errors);
}
// Aktualisiere die Felder
return (true, "Passwort erfolgreich geändert.");
}
// << NEUE IMPLEMENTIERUNG: UpdateMyProfileAsync verarbeitet alle Felder >>
public async Task<(bool Success, string ErrorMessage)> UpdateMyProfileAsync(string userId, UpdateCustomerProfileDto profileDto)
{
var customer = await _customerRepository.GetByUserIdAsync(userId);
if (customer == null) return (false, "Kundenprofil nicht gefunden.");
var identityUser = await _userManager.FindByIdAsync(userId);
if (identityUser == null) return (false, "Benutzerkonto nicht gefunden.");
// 1. Aktuelles Passwort prüfen (für alle sensiblen Änderungen)
if (!await _userManager.CheckPasswordAsync(identityUser, profileDto.CurrentPassword))
{
return (false, "Falsches aktuelles Passwort zur Bestätigung.");
}
// 2. Felder der Customer-Entität aktualisieren (FirstName, LastName)
customer.FirstName = profileDto.FirstName;
customer.LastName = profileDto.LastName;
// customer.PhoneNumber = profileDto.PhoneNumber; // Entfernt, da es jetzt in ApplicationUser zentralisiert ist
await _customerRepository.UpdateAsync(customer); // Speichert Änderungen im Customer-Profil
await _customerRepository.UpdateAsync(customer);
return true;
// 3. Felder des ApplicationUser (IdentityUser) aktualisieren (Email, PhoneNumber)
bool identityUserChanged = false;
// E-Mail aktualisieren (wenn anders und nicht leer)
if (!string.IsNullOrEmpty(profileDto.Email) && identityUser.Email != profileDto.Email)
{
identityUser.Email = profileDto.Email;
identityUser.NormalizedEmail = _userManager.NormalizeEmail(profileDto.Email);
identityUser.UserName = profileDto.Email; // Oft wird der UserName auch mit der E-Mail synchronisiert
identityUser.NormalizedUserName = _userManager.NormalizeName(profileDto.Email);
// Optional: user.EmailConfirmed = false; wenn Sie Bestätigungs-E-Mails senden
identityUserChanged = true;
}
// Telefonnummer aktualisieren (wenn anders und nicht leer)
if (!string.IsNullOrEmpty(profileDto.PhoneNumber) && identityUser.PhoneNumber != profileDto.PhoneNumber)
{
identityUser.PhoneNumber = profileDto.PhoneNumber;
// Optional: identityUser.PhoneNumberConfirmed = false;
identityUserChanged = true;
}
if (identityUserChanged)
{
var updateResult = await _userManager.UpdateAsync(identityUser);
if (!updateResult.Succeeded)
{
var errors = string.Join(" ", updateResult.Errors.Select(e => e.Description));
return (false, $"Fehler beim Aktualisieren der Kontaktdaten: {errors}");
}
}
return (true, "Profil und Kontaktdaten erfolgreich aktualisiert.");
}
// << ENTFERNT: UpdateMyContactInfoAsync >>
}
}

View File

@@ -1,11 +1,16 @@
// src/Webshop.Application/Services/Customers/ICustomerService.cs
using System.Threading.Tasks;
using Webshop.Application.DTOs.Customers; // Korrektes Using für DTOs
using Webshop.Application.DTOs; // CustomerDto
using Webshop.Application.DTOs.Auth; // ChangePasswordRequestDto
using Webshop.Application.DTOs.Customers; // UpdateCustomerProfileDto
namespace Webshop.Application.Services.Customers.Interfaces
namespace Webshop.Application.Services.Customers
{
public interface ICustomerService
{
Task<CustomerDto?> GetMyProfileAsync(string userId);
Task<bool> UpdateMyProfileAsync(string userId, UpdateCustomerProfileDto profileDto);
Task<(bool Success, string ErrorMessage)> ChangePasswordAsync(string userId, ChangePasswordRequestDto request);
Task<(bool Success, string ErrorMessage)> UpdateMyProfileAsync(string userId, UpdateCustomerProfileDto profileDto);
}
}

View File

@@ -1,13 +0,0 @@
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
namespace Webshop.Domain.Entities
{
public class ApplicationUser : IdentityUser
{
public DateTimeOffset CreatedDate { get; set; }
public DateTimeOffset? LastActive { get; set; }
public virtual Customer Customer { get; set; }
}
}

View File

@@ -1,25 +1,40 @@
using System;
// src/Webshop.Domain/Entities/Customer.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using Webshop.Domain.Identity; // Für ApplicationUser
namespace Webshop.Domain.Entities
{
public class Customer
{
[Key]
public Guid Id { get; set; }
[Required]
public string AspNetUserId { get; set; }
[Required]
[MaxLength(100)]
public string FirstName { get; set; }
[Required]
[MaxLength(100)]
public string LastName { get; set; }
public Guid Id { get; set; } = Guid.NewGuid(); // Default-Wert setzen
public virtual ApplicationUser User { get; set; }
[Required]
public string AspNetUserId { get; set; } // Fremdschlüssel zu ApplicationUser.Id
[Required]
[MaxLength(100)]
public string FirstName { get; set; } = string.Empty;
[Required]
[MaxLength(100)]
public string LastName { get; set; } = string.Empty;
// << ENTFERNT: Email ist auf ApplicationUser >>
// << ENTFERNT: PhoneNumber ist auf ApplicationUser >>
// << ENTFERNT: CreatedDate ist auf ApplicationUser >>
public Guid? DefaultShippingAddressId { get; set; } // Fremdschlüssel zur Standardversandadresse
public Guid? DefaultBillingAddressId { get; set; } // Fremdschlüssel zur Standardrechnungsadresse
// Navigation Property zum ApplicationUser
public virtual ApplicationUser User { get; set; } = default!; // Muss auf ApplicationUser verweisen
// Navigation Properties zu Collections
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>();
}
}
}

View File

@@ -0,0 +1,16 @@
// src/Webshop.Domain/Identity/ApplicationUser.cs
using Microsoft.AspNetCore.Identity;
using System;
namespace Webshop.Domain.Identity // KORREKTER NAMESPACE
{
public class ApplicationUser : IdentityUser
{
public DateTimeOffset CreatedDate { get; set; } = DateTimeOffset.UtcNow; // Setzt Standardwert
public DateTimeOffset? LastActive { get; set; }
// Navigation Property zur Customer-Entität (One-to-One)
// Customer ist nullable, falls nicht jeder ApplicationUser ein Customer-Profil hat
public virtual Entities.Customer? Customer { get; set; }
}
}

View File

@@ -1,4 +1,5 @@
using System;
// src/Webshop.Domain/Interfaces/ICustomerRepository.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using Webshop.Domain.Entities;
@@ -6,7 +7,12 @@ namespace Webshop.Domain.Interfaces
{
public interface ICustomerRepository
{
Task<Customer?> GetByUserIdAsync(string userId);
Task UpdateAsync(Customer customer);
Task<IEnumerable<Customer>> GetAllAsync(); // Standard CRUD
Task<Customer?> GetByIdAsync(Guid id); // Standard CRUD
Task AddAsync(Customer entity); // Standard CRUD
Task UpdateAsync(Customer entity); // Standard CRUD
Task DeleteAsync(Guid id); // Standard CRUD
Task<Customer?> GetByUserIdAsync(string userId); // << DIESE METHODE IST NEU UND WICHTIG >>
}
}

View File

@@ -1,13 +1,15 @@
// src/Webshop.Infrastructure/Repositories/CustomerRepository.cs
using Microsoft.EntityFrameworkCore;
using Webshop.Domain.Entities;
using Webshop.Domain.Interfaces;
using Webshop.Infrastructure.Data;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Webshop.Infrastructure.Repositories
{
public class CustomerRepository : ICustomerRepository
public class CustomerRepository : ICustomerRepository // MUSS public sein
{
private readonly ApplicationDbContext _context;
@@ -16,15 +18,17 @@ namespace Webshop.Infrastructure.Repositories
_context = context;
}
public async Task<Customer?> GetByUserIdAsync(string userId)
{
return await _context.Customers.FirstOrDefaultAsync(c => c.AspNetUserId == userId);
}
// Beispiel-Implementierungen (wenn nicht schon vorhanden):
public async Task<IEnumerable<Customer>> GetAllAsync() => await _context.Customers.ToListAsync();
public async Task<Customer?> GetByIdAsync(Guid id) => await _context.Customers.FindAsync(id);
public async Task AddAsync(Customer entity) { _context.Customers.Add(entity); await _context.SaveChangesAsync(); }
public async Task UpdateAsync(Customer entity) { _context.Customers.Update(entity); await _context.SaveChangesAsync(); }
public async Task DeleteAsync(Guid id) { var entity = await _context.Customers.FindAsync(id); if (entity != null) { _context.Customers.Remove(entity); await _context.SaveChangesAsync(); } }
public async Task UpdateAsync(Customer customer)
// KORREKTE IMPLEMENTIERUNG F<>R GetByUserIdAsync
public async Task<Customer?> GetByUserIdAsync(string aspNetUserId)
{
_context.Customers.Update(customer);
await _context.SaveChangesAsync();
return await _context.Customers.FirstOrDefaultAsync(c => c.AspNetUserId == aspNetUserId);
}
}
}

View File

@@ -1,16 +1,15 @@
// src/Webshop.Infrastructure/Repositories/SupplierRepository.cs
// Auto-generiert von CreateWebshopFiles.ps1 (aktualisiert um Implementierungen)
using Microsoft.EntityFrameworkCore; // Wichtig für ToListAsync, FindAsync etc.
using Microsoft.EntityFrameworkCore;
using Webshop.Domain.Entities;
using Webshop.Domain.Interfaces;
using Webshop.Infrastructure.Data;
using System; // Für Guid
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Webshop.Infrastructure.Repositories
{
public class SupplierRepository : ISupplierRepository // Hier muss das Interface stehen
public class SupplierRepository : ISupplierRepository // MUSS public sein
{
private readonly ApplicationDbContext _context;
@@ -19,8 +18,6 @@ namespace Webshop.Infrastructure.Repositories
_context = context;
}
// --- IMPLEMENTIERUNG DER ISupplierRepository METHODEN ---
public async Task<IEnumerable<Supplier>> GetAllSuppliersAsync()
{
return await _context.Suppliers.ToListAsync();
@@ -34,7 +31,7 @@ namespace Webshop.Infrastructure.Repositories
public async Task AddSupplierAsync(Supplier supplier)
{
_context.Suppliers.Add(supplier);
await _context.SaveChangesAsync(); // Änderungen in der DB speichern
await _context.SaveChangesAsync();
}
public async Task UpdateSupplierAsync(Supplier supplier)
@@ -45,11 +42,11 @@ namespace Webshop.Infrastructure.Repositories
public async Task DeleteSupplierAsync(Guid id)
{
var supplier = await _context.Suppliers.FindAsync(id); // Lieferant zuerst finden
var supplier = await _context.Suppliers.FindAsync(id);
if (supplier != null)
{
_context.Suppliers.Remove(supplier); // Lieferant entfernen
await _context.SaveChangesAsync(); // Änderungen speichern
_context.Suppliers.Remove(supplier);
await _context.SaveChangesAsync();
}
}
}