160 lines
4.5 KiB
TypeScript
160 lines
4.5 KiB
TypeScript
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;
|
|
}
|
|
}
|