From 4d3290f02946a1bce97321ff37d249c007a9329d Mon Sep 17 00:00:00 2001 From: hailin Date: Sat, 13 Dec 2025 02:01:48 -0800 Subject: [PATCH] =?UTF-8?q?fix(wallet-service):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E8=B4=A6=E6=88=B7=E8=B5=84=E9=87=91=E5=88=86?= =?UTF-8?q?=E9=85=8D=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: - 认种订单支付后,系统账户(成本费、运营费、总部社区、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 --- .../services/wallet-application.service.ts | 82 +++++++++++++------ .../aggregates/wallet-account.aggregate.ts | 10 +++ .../value-objects/ledger-entry-type.enum.ts | 37 +++++---- .../src/domain/value-objects/user-id.vo.ts | 60 +++++++------- 4 files changed, 117 insertions(+), 72 deletions(-) diff --git a/backend/services/wallet-service/src/application/services/wallet-application.service.ts b/backend/services/wallet-service/src/application/services/wallet-application.service.ts index 7c66644f..5fb8f881 100644 --- a/backend/services/wallet-service/src/application/services/wallet-application.service.ts +++ b/backend/services/wallet-service/src/application/services/wallet-application.service.ts @@ -685,31 +685,63 @@ export class WalletApplicationService { ); } - /** - * 分配资金到系统账户 - * 通过记录流水,实际资金转移由 authorization-service 处理 - */ - private async allocateToSystemAccount( - allocation: FundAllocationItem, - orderId: string, - ): Promise { - // 记录系统账户分配流水(用于审计和对账) - // 系统账户不通过 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 { + 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 =============== /** diff --git a/backend/services/wallet-service/src/domain/aggregates/wallet-account.aggregate.ts b/backend/services/wallet-service/src/domain/aggregates/wallet-account.aggregate.ts index 69d47170..cc43222d 100644 --- a/backend/services/wallet-service/src/domain/aggregates/wallet-account.aggregate.ts +++ b/backend/services/wallet-service/src/domain/aggregates/wallet-account.aggregate.ts @@ -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(); diff --git a/backend/services/wallet-service/src/domain/value-objects/ledger-entry-type.enum.ts b/backend/services/wallet-service/src/domain/value-objects/ledger-entry-type.enum.ts index 77aa0484..70443210 100644 --- a/backend/services/wallet-service/src/domain/value-objects/ledger-entry-type.enum.ts +++ b/backend/services/wallet-service/src/domain/value-objects/ledger-entry-type.enum.ts @@ -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', // 系统账户分配 +} diff --git a/backend/services/wallet-service/src/domain/value-objects/user-id.vo.ts b/backend/services/wallet-service/src/domain/value-objects/user-id.vo.ts index 1d9477e4..943287f2 100644 --- a/backend/services/wallet-service/src/domain/value-objects/user-id.vo.ts +++ b/backend/services/wallet-service/src/domain/value-objects/user-id.vo.ts @@ -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(); + } +}