| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 |
- using System.ComponentModel.DataAnnotations;
- using System.ComponentModel.DataAnnotations.Schema;
- namespace Domain.Entities.Members;
- /// <summary>
- /// 회원 예측 트랙레코드 집계 (D2 M4 예측 채점+트랙레코드, d2 §③).
- /// PredictionSettlementService(M4)가 채점 후 회원별로 재집계한다 — 매 실행마다 해당 회원의
- /// 전체 non-Pending 예측으로 처음부터 다시 계산하므로 멱등하다.
- /// MemberID = PK(FK Member). Predictions=채점 완료 총계(Hits+Misses+Voids), HitRate=Hits/(Hits+Misses)*100(Void 제외).
- /// 리더보드는 채점 표본(Hits+Misses) 10+ 회원만 HitRate 내림차순 필터 인덱스로 조회한다.
- /// </summary>
- public class MemberTrackRecord
- {
- [ForeignKey(nameof(MemberID))]
- public virtual Member Member { get; private set; } = null!;
- /// <summary>회원 ID (PK, FK Member)</summary>
- [Key]
- public int MemberID { get; private set; }
- /// <summary>채점 완료 예측 총계 (Hits + Misses + Voids)</summary>
- public int Predictions { get; private set; }
- /// <summary>적중 수</summary>
- public int Hits { get; private set; }
- /// <summary>실패 수</summary>
- public int Misses { get; private set; }
- /// <summary>무효 수 (거래정지·상폐·시세결측 — 적중률 표본 제외)</summary>
- public int Voids { get; private set; }
- /// <summary>적중률 % = Hits/(Hits+Misses)*100 (Void 제외). 표본 0 이면 0.</summary>
- public decimal HitRate { get; private set; }
- /// <summary>현재 연속 적중 수 (가장 최근 채점부터의 Hit 연속, Void 무시)</summary>
- public int CurrentStreak { get; private set; }
- /// <summary>역대 최고 연속 적중 수</summary>
- public int BestStreak { get; private set; }
- /// <summary>배지 등급 (0=None/1=Bronze/2=Silver/3=Gold/4=GoldAnt)</summary>
- public TrackRecordTier Tier { get; private set; }
- public DateTime UpdatedAt { get; private set; } = DateTime.UtcNow;
- private MemberTrackRecord() { }
- /// <summary>빈 트랙레코드 생성 (최초 채점 전). 이후 <see cref="Recompute"/> 로 채운다.</summary>
- public static MemberTrackRecord Create(int memberID)
- {
- ArgumentOutOfRangeException.ThrowIfNegativeOrZero(memberID);
- return new MemberTrackRecord { MemberID = memberID };
- }
- /// <summary>
- /// 집계값 일괄 갱신 (M4 채점 배치가 회원 예측 전체를 다시 계산해 호출 — 멱등 재집계).
- /// hitRate 는 백분율(0~100), tier 는 (hits+misses, hitRate) 로 산정한 값.
- /// </summary>
- public void Recompute(int predictions, int hits, int misses, int voids, decimal hitRate, int currentStreak, int bestStreak, TrackRecordTier tier)
- {
- Predictions = predictions;
- Hits = hits;
- Misses = misses;
- Voids = voids;
- HitRate = hitRate;
- CurrentStreak = currentStreak;
- BestStreak = bestStreak;
- Tier = tier;
- UpdatedAt = DateTime.UtcNow;
- }
- }
|