rwadurian/backend/services/wallet-service/src/domain/aggregates/pending-reward.aggregate.ts

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;
}
}