| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- using System.ComponentModel.DataAnnotations;
- using System.ComponentModel.DataAnnotations.Schema;
- using Domain.Entities.Store.ValueObject;
- namespace Domain.Entities.Store;
- /// <summary>
- /// 쿠폰 메타. 실제 코드 풀은 CouponCode 1:N 으로 관리.
- /// ProductID 필수 (어떤 Digital 상품에 발급할 쿠폰인지), GameID 필수 (사용자 정책: 게임 필수 연결).
- /// </summary>
- public class Coupon
- {
- [ForeignKey(nameof(ProductID))]
- public virtual Product? Product { get; private set; }
- [ForeignKey(nameof(GameID))]
- public virtual Game? Game { get; private set; }
- [Key]
- public int ID { get; private set; }
- public int ProductID { get; private set; }
- public int GameID { get; private set; }
- public string Name { get; private set; } = default!;
- public DateTime? ExpiresAt { get; private set; }
- public CouponUsagePolicy UsagePolicy { get; private set; } = CouponUsagePolicy.SingleUse;
- /// <summary>누적 발급 가능 코드 수 (CouponCode 풀의 총 row 수). 통계용.</summary>
- public int GeneratedCount { get; private set; }
- /// <summary>실제 사용된 코드 수 (Status=Used). 통계용.</summary>
- public int UsedCount { get; private set; }
- /// <summary>잔여 코드 부족 알림 임계치 (Available 코드가 이 값 미만이면 Admin 알림).</summary>
- public int LowStockThreshold { get; private set; } = 10;
- public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
- public DateTime? UpdatedAt { get; private set; }
- private Coupon() { }
- public static Coupon Create(
- int productID,
- int gameID,
- string name,
- CouponUsagePolicy usagePolicy = CouponUsagePolicy.SingleUse,
- DateTime? expiresAt = null,
- int lowStockThreshold = 10
- ) {
- if (productID <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(productID), "Product is required.");
- }
- if (gameID <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(gameID), "Game is required.");
- }
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentException("Name is required.", nameof(name));
- }
- if (name.Length > 200)
- {
- throw new ArgumentOutOfRangeException(nameof(name));
- }
- if (lowStockThreshold < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(lowStockThreshold));
- }
- return new Coupon
- {
- ProductID = productID,
- GameID = gameID,
- Name = name,
- UsagePolicy = usagePolicy,
- ExpiresAt = expiresAt,
- LowStockThreshold = lowStockThreshold
- };
- }
- public void Update(string name, DateTime? expiresAt, int lowStockThreshold)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentException("Name is required.", nameof(name));
- }
- if (lowStockThreshold < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(lowStockThreshold));
- }
- Name = name;
- ExpiresAt = expiresAt;
- LowStockThreshold = lowStockThreshold;
- UpdatedAt = DateTime.UtcNow;
- }
- /// <summary>BulkUpload 후 발급 가능 코드 수 누적.</summary>
- public void IncreaseGeneratedCount(int delta)
- {
- if (delta <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(delta));
- }
- GeneratedCount += delta;
- UpdatedAt = DateTime.UtcNow;
- }
- /// <summary>코드 1개가 Used 상태로 전이될 때 호출 (실제 사용 보고용 — 결제 발급 시점이 아니라 사용자가 사용 누른 시점).</summary>
- public void IncreaseUsedCount(int delta = 1)
- {
- if (delta <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(delta));
- }
- UsedCount += delta;
- UpdatedAt = DateTime.UtcNow;
- }
- }
|