| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- using System.ComponentModel.DataAnnotations;
- using System.ComponentModel.DataAnnotations.Schema;
- using Domain.Entities.Members;
- namespace Domain.Entities.Forum.Posts;
- /// <summary>
- /// 분석글 응원(Cheer) — D3 M4 (Twitch Bits 모델).
- /// 후원자(From)가 글 작성자(To)에게 캐시/토큰을 팁으로 보내며 플랫폼이 수수료를 취한다.
- /// 순액(NetAmount = Amount − FeeAmount)만 작성자 지갑 Reward 파티션으로 적립되고(출금 불가),
- /// 수수료(FeeAmount)는 어디에도 적립되지 않고 소각(=유통에서 제거)된다.
- ///
- /// 규제 가드라인(§⑧): 응원은 사후·자율·무대가이며 글 열람 조건이 아니다(글은 항상 무료).
- /// 본인 글 응원 금지·일일 발신 캡·수신 적립분 출금 불가는 자전 수수료 소각 어뷰징을 차단한다.
- /// </summary>
- public class PostCheer
- {
- [ForeignKey(nameof(PostID))]
- public virtual Post Post { get; private set; } = null!;
- [ForeignKey(nameof(FromMemberID))]
- public virtual Member? FromMember { get; private set; }
- [ForeignKey(nameof(ToMemberID))]
- public virtual Member? ToMember { get; private set; }
- [Key]
- public int ID { get; private set; }
- /// <summary>응원 대상 게시글</summary>
- public int PostID { get; private set; }
- /// <summary>응원 발신자 (후원자)</summary>
- public int FromMemberID { get; private set; }
- /// <summary>응원 수신자 (글 작성자 스냅샷)</summary>
- public int ToMemberID { get; private set; }
- /// <summary>총 응원 금액 (발신자 차감액)</summary>
- public int Amount { get; private set; }
- /// <summary>플랫폼 수수료 (소각분, 어디에도 적립되지 않음)</summary>
- public int FeeAmount { get; private set; }
- /// <summary>작성자 수령 순액 (Amount − FeeAmount, Reward 파티션 적립)</summary>
- public int NetAmount { get; private set; }
- /// <summary>응원 한마디 (선택)</summary>
- public string? Message { get; private set; }
- public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
- private PostCheer() { }
- private PostCheer(int postID, int fromMemberID, int toMemberID, int amount, int feeAmount, int netAmount, string? message)
- {
- PostID = postID;
- FromMemberID = fromMemberID;
- ToMemberID = toMemberID;
- Amount = amount;
- FeeAmount = feeAmount;
- NetAmount = netAmount;
- Message = message;
- }
- public static PostCheer Create(int postID, int fromMemberID, int toMemberID, int amount, int feeAmount, int netAmount, string? message)
- {
- if (postID <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(postID));
- }
- if (fromMemberID <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(fromMemberID));
- }
- if (toMemberID <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(toMemberID));
- }
- if (fromMemberID == toMemberID)
- {
- throw new ArgumentException("본인 글에는 응원할 수 없습니다.", nameof(toMemberID));
- }
- if (amount <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(amount));
- }
- if (feeAmount < 0 || netAmount <= 0 || feeAmount + netAmount != amount)
- {
- throw new ArgumentException("수수료/순액 계산이 올바르지 않습니다.", nameof(netAmount));
- }
- return new PostCheer(postID, fromMemberID, toMemberID, amount, feeAmount, netAmount, message);
- }
- }
|