littleshop/LittleShop/Services/CustomerService.cs
2025-08-27 18:02:39 +01:00

296 lines
10 KiB
C#

using AutoMapper;
using Microsoft.EntityFrameworkCore;
using LittleShop.Data;
using LittleShop.DTOs;
using LittleShop.Models;
namespace LittleShop.Services;
public class CustomerService : ICustomerService
{
private readonly LittleShopContext _context;
private readonly IMapper _mapper;
private readonly ILogger<CustomerService> _logger;
public CustomerService(LittleShopContext context, IMapper mapper, ILogger<CustomerService> logger)
{
_context = context;
_mapper = mapper;
_logger = logger;
}
public async Task<CustomerDto?> GetCustomerByIdAsync(Guid id)
{
var customer = await _context.Customers
.Include(c => c.Orders)
.FirstOrDefaultAsync(c => c.Id == id);
if (customer == null) return null;
var dto = _mapper.Map<CustomerDto>(customer);
dto.DisplayName = customer.DisplayName;
dto.CustomerType = customer.CustomerType;
return dto;
}
public async Task<CustomerDto?> GetCustomerByTelegramUserIdAsync(long telegramUserId)
{
var customer = await _context.Customers
.Include(c => c.Orders)
.FirstOrDefaultAsync(c => c.TelegramUserId == telegramUserId);
if (customer == null) return null;
var dto = _mapper.Map<CustomerDto>(customer);
dto.DisplayName = customer.DisplayName;
dto.CustomerType = customer.CustomerType;
return dto;
}
public async Task<CustomerDto> CreateCustomerAsync(CreateCustomerDto createCustomerDto)
{
// Check if customer already exists
var existingCustomer = await _context.Customers
.FirstOrDefaultAsync(c => c.TelegramUserId == createCustomerDto.TelegramUserId);
if (existingCustomer != null)
{
throw new InvalidOperationException($"Customer with Telegram ID {createCustomerDto.TelegramUserId} already exists");
}
var customer = _mapper.Map<Customer>(createCustomerDto);
customer.Id = Guid.NewGuid();
customer.CreatedAt = DateTime.UtcNow;
customer.UpdatedAt = DateTime.UtcNow;
customer.LastActiveAt = DateTime.UtcNow;
customer.IsActive = true;
// Set data retention date (default: 2 years after creation)
customer.DataRetentionDate = DateTime.UtcNow.AddYears(2);
_context.Customers.Add(customer);
await _context.SaveChangesAsync();
_logger.LogInformation("Created new customer {CustomerId} for Telegram user {TelegramUserId}",
customer.Id, customer.TelegramUserId);
var dto = _mapper.Map<CustomerDto>(customer);
dto.DisplayName = customer.DisplayName;
dto.CustomerType = customer.CustomerType;
return dto;
}
public async Task<CustomerDto?> UpdateCustomerAsync(Guid id, UpdateCustomerDto updateCustomerDto)
{
var customer = await _context.Customers.FindAsync(id);
if (customer == null) return null;
_mapper.Map(updateCustomerDto, customer);
customer.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
_logger.LogInformation("Updated customer {CustomerId}", id);
var dto = _mapper.Map<CustomerDto>(customer);
dto.DisplayName = customer.DisplayName;
dto.CustomerType = customer.CustomerType;
return dto;
}
public async Task<bool> DeleteCustomerAsync(Guid id)
{
var customer = await _context.Customers.FindAsync(id);
if (customer == null) return false;
// Instead of hard delete, mark as inactive for data retention compliance
customer.IsActive = false;
customer.DataRetentionDate = DateTime.UtcNow.AddDays(30); // Delete in 30 days
customer.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
_logger.LogInformation("Marked customer {CustomerId} for deletion", id);
return true;
}
public async Task<IEnumerable<CustomerDto>> GetAllCustomersAsync()
{
var customers = await _context.Customers
.Where(c => c.IsActive)
.Include(c => c.Orders)
.OrderByDescending(c => c.LastActiveAt)
.ToListAsync();
return customers.Select(c =>
{
var dto = _mapper.Map<CustomerDto>(c);
dto.DisplayName = c.DisplayName;
dto.CustomerType = c.CustomerType;
return dto;
});
}
public async Task<IEnumerable<CustomerDto>> SearchCustomersAsync(string searchTerm)
{
var query = _context.Customers
.Where(c => c.IsActive)
.Include(c => c.Orders)
.AsQueryable();
if (!string.IsNullOrWhiteSpace(searchTerm))
{
searchTerm = searchTerm.ToLower();
query = query.Where(c =>
c.TelegramUsername.ToLower().Contains(searchTerm) ||
c.TelegramDisplayName.ToLower().Contains(searchTerm) ||
c.TelegramFirstName.ToLower().Contains(searchTerm) ||
c.TelegramLastName.ToLower().Contains(searchTerm) ||
(c.Email != null && c.Email.ToLower().Contains(searchTerm)));
}
var customers = await query
.OrderByDescending(c => c.LastActiveAt)
.Take(50) // Limit search results
.ToListAsync();
return customers.Select(c =>
{
var dto = _mapper.Map<CustomerDto>(c);
dto.DisplayName = c.DisplayName;
dto.CustomerType = c.CustomerType;
return dto;
});
}
public async Task<CustomerDto?> GetOrCreateCustomerAsync(long telegramUserId, string displayName, string username = "", string firstName = "", string lastName = "")
{
// Try to find existing customer
var customer = await _context.Customers
.Include(c => c.Orders)
.FirstOrDefaultAsync(c => c.TelegramUserId == telegramUserId);
if (customer != null)
{
// Update customer information if provided
bool updated = false;
if (!string.IsNullOrEmpty(displayName) && customer.TelegramDisplayName != displayName)
{
customer.TelegramDisplayName = displayName;
updated = true;
}
if (!string.IsNullOrEmpty(username) && customer.TelegramUsername != username)
{
customer.TelegramUsername = username;
updated = true;
}
if (!string.IsNullOrEmpty(firstName) && customer.TelegramFirstName != firstName)
{
customer.TelegramFirstName = firstName;
updated = true;
}
if (!string.IsNullOrEmpty(lastName) && customer.TelegramLastName != lastName)
{
customer.TelegramLastName = lastName;
updated = true;
}
customer.LastActiveAt = DateTime.UtcNow;
if (updated)
{
customer.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
_logger.LogInformation("Updated existing customer {CustomerId} information", customer.Id);
}
else
{
await _context.SaveChangesAsync(); // Just update LastActiveAt
}
var existingDto = _mapper.Map<CustomerDto>(customer);
existingDto.DisplayName = customer.DisplayName;
existingDto.CustomerType = customer.CustomerType;
return existingDto;
}
// Create new customer
customer = new Customer
{
Id = Guid.NewGuid(),
TelegramUserId = telegramUserId,
TelegramUsername = username,
TelegramDisplayName = displayName,
TelegramFirstName = firstName,
TelegramLastName = lastName,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
LastActiveAt = DateTime.UtcNow,
DataRetentionDate = DateTime.UtcNow.AddYears(2),
IsActive = true,
AllowOrderUpdates = true,
AllowMarketing = false,
Language = "en",
Timezone = "UTC"
};
_context.Customers.Add(customer);
await _context.SaveChangesAsync();
_logger.LogInformation("Created new customer {CustomerId} for Telegram user {TelegramUserId} ({DisplayName})",
customer.Id, telegramUserId, displayName);
var dto = _mapper.Map<CustomerDto>(customer);
dto.DisplayName = customer.DisplayName;
dto.CustomerType = customer.CustomerType;
return dto;
}
public async Task UpdateCustomerMetricsAsync(Guid customerId)
{
var customer = await _context.Customers
.Include(c => c.Orders)
.FirstOrDefaultAsync(c => c.Id == customerId);
if (customer == null) return;
customer.UpdateMetrics();
await _context.SaveChangesAsync();
_logger.LogInformation("Updated metrics for customer {CustomerId}", customerId);
}
public async Task<bool> BlockCustomerAsync(Guid customerId, string reason)
{
var customer = await _context.Customers.FindAsync(customerId);
if (customer == null) return false;
customer.IsBlocked = true;
customer.BlockReason = reason;
customer.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
_logger.LogWarning("Blocked customer {CustomerId} - Reason: {Reason}", customerId, reason);
return true;
}
public async Task<bool> UnblockCustomerAsync(Guid customerId)
{
var customer = await _context.Customers.FindAsync(customerId);
if (customer == null) return false;
customer.IsBlocked = false;
customer.BlockReason = null;
customer.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
_logger.LogInformation("Unblocked customer {CustomerId}", customerId);
return true;
}
}