137 lines
5.0 KiB
C#
137 lines
5.0 KiB
C#
|
|
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<IReadOnlyList<CustomerDto>> SearchAsync(string cafeId, string? query, CancellationToken cancellationToken = default);
|
||
|
|
Task<CustomerDto?> GetAsync(string cafeId, string id, CancellationToken cancellationToken = default);
|
||
|
|
Task<CustomerDto?> CreateAsync(string cafeId, CreateCustomerRequest request, CancellationToken cancellationToken = default);
|
||
|
|
Task<CustomerDto?> UpdateAsync(string cafeId, string id, UpdateCustomerRequest request, CancellationToken cancellationToken = default);
|
||
|
|
Task<bool> 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<IReadOnlyList<CustomerDto>> 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<CustomerDto?> 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<CustomerDto?> 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<CustomerDto?> 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<bool> 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);
|
||
|
|
}
|