shop info

This commit is contained in:
Tizian.Breuch
2025-08-12 11:07:15 +02:00
parent 0a43b1d7e8
commit 1692fe198e
13 changed files with 387 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
// src/Webshop.Api/Controllers/Admin/AdminShopInfoController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Webshop.Application.DTOs.Shop;
using Webshop.Application.Services.Admin;
namespace Webshop.Api.Controllers.Admin
{
[ApiController]
[Route("api/v1/admin/[controller]")]
[Authorize(Roles = "Admin")]
public class AdminShopInfoController : ControllerBase
{
private readonly IAdminShopInfoService _shopInfoService;
public AdminShopInfoController(IAdminShopInfoService shopInfoService)
{
_shopInfoService = shopInfoService;
}
[HttpGet]
public async Task<ActionResult<AdminShopInfoDto>> GetShopInfo()
{
var info = await _shopInfoService.GetShopInfoAsync();
return Ok(info);
}
[HttpPut]
public async Task<IActionResult> UpdateShopInfo([FromBody] AdminShopInfoDto shopInfoDto)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
await _shopInfoService.UpdateShopInfoAsync(shopInfoDto);
return NoContent();
}
}
}

View File

@@ -0,0 +1,29 @@
// src/Webshop.Api/Controllers/Public/ShopInfoController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Webshop.Application.DTOs.Shop;
using Webshop.Application.Services.Public;
namespace Webshop.Api.Controllers.Public
{
[ApiController]
[Route("api/v1/public/[]controller")]
[AllowAnonymous]
public class ShopInfoController : ControllerBase
{
private readonly IShopInfoService _shopInfoService;
public ShopInfoController(IShopInfoService shopInfoService)
{
_shopInfoService = shopInfoService;
}
[HttpGet]
public async Task<ActionResult<PublicShopInfoDto>> GetPublicShopInfo()
{
var info = await _shopInfoService.GetPublicShopInfoAsync();
return Ok(info);
}
}
}

View File

@@ -105,6 +105,7 @@ builder.Services.AddScoped<IAddressRepository, AddressRepository>();
builder.Services.AddScoped<IDiscountRepository, DiscountRepository>(); builder.Services.AddScoped<IDiscountRepository, DiscountRepository>();
builder.Services.AddScoped<IReviewRepository, ReviewRepository>(); builder.Services.AddScoped<IReviewRepository, ReviewRepository>();
builder.Services.AddScoped<ISettingRepository, SettingRepository>(); builder.Services.AddScoped<ISettingRepository, SettingRepository>();
builder.Services.AddScoped<IShopInfoRepository, ShopInfoRepository>();
// Services // Services
builder.Services.AddScoped<IAuthService, AuthService>(); builder.Services.AddScoped<IAuthService, AuthService>();
@@ -125,6 +126,8 @@ builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.AddScoped<IAddressService, AddressService>(); builder.Services.AddScoped<IAddressService, AddressService>();
builder.Services.AddScoped<ICheckoutService, CheckoutService>(); builder.Services.AddScoped<ICheckoutService, CheckoutService>();
builder.Services.AddScoped<IReviewService, ReviewService>(); builder.Services.AddScoped<IReviewService, ReviewService>();
builder.Services.AddScoped<IAdminShopInfoService, AdminShopInfoService>();
builder.Services.AddScoped<IShopInfoService, ShopInfoService>();
// Controller und API-Infrastruktur // Controller und API-Infrastruktur
builder.Services.AddControllers() builder.Services.AddControllers()

View File

