148 lines
4.6 KiB
C#
148 lines
4.6 KiB
C#
using System.ComponentModel.DataAnnotations;
|
|
using System.ComponentModel.DataAnnotations.Schema;
|
|
|
|
namespace LittleShop.Models;
|
|
|
|
public class Customer
|
|
{
|
|
[Key]
|
|
public Guid Id { get; set; }
|
|
|
|
// Telegram Information
|
|
[Required]
|
|
public long TelegramUserId { get; set; }
|
|
|
|
[StringLength(100)]
|
|
public string TelegramUsername { get; set; } = string.Empty;
|
|
|
|
[Required]
|
|
[StringLength(200)]
|
|
public string TelegramDisplayName { get; set; } = string.Empty;
|
|
|
|
[StringLength(50)]
|
|
public string TelegramFirstName { get; set; } = string.Empty;
|
|
|
|
[StringLength(50)]
|
|
public string TelegramLastName { get; set; } = string.Empty;
|
|
|
|
// Optional Contact Information (if customer provides)
|
|
[StringLength(100)]
|
|
public string? Email { get; set; }
|
|
|
|
[StringLength(20)]
|
|
public string? PhoneNumber { get; set; }
|
|
|
|
// Customer Preferences
|
|
public bool AllowMarketing { get; set; } = false;
|
|
|
|
public bool AllowOrderUpdates { get; set; } = true;
|
|
|
|
[StringLength(10)]
|
|
public string Language { get; set; } = "en";
|
|
|
|
[StringLength(10)]
|
|
public string Timezone { get; set; } = "UTC";
|
|
|
|
// Customer Metrics
|
|
public int TotalOrders { get; set; } = 0;
|
|
|
|
[Column(TypeName = "decimal(18,2)")]
|
|
public decimal TotalSpent { get; set; } = 0;
|
|
|
|
[Column(TypeName = "decimal(18,2)")]
|
|
public decimal AverageOrderValue { get; set; } = 0;
|
|
|
|
public DateTime FirstOrderDate { get; set; }
|
|
|
|
public DateTime LastOrderDate { get; set; }
|
|
|
|
// Customer Service Notes
|
|
[StringLength(2000)]
|
|
public string? CustomerNotes { get; set; }
|
|
|
|
public bool IsBlocked { get; set; } = false;
|
|
|
|
[StringLength(500)]
|
|
public string? BlockReason { get; set; }
|
|
|
|
// Risk Assessment
|
|
public int RiskScore { get; set; } = 0; // 0-100, 0 = trusted, 100 = high risk
|
|
|
|
public int SuccessfulOrders { get; set; } = 0;
|
|
|
|
public int CancelledOrders { get; set; } = 0;
|
|
|
|
public int DisputedOrders { get; set; } = 0;
|
|
|
|
// Data Management
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
|
|
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
|
|
|
public DateTime LastActiveAt { get; set; } = DateTime.UtcNow;
|
|
|
|
public DateTime? DataRetentionDate { get; set; } // When to delete this customer's data
|
|
|
|
public bool IsActive { get; set; } = true;
|
|
|
|
// Navigation properties
|
|
public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
|
|
public virtual ICollection<CustomerMessage> Messages { get; set; } = new List<CustomerMessage>();
|
|
|
|
// Calculated Properties
|
|
public string DisplayName =>
|
|
!string.IsNullOrEmpty(TelegramDisplayName) ? TelegramDisplayName :
|
|
!string.IsNullOrEmpty(TelegramUsername) ? $"@{TelegramUsername}" :
|
|
$"{TelegramFirstName} {TelegramLastName}".Trim();
|
|
|
|
public string CustomerType =>
|
|
TotalOrders == 0 ? "New" :
|
|
TotalOrders == 1 ? "First-time" :
|
|
TotalOrders < 5 ? "Regular" :
|
|
TotalOrders < 20 ? "Loyal" : "VIP";
|
|
|
|
public void UpdateMetrics()
|
|
{
|
|
if (Orders?.Any() == true)
|
|
{
|
|
TotalOrders = Orders.Count();
|
|
TotalSpent = Orders.Sum(o => o.TotalAmount);
|
|
AverageOrderValue = TotalOrders > 0 ? TotalSpent / TotalOrders : 0;
|
|
FirstOrderDate = Orders.Min(o => o.CreatedAt);
|
|
LastOrderDate = Orders.Max(o => o.CreatedAt);
|
|
SuccessfulOrders = Orders.Count(o => o.Status == Enums.OrderStatus.Delivered);
|
|
CancelledOrders = Orders.Count(o => o.Status == Enums.OrderStatus.Cancelled);
|
|
}
|
|
|
|
UpdatedAt = DateTime.UtcNow;
|
|
|
|
// Update risk score based on order history
|
|
CalculateRiskScore();
|
|
}
|
|
|
|
private void CalculateRiskScore()
|
|
{
|
|
int score = 0;
|
|
|
|
// New customers have moderate risk
|
|
if (TotalOrders == 0) score += 30;
|
|
|
|
// High cancellation rate increases risk
|
|
if (TotalOrders > 0)
|
|
{
|
|
var cancellationRate = (double)CancelledOrders / TotalOrders;
|
|
if (cancellationRate > 0.5) score += 40;
|
|
else if (cancellationRate > 0.3) score += 20;
|
|
}
|
|
|
|
// Disputes increase risk significantly
|
|
score += DisputedOrders * 25;
|
|
|
|
// Long-term customers with good history reduce risk
|
|
if (TotalOrders > 10 && SuccessfulOrders > 8) score -= 20;
|
|
if (TotalSpent > 500) score -= 10;
|
|
|
|
// Clamp between 0-100
|
|
RiskScore = Math.Max(0, Math.Min(100, score));
|
|
}
|
|
} |