using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Domain.Entities.Store; /// /// 게임별 인앱 상품 SKU 정가 카탈로그. 파트너 결제 보고(ApiPurchase) 금액 검증용 — /// 보고가가 정가와 다르면 ApiPurchase.PriceMismatched flag (등록은 통과, Admin 검토). /// 카탈로그 미등록 SKU 는 검증 없이 통과. /// public class GameProductCatalog { [ForeignKey(nameof(GameID))] public virtual Game? Game { get; private set; } [Key] public int ID { get; private set; } public int GameID { get; private set; } /// 인앱 상품 SKU (예: "diamond_100"). (GameID, ProductID) UNIQUE. public string ProductID { get; private set; } = default!; /// 정가 (KRW) public int Price { get; private set; } public bool IsActive { get; private set; } = true; public DateTime CreatedAt { get; private set; } = DateTime.UtcNow; public DateTime? UpdatedAt { get; private set; } private GameProductCatalog() { } public static GameProductCatalog Create(int gameID, string productID, int price) { if (gameID <= 0) { throw new ArgumentOutOfRangeException(nameof(gameID)); } ValidateProductID(productID); ValidatePrice(price); return new GameProductCatalog { GameID = gameID, ProductID = productID.Trim(), Price = price }; } public void Update(int price, bool isActive) { ValidatePrice(price); Price = price; IsActive = isActive; UpdatedAt = DateTime.UtcNow; } private static void ValidateProductID(string productID) { if (string.IsNullOrWhiteSpace(productID)) { throw new ArgumentException("ProductID is required.", nameof(productID)); } if (productID.Length > 100) { throw new ArgumentOutOfRangeException(nameof(productID), "ProductID 는 100자 이하로 입력하세요."); } } private static void ValidatePrice(int price) { if (price <= 0) { throw new ArgumentOutOfRangeException(nameof(price), "Price must be positive."); } } }