@@ -0,0 +1,47 @@
// src/Webshop.Application/DTOs/Admin/AdminShopInfoDto.cs
using System.ComponentModel.DataAnnotations;
namespace Webshop.Application.DTOs.Shop
{
public class AdminShopInfoDto
{
[Required, MaxLength(255)]
public string ShopName { get; set; } = string.Empty;
[MaxLength(500)]
public string? Slogan { get; set; }
[Required, EmailAddress, MaxLength(255)]
public string ContactEmail { get; set; } = string.Empty;
[Phone, MaxLength(50)]
public string? PhoneNumber { get; set; }
[MaxLength(255)]
public string? Street { get; set; }
[MaxLength(100)]
public string? City { get; set; }
[MaxLength(20)]
public string? PostalCode { get; set; }
[MaxLength(100)]
public string? Country { get; set; }
[MaxLength(50)]
public string? VatNumber { get; set; }
[MaxLength(100)]
public string? CompanyRegistrationNumber { get; set; }
[Url, MaxLength(255)]
public string? FacebookUrl { get; set; }
[Url, MaxLength(255)]
public string? InstagramUrl { get; set; }
[Url, MaxLength(255)]
public string? TwitterUrl { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
// src/Webshop.Application/DTOs/Shop/PublicShopInfoDto.cs
namespace Webshop.Application.DTOs.Shop
{
// Dieses DTO enthält nur die Informationen, die ein normaler Kunde sehen soll.
public class PublicShopInfoDto
{
public string ShopName { get; set; } = string.Empty;
public string? Slogan { get; set; }
public string ContactEmail { get; set; } = string.Empty;
public string? PhoneNumber { get; set; }
public string? Street { get; set; }
public string? City { get; set; }
public string? PostalCode { get; set; }
public string? Country { get; set; }
public string? FacebookUrl { get; set; }
public string? InstagramUrl { get; set; }
public string? TwitterUrl { get; set; }
}
}

View File

@@ -0,0 +1,74 @@
// src/Webshop.Application/Services/Admin/AdminShopInfoService.cs
using System;
using System.Threading.Tasks;
using Webshop.Application.DTOs.Shop;
using Webshop.Domain.Entities;
using Webshop.Domain.Interfaces;
namespace Webshop.Application.Services.Admin
{
public class AdminShopInfoService : IAdminShopInfoService
{
private readonly IShopInfoRepository _shopInfoRepository;
public AdminShopInfoService(IShopInfoRepository shopInfoRepository)
{
_shopInfoRepository = shopInfoRepository;
}
public async Task<AdminShopInfoDto> GetShopInfoAsync()
{
var shopInfo = await _shopInfoRepository.GetAsync();
if (shopInfo == null)
{
// Erstelle einen Standardeintrag, falls keiner existiert
shopInfo = new ShopInfo { Id = Guid.NewGuid(), ShopName = "Mein Webshop", ContactEmail = "admin@example.com" };
await _shopInfoRepository.AddAsync(shopInfo);
}
return new AdminShopInfoDto
{
ShopName = shopInfo.ShopName,
Slogan = shopInfo.Slogan,
ContactEmail = shopInfo.ContactEmail,
PhoneNumber = shopInfo.PhoneNumber,
Street = shopInfo.Street,
City = shopInfo.City,
PostalCode = shopInfo.PostalCode,
Country = shopInfo.Country,
VatNumber = shopInfo.VatNumber,
CompanyRegistrationNumber = shopInfo.CompanyRegistrationNumber,
FacebookUrl = shopInfo.FacebookUrl,
InstagramUrl = shopInfo.InstagramUrl,
TwitterUrl = shopInfo.TwitterUrl
};
}
public async Task UpdateShopInfoAsync(AdminShopInfoDto shopInfoDto)
{
var shopInfo = await _shopInfoRepository.GetAsync();
if (shopInfo == null)
{
throw new InvalidOperationException("ShopInfo entity does not exist and cannot be updated.");
}
// Mappe die Werte vom DTO auf die Entität
shopInfo.ShopName = shopInfoDto.ShopName;
shopInfo.Slogan = shopInfoDto.Slogan;
shopInfo.ContactEmail = shopInfoDto.ContactEmail;
shopInfo.PhoneNumber = shopInfoDto.PhoneNumber;
shopInfo.Street = shopInfoDto.Street;
shopInfo.City = shopInfoDto.City;
shopInfo.PostalCode = shopInfoDto.PostalCode;
shopInfo.Country = shopInfoDto.Country;
shopInfo.VatNumber = shopInfoDto.VatNumber;
shopInfo.CompanyRegistrationNumber = shopInfoDto.CompanyRegistrationNumber;
shopInfo.FacebookUrl = shopInfoDto.FacebookUrl;
shopInfo.InstagramUrl = shopInfoDto.InstagramUrl;
shopInfo.TwitterUrl = shopInfoDto.TwitterUrl;
await _shopInfoRepository.UpdateAsync(shopInfo);
}
}
}

View File

@@ -0,0 +1,12 @@
// src/Webshop.Application/Services/Admin/IAdminShopInfoService.cs
using System.Threading.Tasks;
using Webshop.Application.DTOs.Shop;
namespace Webshop.Application.Services.Admin
{
public interface IAdminShopInfoService
{
Task<AdminShopInfoDto> GetShopInfoAsync();
Task UpdateShopInfoAsync(AdminShopInfoDto shopInfoDto);
}
}

View File

@@ -0,0 +1,11 @@
// src/Webshop.Application/Services/Public/IShopInfoService.cs
using System.Threading.Tasks;
using Webshop.Application.DTOs.Shop;
namespace Webshop.Application.Services.Public
{
public interface IShopInfoService
{
Task<PublicShopInfoDto> GetPublicShopInfoAsync();
}
}

View File

@@ -0,0 +1,43 @@
// src/Webshop.Application/Services/Public/ShopInfoService.cs
using System.Threading.Tasks;
using Webshop.Application.DTOs.Shop;
using Webshop.Domain.Interfaces;
namespace Webshop.Application.Services.Public
{
public class ShopInfoService : IShopInfoService
{
private readonly IShopInfoRepository _shopInfoRepository;
public ShopInfoService(IShopInfoRepository shopInfoRepository)
{
_shopInfoRepository = shopInfoRepository;
}
public async Task<PublicShopInfoDto> GetPublicShopInfoAsync()
{
var shopInfo = await _shopInfoRepository.GetAsync();
if (shopInfo == null)
{
// Gib Standardwerte zurück, wenn nichts konfiguriert ist
return new PublicShopInfoDto { ShopName = "Webshop" };
}
return new PublicShopInfoDto
{
ShopName = shopInfo.ShopName,
Slogan = shopInfo.Slogan,
ContactEmail = shopInfo.ContactEmail,
PhoneNumber = shopInfo.PhoneNumber,
Street = shopInfo.Street,
City = shopInfo.City,
PostalCode = shopInfo.PostalCode,
Country = shopInfo.Country,
FacebookUrl = shopInfo.FacebookUrl,
InstagramUrl = shopInfo.InstagramUrl,
TwitterUrl = shopInfo.TwitterUrl
};
}
}
}

View File

@@ -0,0 +1,58 @@
// src/Webshop.Domain/Entities/ShopInfo.cs
using System;
using System.ComponentModel.DataAnnotations;
namespace Webshop.Domain.Entities
{
/// <summary>
/// Enthält die zentralen Stammdaten des Shops.
/// Es sollte nur eine einzige Zeile in dieser Tabelle existieren.
/// </summary>
public class ShopInfo
{
[Key]
public Guid Id { get; set; }
[Required, MaxLength(255)]
public string ShopName { get; set; } = string.Empty;
[MaxLength(500)]
public string? Slogan { get; set; } // << NEU: Für Marketing >>
[Required, MaxLength(255)]
public string ContactEmail { get; set; } = string.Empty;
[MaxLength(50)]
public string? PhoneNumber { get; set; }
// --- Firmenadresse ---
[MaxLength(255)]
public string? Street { get; set; }
[MaxLength(100)]
public string? City { get; set; }
[MaxLength(20)]
public string? PostalCode { get; set; }
[MaxLength(100)]
public string? Country { get; set; }
// --- Rechtliches ---
[MaxLength(50)]
public string? VatNumber { get; set; } // Umsatzsteuer-ID
[MaxLength(100)]
public string? CompanyRegistrationNumber { get; set; } // Handelsregisternummer
// --- Social Media & Links ---
[MaxLength(255)]
public string? FacebookUrl { get; set; } // << NEU >>
[MaxLength(255)]
public string? InstagramUrl { get; set; } // << NEU >>
[MaxLength(255)]
public string? TwitterUrl { get; set; } // << NEU >>
}
}

View File

@@ -0,0 +1,13 @@
// src/Webshop.Domain/Interfaces/IShopInfoRepository.cs
using System.Threading.Tasks;
using Webshop.Domain.Entities;
namespace Webshop.Domain.Interfaces
{
public interface IShopInfoRepository
{
Task<ShopInfo?> GetAsync(); // Ruft die (einzige) ShopInfo-Zeile ab
Task UpdateAsync(ShopInfo shopInfo);
Task AddAsync(ShopInfo shopInfo);
}
}

View File

@@ -30,6 +30,7 @@ namespace Webshop.Infrastructure.Data
public DbSet<ProductDiscount> ProductDiscounts { get; set; } = default!; public DbSet<ProductDiscount> ProductDiscounts { get; set; } = default!;
public DbSet<CategorieDiscount> categorieDiscounts { get; set; } = default!; public DbSet<CategorieDiscount> categorieDiscounts { get; set; } = default!;
public DbSet<ProductImage> ProductImages { get; set; } = default!; public DbSet<ProductImage> ProductImages { get; set; } = default!;
public DbSet<ShopInfo> ShopInfos { get; set; } = default!;
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)

View File

@@ -0,0 +1,37 @@
// src/Webshop.Infrastructure/Repositories/ShopInfoRepository.cs
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;
using Webshop.Domain.Entities;
using Webshop.Domain.Interfaces;
using Webshop.Infrastructure.Data;
namespace Webshop.Infrastructure.Repositories
{
public class ShopInfoRepository : IShopInfoRepository
{
private readonly ApplicationDbContext _context;
public ShopInfoRepository(ApplicationDbContext context)
{
_context = context;
}
public async Task<ShopInfo?> GetAsync()
{
// Es sollte immer nur eine Zeile geben, also nehmen wir die erste
return await _context.ShopInfos.FirstOrDefaultAsync();
}
public async Task UpdateAsync(ShopInfo shopInfo)
{
_context.ShopInfos.Update(shopInfo);
await _context.SaveChangesAsync();
}
public async Task AddAsync(ShopInfo shopInfo)
{
await _context.ShopInfos.AddAsync(shopInfo);
await _context.SaveChangesAsync();
}
}
}