fix(wallet-service): 修复系统账户资金分配功能
问题: - 认种订单支付后,系统账户(成本费、运营费、总部社区、RWA底池)余额始终为0 - reward-service 正确计算分配,但 wallet-service 未实际执行系统账户的资金转移 根本原因: 1. allocateToSystemAccount() 方法只打印日志,未执行任何数据库操作(遗留的 TODO) 2. UserId 值对象不允许负数,而系统账户 user_id 为负数(-1 到 -4) 修复内容: 1. wallet-application.service.ts - allocateToSystemAccount() - 实现完整的系统账户资金分配逻辑 - 通过 findByAccountSequence() 获取系统账户 - 调用 addAvailableBalance() 直接增加可用余额 - 创建 SYSTEM_ALLOCATION 类型的流水记录 2. wallet-account.aggregate.ts - 新增 addAvailableBalance(amount: Money) 方法 - 用于系统账户直接增加余额(无需待领取/过期机制) 3. ledger-entry-type.enum.ts - 新增 SYSTEM_ALLOCATION 枚举值,用于系统账户分配流水 4. user-id.vo.ts - 移除负数校验,允许系统账户使用负数 user_id - 系统账户约定:-1(总部社区)、-2(成本费)、-3(运营费)、-4(RWA底池) 验证结果(认种1棵树=2199 USDT): - S0000000001 总部社区: 9 USDT ✓ - S0000000002 成本费账户: 400 USDT ✓ - S0000000003 运营费账户: 300 USDT ✓ - S0000000004 RWA底池: 800 USDT ✓ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
98d8bee20d
commit
4d3290f029
|
|
@ -685,31 +685,63 @@ export class WalletApplicationService {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配资金到系统账户
|
||||
* 通过记录流水,实际资金转移由 authorization-service 处理
|
||||
*/
|
||||
private async allocateToSystemAccount(
|
||||
allocation: FundAllocationItem,
|
||||
orderId: string,
|
||||
): Promise<void> {
|
||||
// 记录系统账户分配流水(用于审计和对账)
|
||||
// 系统账户不通过 wallet-service 管理余额,而是发送事件通知 authorization-service
|
||||
this.logger.debug(
|
||||
`System account allocation: ${allocation.amount} USDT to ${allocation.targetId} for ${allocation.allocationType}`,
|
||||
);
|
||||
|
||||
// TODO: 发布 Kafka 事件通知 authorization-service 更新系统账户余额
|
||||
// await this.eventPublisher.publish('system-account.funds-allocated', {
|
||||
// targetAccountType: allocation.targetId,
|
||||
// amount: allocation.amount,
|
||||
// allocationType: allocation.allocationType,
|
||||
// sourceOrderId: orderId,
|
||||
// hashpowerPercent: allocation.hashpowerPercent,
|
||||
// metadata: allocation.metadata,
|
||||
// });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分配资金到系统账户
|
||||
* 系统账户(S开头)已由 migration seed 创建,直接更新余额
|
||||
*
|
||||
* 系统账户说明:
|
||||
* - S0000000001: 总部社区 (user_id = -1)
|
||||
* - S0000000002: 成本费账户 (user_id = -2)
|
||||
* - S0000000003: 运营费账户 (user_id = -3)
|
||||
* - S0000000004: RWA底池 (user_id = -4)
|
||||
*/
|
||||
private async allocateToSystemAccount(
|
||||
allocation: FundAllocationItem,
|
||||
orderId: string,
|
||||
): Promise<void> {
|
||||
this.logger.debug(
|
||||
`System account allocation: ${allocation.amount} USDT to ${allocation.targetId} for ${allocation.allocationType}`,
|
||||
);
|
||||
|
||||
const targetId = allocation.targetId;
|
||||
if (!targetId.startsWith('S')) {
|
||||
this.logger.warn(`Invalid system account format: ${targetId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取系统账户(已由 migration seed 创建)
|
||||
const wallet = await this.walletRepo.findByAccountSequence(targetId);
|
||||
if (!wallet) {
|
||||
this.logger.error(`System account not found: ${targetId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const amount = Money.USDT(allocation.amount);
|
||||
|
||||
// 系统账户直接增加可用余额(不需要待领取/过期机制)
|
||||
wallet.addAvailableBalance(amount);
|
||||
await this.walletRepo.save(wallet);
|
||||
|
||||
// 记录流水
|
||||
const ledgerEntry = LedgerEntry.create({
|
||||
accountSequence: wallet.accountSequence,
|
||||
userId: wallet.userId,
|
||||
entryType: LedgerEntryType.SYSTEM_ALLOCATION,
|
||||
amount,
|
||||
refOrderId: orderId,
|
||||
memo: `${allocation.allocationType} - system account allocation`,
|
||||
payloadJson: {
|
||||
allocationType: allocation.allocationType,
|
||||
metadata: allocation.metadata,
|
||||
},
|
||||
});
|
||||
await this.ledgerRepo.save(ledgerEntry);
|
||||
|
||||
this.logger.debug(
|
||||
`Allocated ${allocation.amount} USDT to system account ${targetId} for ${allocation.allocationType}`,
|
||||
);
|
||||
}
|
||||
// =============== Region Accounts ===============
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -186,6 +186,16 @@ export class WalletAccount {
|
|||
}));
|
||||
}
|
||||
|
||||
// 直接增加可用余额(用于系统账户分配)
|
||||
addAvailableBalance(amount: Money): void {
|
||||
this.ensureActive();
|
||||
|
||||
const balance = this.getBalance(amount.currency as AssetType);
|
||||
const newBalance = balance.add(amount);
|
||||
this.setBalance(amount.currency as AssetType, newBalance);
|
||||
this._updatedAt = new Date();
|
||||
}
|
||||
|
||||
// 扣款 (如认种支付)
|
||||
deduct(amount: Money, reason: string, refOrderId?: string): void {
|
||||
this.ensureActive();
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
export enum LedgerEntryType {
|
||||
DEPOSIT_KAVA = 'DEPOSIT_KAVA',
|
||||
DEPOSIT_BSC = 'DEPOSIT_BSC',
|
||||
PLANT_PAYMENT = 'PLANT_PAYMENT',
|
||||
PLANT_FREEZE = 'PLANT_FREEZE', // 认种冻结
|
||||
PLANT_UNFREEZE = 'PLANT_UNFREEZE', // 认种解冻(失败回滚)
|
||||
REWARD_PENDING = 'REWARD_PENDING',
|
||||
REWARD_TO_SETTLEABLE = 'REWARD_TO_SETTLEABLE',
|
||||
REWARD_EXPIRED = 'REWARD_EXPIRED',
|
||||
REWARD_SETTLED = 'REWARD_SETTLED',
|
||||
TRANSFER_TO_POOL = 'TRANSFER_TO_POOL',
|
||||
SWAP_EXECUTED = 'SWAP_EXECUTED',
|
||||
WITHDRAWAL = 'WITHDRAWAL',
|
||||
TRANSFER_IN = 'TRANSFER_IN',
|
||||
TRANSFER_OUT = 'TRANSFER_OUT',
|
||||
FREEZE = 'FREEZE',
|
||||
UNFREEZE = 'UNFREEZE',
|
||||
}
|
||||
export enum LedgerEntryType {
|
||||
DEPOSIT_KAVA = 'DEPOSIT_KAVA',
|
||||
DEPOSIT_BSC = 'DEPOSIT_BSC',
|
||||
PLANT_PAYMENT = 'PLANT_PAYMENT',
|
||||
PLANT_FREEZE = 'PLANT_FREEZE', // 认种冻结
|
||||
PLANT_UNFREEZE = 'PLANT_UNFREEZE', // 认种解冻(失败回滚)
|
||||
REWARD_PENDING = 'REWARD_PENDING',
|
||||
REWARD_TO_SETTLEABLE = 'REWARD_TO_SETTLEABLE',
|
||||
REWARD_EXPIRED = 'REWARD_EXPIRED',
|
||||
REWARD_SETTLED = 'REWARD_SETTLED',
|
||||
TRANSFER_TO_POOL = 'TRANSFER_TO_POOL',
|
||||
SWAP_EXECUTED = 'SWAP_EXECUTED',
|
||||
WITHDRAWAL = 'WITHDRAWAL',
|
||||
TRANSFER_IN = 'TRANSFER_IN',
|
||||
TRANSFER_OUT = 'TRANSFER_OUT',
|
||||
FREEZE = 'FREEZE',
|
||||
UNFREEZE = 'UNFREEZE',
|
||||
SYSTEM_ALLOCATION = 'SYSTEM_ALLOCATION', // 系统账户分配
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,31 @@
|
|||
import { DomainError } from '@/shared/exceptions/domain.exception';
|
||||
|
||||
export class UserId {
|
||||
private readonly _value: bigint;
|
||||
|
||||
private constructor(value: bigint) {
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
static create(value: bigint | number | string): UserId {
|
||||
const bigintValue = typeof value === 'bigint' ? value : BigInt(value);
|
||||
if (bigintValue < 0) {
|
||||
throw new DomainError('UserId cannot be negative');
|
||||
}
|
||||
return new UserId(bigintValue);
|
||||
}
|
||||
|
||||
get value(): bigint {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
equals(other: UserId): boolean {
|
||||
return this._value === other._value;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this._value.toString();
|
||||
}
|
||||
}
|
||||
import { DomainError } from '@/shared/exceptions/domain.exception';
|
||||
|
||||
export class UserId {
|
||||
private readonly _value: bigint;
|
||||
|
||||
private constructor(value: bigint) {
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
static create(value: bigint | number | string): UserId {
|
||||
const bigintValue = typeof value === 'bigint' ? value : BigInt(value);
|
||||
// 允许负数 userId,用于系统账户:
|
||||
// -1: 总部社区 (S0000000001)
|
||||
// -2: 成本费账户 (S0000000002)
|
||||
// -3: 运营费账户 (S0000000003)
|
||||
// -4: RWA底池 (S0000000004)
|
||||
return new UserId(bigintValue);
|
||||
}
|
||||
|
||||
get value(): bigint {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
equals(other: UserId): boolean {
|
||||
return this._value === other._value;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this._value.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue