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.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Security.Claims; 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 System.Threading.Tasks;
using Webshop.Application.DTOs.Customers; using Webshop.Application.Services.Customers;
using Webshop.Application.Services.Customers.Interfaces;
namespace Webshop.Api.Controllers.Customers namespace Webshop.Api.Controllers.Customer
{ {
[ApiController] [ApiController]
[Route("api/v1/customer/profile")] // Eindeutige Route f<>r das Profil [Route("api/v1/customer/[controller]")] // z.B. /api/v1/customer/profile
[Authorize(Roles = "Customer")] // Nur f<>r eingeloggte Kunden! [Authorize(Roles = "Customer")]
public class ProfileController : ControllerBase public class ProfileController : ControllerBase
{ {
private readonly ICustomerService _customerService; private readonly ICustomerService _customerService;
@@ -19,33 +23,49 @@ namespace Webshop.Api.Controllers.Customers
_customerService = customerService; _customerService = customerService;
} }
// Hilfsmethode, um die ID des eingeloggten Benutzers aus dem Token zu holen [HttpGet("me")] // /api/v1/customer/profile/me
private string GetUserId() => User.FindFirstValue(ClaimTypes.NameIdentifier)!;
[HttpGet("me")] // GET /api/v1/customer/profile/me
public async Task<ActionResult<CustomerDto>> GetMyProfile() public async Task<ActionResult<CustomerDto>> GetMyProfile()
{ {
var userId = GetUserId(); var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var profile = await _customerService.GetMyProfileAsync(userId); if (string.IsNullOrEmpty(userId)) return Unauthorized(new { Message = "Benutzer-ID nicht im Token gefunden." });
if (profile == null) var customerProfile = await _customerService.GetMyProfileAsync(userId);
{ if (customerProfile == null) return NotFound(new { Message = "Kundenprofil nicht gefunden. Bitte erstellen Sie es." });
return NotFound("Kundenprofil nicht gefunden.");
} return Ok(customerProfile);
return Ok(profile);
} }
[HttpPut("me")] // PUT /api/v1/customer/profile/me [HttpPost("change-password")] // /api/v1/customer/profile/change-password
public async Task<IActionResult> UpdateMyProfile([FromBody] UpdateCustomerProfileDto profileDto) public async Task<IActionResult> ChangePassword([FromBody] ChangePasswordRequestDto request)
{ {
var userId = GetUserId(); if (!ModelState.IsValid) return BadRequest(ModelState);
var success = await _customerService.UpdateMyProfileAsync(userId, profileDto);
if (!success) 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)
{ {
return NotFound("Kundenprofil nicht gefunden."); if (!ModelState.IsValid) return BadRequest(ModelState);
}
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.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.Public.Interfaces;
using Webshop.Application.Services.Customers.Interfaces; using Webshop.Application.Services.Customers.Interfaces;
using Webshop.Application.Services.Customers; using Webshop.Application.Services.Customers;
using Webshop.Domain.Identity;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@@ -136,6 +137,7 @@ builder.Services.AddSwaggerGen(c =>
var app = builder.Build(); // <-- Hier wird die App gebaut 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 // OPTIONALE BL<42>CKE F<>R MIGRATION UND BENUTZERINITIALISIERUNG - DIESER CODE WIRD VOR APP.RUN() AUSGEF<45>HRT
using (var scope = app.Services.CreateScope()) 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. // 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! // BITTE ENTFERNEN ODER KOMMENTIEREN SIE DIESEN BLOCK AUS, NACHDEM SIE IHRE ERSTEN BENUTZER ERSTELLT HABEN!
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>(); 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" }; string[] roleNames = { "Admin", "Customer" };
@@ -162,23 +164,39 @@ using (var scope = app.Services.CreateScope())
} }
} }
// Erstelle einen initialen Admin-Benutzer // Erstelle einen initialen Admin-Benutzer und sein Customer-Profil
var adminUser = await userManager.FindByEmailAsync("admin@yourwebshop.com"); var adminUser = await userManager.FindByEmailAsync("admin@yourwebshop.com"); // << ANPASSEN >>
if (adminUser == null) if (adminUser == null)
{ {
// Erstellen Sie hier eine Instanz von ApplicationUser adminUser = new ApplicationUser // << KORREKT: ERSTELLT ApplicationUser >>
adminUser = new ApplicationUser
{ {
UserName = "admin@yourwebshop.com", UserName = "admin@yourwebshop.com", // << ANPASSEN >>
Email = "admin@yourwebshop.com", Email = "admin@yourwebshop.com", // << ANPASSEN >>
EmailConfirmed = true, 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) if (createAdmin.Succeeded)
{ {
await userManager.AddToRoleAsync(adminUser, "Admin"); await userManager.AddToRoleAsync(adminUser, "Admin");
Console.WriteLine("Admin user created."); 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 else
{ {
@@ -186,29 +204,46 @@ using (var scope = app.Services.CreateScope())
} }
} }
// Erstelle einen initialen Kunden-Benutzer // Erstelle einen initialen Kunden-Benutzer und sein Customer-Profil (KOMBINIERT)
var customerUser = await userManager.FindByEmailAsync("customer@yourwebshop.com"); var customerUser = await userManager.FindByEmailAsync("customer@yourwebshop.com"); // << ANPASSEN >>
if (customerUser == null) if (customerUser == null)
{ {
// Erstellen Sie auch hier eine Instanz von ApplicationUser customerUser = new ApplicationUser // << KORREKT: ERSTELLT ApplicationUser >>
customerUser = new ApplicationUser
{ {
UserName = "customer@yourwebshop.com", UserName = "customer@yourwebshop.com", // << ANPASSEN >>
Email = "customer@yourwebshop.com", Email = "customer@yourwebshop.com", // << ANPASSEN >>
EmailConfirmed = true, 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) if (createCustomer.Succeeded)
{ {
await userManager.AddToRoleAsync(customerUser, "Customer"); await userManager.AddToRoleAsync(customerUser, "Customer");
Console.WriteLine("Customer user created."); 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 else
{ {
Console.WriteLine($"Error creating customer user: {string.Join(", ", createCustomer.Errors.Select(e => e.Description))}"); Console.WriteLine($"Error creating customer user: {string.Join(", ", createCustomer.Errors.Select(e => e.Description))}");
} }
} }
// --- ENDE DES TEMPOR<4F>REN SETUP-BLOCKS ---
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -14,7 +14,7 @@ using Webshop.Application.DTOs.Products;
using Webshop.Application.DTOs.Payments; using Webshop.Application.DTOs.Payments;
using Webshop.Application.DTOs.Orders; using Webshop.Application.DTOs.Orders;
using Webshop.Application.DTOs.Discounts; using Webshop.Application.DTOs.Discounts;
using Webshop.Application.DTOs.Categorys; // Für Guid.NewGuid() using Webshop.Application.DTOs.Categorys;
namespace Webshop.Api.SwaggerFilters 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 namespace Webshop.Application.DTOs.Customers
{ {
public class UpdateCustomerProfileDto public class UpdateCustomerProfileDto
{ {
[Required(ErrorMessage = "Vorname ist erforderlich.")] [Required(ErrorMessage = "Vorname ist erforderlich.")]
[MaxLength(100)] [StringLength(100)]
public string FirstName { get; set; } = string.Empty; public string FirstName { get; set; } = string.Empty;
[Required(ErrorMessage = "Nachname ist erforderlich.")] [Required(ErrorMessage = "Nachname ist erforderlich.")]
[MaxLength(100)] [StringLength(100)]
public string LastName { get; set; } = string.Empty; 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 // src/Webshop.Application/DTOs/Users/UserDto.cs
using System;
using System.Collections.Generic;
namespace Webshop.Application.DTOs.Users namespace Webshop.Application.DTOs.Users
{ {
public class UserDto public class UserDto
{ {
public string Id { get; set; } = string.Empty; public string Id { get; set; } = string.Empty; // Vom ApplicationUser.Id
public string Email { get; set; } = string.Empty; public string Email { get; set; } = string.Empty; // Vom ApplicationUser.Email
public string UserName { get; set; } = string.Empty; public string UserName { get; set; } = string.Empty; // Vom ApplicationUser.UserName
public List<string> Roles { get; set; } = new List<string>(); public List<string> Roles { get; set; } = new List<string>(); // Aus Identity-System
public DateTimeOffset CreatedDate { get; set; } public DateTimeOffset CreatedDate { get; set; } // Vom ApplicationUser.CreatedDate
public bool EmailConfirmed { get; set; } public bool EmailConfirmed { get; set; } // Vom ApplicationUser.EmailConfirmed
// Hinzugef<65>gte Felder public DateTimeOffset? LastActive { get; set; } // Vom ApplicationUser.LastActive
public DateTimeOffset? LastActive { get; set; } public string? PhoneNumber { get; set; } // Vom ApplicationUser.PhoneNumber
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty; // << 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.DTOs.Products;
using Webshop.Application.Services.Admin.Interfaces; // F<>r Select using Webshop.Application.Services.Admin.Interfaces; // F<>r Select
namespace Webshop.Application.Services.Admin namespace Webshop.Application.Services.Admin
{ {
public class AdminProductService : IAdminProductService // Sicherstellen, dass IAdminProductService implementiert wird public class AdminProductService : IAdminProductService // Sicherstellen, dass IAdminProductService implementiert wird

View File

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

View File

@@ -8,6 +8,7 @@ using Webshop.Application.DTOs.Users;
using Webshop.Application.Services.Admin.Interfaces; using Webshop.Application.Services.Admin.Interfaces;
using Webshop.Domain.Entities; using Webshop.Domain.Entities;
using Webshop.Infrastructure.Data; // WICHTIG: Stellt sicher, dass ApplicationDbContext gefunden wird. using Webshop.Infrastructure.Data; // WICHTIG: Stellt sicher, dass ApplicationDbContext gefunden wird.
using Webshop.Domain.Identity;
namespace Webshop.Application.Services.Admin 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 System.Threading.Tasks;
using Webshop.Application.DTOs.Users; 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 System.Threading.Tasks;
using Webshop.Application.DTOs.Customers; using Webshop.Application.DTOs; // CustomerDto
using Webshop.Application.Services.Customers.Interfaces; using Webshop.Application.DTOs.Auth; // ChangePasswordRequestDto
using Webshop.Domain.Interfaces; // Wichtig für ICustomerRepository 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 namespace Webshop.Application.Services.Customers
{ {
public class CustomerService : ICustomerService public class CustomerService : ICustomerService
{ {
private readonly ICustomerRepository _customerRepository; private readonly ICustomerRepository _customerRepository;
private readonly UserManager<ApplicationUser> _userManager;
public CustomerService(ICustomerRepository customerRepository) public CustomerService(ICustomerRepository customerRepository, UserManager<ApplicationUser> userManager)
{ {
_customerRepository = customerRepository; _customerRepository = customerRepository;
_userManager = userManager;
} }
public async Task<CustomerDto?> GetMyProfileAsync(string userId) public async Task<CustomerDto?> GetMyProfileAsync(string userId)
{ {
var customer = await _customerRepository.GetByUserIdAsync(userId); var customer = await _customerRepository.GetByUserIdAsync(userId);
if (customer == null) if (customer == null) return null;
{
return null; var identityUser = await _userManager.FindByIdAsync(userId);
} if (identityUser == null) return null;
// Mappe die Entity auf das CustomerDto
return new CustomerDto return new CustomerDto
{ {
Id = customer.Id, Id = customer.Id,
UserId = customer.AspNetUserId, UserId = customer.AspNetUserId,
FirstName = customer.FirstName, FirstName = customer.FirstName,
LastName = customer.LastName, 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 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)
{
var errors = string.Join(" ", result.Errors.Select(e => e.Description));
return (false, errors);
}
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); var customer = await _customerRepository.GetByUserIdAsync(userId);
if (customer == null) 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; // Kunde nicht gefunden return (false, "Falsches aktuelles Passwort zur Bestätigung.");
} }
// Aktualisiere die Felder // 2. Felder der Customer-Entität aktualisieren (FirstName, LastName)
customer.FirstName = profileDto.FirstName; customer.FirstName = profileDto.FirstName;
customer.LastName = profileDto.LastName; 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); // 3. Felder des ApplicationUser (IdentityUser) aktualisieren (Email, PhoneNumber)
return true; 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 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 public interface ICustomerService
{ {
Task<CustomerDto?> GetMyProfileAsync(string userId); 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,23 +1,38 @@
using System; // src/Webshop.Domain/Entities/Customer.cs
using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using Webshop.Domain.Identity; // Für ApplicationUser
namespace Webshop.Domain.Entities namespace Webshop.Domain.Entities
{ {
public class Customer public class Customer
{ {
[Key] [Key]
public Guid Id { get; set; } public Guid Id { get; set; } = Guid.NewGuid(); // Default-Wert setzen
[Required]
public string AspNetUserId { get; set; }
[Required]
[MaxLength(100)]
public string FirstName { get; set; }
[Required]
[MaxLength(100)]
public string LastName { get; set; }
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<Address> Addresses { get; set; } = new List<Address>();
public virtual ICollection<Order> Orders { get; set; } = new List<Order>(); public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
public virtual ICollection<Review> Reviews { get; set; } = new List<Review>(); 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 System.Threading.Tasks;
using Webshop.Domain.Entities; using Webshop.Domain.Entities;
@@ -6,7 +7,12 @@ namespace Webshop.Domain.Interfaces
{ {
public interface ICustomerRepository public interface ICustomerRepository
{ {
Task<Customer?> GetByUserIdAsync(string userId); Task<IEnumerable<Customer>> GetAllAsync(); // Standard CRUD
Task UpdateAsync(Customer customer); 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 Microsoft.EntityFrameworkCore;
using Webshop.Domain.Entities; using Webshop.Domain.Entities;
using Webshop.Domain.Interfaces; using Webshop.Domain.Interfaces;
using Webshop.Infrastructure.Data; using Webshop.Infrastructure.Data;
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Webshop.Infrastructure.Repositories namespace Webshop.Infrastructure.Repositories
{ {
public class CustomerRepository : ICustomerRepository public class CustomerRepository : ICustomerRepository // MUSS public sein
{ {
private readonly ApplicationDbContext _context; private readonly ApplicationDbContext _context;
@@ -16,15 +18,17 @@ namespace Webshop.Infrastructure.Repositories
_context = context; _context = context;
} }
public async Task<Customer?> GetByUserIdAsync(string userId) // Beispiel-Implementierungen (wenn nicht schon vorhanden):
{ public async Task<IEnumerable<Customer>> GetAllAsync() => await _context.Customers.ToListAsync();
return await _context.Customers.FirstOrDefaultAsync(c => c.AspNetUserId == userId); 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); return await _context.Customers.FirstOrDefaultAsync(c => c.AspNetUserId == aspNetUserId);
await _context.SaveChangesAsync();
} }
} }
} }

View File

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