using Meezi.API.Models.Crm; using Meezi.Core.Entities; using Meezi.Core.Utilities; using Meezi.Infrastructure.Data; using Microsoft.EntityFrameworkCore; namespace Meezi.API.Services; public interface ICustomerService { Task> SearchAsync(string cafeId, string? query, CancellationToken cancellationToken = default); Task GetAsync(string cafeId, string id, CancellationToken cancellationToken = default); Task CreateAsync(string cafeId, CreateCustomerRequest request, CancellationToken cancellationToken = default); Task UpdateAsync(string cafeId, string id, UpdateCustomerRequest request, CancellationToken cancellationToken = default); Task DeleteAsync(string cafeId, string id, CancellationToken cancellationToken = default); } public class CustomerService : ICustomerService { private readonly AppDbContext _db; public CustomerService(AppDbContext db) { _db = db; } public async Task> SearchAsync( string cafeId, string? query, CancellationToken cancellationToken = default) { var q = _db.Customers.Where(c => c.CafeId == cafeId && c.DeletedAt == null); if (!string.IsNullOrWhiteSpace(query)) { var term = query.Trim(); var normalizedPhone = PhoneNormalizer.Normalize(term); q = q.Where(c => c.Name.Contains(term) || c.Phone.Contains(term) || (c.NationalId != null && c.NationalId.Contains(term)) || (normalizedPhone.Length >= 10 && c.Phone.Contains(normalizedPhone))); } var list = await q .OrderByDescending(c => c.CreatedAt) .Take(100) .ToListAsync(cancellationToken); return list.Select(ToDto).ToList(); } public async Task GetAsync(string cafeId, string id, CancellationToken cancellationToken = default) { var entity = await _db.Customers .FirstOrDefaultAsync(c => c.Id == id && c.CafeId == cafeId && c.DeletedAt == null, cancellationToken); return entity is null ? null : ToDto(entity); } public async Task CreateAsync( string cafeId, CreateCustomerRequest request, CancellationToken cancellationToken = default) { var phone = PhoneNormalizer.Normalize(request.Phone); var exists = await _db.Customers.AnyAsync( c => c.CafeId == cafeId && c.Phone == phone, cancellationToken); if (exists) return null; var entity = new Customer { CafeId = cafeId, Name = request.Name, Phone = phone, NationalId = request.NationalId, BirthDateJalali = request.BirthDateJalali, Group = request.Group, ReferredBy = request.ReferredBy }; _db.Customers.Add(entity); await _db.SaveChangesAsync(cancellationToken); return ToDto(entity); } public async Task UpdateAsync( string cafeId, string id, UpdateCustomerRequest request, CancellationToken cancellationToken = default) { var entity = await _db.Customers .FirstOrDefaultAsync(c => c.Id == id && c.CafeId == cafeId && c.DeletedAt == null, cancellationToken); if (entity is null) return null; if (request.Name is not null) entity.Name = request.Name; if (request.Phone is not null) { var phone = PhoneNormalizer.Normalize(request.Phone); var phoneTaken = await _db.Customers.AnyAsync( c => c.CafeId == cafeId && c.Phone == phone && c.Id != id && c.DeletedAt == null, cancellationToken); if (phoneTaken) return null; entity.Phone = phone; } if (request.NationalId is not null) entity.NationalId = request.NationalId; if (request.BirthDateJalali is not null) entity.BirthDateJalali = request.BirthDateJalali; if (request.Group.HasValue) entity.Group = request.Group.Value; if (request.LoyaltyPoints.HasValue) entity.LoyaltyPoints = request.LoyaltyPoints.Value; if (request.ReferredBy is not null) entity.ReferredBy = request.ReferredBy; await _db.SaveChangesAsync(cancellationToken); return ToDto(entity); } public async Task DeleteAsync(string cafeId, string id, CancellationToken cancellationToken = default) { var entity = await _db.Customers .FirstOrDefaultAsync(c => c.Id == id && c.CafeId == cafeId, cancellationToken); if (entity is null) return false; entity.DeletedAt = DateTime.UtcNow; await _db.SaveChangesAsync(cancellationToken); return true; } private static CustomerDto ToDto(Customer c) => new( c.Id, c.Name, c.Phone, c.NationalId, c.BirthDateJalali, c.Group, c.LoyaltyPoints, c.ReferredBy, c.CreatedAt); }