rwadurian/backend/services/authorization-service/src/domain/aggregates/system-account.aggregate.ts

367 lines
9.5 KiB
TypeScript

import { AggregateRoot } from './aggregate-root.base'
import { SystemAccountType, SystemLedgerEntryType, SystemAccountStatus } from '@/domain/enums'
import { DomainError } from '@/shared/exceptions'
import {
SystemAccountCreatedEvent,
SystemAccountWalletGeneratedEvent,
SystemAccountFundsReceivedEvent,
SystemAccountFundsTransferredEvent,
} from '@/domain/events'
import Decimal from 'decimal.js'
export interface SystemAccountProps {
id: bigint
accountType: SystemAccountType
regionCode: string | null
regionName: string | null
walletAddress: string | null
mpcPublicKey: string | null
usdtBalance: Decimal
hashpower: Decimal
totalReceived: Decimal
totalTransferred: Decimal
status: SystemAccountStatus
createdAt: Date
updatedAt: Date
}
export interface SystemAccountLedgerEntryProps {
id: bigint
accountId: bigint
entryType: SystemLedgerEntryType
amount: Decimal
balanceAfter: Decimal
sourceOrderId: bigint | null
sourceRewardId: bigint | null
txHash: string | null
memo: string | null
createdAt: Date
}
export class SystemAccount extends AggregateRoot {
private _id: bigint
private _accountType: SystemAccountType
private _regionCode: string | null
private _regionName: string | null
private _walletAddress: string | null
private _mpcPublicKey: string | null
private _usdtBalance: Decimal
private _hashpower: Decimal
private _totalReceived: Decimal
private _totalTransferred: Decimal
private _status: SystemAccountStatus
private _createdAt: Date
private _updatedAt: Date
// Getters
get id(): bigint {
return this._id
}
get accountType(): SystemAccountType {
return this._accountType
}
get regionCode(): string | null {
return this._regionCode
}
get regionName(): string | null {
return this._regionName
}
get walletAddress(): string | null {
return this._walletAddress
}
get mpcPublicKey(): string | null {
return this._mpcPublicKey
}
get usdtBalance(): Decimal {
return this._usdtBalance
}
get hashpower(): Decimal {
return this._hashpower
}
get totalReceived(): Decimal {
return this._totalReceived
}
get totalTransferred(): Decimal {
return this._totalTransferred
}
get status(): SystemAccountStatus {
return this._status
}
get createdAt(): Date {
return this._createdAt
}
get updatedAt(): Date {
return this._updatedAt
}
get isActive(): boolean {
return this._status === SystemAccountStatus.ACTIVE
}
get hasWallet(): boolean {
return this._walletAddress !== null
}
// 私有构造函数
private constructor(props: SystemAccountProps) {
super()
this._id = props.id
this._accountType = props.accountType
this._regionCode = props.regionCode
this._regionName = props.regionName
this._walletAddress = props.walletAddress
this._mpcPublicKey = props.mpcPublicKey
this._usdtBalance = props.usdtBalance
this._hashpower = props.hashpower
this._totalReceived = props.totalReceived
this._totalTransferred = props.totalTransferred
this._status = props.status
this._createdAt = props.createdAt
this._updatedAt = props.updatedAt
}
// 工厂方法 - 从数据库重建
static fromPersistence(props: SystemAccountProps): SystemAccount {
return new SystemAccount(props)
}
// 工厂方法 - 创建固定系统账户(成本、运营、总部社区、矿池)
static createFixedAccount(accountType: SystemAccountType): SystemAccount {
if (
accountType === SystemAccountType.SYSTEM_PROVINCE ||
accountType === SystemAccountType.SYSTEM_CITY
) {
throw new DomainError('区域系统账户需要指定区域信息')
}
const account = new SystemAccount({
id: BigInt(0), // 由数据库生成
accountType,
regionCode: null,
regionName: null,
walletAddress: null,
mpcPublicKey: null,
usdtBalance: new Decimal(0),
hashpower: new Decimal(0),
totalReceived: new Decimal(0),
totalTransferred: new Decimal(0),
status: SystemAccountStatus.ACTIVE,
createdAt: new Date(),
updatedAt: new Date(),
})
account.addDomainEvent(
new SystemAccountCreatedEvent({
accountType,
regionCode: null,
}),
)
return account
}
// 工厂方法 - 创建区域系统账户(省/市)
static createRegionAccount(params: {
accountType: SystemAccountType.SYSTEM_PROVINCE | SystemAccountType.SYSTEM_CITY
regionCode: string
regionName: string
}): SystemAccount {
const account = new SystemAccount({
id: BigInt(0), // 由数据库生成
accountType: params.accountType,
regionCode: params.regionCode,
regionName: params.regionName,
walletAddress: null,
mpcPublicKey: null,
usdtBalance: new Decimal(0),
hashpower: new Decimal(0),
totalReceived: new Decimal(0),
totalTransferred: new Decimal(0),
status: SystemAccountStatus.ACTIVE,
createdAt: new Date(),
updatedAt: new Date(),
})
account.addDomainEvent(
new SystemAccountCreatedEvent({
accountType: params.accountType,
regionCode: params.regionCode,
}),
)
return account
}
// 核心领域行为
/**
* 设置 MPC 生成的钱包地址
*/
setWalletAddress(walletAddress: string, mpcPublicKey: string): void {
if (this._walletAddress) {
throw new DomainError('钱包地址已设置,不能重复设置')
}
this._walletAddress = walletAddress
this._mpcPublicKey = mpcPublicKey
this._updatedAt = new Date()
this.addDomainEvent(
new SystemAccountWalletGeneratedEvent({
accountId: this._id.toString(),
accountType: this._accountType,
walletAddress,
mpcPublicKey,
}),
)
}
/**
* 接收资金(从认种分配或过期奖励)
*/
receiveFunds(params: {
amount: Decimal
entryType: SystemLedgerEntryType
sourceOrderId?: bigint
sourceRewardId?: bigint
txHash?: string
memo?: string
}): SystemAccountLedgerEntryProps {
if (!this.isActive) {
throw new DomainError('系统账户已停用')
}
if (params.amount.lte(0)) {
throw new DomainError('金额必须大于0')
}
const newBalance = this._usdtBalance.plus(params.amount)
this._usdtBalance = newBalance
this._totalReceived = this._totalReceived.plus(params.amount)
this._updatedAt = new Date()
const ledgerEntry: SystemAccountLedgerEntryProps = {
id: BigInt(0), // 由数据库生成
accountId: this._id,
entryType: params.entryType,
amount: params.amount,
balanceAfter: newBalance,
sourceOrderId: params.sourceOrderId || null,
sourceRewardId: params.sourceRewardId || null,
txHash: params.txHash || null,
memo: params.memo || null,
createdAt: new Date(),
}
this.addDomainEvent(
new SystemAccountFundsReceivedEvent({
accountId: this._id.toString(),
accountType: this._accountType,
amount: params.amount.toString(),
entryType: params.entryType,
balanceAfter: newBalance.toString(),
}),
)
return ledgerEntry
}
/**
* 转出资金
*/
transferFunds(params: {
amount: Decimal
txHash?: string
memo?: string
}): SystemAccountLedgerEntryProps {
if (!this.isActive) {
throw new DomainError('系统账户已停用')
}
if (params.amount.lte(0)) {
throw new DomainError('金额必须大于0')
}
if (this._usdtBalance.lt(params.amount)) {
throw new DomainError('余额不足')
}
const newBalance = this._usdtBalance.minus(params.amount)
this._usdtBalance = newBalance
this._totalTransferred = this._totalTransferred.plus(params.amount)
this._updatedAt = new Date()
const ledgerEntry: SystemAccountLedgerEntryProps = {
id: BigInt(0),
accountId: this._id,
entryType: SystemLedgerEntryType.TRANSFER_OUT,
amount: params.amount.neg(),
balanceAfter: newBalance,
sourceOrderId: null,
sourceRewardId: null,
txHash: params.txHash || null,
memo: params.memo || null,
createdAt: new Date(),
}
this.addDomainEvent(
new SystemAccountFundsTransferredEvent({
accountId: this._id.toString(),
accountType: this._accountType,
amount: params.amount.toString(),
txHash: params.txHash || null,
balanceAfter: newBalance.toString(),
}),
)
return ledgerEntry
}
/**
* 增加算力
*/
addHashpower(amount: Decimal): void {
if (amount.lte(0)) {
throw new DomainError('算力必须大于0')
}
this._hashpower = this._hashpower.plus(amount)
this._updatedAt = new Date()
}
/**
* 停用账户
*/
deactivate(): void {
this._status = SystemAccountStatus.INACTIVE
this._updatedAt = new Date()
}
/**
* 激活账户
*/
activate(): void {
this._status = SystemAccountStatus.ACTIVE
this._updatedAt = new Date()
}
/**
* 转换为持久化数据
*/
toPersistence(): Record<string, any> {
return {
id: this._id,
accountType: this._accountType,
regionCode: this._regionCode,
regionName: this._regionName,
walletAddress: this._walletAddress,
mpcPublicKey: this._mpcPublicKey,
usdtBalance: this._usdtBalance,
hashpower: this._hashpower,
totalReceived: this._totalReceived,
totalTransferred: this._totalTransferred,
status: this._status,
createdAt: this._createdAt,
updatedAt: this._updatedAt,
}
}
}