import Decimal from 'decimal.js'; import { UserId, Money, Hashpower } from '@/domain/value-objects'; export enum PendingRewardStatus { PENDING = 'PENDING', SETTLED = 'SETTLED', EXPIRED = 'EXPIRED', } export class PendingReward { private readonly _id: bigint; private readonly _accountSequence: string; private readonly _userId: UserId; private readonly _usdtAmount: Money; private readonly _hashpowerAmount: Hashpower; private readonly _sourceOrderId: string; private readonly _allocationType: string; private readonly _expireAt: Date; private _status: PendingRewardStatus; private _settledAt: Date | null; private _expiredAt: Date | null; private readonly _createdAt: Date; private constructor( id: bigint, accountSequence: string, userId: UserId, usdtAmount: Money, hashpowerAmount: Hashpower, sourceOrderId: string, allocationType: string, expireAt: Date, status: PendingRewardStatus, settledAt: Date | null, expiredAt: Date | null, createdAt: Date, ) { this._id = id; this._accountSequence = accountSequence; this._userId = userId; this._usdtAmount = usdtAmount; this._hashpowerAmount = hashpowerAmount; this._sourceOrderId = sourceOrderId; this._allocationType = allocationType; this._expireAt = expireAt; this._status = status; this._settledAt = settledAt; this._expiredAt = expiredAt; this._createdAt = createdAt; } // Getters get id(): bigint { return this._id; } get accountSequence(): string { return this._accountSequence; } get userId(): UserId { return this._userId; } get usdtAmount(): Money { return this._usdtAmount; } get hashpowerAmount(): Hashpower { return this._hashpowerAmount; } get sourceOrderId(): string { return this._sourceOrderId; } get allocationType(): string { return this._allocationType; } get expireAt(): Date { return this._expireAt; } get status(): PendingRewardStatus { return this._status; } get settledAt(): Date | null { return this._settledAt; } get expiredAt(): Date | null { return this._expiredAt; } get createdAt(): Date { return this._createdAt; } get isPending(): boolean { return this._status === PendingRewardStatus.PENDING; } get isSettled(): boolean { return this._status === PendingRewardStatus.SETTLED; } get isExpired(): boolean { return this._status === PendingRewardStatus.EXPIRED; } /** * 创建新的待领取奖励 */ static create(params: { accountSequence: string; userId: UserId; usdtAmount: Money; hashpowerAmount: Hashpower; sourceOrderId: string; allocationType: string; expireAt: Date; }): PendingReward { return new PendingReward( BigInt(0), // Will be set by database params.accountSequence, params.userId, params.usdtAmount, params.hashpowerAmount, params.sourceOrderId, params.allocationType, params.expireAt, PendingRewardStatus.PENDING, null, null, new Date(), ); } /** * 从数据库重建 */ static reconstruct(params: { id: bigint; accountSequence: string; userId: bigint; usdtAmount: Decimal; hashpowerAmount: Decimal; sourceOrderId: string; allocationType: string; expireAt: Date; status: string; settledAt: Date | null; expiredAt: Date | null; createdAt: Date; }): PendingReward { return new PendingReward( params.id, params.accountSequence, UserId.create(params.userId), Money.USDT(params.usdtAmount), Hashpower.create(params.hashpowerAmount), params.sourceOrderId, params.allocationType, params.expireAt, params.status as PendingRewardStatus, params.settledAt, params.expiredAt, params.createdAt, ); } /** * 标记为已结算 (用户认种后,待领取转为可结算) */ markAsSettled(): void { if (this._status !== PendingRewardStatus.PENDING) { throw new Error(`Cannot settle reward in status: ${this._status}`); } this._status = PendingRewardStatus.SETTLED; this._settledAt = new Date(); } /** * 标记为已过期 (24小时未认种) */ markAsExpired(): void { if (this._status !== PendingRewardStatus.PENDING) { throw new Error(`Cannot expire reward in status: ${this._status}`); } this._status = PendingRewardStatus.EXPIRED; this._expiredAt = new Date(); } /** * 检查是否已过期 */ isOverdue(now: Date = new Date()): boolean { return this._status === PendingRewardStatus.PENDING && now > this._expireAt; } }