using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Domain.Entities.Members;
namespace Domain.Entities.Store;
///
/// 회원 보관함. Digital 상품 결제 완료 시 1 row INSERT.
/// 사용자가 보관함에서 "사용" 누르면 UsedAt 마킹 + CouponCode.MarkUsed() 호출.
///
public class MemberInventory
{
[ForeignKey(nameof(MemberID))]
public virtual Member? Member { get; private set; }
[ForeignKey(nameof(OrderItemID))]
public virtual OrderItem? OrderItem { get; private set; }
[ForeignKey(nameof(CouponCodeID))]
public virtual CouponCode? CouponCode { get; private set; }
[Key]
public int ID { get; private set; }
public int MemberID { get; private set; }
public int OrderItemID { get; private set; }
public int CouponCodeID { get; private set; }
public DateTime AcquiredAt { get; private set; } = DateTime.UtcNow;
/// 사용자가 "사용" 클릭한 시점 (코드 노출 후). null = 미사용.
public DateTime? UsedAt { get; private set; }
/// 쿠폰 만료 일시 (Coupon.ExpiresAt 스냅샷). 만료 후 보관함에서 자동 제외.
public DateTime? ExpiresAt { get; private set; }
/// 사용자 보관함에서 소프트 삭제된 일시. null = 보관함 노출. 값 있음 = 사용자 화면에서 숨김 (DB 행은 보존, 환불·통계에 사용).
public DateTime? DeletedAt { get; private set; }
private MemberInventory() { }
public static MemberInventory Create(int memberID, int orderItemID, int couponCodeID, DateTime? expiresAt = null)
{
if (memberID <= 0)
{
throw new ArgumentOutOfRangeException(nameof(memberID));
}
if (orderItemID <= 0)
{
throw new ArgumentOutOfRangeException(nameof(orderItemID));
}
if (couponCodeID <= 0)
{
throw new ArgumentOutOfRangeException(nameof(couponCodeID));
}
return new MemberInventory
{
MemberID = memberID,
OrderItemID = orderItemID,
CouponCodeID = couponCodeID,
ExpiresAt = expiresAt
};
}
public void MarkUsed()
{
if (UsedAt.HasValue)
{
throw new InvalidOperationException("이미 사용한 쿠폰입니다.");
}
UsedAt = DateTime.UtcNow;
}
///
/// 사용자 보관함에서 소프트 삭제. DB row 는 유지되며 DeletedAt 만 마킹.
/// 환불 시점에는 DeletedAt 무시하고 정상 처리 (refund handler 가 모든 inventory 를 본다).
///
public void SoftDelete()
{
if (DeletedAt.HasValue)
{
throw new InvalidOperationException("이미 삭제된 보관함 항목입니다.");
}
DeletedAt = DateTime.UtcNow;
}
///
/// 관리자가 연결된 CouponCode 를 일괄 만료시킬 때 호출. ExpiresAt 을 즉시 now 로 설정해
/// 회원 보관함에서 자연 만료된 것처럼 표시되도록 한다 (기존 IsExpired UI 재사용).
///
public void ForceExpire()
{
ExpiresAt = DateTime.UtcNow;
}
///
/// 관리자가 만료 처리한 CouponCode 를 복귀시킬 때 호출. ExpiresAt 을 null 로 해제 (영구 사용 가능 상태).
///
public void ClearExpiry()
{
ExpiresAt = null;
}
}