import { Injectable, Inject, Logger, BadRequestException } from '@nestjs/common';
import {
  IWalletAccountRepository, WALLET_ACCOUNT_REPOSITORY,
  ILedgerEntryRepository, LEDGER_ENTRY_REPOSITORY,
  IDepositOrderRepository, DEPOSIT_ORDER_REPOSITORY,
  ISettlementOrderRepository, SETTLEMENT_ORDER_REPOSITORY,
  IWithdrawalOrderRepository, WITHDRAWAL_ORDER_REPOSITORY,
  IPendingRewardRepository, PENDING_REWARD_REPOSITORY,
} from '@/domain/repositories';
import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service';
import { IUnitOfWork, UNIT_OF_WORK } from '@/infrastructure/persistence/unit-of-work';
import { LedgerEntry, DepositOrder, SettlementOrder, WithdrawalOrder, PendingReward, PendingRewardStatus, WalletAccount } from '@/domain/aggregates';
import {
  UserId, Money, Hashpower, LedgerEntryType, AssetType, ChainType, SettleCurrency,
} from '@/domain/value-objects';
import {
  HandleDepositCommand, DeductForPlantingCommand, AddRewardsCommand,
  ClaimRewardsCommand, SettleRewardsCommand, AllocateFundsCommand, FundAllocationItem,
  RequestWithdrawalCommand, UpdateWithdrawalStatusCommand,
  FreezeForPlantingCommand, ConfirmPlantingDeductionCommand, UnfreezeForPlantingCommand,
  FreezeForTransferCommand, UnfreezeForTransferCommand, SettleTransferPaymentCommand,
} from '@/application/commands';
import { GetMyWalletQuery, GetMyLedgerQuery } from '@/application/queries';
import { DuplicateTransactionError, WalletNotFoundError, OptimisticLockError } from '@/shared/exceptions/domain.exception';
import { WalletCacheService, HotWalletCacheService } from '@/infrastructure/redis';
import { EventPublisherService } from '@/infrastructure/kafka';
import { WithdrawalRequestedEvent } from '@/domain/events';
import { FeeConfigRepositoryImpl } from '@/infrastructure/persistence/repositories';
import { FeeType } from '@/api/dto/response';
import { IdentityClientService } from '@/infrastructure/external/identity/identity-client.service';

export interface WalletDTO {
  walletId: string;
  userId: string;
  balances: {
    usdt: { available: number; frozen: number };
    dst: { available: number; frozen: number };
    bnb: { available: number; frozen: number };
    og: { available: number; frozen: number };
    rwad: { available: number; frozen: number };
  };
  hashpower: number;
  rewards: {
    pendingUsdt: number;
    pendingHashpower: number;
    pendingExpireAt: string | null;
    settleableUsdt: number;
    settleableHashpower: number;
    settledTotalUsdt: number;
    settledTotalHashpower: number;
    expiredTotalUsdt: number;
    expiredTotalHashpower: number;
  };
  status: string;
}

export interface LedgerEntryDTO {
  id: string;
  entryType: string;
  amount: number;
  assetType: string;
  balanceAfter: number | null;
  refOrderId: string | null;
  refTxHash: string | null;
  memo: string | null;
  allocationType: string | null;
  /** 来源用户账户序列号（从 payloadJson.metadata 提取） */
  sourceAccountSequence: string | null;
  /** 来源中文备注（从 payloadJson.metadata.memo 提取） */
  sourceMemo: string | null;
  createdAt: string;
}

export interface PaginatedLedgerDTO {
  data: LedgerEntryDTO[];
  total: number;
  page: number;
  pageSize: number;
  totalPages: number;
}

/** 从 payloadJson 中提取 metadata 信息用于 LedgerEntryDTO */
function extractPayloadInfo(payloadJson: unknown): {
  allocationType: string | null;
  sourceAccountSequence: string | null;
  sourceMemo: string | null;
} {
  const payload = payloadJson as Record<string, unknown> | null;
  const metadata = payload?.metadata as Record<string, unknown> | null;
  return {
    allocationType: (payload?.allocationType as string) ?? null,
    sourceAccountSequence: (metadata?.sourceAccountSequence as string) ?? null,
    sourceMemo: (metadata?.memo as string) ?? null,
  };
}

@Injectable()
export class WalletApplicationService {
  private readonly logger = new Logger(WalletApplicationService.name);

  constructor(
    @Inject(WALLET_ACCOUNT_REPOSITORY)
    private readonly walletRepo: IWalletAccountRepository,
    @Inject(LEDGER_ENTRY_REPOSITORY)
    private readonly ledgerRepo: ILedgerEntryRepository,
    @Inject(DEPOSIT_ORDER_REPOSITORY)
    private readonly depositRepo: IDepositOrderRepository,
    @Inject(SETTLEMENT_ORDER_REPOSITORY)
    private readonly settlementRepo: ISettlementOrderRepository,
    @Inject(WITHDRAWAL_ORDER_REPOSITORY)
    private readonly withdrawalRepo: IWithdrawalOrderRepository,
    @Inject(PENDING_REWARD_REPOSITORY)
    private readonly pendingRewardRepo: IPendingRewardRepository,
    private readonly walletCacheService: WalletCacheService,
    private readonly hotWalletCacheService: HotWalletCacheService,
    private readonly eventPublisher: EventPublisherService,
    private readonly prisma: PrismaService,
    private readonly feeConfigRepo: FeeConfigRepositoryImpl,
    private readonly identityClient: IdentityClientService,
    @Inject(UNIT_OF_WORK)
    private readonly unitOfWork: IUnitOfWork,
  ) {}

  // =============== Commands ===============

  /**
   * Handle deposit with transaction protection and optimistic locking
   *
   * Uses database transaction to ensure atomicity of:
   * 1. Deposit order creation
   * 2. Wallet balance update (with optimistic lock)
   * 3. Ledger entry creation
   */
  async handleDeposit(command: HandleDepositCommand): Promise<void> {
    const MAX_RETRIES = 3;
    let retries = 0;

    while (retries < MAX_RETRIES) {
      try {
        await this.executeHandleDeposit(command);
        return; // Success, exit
      } catch (error) {
        if (this.isOptimisticLockError(error)) {
          retries++;
          this.logger.warn(`[DEPOSIT] Optimistic lock conflict for ${command.txHash}, retry ${retries}/${MAX_RETRIES}`);
          if (retries >= MAX_RETRIES) {
            this.logger.error(`[DEPOSIT] Max retries exceeded for ${command.txHash}`);
            throw error;
          }
          // Brief delay before retry
          await this.sleep(50 * retries);
        } else {
          throw error;
        }
      }
    }
  }

  /**
   * Execute deposit logic within a transaction
   */
  private async executeHandleDeposit(command: HandleDepositCommand): Promise<void> {
    const accountSequence = command.accountSequence;
    const userIdBigint = BigInt(command.userId);
    const amountDecimal = new (await import('decimal.js')).default(command.amount);

    await this.prisma.$transaction(async (tx) => {
      // Check for duplicate transaction within transaction
      const existingDeposit = await tx.depositOrder.findUnique({
        where: { txHash: command.txHash },
      });
      if (existingDeposit) {
        throw new DuplicateTransactionError(command.txHash);
      }

      // Get or create wallet
      let walletRecord = await tx.walletAccount.findUnique({
        where: { accountSequence },
      });

      if (!walletRecord) {
        walletRecord = await tx.walletAccount.create({
          data: {
            accountSequence,
            userId: userIdBigint,
            usdtAvailable: 0,
            usdtFrozen: 0,
            dstAvailable: 0,
            dstFrozen: 0,
            bnbAvailable: 0,
            bnbFrozen: 0,
            ogAvailable: 0,
            ogFrozen: 0,
            rwadAvailable: 0,
            rwadFrozen: 0,
            hashpower: 0,
            pendingUsdt: 0,
            pendingHashpower: 0,
            settleableUsdt: 0,
            settleableHashpower: 0,
            settledTotalUsdt: 0,
            settledTotalHashpower: 0,
            expiredTotalUsdt: 0,
            expiredTotalHashpower: 0,
            status: 'ACTIVE',
            version: 0,
          },
        });
      }

      // Create deposit order
      await tx.depositOrder.create({
        data: {
          accountSequence,
          userId: userIdBigint,
          chainType: command.chainType,
          amount: amountDecimal,
          txHash: command.txHash,
          status: 'CONFIRMED',
          confirmedAt: new Date(),
        },
      });

      // Update wallet balance with optimistic lock
      const Decimal = (await import('decimal.js')).default;
      const currentAvailable = new Decimal(walletRecord.usdtAvailable.toString());
      const newAvailable = currentAvailable.add(amountDecimal);
      const currentVersion = walletRecord.version;

      const updateResult = await tx.walletAccount.updateMany({
        where: {
          id: walletRecord.id,
          version: currentVersion, // Optimistic lock condition
        },
        data: {
          usdtAvailable: newAvailable,
          version: currentVersion + 1, // Increment version
          updatedAt: new Date(),
        },
      });

      if (updateResult.count === 0) {
        // Version mismatch - another transaction modified the record
        throw new OptimisticLockError(`Optimistic lock conflict for wallet ${walletRecord.id}`);
      }

      // Record ledger entry
      const entryType = command.chainType === ChainType.KAVA
        ? LedgerEntryType.DEPOSIT_KAVA
        : LedgerEntryType.DEPOSIT_BSC;

      await tx.ledgerEntry.create({
        data: {
          accountSequence,
          userId: userIdBigint,
          entryType,
          amount: amountDecimal,
          assetType: 'USDT',
          balanceAfter: newAvailable,
          refTxHash: command.txHash,
          memo: `Deposit from ${command.chainType}`,
        },
      });

      this.logger.log(`[DEPOSIT] Credited ${amountDecimal.toString()} USDT to ${accountSequence} (version: ${currentVersion} -> ${currentVersion + 1})`);
    });

    // Invalidate wallet cache after deposit (outside transaction)
    await this.walletCacheService.invalidateWallet(userIdBigint);
  }

  /**
   * Check if error is an optimistic lock error
   */
  private isOptimisticLockError(error: unknown): boolean {
    return error instanceof OptimisticLockError ||
           (error instanceof Error && error.message.includes('Optimistic lock conflict'));
  }

  /**
   * Sleep for specified milliseconds
   */
  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  async deductForPlanting(command: DeductForPlantingCommand): Promise<boolean> {
    const userId = BigInt(command.userId);
    const amount = Money.USDT(command.amount);

    // 幂等性检查：通过 orderId 检查是否已经扣款
    const existingEntries = await this.ledgerRepo.findByRefOrderId(command.orderId);
    const alreadyDeducted = existingEntries.some(
      (entry) => entry.entryType === LedgerEntryType.PLANT_PAYMENT,
    );
    if (alreadyDeducted) {
      this.logger.warn(
        `Order ${command.orderId} already deducted, returning success (idempotent)`,
      );
      return true;
    }

    // 优先按 accountSequence 查找，如果未找到则按 userId 查找
    let wallet = await this.walletRepo.findByAccountSequence(command.userId);
    if (!wallet) {
      wallet = await this.walletRepo.findByUserId(userId);
    }
    if (!wallet) {
      throw new WalletNotFoundError(`userId/accountSequence: ${command.userId}`);
    }

    // Deduct from wallet
    wallet.deduct(amount, 'Plant payment', command.orderId);
    await this.walletRepo.save(wallet);

    // Record ledger entry (使用 wallet.accountSequence 作为跨服务关联键)
    const ledgerEntry = LedgerEntry.create({
      accountSequence: wallet.accountSequence,
      userId: UserId.create(userId),
      entryType: LedgerEntryType.PLANT_PAYMENT,
      amount: Money.signed(-command.amount, 'USDT'), // Negative for deduction
      balanceAfter: Money.USDT(wallet.balances.usdt.available.value), // Use value to create new Money
      refOrderId: command.orderId,
      memo: 'Plant payment',
    });
    await this.ledgerRepo.save(ledgerEntry);

    // Invalidate wallet cache after deduction
    await this.walletCacheService.invalidateWallet(userId);
    return true;
  }

  /**
   * 冻结资金用于认种
   * 幂等设计：如果订单已冻结，直接返回成功
   *
   * 使用数据库事务 + 乐观锁确保：
   * 1. 余额检查和冻结操作的原子性
   * 2. 钱包更新和流水记录的一致性
   * 3. 并发安全（通过 version 字段）
   */
  async freezeForPlanting(command: FreezeForPlantingCommand): Promise<{
    success: boolean;
    frozenAmount: number;
  }> {
    const MAX_RETRIES = 3;
    let retries = 0;

    while (retries < MAX_RETRIES) {
      try {
        return await this.executeFreezeForPlanting(command);
      } catch (error) {
        if (this.isOptimisticLockError(error)) {
          retries++;
          this.logger.warn(`[freezeForPlanting] Optimistic lock conflict for ${command.orderId}, retry ${retries}/${MAX_RETRIES}`);
          if (retries >= MAX_RETRIES) {
            this.logger.error(`[freezeForPlanting] Max retries exceeded for ${command.orderId}`);
            throw error;
          }
          await this.sleep(50 * retries);
        } else {
          throw error;
        }
      }
    }
    throw new Error('Unexpected: exited retry loop without result');
  }

  /**
   * Execute freeze logic within a transaction
   */
  private async executeFreezeForPlanting(command: FreezeForPlantingCommand): Promise<{
    success: boolean;
    frozenAmount: number;
  }> {
    this.logger.log(`[freezeForPlanting] ========== 开始处理 ==========`);
    this.logger.log(`[freezeForPlanting] userId/accountSequence: ${command.userId}`);
    this.logger.log(`[freezeForPlanting] amount: ${command.amount}`);
    this.logger.log(`[freezeForPlanting] orderId: ${command.orderId}`);

    const Decimal = (await import('decimal.js')).default;
    const amountDecimal = new Decimal(command.amount);
    let walletUserId: bigint | null = null;

    await this.prisma.$transaction(async (tx) => {
      // 幂等性检查：通过 orderId 检查是否已经冻结
      const existingEntry = await tx.ledgerEntry.findFirst({
        where: {
          refOrderId: command.orderId,
          entryType: LedgerEntryType.PLANT_FREEZE,
        },
      });

      if (existingEntry) {
        this.logger.warn(`Order ${command.orderId} already frozen, returning success (idempotent)`);
        return; // 幂等返回
      }

      // 查找钱包
      const isAccountSequence = command.userId.startsWith('D');
      const walletRecord = isAccountSequence
        ? await tx.walletAccount.findUnique({ where: { accountSequence: command.userId } })
        : await tx.walletAccount.findFirst({ where: { userId: BigInt(command.userId) } });

      if (!walletRecord) {
        this.logger.error(`[freezeForPlanting] 钱包不存在: userId/accountSequence=${command.userId}`);
        throw new WalletNotFoundError(`userId/accountSequence: ${command.userId}`);
      }

      walletUserId = walletRecord.userId;
      const currentAvailable = new Decimal(walletRecord.usdtAvailable.toString());
      const currentFrozen = new Decimal(walletRecord.usdtFrozen.toString());
      const currentVersion = walletRecord.version;

      this.logger.log(`[freezeForPlanting] 钱包信息:`);
      this.logger.log(`[freezeForPlanting]   walletId: ${walletRecord.id}`);
      this.logger.log(`[freezeForPlanting]   accountSequence: ${walletRecord.accountSequence}`);
      this.logger.log(`[freezeForPlanting]   USDT可用余额: ${currentAvailable.toString()}`);
      this.logger.log(`[freezeForPlanting]   USDT冻结余额: ${currentFrozen.toString()}`);
      this.logger.log(`[freezeForPlanting]   version: ${currentVersion}`);

      // 检查余额是否足够
      if (currentAvailable.lessThan(amountDecimal)) {
        this.logger.error(`[freezeForPlanting] 余额不足!`);
        throw new BadRequestException(
          `余额不足: 需要 ${command.amount} USDT, 当前可用 ${currentAvailable.toString()} USDT`,
        );
      }

      // 计算新余额
      const newAvailable = currentAvailable.minus(amountDecimal);
      const newFrozen = currentFrozen.plus(amountDecimal);

      // 使用乐观锁更新钱包
      const updateResult = await tx.walletAccount.updateMany({
        where: {
          id: walletRecord.id,
          version: currentVersion,
        },
        data: {
          usdtAvailable: newAvailable,
          usdtFrozen: newFrozen,
          version: currentVersion + 1,
          updatedAt: new Date(),
        },
      });

      if (updateResult.count === 0) {
        throw new OptimisticLockError(`Optimistic lock conflict for wallet ${walletRecord.id}`);
      }

      // 记录冻结流水
      await tx.ledgerEntry.create({
        data: {
          accountSequence: walletRecord.accountSequence,
          userId: walletRecord.userId,
          entryType: LedgerEntryType.PLANT_FREEZE,
          amount: amountDecimal.negated(), // Negative: 可用余额减少
          assetType: 'USDT',
          balanceAfter: newAvailable,
          refOrderId: command.orderId,
          memo: 'Plant freeze',
        },
      });

      this.logger.log(`[freezeForPlanting] 成功冻结 ${command.amount} USDT (version: ${currentVersion} -> ${currentVersion + 1})`);
    });

    // 事务成功后，使缓存失效
    if (walletUserId) {
      await this.walletCacheService.invalidateWallet(walletUserId);
    }

    this.logger.log(`[freezeForPlanting] 订单 ${command.orderId} 冻结完成`);
    return { success: true, frozenAmount: command.amount };
  }

  /**
   * 确认认种扣款（从冻结金额中正式扣除）
   * 幂等设计：如果订单已确认扣款，直接返回成功
   *
   * 使用数据库事务 + 乐观锁确保：
   * 1. 钱包冻结余额扣除和流水记录的一致性
   * 2. 并发安全（通过 version 字段）
   */
  async confirmPlantingDeduction(command: ConfirmPlantingDeductionCommand): Promise<boolean> {
    const MAX_RETRIES = 3;
    let retries = 0;

    while (retries < MAX_RETRIES) {
      try {
        return await this.executeConfirmPlantingDeduction(command);
      } catch (error) {
        if (this.isOptimisticLockError(error)) {
          retries++;
          this.logger.warn(`[confirmPlantingDeduction] Optimistic lock conflict for ${command.orderId}, retry ${retries}/${MAX_RETRIES}`);
          if (retries >= MAX_RETRIES) {
            this.logger.error(`[confirmPlantingDeduction] Max retries exceeded for ${command.orderId}`);
            throw error;
          }
          await this.sleep(50 * retries);
        } else {
          throw error;
        }
      }
    }
    throw new Error('Unexpected: exited retry loop without result');
  }

  /**
   * Execute confirm deduction logic within a transaction
   */
  private async executeConfirmPlantingDeduction(command: ConfirmPlantingDeductionCommand): Promise<boolean> {
    const Decimal = (await import('decimal.js')).default;
    let walletUserId: bigint | null = null;

    await this.prisma.$transaction(async (tx) => {
      // 幂等性检查：是否已经扣款
      const existingPayment = await tx.ledgerEntry.findFirst({
        where: {
          refOrderId: command.orderId,
          entryType: LedgerEntryType.PLANT_PAYMENT,
        },
      });

      if (existingPayment) {
        this.logger.warn(`Order ${command.orderId} already confirmed deduction, returning success (idempotent)`);
        return; // 幂等返回
      }

      // 查找冻结记录
      const freezeEntry = await tx.ledgerEntry.findFirst({
        where: {
          refOrderId: command.orderId,
          entryType: LedgerEntryType.PLANT_FREEZE,
        },
      });

      if (!freezeEntry) {
        throw new BadRequestException(`订单 ${command.orderId} 未找到冻结记录`);
      }

      // 获取冻结金额（流水中是负数，取绝对值）
      const frozenAmount = new Decimal(freezeEntry.amount.toString()).abs();

      // 查找钱包
      const isAccountSequence = command.userId.startsWith('D');
      const walletRecord = isAccountSequence
        ? await tx.walletAccount.findUnique({ where: { accountSequence: command.userId } })
        : await tx.walletAccount.findFirst({ where: { userId: BigInt(command.userId) } });

      if (!walletRecord) {
        throw new WalletNotFoundError(`userId/accountSequence: ${command.userId}`);
      }

      walletUserId = walletRecord.userId;
      const currentFrozen = new Decimal(walletRecord.usdtFrozen.toString());
      const currentVersion = walletRecord.version;

      // 检查冻结余额是否足够
      if (currentFrozen.lessThan(frozenAmount)) {
        throw new BadRequestException(
          `冻结余额不足: 需要 ${frozenAmount.toString()} USDT, 当前冻结 ${currentFrozen.toString()} USDT`,
        );
      }

      // 计算新冻结余额（从冻结扣款，不影响可用）
      const newFrozen = currentFrozen.minus(frozenAmount);

      // 使用乐观锁更新钱包
      const updateResult = await tx.walletAccount.updateMany({
        where: {
          id: walletRecord.id,
          version: currentVersion,
        },
        data: {
          usdtFrozen: newFrozen,
          version: currentVersion + 1,
          updatedAt: new Date(),
        },
      });

      if (updateResult.count === 0) {
        throw new OptimisticLockError(`Optimistic lock conflict for wallet ${walletRecord.id}`);
      }

      // 记录扣款流水
      await tx.ledgerEntry.create({
        data: {
          accountSequence: walletRecord.accountSequence,
          userId: walletRecord.userId,
          entryType: LedgerEntryType.PLANT_PAYMENT,
          amount: frozenAmount.negated(), // Negative: 冻结余额减少
          assetType: 'USDT',
          balanceAfter: new Decimal(walletRecord.usdtAvailable.toString()), // 可用余额不变
          refOrderId: command.orderId,
          memo: 'Plant payment (from frozen)',
        },
      });

      this.logger.log(`[confirmPlantingDeduction] 成功扣款 ${frozenAmount.toString()} USDT (version: ${currentVersion} -> ${currentVersion + 1})`);
    });

    // 事务成功后，使缓存失效
    if (walletUserId) {
      await this.walletCacheService.invalidateWallet(walletUserId);
    }

    this.logger.log(`[confirmPlantingDeduction] 订单 ${command.orderId} 扣款确认完成`);
    return true;
  }

  /**
   * 解冻认种资金（认种失败时回滚）
   * 幂等设计：如果订单已解冻或未冻结，直接返回成功
   *
   * 使用数据库事务 + 乐观锁确保：
   * 1. 钱包解冻和流水记录的一致性
   * 2. 并发安全（通过 version 字段）
   */
  async unfreezeForPlanting(command: UnfreezeForPlantingCommand): Promise<boolean> {
    const MAX_RETRIES = 3;
    let retries = 0;

    while (retries < MAX_RETRIES) {
      try {
        return await this.executeUnfreezeForPlanting(command);
      } catch (error) {
        if (this.isOptimisticLockError(error)) {
          retries++;
          this.logger.warn(`[unfreezeForPlanting] Optimistic lock conflict for ${command.orderId}, retry ${retries}/${MAX_RETRIES}`);
          if (retries >= MAX_RETRIES) {
            this.logger.error(`[unfreezeForPlanting] Max retries exceeded for ${command.orderId}`);
            throw error;
          }
          await this.sleep(50 * retries);
        } else {
          throw error;
        }
      }
    }
    throw new Error('Unexpected: exited retry loop without result');
  }

  /**
   * Execute unfreeze logic within a transaction
   */
  private async executeUnfreezeForPlanting(command: UnfreezeForPlantingCommand): Promise<boolean> {
    const Decimal = (await import('decimal.js')).default;
    let walletUserId: bigint | null = null;

    await this.prisma.$transaction(async (tx) => {
      // 幂等性检查：是否已经解冻
      const existingUnfreeze = await tx.ledgerEntry.findFirst({
        where: {
          refOrderId: command.orderId,
          entryType: LedgerEntryType.PLANT_UNFREEZE,
        },
      });

      if (existingUnfreeze) {
        this.logger.warn(`Order ${command.orderId} already unfrozen, returning success (idempotent)`);
        return; // 幂等返回
      }

      // 检查是否已经扣款（扣款后不能解冻）
      const existingPayment = await tx.ledgerEntry.findFirst({
        where: {
          refOrderId: command.orderId,
          entryType: LedgerEntryType.PLANT_PAYMENT,
        },
      });

      if (existingPayment) {
        this.logger.warn(`Order ${command.orderId} already deducted, cannot unfreeze`);
        throw new BadRequestException(`订单 ${command.orderId} 已扣款，无法解冻`);
      }

      // 查找冻结记录
      const freezeEntry = await tx.ledgerEntry.findFirst({
        where: {
          refOrderId: command.orderId,
          entryType: LedgerEntryType.PLANT_FREEZE,
        },
      });

      if (!freezeEntry) {
        // 没有冻结记录，可能从未冻结，直接返回成功
        this.logger.warn(`Order ${command.orderId} has no freeze record, returning success`);
        return; // 幂等返回
      }

      // 获取冻结金额
      const frozenAmount = new Decimal(freezeEntry.amount.toString()).abs();

      // 查找钱包
      const isAccountSequence = command.userId.startsWith('D');
      const walletRecord = isAccountSequence
        ? await tx.walletAccount.findUnique({ where: { accountSequence: command.userId } })
        : await tx.walletAccount.findFirst({ where: { userId: BigInt(command.userId) } });

      if (!walletRecord) {
        throw new WalletNotFoundError(`userId/accountSequence: ${command.userId}`);
      }

      walletUserId = walletRecord.userId;
      const currentAvailable = new Decimal(walletRecord.usdtAvailable.toString());
      const currentFrozen = new Decimal(walletRecord.usdtFrozen.toString());
      const currentVersion = walletRecord.version;

      // 计算新余额（解冻：冻结减少，可用增加）
      const newAvailable = currentAvailable.plus(frozenAmount);
      const newFrozen = currentFrozen.minus(frozenAmount);

      // 使用乐观锁更新钱包
      const updateResult = await tx.walletAccount.updateMany({
        where: {
          id: walletRecord.id,
          version: currentVersion,
        },
        data: {
          usdtAvailable: newAvailable,
          usdtFrozen: newFrozen,
          version: currentVersion + 1,
          updatedAt: new Date(),
        },
      });

      if (updateResult.count === 0) {
        throw new OptimisticLockError(`Optimistic lock conflict for wallet ${walletRecord.id}`);
      }

      // 记录解冻流水
      await tx.ledgerEntry.create({
        data: {
          accountSequence: walletRecord.accountSequence,
          userId: walletRecord.userId,
          entryType: LedgerEntryType.PLANT_UNFREEZE,
          amount: frozenAmount, // Positive: 可用余额增加
          assetType: 'USDT',
          balanceAfter: newAvailable,
          refOrderId: command.orderId,
          memo: 'Plant unfreeze (rollback)',
        },
      });

      this.logger.log(`[unfreezeForPlanting] 成功解冻 ${frozenAmount.toString()} USDT (version: ${currentVersion} -> ${currentVersion + 1})`);
    });

    // 事务成功后，使缓存失效
    if (walletUserId) {
      await this.walletCacheService.invalidateWallet(walletUserId);
    }

    this.logger.log(`[unfreezeForPlanting] 订单 ${command.orderId} 解冻完成`);
    return true;
  }

  async addRewards(command: AddRewardsCommand): Promise<void> {
    const userId = BigInt(command.userId);

    // 优先按 accountSequence 查找，如果未找到则按 userId 查找
    let wallet = await this.walletRepo.findByAccountSequence(command.userId);
    if (!wallet) {
      wallet = await this.walletRepo.findByUserId(userId);
    }
    if (!wallet) {
      throw new WalletNotFoundError(`userId/accountSequence: ${command.userId}`);
    }

    const usdtAmount = Money.USDT(command.usdtAmount);
    const hashpowerAmount = Hashpower.create(command.hashpowerAmount);

    wallet.addPendingReward(usdtAmount, hashpowerAmount, command.expireAt, command.refOrderId);
    await this.walletRepo.save(wallet);

    // Record ledger entry for USDT reward (使用 wallet.accountSequence)
    if (command.usdtAmount > 0) {
      const usdtEntry = LedgerEntry.create({
        accountSequence: wallet.accountSequence,
        userId: UserId.create(userId),
        entryType: LedgerEntryType.REWARD_PENDING,
        amount: usdtAmount,
        refOrderId: command.refOrderId,
        memo: 'Pending reward added',
        payloadJson: { expireAt: command.expireAt.toISOString() },
      });
      await this.ledgerRepo.save(usdtEntry);
    }

    // Record ledger entry for hashpower reward
    if (command.hashpowerAmount > 0) {
      const hpEntry = LedgerEntry.create({
        accountSequence: wallet.accountSequence,
        userId: UserId.create(userId),
        entryType: LedgerEntryType.REWARD_PENDING,
        amount: Money.create(command.hashpowerAmount, 'HASHPOWER'),
        refOrderId: command.refOrderId,
        memo: 'Pending hashpower reward added',
        payloadJson: { expireAt: command.expireAt.toISOString() },
      });
      await this.ledgerRepo.save(hpEntry);
    }

    // Invalidate wallet cache after adding rewards
    await this.walletCacheService.invalidateWallet(userId);
  }

  async claimRewards(command: ClaimRewardsCommand): Promise<void> {
    const userId = BigInt(command.userId);

    // 优先按 accountSequence 查找，如果未找到则按 userId 查找
    let wallet = await this.walletRepo.findByAccountSequence(command.userId);
    if (!wallet) {
      wallet = await this.walletRepo.findByUserId(userId);
    }
    if (!wallet) {
      throw new WalletNotFoundError(`userId/accountSequence: ${command.userId}`);
    }

    const pendingUsdt = wallet.rewards.pendingUsdt.value;
    const pendingHashpower = wallet.rewards.pendingHashpower.value;

    wallet.movePendingToSettleable();
    await this.walletRepo.save(wallet);

    // Record ledger entries (使用 wallet.accountSequence)
    if (pendingUsdt > 0) {
      const usdtEntry = LedgerEntry.create({
        accountSequence: wallet.accountSequence,
        userId: UserId.create(userId),
        entryType: LedgerEntryType.REWARD_TO_SETTLEABLE,
        amount: Money.USDT(pendingUsdt),
        memo: 'Reward claimed to settleable',
      });
      await this.ledgerRepo.save(usdtEntry);
    }

    if (pendingHashpower > 0) {
      const hpEntry = LedgerEntry.create({
        accountSequence: wallet.accountSequence,
        userId: UserId.create(userId),
        entryType: LedgerEntryType.REWARD_TO_SETTLEABLE,
        amount: Money.create(pendingHashpower, 'HASHPOWER'),
        balanceAfter: Money.create(wallet.hashpower.value, 'HASHPOWER'),
        memo: 'Hashpower reward claimed',
      });
      await this.ledgerRepo.save(hpEntry);
    }

    // Invalidate wallet cache after claiming rewards
    await this.walletCacheService.invalidateWallet(userId);
  }

  async settleRewards(command: SettleRewardsCommand): Promise<string> {
    const userId = BigInt(command.userId);
    const usdtAmount = Money.USDT(command.usdtAmount);

    // 优先按 accountSequence 查找，如果未找到则按 userId 查找
    let wallet = await this.walletRepo.findByAccountSequence(command.userId);
    if (!wallet) {
      wallet = await this.walletRepo.findByUserId(userId);
    }
    if (!wallet) {
      throw new WalletNotFoundError(`userId/accountSequence: ${command.userId}`);
    }

    // Create settlement order
    const settlementOrder = SettlementOrder.create({
      userId: UserId.create(userId),
      usdtAmount,
      settleCurrency: command.settleCurrency,
    });
    const savedOrder = await this.settlementRepo.save(settlementOrder);

    // For now, simulate immediate settlement (in production, this would be async)
    // Assume 1:1 exchange rate for simplicity
    const receivedAmount = Money.create(command.usdtAmount, command.settleCurrency);
    const swapTxHash = `swap_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

    wallet.settleRewards(
      usdtAmount,
      command.settleCurrency,
      receivedAmount,
      savedOrder.id.toString(),
      swapTxHash,
    );
    await this.walletRepo.save(wallet);

    // Update settlement order
    settlementOrder.complete(swapTxHash, receivedAmount);
    await this.settlementRepo.save(settlementOrder);

    // Record ledger entry (使用 wallet.accountSequence)
    const ledgerEntry = LedgerEntry.create({
      accountSequence: wallet.accountSequence,
      userId: UserId.create(userId),
      entryType: LedgerEntryType.REWARD_SETTLED,
      amount: receivedAmount,
      balanceAfter: wallet.balances[command.settleCurrency.toLowerCase() as keyof typeof wallet.balances]?.available,
      refOrderId: savedOrder.id.toString(),
      refTxHash: swapTxHash,
      memo: `Settled ${usdtAmount.value} USDT to ${command.settleCurrency}`,
    });
    await this.ledgerRepo.save(ledgerEntry);

    // Invalidate wallet cache after settlement
    await this.walletCacheService.invalidateWallet(userId);

    return savedOrder.id.toString();
  }

  /**
   * 结算到余额 (可结算USDT → 钱包USDT余额，无币种兑换)
   * 将可结算收益直接转入钱包 USDT 余额
   *
   * @param params 结算参数
   * @returns 结算结果
   */
  async settleToBalance(params: {
    accountSequence: string;
    usdtAmount: number;
    rewardEntryIds: string[];
    breakdown?: Record<string, number>;
    memo?: string;
  }): Promise<{
    success: boolean;
    settlementId: string;
    settledAmount: number;
    balanceAfter: number;
    error?: string;
  }> {
    this.logger.log(`Settling ${params.usdtAmount} USDT to balance for ${params.accountSequence}`);

    try {
      // 1. 查找钱包
      const wallet = await this.walletRepo.findByAccountSequence(params.accountSequence);
      if (!wallet) {
        throw new WalletNotFoundError(`accountSequence: ${params.accountSequence}`);
      }

      const usdtAmount = Money.USDT(params.usdtAmount);
      const userId = wallet.userId.value;

      // 2. 生成结算ID
      const settlementId = `STL_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

      // 3. 在事务中执行钱包结算和记录流水（确保原子性）
      const savedWallet = await this.unitOfWork.runInTransaction(async (tx) => {
        // 执行钱包结算
        wallet.settleToBalance(usdtAmount, settlementId);
        const updated = await this.walletRepo.save(wallet, { tx });

        // 记录账本流水（含详细来源信息）
        const ledgerEntry = LedgerEntry.create({
          accountSequence: wallet.accountSequence,
          userId: UserId.create(userId),
          entryType: LedgerEntryType.REWARD_SETTLED,
          amount: usdtAmount,
          balanceAfter: updated.balances.usdt.available,
          refOrderId: settlementId,
          memo: params.memo || `结算 ${params.usdtAmount} 绿积分到钱包余额`,
          payloadJson: {
            settlementType: 'SETTLE_TO_BALANCE',
            rewardEntryIds: params.rewardEntryIds,
            rewardCount: params.rewardEntryIds.length,
            breakdown: params.breakdown,
          },
        });
        await this.ledgerRepo.save(ledgerEntry, { tx });

        return updated;
      });

      // 5. 使缓存失效
      await this.walletCacheService.invalidateWallet(userId);

      this.logger.log(`Successfully settled ${params.usdtAmount} USDT to balance for ${params.accountSequence}`);

      return {
        success: true,
        settlementId,
        settledAmount: params.usdtAmount,
        balanceAfter: savedWallet.balances.usdt.available.value,
      };
    } catch (error) {
      this.logger.error(`Failed to settle to balance for ${params.accountSequence}: ${error.message}`);
      return {
        success: false,
        settlementId: '',
        settledAmount: 0,
        balanceAfter: 0,
        error: error.message,
      };
    }
  }

  /**
   * [2026-03-01] 预种可结算收益结算
   *
   * 背景：
   *   预种奖励通过 planting-service → wallet-service allocateFunds 链路直接写入 wallet_accounts.settleable_usdt，
   *   不经过 reward-service，因此 reward-service 的"一键结算"（settleToBalance）无法覆盖这部分金额。
   *   本方法专门结算 wallet 中「reward-service 未涉及」的预种可结算余额。
   *
   * 调用时机：
   *   mobile-app 一键结算时，先调 reward-service settleToBalance（结算正常认种部分），
   *   再调本方法（结算预种部分）。两步串行执行，互不干扰。
   *
   * 安全性：
   *   - 不修改 reward-service 任何数据或逻辑
   *   - 仅操作 wallet_accounts.settleable_usdt → usdt_available，与 settleToBalance 域逻辑完全一致
   *   - 幂等：settleable_usdt=0 时直接返回 success，不创建空流水
   */
  async settlePrePlantingToBalance(accountSequence: string): Promise<{
    success: boolean;
    settlementId: string;
    settledAmount: number;
    balanceAfter: number;
    error?: string;
  }> {
    this.logger.log(`[settlePrePlantingToBalance] Processing for ${accountSequence}`);

    try {
      const wallet = await this.walletRepo.findByAccountSequence(accountSequence);
      if (!wallet) {
        throw new WalletNotFoundError(`accountSequence: ${accountSequence}`);
      }

      const remainingSettleable = wallet.rewards.settleableUsdt.value;
      if (remainingSettleable <= 0) {
        this.logger.log(`[settlePrePlantingToBalance] No remaining settleable for ${accountSequence}, skip`);
        return { success: true, settlementId: '', settledAmount: 0, balanceAfter: wallet.balances.usdt.available.value };
      }

      const usdtAmount = Money.USDT(remainingSettleable);
      const userId = wallet.userId.value;
      const settlementId = `STL_PP_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

      const savedWallet = await this.unitOfWork.runInTransaction(async (tx) => {
        wallet.settleToBalance(usdtAmount, settlementId);
        const updated = await this.walletRepo.save(wallet, { tx });

        const ledgerEntry = LedgerEntry.create({
          accountSequence: wallet.accountSequence,
          userId: UserId.create(userId),
          entryType: LedgerEntryType.REWARD_SETTLED,
          amount: usdtAmount,
          balanceAfter: updated.balances.usdt.available,
          refOrderId: settlementId,
          memo: `[预种] 结算预种可结算收益 ${remainingSettleable.toFixed(2)} 绿积分到钱包余额`,
          payloadJson: {
            settlementType: 'PRE_PLANTING_SETTLE_TO_BALANCE',
            source: 'pre-planting',
          },
        });
        await this.ledgerRepo.save(ledgerEntry, { tx });

        return updated;
      });

      await this.walletCacheService.invalidateWallet(userId);

      this.logger.log(
        `[settlePrePlantingToBalance] Settled ${remainingSettleable} USDT for ${accountSequence}, balanceAfter=${savedWallet.balances.usdt.available.value}`,
      );

      return {
        success: true,
        settlementId,
        settledAmount: remainingSettleable,
        balanceAfter: savedWallet.balances.usdt.available.value,
      };
    } catch (error) {
      this.logger.error(`[settlePrePlantingToBalance] Failed for ${accountSequence}: ${error.message}`);
      return { success: false, settlementId: '', settledAmount: 0, balanceAfter: 0, error: error.message };
    }
  }

  /**
   * 分配资金 - 用于认种订单支付后的资金分配
   * 支持分配给用户钱包或系统账户
   */
  async allocateFunds(command: AllocateFundsCommand): Promise<{
    success: boolean;
    allocatedCount: number;
    totalAmount: number;
  }> {
    this.logger.log(`Allocating funds for order ${command.orderId}`);

    let totalAmount = 0;
    let allocatedCount = 0;
    let skippedCount = 0;

    for (const allocation of command.allocations) {
      try {
        // 幂等性检查：检查该订单+账户+分配类型是否已分配
        const alreadyAllocated = await this.checkAllocationExists(
          command.orderId,
          allocation.targetId,
          allocation.allocationType,
        );

        if (alreadyAllocated) {
          this.logger.warn(
            `[IDEMPOTENT] Allocation already exists for order ${command.orderId}, ` +
            `target ${allocation.targetId}, type ${allocation.allocationType}. Skipping.`,
          );
          skippedCount++;
          continue;
        }

        if (allocation.targetType === 'USER') {
          // 分配给用户钱包
          await this.allocateToUserWallet(allocation, command.orderId);
        } else {
          // 分配给系统账户 - 通过 Kafka 事件通知 authorization-service
          await this.allocateToSystemAccount(allocation, command.orderId);
        }

        totalAmount += allocation.amount;
        allocatedCount++;
      } catch (error) {
        this.logger.error(
          `Failed to allocate ${allocation.allocationType} to ${allocation.targetId}`,
          error,
        );
      }
    }

    this.logger.log(
      `Allocated ${allocatedCount}/${command.allocations.length} items (skipped ${skippedCount}), total ${totalAmount} USDT`,
    );

    return {
      success: allocatedCount > 0 || skippedCount > 0,
      allocatedCount,
      totalAmount,
    };
  }

  /**
   * [新增] 预种来源备注前缀
   * 当 metadata.source === 'PRE_PLANTING' 时返回 "[预种] "，否则返回空字符串。
   * 只影响预种权益流水，不修改普通认种的任何备注。
   */
  private prePlantingPrefix(allocation: FundAllocationItem): string {
    return allocation.metadata?.source === 'PRE_PLANTING' ? '[预种] ' : '';
  }

  /**
   * 构建包含来源信息的分配备注
   *
   * 优先使用 metadata.memo（reward-service / pre-planting 已包含完整中文描述和来源用户）
   * 例: "成本费：来自用户D25121400002的认种，1棵树"（正常认种）
   * 例: "预种成本费"（预种 — 简短，需补充来源）
   *
   * 当 metadata.memo 较短（不含"来自"字样）时，追加来源账户信息
   * 前缀 [预种] 通过 prePlantingPrefix() 自动添加
   */
  private buildAllocationMemo(allocation: FundAllocationItem, orderId: string, suffix?: string): string {
    const prefix = this.prePlantingPrefix(allocation);
    const memo = allocation.metadata?.memo || allocation.allocationType;
    const sourceAccount = allocation.metadata?.sourceAccountSequence;

    // 如果 memo 已包含来源用户信息（正常认种的 reward-service 格式），不再重复拼接
    const hasSourceInfo = memo.includes('来自');
    const parts = [prefix + memo];
    if (sourceAccount && !hasSourceInfo) {
      parts.push(`来源: ${sourceAccount} (${orderId})`);
    }
    if (suffix) {
      parts.push(suffix);
    }
    return parts.join(' | ');
  }

  /**
   * 检查分配是否已存在（幂等性检查）
   * 通过查询流水表判断该订单+账户+分配类型是否已处理
   */
  private async checkAllocationExists(
    orderId: string,
    targetId: string,
    allocationType: string,
  ): Promise<boolean> {
    // 查询流水表，检查是否已存在该订单的分配记录
    // 流水的 payloadJson 中存储了 allocationType
    const existingEntry = await this.prisma.ledgerEntry.findFirst({
      where: {
        refOrderId: orderId,
        accountSequence: targetId,
        payloadJson: {
          path: ['allocationType'],
          equals: allocationType,
        },
      },
    });

    return !!existingEntry;
  }

  /**
   * 分配资金到用户钱包
   * 支持用户账户 (D+日期+序号) 和系统账户 (S+序号, 7+省代码, 6+市代码, 9+省代码, 8+市代码)
   *
   * 分配规则：
   * - SHARE_RIGHT (分享权益): 写入 pending_rewards 表，24小时待领取
   * - PROVINCE_TEAM_RIGHT, PROVINCE_AREA_RIGHT, CITY_TEAM_RIGHT, CITY_AREA_RIGHT: 直接到账
   * - COMMUNITY_RIGHT (社区权益): 根据 targetId 分配
   *   - D 开头（用户账户）: 分配到社区授权人账户，直接到账
   *   - S 开头或 '1'（系统账户）: 分配到总部社区账户 S0000000001
   */
  private async allocateToUserWallet(
    allocation: FundAllocationItem,
    orderId: string,
  ): Promise<void> {
    const amount = Money.USDT(allocation.amount);
    const allocationType = allocation.allocationType;

    // 社区权益根据 targetId 分配
    if (allocationType === 'COMMUNITY_RIGHT') {
      await this.allocateCommunityRight(allocation, orderId);
      return;
    }

    // 省市团队/区域权益直接到账
    if (['PROVINCE_TEAM_RIGHT', 'PROVINCE_AREA_RIGHT', 'CITY_TEAM_RIGHT', 'CITY_AREA_RIGHT'].includes(allocationType)) {
      await this.allocateToRegionAccount(allocation, orderId);
      return;
    }

    // 分享权益 (SHARE_RIGHT)
    // targetId 是 accountSequence (用户账户: D2512120001)
    const targetId = allocation.targetId;

    // 用户账户 D 开头，userId 从 targetId 解析不出来，需要查询
    const wallet = await this.walletRepo.findByAccountSequence(targetId);
    if (!wallet) {
      this.logger.warn(`Failed to find wallet for accountSequence ${targetId}`);
      return;
    }

    // 根据用户是否已认种，决定分配方式
    if (wallet.hasPlanted) {
      // 已认种用户：直接进入可结算余额
      wallet.addSettleableReward(amount, Hashpower.create(0));
      await this.walletRepo.save(wallet);

      // 记录流水 - 直接可结算
      const ledgerEntry = LedgerEntry.create({
        accountSequence: wallet.accountSequence,
        userId: wallet.userId,
        entryType: LedgerEntryType.REWARD_TO_SETTLEABLE,
        amount,
        refOrderId: orderId,
        memo: this.buildAllocationMemo(allocation, orderId, '可结算'),
        payloadJson: {
          allocationType: allocation.allocationType,
          metadata: allocation.metadata,
        },
      });
      await this.ledgerRepo.save(ledgerEntry);

      await this.walletCacheService.invalidateWallet(wallet.userId.value);

      this.logger.debug(
        `Allocated ${allocation.amount} USDT to user ${allocation.targetId} for ${allocation.allocationType} (direct settleable)`,
      );
    } else {
      // 未认种用户：写入 pending_rewards 表待领取
      const expireAt = new Date(Date.now() + 24 * 60 * 60 * 1000);

      const pendingReward = PendingReward.create({
        accountSequence: wallet.accountSequence,
        userId: wallet.userId,
        usdtAmount: amount,
        hashpowerAmount: Hashpower.create(0),
        sourceOrderId: orderId,
        allocationType: allocation.allocationType,
        expireAt,
      });
      await this.pendingRewardRepo.save(pendingReward);

      // 同步更新 wallet_accounts 表的 pending_usdt 字段
      wallet.addPendingReward(amount, Hashpower.create(0), expireAt, orderId);
      await this.walletRepo.save(wallet);

      // 记录流水
      const ledgerEntry = LedgerEntry.create({
        accountSequence: wallet.accountSequence,
        userId: wallet.userId,
        entryType: LedgerEntryType.REWARD_PENDING,
        amount,
        refOrderId: orderId,
        memo: this.buildAllocationMemo(allocation, orderId, '待领取'),
        payloadJson: {
          allocationType: allocation.allocationType,
          expireAt: expireAt.toISOString(),
          metadata: allocation.metadata,
        },
      });
      await this.ledgerRepo.save(ledgerEntry);

      await this.walletCacheService.invalidateWallet(wallet.userId.value);

      this.logger.debug(
        `Allocated ${allocation.amount} USDT to user ${allocation.targetId} for ${allocation.allocationType} (pending)`,
      );
    }
  }

  /**
   * 社区权益分配
   * 根据 targetId 决定分配目标：
   * - D 开头（用户账户）: 分配到社区授权人账户
   * - S 开头或 '1'（系统账户）: 分配到总部社区账户 S0000000001
   */
  private async allocateCommunityRight(
    allocation: FundAllocationItem,
    orderId: string,
  ): Promise<void> {
    const targetId = allocation.targetId;
    let wallet: WalletAccount | null;
    let isUserAccount = false;

    // 判断 targetId 类型
    if (targetId.startsWith('D')) {
      // 用户账户（D+日期+序号）: 社区授权人
      wallet = await this.walletRepo.findByAccountSequence(targetId);
      if (!wallet) {
        this.logger.error(`User wallet not found for community right allocation: ${targetId}`);
        return;
      }
      isUserAccount = true;
    } else {
      // 系统账户（S 开头或 '1'）: 总部社区
      const headquartersAccountSequence = 'S0000000001';
      wallet = await this.walletRepo.findByAccountSequence(headquartersAccountSequence);
      if (!wallet) {
        this.logger.error(`Headquarters community account not found: ${headquartersAccountSequence}`);
        return;
      }
    }

    const amount = Money.USDT(allocation.amount);
    // 所有奖励都应该进入可结算余额 (settleable_usdt)，与 reward-service 保持一致
    // 用户需要主动点击"结算"才能将 settleable_usdt 转入 usdt_available
    wallet.addSettleableReward(amount, Hashpower.create(0));
    await this.walletRepo.save(wallet);

    // 记录流水 - 使用 REWARD_TO_SETTLEABLE 类型，与 SHARE_RIGHT 保持一致
    const ledgerEntry = LedgerEntry.create({
      accountSequence: wallet.accountSequence,
      userId: wallet.userId,
      entryType: LedgerEntryType.REWARD_TO_SETTLEABLE,
      amount,
      refOrderId: orderId,
      memo: this.buildAllocationMemo(allocation, orderId, isUserAccount ? '社区授权' : '归总部'),
      payloadJson: {
        allocationType: allocation.allocationType,
        metadata: allocation.metadata,
      },
    });
    await this.ledgerRepo.save(ledgerEntry);

    await this.walletCacheService.invalidateWallet(wallet.userId.value);

    this.logger.debug(
      `Allocated ${allocation.amount} USDT to ${isUserAccount ? 'community user' : 'headquarters'} ${wallet.accountSequence} for ${allocation.allocationType} (settleable)`,
    );
  }

  /**
   * 省市团队/区域权益直接到账
   * - 省团队账户: 7+provinceCode (e.g., 7440000)
   * - 市团队账户: 6+cityCode (e.g., 6440100)
   * - 省区域账户: 9+provinceCode (e.g., 9440000)
   * - 市区域账户: 8+cityCode (e.g., 8440100)
   * - 用户账户: D+日期+序号 (e.g., D25122600002) - 授权市/省公司用户
   */
  private async allocateToRegionAccount(
    allocation: FundAllocationItem,
    orderId: string,
  ): Promise<void> {
    const targetId = allocation.targetId;

    let wallet: WalletAccount | null;

    // 判断 targetId 是用户账户还是区域账户
    if (targetId.startsWith('D')) {
      // 用户账户（D+日期+序号），直接查找已存在的钱包
      wallet = await this.walletRepo.findByAccountSequence(targetId);
      if (!wallet) {
        this.logger.error(`User wallet not found for region allocation: ${targetId}`);
        return;
      }
    } else {
      // 区域账户（纯数字如 6440100），使用 getOrCreate
      const systemUserId = BigInt(targetId);
      wallet = await this.walletRepo.getOrCreate(targetId, systemUserId);
      if (!wallet) {
        this.logger.warn(`Failed to get or create wallet for region account ${targetId}`);
        return;
      }
    }

    const amount = Money.USDT(allocation.amount);
    // 所有奖励都应该进入可结算余额 (settleable_usdt)，与 reward-service 保持一致
    // 用户需要主动点击"结算"才能将 settleable_usdt 转入 usdt_available
    wallet.addSettleableReward(amount, Hashpower.create(0));
    await this.walletRepo.save(wallet);

    // 记录流水 - 使用 REWARD_TO_SETTLEABLE 类型，与 SHARE_RIGHT 保持一致
    const ledgerEntry = LedgerEntry.create({
      accountSequence: wallet.accountSequence,
      userId: wallet.userId,
      entryType: LedgerEntryType.REWARD_TO_SETTLEABLE,
      amount,
      refOrderId: orderId,
      memo: this.buildAllocationMemo(allocation, orderId, '可结算'),
      payloadJson: {
        allocationType: allocation.allocationType,
        metadata: allocation.metadata,
      },
    });
    await this.ledgerRepo.save(ledgerEntry);

    await this.walletCacheService.invalidateWallet(wallet.userId.value);

    this.logger.debug(
      `Allocated ${allocation.amount} USDT to region account ${targetId} for ${allocation.allocationType} (settleable)`,
    );
  }


  /**
   * 分配资金到系统账户
   * 系统账户（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: this.buildAllocationMemo(allocation, orderId),
      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 ===============

  /**
   * 确保区域账户和团队账户存在
   * 在用户选择省市确认后调用，动态创建省/市区域账户和团队账户
   *
   * 账户序列号规则:
   * - 省区域账户: 9 + provinceCode (例: 9440000) - 用于省区域权益
   * - 市区域账户: 8 + cityCode (例: 8440100) - 用于市区域权益
   * - 省团队账户: 7 + provinceCode (例: 7440000) - 用于省团队权益（无授权省公司时）
   * - 市团队账户: 6 + cityCode (例: 6440100) - 用于市团队权益（无授权市公司时）
   */
  async ensureRegionAccounts(params: {
    provinceCode: string;
    provinceName: string;
    cityCode: string;
    cityName: string;
  }): Promise<{
    provinceAccount: { accountSequence: string; created: boolean };
    cityAccount: { accountSequence: string; created: boolean };
    provinceTeamAccount: { accountSequence: string; created: boolean };
    cityTeamAccount: { accountSequence: string; created: boolean };
  }> {
    const { provinceCode, provinceName, cityCode, cityName } = params;

    // 省区域账户: 9 + provinceCode
    const provinceAccountSequence = `9${provinceCode}`;
    const provinceUserId = BigInt(provinceAccountSequence);

    // 市区域账户: 8 + cityCode
    const cityAccountSequence = `8${cityCode}`;
    const cityUserId = BigInt(cityAccountSequence);

    // 省团队账户: 7 + provinceCode
    const provinceTeamAccountSequence = `7${provinceCode}`;
    const provinceTeamUserId = BigInt(provinceTeamAccountSequence);

    // 市团队账户: 6 + cityCode
    const cityTeamAccountSequence = `6${cityCode}`;
    const cityTeamUserId = BigInt(cityTeamAccountSequence);

    this.logger.log(`确保区域和团队账户存在: 省区域=${provinceAccountSequence}, 市区域=${cityAccountSequence}, 省团队=${provinceTeamAccountSequence}, 市团队=${cityTeamAccountSequence}`);

    // 检查省区域账户是否已存在
    let provinceWallet = await this.walletRepo.findByAccountSequence(provinceAccountSequence);
    let provinceCreated = false;
    if (!provinceWallet) {
      provinceWallet = await this.walletRepo.getOrCreate(provinceAccountSequence, provinceUserId);
      provinceCreated = true;
      this.logger.log(`创建省区域账户: ${provinceAccountSequence} (${provinceName})`);
    }

    // 检查市区域账户是否已存在
    let cityWallet = await this.walletRepo.findByAccountSequence(cityAccountSequence);
    let cityCreated = false;
    if (!cityWallet) {
      cityWallet = await this.walletRepo.getOrCreate(cityAccountSequence, cityUserId);
      cityCreated = true;
      this.logger.log(`创建市区域账户: ${cityAccountSequence} (${cityName})`);
    }

    // 检查省团队账户是否已存在
    let provinceTeamWallet = await this.walletRepo.findByAccountSequence(provinceTeamAccountSequence);
    let provinceTeamCreated = false;
    if (!provinceTeamWallet) {
      provinceTeamWallet = await this.walletRepo.getOrCreate(provinceTeamAccountSequence, provinceTeamUserId);
      provinceTeamCreated = true;
      this.logger.log(`创建省团队账户: ${provinceTeamAccountSequence} (${provinceName}团队)`);
    }

    // 检查市团队账户是否已存在
    let cityTeamWallet = await this.walletRepo.findByAccountSequence(cityTeamAccountSequence);
    let cityTeamCreated = false;
    if (!cityTeamWallet) {
      cityTeamWallet = await this.walletRepo.getOrCreate(cityTeamAccountSequence, cityTeamUserId);
      cityTeamCreated = true;
      this.logger.log(`创建市团队账户: ${cityTeamAccountSequence} (${cityName}团队)`);
    }

    return {
      provinceAccount: {
        accountSequence: provinceAccountSequence,
        created: provinceCreated,
      },
      cityAccount: {
        accountSequence: cityAccountSequence,
        created: cityCreated,
      },
      provinceTeamAccount: {
        accountSequence: provinceTeamAccountSequence,
        created: provinceTeamCreated,
      },
      cityTeamAccount: {
        accountSequence: cityTeamAccountSequence,
        created: cityTeamCreated,
      },
    };
  }

  // =============== Withdrawal ===============

  /**
   * 最小提现金额
   */
  private readonly MIN_WITHDRAWAL_AMOUNT = 5; // 5 USDT

  /**
   * 请求提现
   *
   * 流程:
   * 1. 获取动态手续费配置
   * 2. 验证余额是否足够 (金额 + 手续费)
   * 3. 创建提现订单
   * 4. 冻结用户余额
   * 5. 记录流水
   * 6. 发布事件通知 blockchain-service
   */
  async requestWithdrawal(command: RequestWithdrawalCommand): Promise<{
    orderNo: string;
    amount: number;
    fee: number;
    netAmount: number;
    status: string;
  }> {
    const MAX_RETRIES = 3;
    let retries = 0;

    while (retries < MAX_RETRIES) {
      try {
        return await this.executeRequestWithdrawal(command);
      } catch (error) {
        if (this.isOptimisticLockError(error)) {
          retries++;
          this.logger.warn(`[requestWithdrawal] Optimistic lock conflict for ${command.userId}, retry ${retries}/${MAX_RETRIES}`);
          if (retries >= MAX_RETRIES) {
            this.logger.error(`[requestWithdrawal] Max retries exceeded for ${command.userId}`);
            throw error;
          }
          await this.sleep(50 * retries);
        } else {
          throw error;
        }
      }
    }

    throw new Error('Unexpected: exited retry loop without result');
  }

  /**
   * Execute the withdrawal request logic
   */
  private async executeRequestWithdrawal(command: RequestWithdrawalCommand): Promise<{
    orderNo: string;
    amount: number;
    fee: number;
    netAmount: number;
    status: string;
  }> {
    const amount = Money.USDT(command.amount);

    // 从配置获取动态手续费
    const { fee: feeAmount, feeType, feeValue } = await this.feeConfigRepo.calculateFee(command.amount);
    const fee = Money.USDT(feeAmount);
    const totalRequired = amount.add(fee);

    const feeDescription = feeType === FeeType.FIXED
      ? `固定 ${feeValue} 绿积分`
      : `${(feeValue * 100).toFixed(2)}%`;

    // 验证最小提现金额
    if (command.amount < this.MIN_WITHDRAWAL_AMOUNT) {
      throw new Error(`最小提现金额为 ${this.MIN_WITHDRAWAL_AMOUNT} USDT`);
    }

    // 检查热钱包余额是否足够（预检查，防止用户资金被冻结后链上执行失败）
    const hotWalletCheck = await this.hotWalletCacheService.checkSufficientBalance(
      command.chainType,
      amount.toDecimal(),
    );
    if (!hotWalletCheck.sufficient) {
      this.logger.warn(`[WITHDRAWAL] Hot wallet balance check failed for ${command.chainType}: ${hotWalletCheck.error}`);
      throw new BadRequestException(hotWalletCheck.error || '财务系统审计中，请稍后再试');
    }

    // [2026-01-16] 仅按 accountSequence 查找钱包，不再 fallback 到 userId
    // 背景：幽灵钱包 D26010800000 (user_id=133) 与真实用户 D26010900000 (user_id=0) 的 userId 冲突
    //       导致按 userId 查询时找到错误的空钱包，用户转账报"余额不足"
    // 修复：command.userId 实际传入的是 accountSequence，直接按 accountSequence 查询
    const wallet = await this.walletRepo.findByAccountSequence(command.userId);
    if (!wallet) {
      throw new WalletNotFoundError(`accountSequence: ${command.userId}`);
    }

    // 从钱包记录获取 userId（用于订单、流水、事件等关联）
    const userId = wallet.userId.value;

    this.logger.log(`Processing withdrawal request for user ${userId} (${command.userId}): ${command.amount} USDT to ${command.toAddress}, fee: ${feeAmount} (${feeDescription})`);

    // 验证余额是否足够
    if (wallet.balances.usdt.available.lessThan(totalRequired)) {
      throw new BadRequestException(
        `余额不足: 需要 ${totalRequired.value} 绿积分 (金额 ${command.amount} + 手续费 ${feeAmount.toFixed(2)}), 当前可用 ${wallet.balances.usdt.available.value} 绿积分`,
      );
    }

    // 检查目标地址是否为系统内用户（内部转账）
    let isInternalTransfer = false;
    let toAccountSequence: string | undefined;
    let toUserId: UserId | undefined;

    const targetUser = await this.identityClient.findUserByWalletAddress(
      command.chainType,
      command.toAddress,
    );

    if (targetUser) {
      // 目标地址属于系统内用户，标记为内部转账
      isInternalTransfer = true;
      toAccountSequence = targetUser.accountSequence;
      toUserId = UserId.create(BigInt(targetUser.userId));
      this.logger.log(
        `Internal transfer detected: ${wallet.accountSequence} -> ${toAccountSequence}`,
      );

      // [2026-01-08] 兜底保障：确保接收方钱包存在
      // 即使用户注册时已创建钱包，这里再次确认以防万一
      try {
        const ensureResult = await this.ensureWalletExists({
          accountSequence: toAccountSequence,
          userId: targetUser.userId,
        });
        this.logger.log(
          `[WITHDRAWAL] Receiver wallet ensured: ${toAccountSequence}, existed=${ensureResult.existed}, walletId=${ensureResult.walletId}`,
        );
      } catch (error) {
        this.logger.error(
          `[WITHDRAWAL] Failed to ensure receiver wallet: ${toAccountSequence}, error: ${error.message}`,
        );
        // 不抛出异常，让后续流程继续（withdrawal-status.handler 也会兜底创建）
      }
    }

    // 创建提现订单
    const withdrawalOrder = WithdrawalOrder.create({
      accountSequence: wallet.accountSequence,
      userId: UserId.create(userId),
      amount,
      fee,
      chainType: command.chainType,
      toAddress: command.toAddress,
      isInternalTransfer,
      toAccountSequence,
      toUserId,
    });

    // 冻结用户余额 (金额 + 手续费)
    wallet.freeze(totalRequired);
    await this.walletRepo.save(wallet);

    // 标记订单已冻结
    withdrawalOrder.markAsFrozen();
    const savedOrder = await this.withdrawalRepo.save(withdrawalOrder);

    // 记录流水 - 冻结
    const freezeEntry = LedgerEntry.create({
      accountSequence: wallet.accountSequence,
      userId: UserId.create(userId),
      entryType: LedgerEntryType.FREEZE,
      amount: Money.signed(-totalRequired.value, 'USDT'),
      balanceAfter: wallet.balances.usdt.available,
      refOrderId: savedOrder.orderNo,
      memo: `提取冻结: ${command.amount} 绿积分 + ${feeAmount.toFixed(2)} 绿积分手续费 (${feeDescription})`,
    });
    await this.ledgerRepo.save(freezeEntry);

    // 发布事件通知 blockchain-service
    const event = new WithdrawalRequestedEvent({
      orderNo: savedOrder.orderNo,
      accountSequence: wallet.accountSequence,
      userId: userId.toString(),
      walletId: wallet.walletId.toString(),
      amount: command.amount.toString(),
      fee: feeAmount.toString(),
      netAmount: command.amount.toString(),  // 接收方收到完整金额，手续费由发送方额外承担
      assetType: 'USDT',
      chainType: command.chainType,
      toAddress: command.toAddress,
    });

    // 发布到 Kafka 通知 blockchain-service
    await this.eventPublisher.publish({
      eventType: 'wallet.withdrawal.requested',
      payload: event.getPayload() as unknown as { [key: string]: unknown },
    });
    this.logger.log(`Withdrawal event published: ${savedOrder.orderNo}`);

    // 清除钱包缓存
    await this.walletCacheService.invalidateWallet(userId);

    this.logger.log(`Withdrawal order created: ${savedOrder.orderNo}`);

    return {
      orderNo: savedOrder.orderNo,
      amount: savedOrder.amount.value,
      fee: savedOrder.fee.value,
      netAmount: savedOrder.netAmount.value,
      status: savedOrder.status,
    };
  }

  /**
   * 更新提现状态 (内部调用，由 blockchain-service 事件触发)
   */
  async updateWithdrawalStatus(command: UpdateWithdrawalStatusCommand): Promise<void> {
    this.logger.log(`Updating withdrawal ${command.orderNo} to status ${command.status}`);

    const order = await this.withdrawalRepo.findByOrderNo(command.orderNo);
    if (!order) {
      throw new Error(`Withdrawal order not found: ${command.orderNo}`);
    }

    const wallet = await this.walletRepo.findByUserId(order.userId.value);
    if (!wallet) {
      throw new WalletNotFoundError(`userId: ${order.userId.value}`);
    }

    const totalFrozen = order.amount.add(order.fee);

    switch (command.status) {
      case 'BROADCASTED':
        if (!command.txHash) {
          throw new Error('txHash is required for BROADCASTED status');
        }
        order.markAsBroadcasted(command.txHash);
        await this.withdrawalRepo.save(order);
        break;

      case 'CONFIRMED':
        // 使用 UnitOfWork 事务保证原子性：订单更新、用户钱包扣款、流水记录、手续费归集
        await this.unitOfWork.runInTransaction(async (tx) => {
          order.markAsConfirmed();
          await this.withdrawalRepo.save(order, { tx });

          // 解冻并扣除
          wallet.unfreeze(totalFrozen);
          wallet.deduct(totalFrozen, 'Withdrawal completed', order.orderNo);
          await this.walletRepo.save(wallet, { tx });

          // 记录提现完成流水
          const withdrawEntry = LedgerEntry.create({
            accountSequence: wallet.accountSequence,
            userId: order.userId,
            entryType: LedgerEntryType.WITHDRAWAL,
            amount: Money.signed(-order.amount.value, 'USDT'),
            balanceAfter: wallet.balances.usdt.available,
            refOrderId: order.orderNo,
            refTxHash: order.txHash ?? undefined,
            memo: `Withdrawal to ${order.toAddress}`,
          });
          await this.ledgerRepo.save(withdrawEntry, { tx });

          // 归集手续费到系统账户 S0000000006
          if (order.fee.value > 0) {
            await this.collectFeeToSystemAccount(order.orderNo, order.fee, 'WITHDRAWAL_FEE', { tx });
          }
        });

        this.logger.log(`Withdrawal ${order.orderNo} confirmed, txHash: ${order.txHash}, fee collected: ${order.fee.value}`);
        break;

      case 'FAILED':
        order.markAsFailed(command.errorMessage || 'Unknown error');
        await this.withdrawalRepo.save(order);

        // 解冻资金
        if (order.needsUnfreeze()) {
          wallet.unfreeze(totalFrozen);
          await this.walletRepo.save(wallet);

          // 记录解冻流水
          const unfreezeEntry = LedgerEntry.create({
            accountSequence: wallet.accountSequence,
            userId: order.userId,
            entryType: LedgerEntryType.UNFREEZE,
            amount: totalFrozen,
            balanceAfter: wallet.balances.usdt.available,
            refOrderId: order.orderNo,
            memo: `Withdrawal failed, funds unfrozen: ${command.errorMessage}`,
          });
          await this.ledgerRepo.save(unfreezeEntry);
        }

        this.logger.warn(`Withdrawal ${order.orderNo} failed: ${command.errorMessage}`);
        break;
    }

    await this.walletCacheService.invalidateWallet(order.userId.value);
  }

  /**
   * 查询用户提现订单
   */
  async getWithdrawals(accountSequence: string): Promise<Array<{
    orderNo: string;
    amount: number;
    fee: number;
    netAmount: number;
    chainType: string;
    toAddress: string;
    txHash: string | null;
    status: string;
    createdAt: string;
  }>> {
    // 使用 accountSequence 查询提现订单（废弃 userId 查询）
    const orders = await this.withdrawalRepo.findByAccountSequence(accountSequence);
    return orders.map(order => ({
      orderNo: order.orderNo,
      amount: order.amount.value,
      fee: order.fee.value,
      netAmount: order.netAmount.value,
      chainType: order.chainType,
      toAddress: order.toAddress,
      txHash: order.txHash,
      status: order.status,
      createdAt: order.createdAt.toISOString(),
    }));
  }

  // =============== 手续费归集 ===============

  /**
   * 手续费归集账户序列号
   * S0000000006: 手续费归集账户 (user_id = -6)
   */
  private static readonly FEE_COLLECTION_ACCOUNT = 'S0000000006';
  private static readonly FEE_COLLECTION_USER_ID = BigInt(-6);

  /**
   * 归集手续费到系统账户
   * 在事务中调用，确保与提现操作的原子性
   *
   * @param refOrderId 关联的订单号
   * @param fee 手续费金额
   * @param feeType 手续费类型 (WITHDRAWAL_FEE | FIAT_WITHDRAWAL_FEE)
   * @param options 仓库保存选项（包含事务客户端）
   */
  private async collectFeeToSystemAccount(
    refOrderId: string,
    fee: Money,
    feeType: 'WITHDRAWAL_FEE' | 'FIAT_WITHDRAWAL_FEE',
    options: { tx: import('@/infrastructure/persistence/unit-of-work').PrismaTransactionClient },
  ): Promise<void> {
    const feeAccountSequence = WalletApplicationService.FEE_COLLECTION_ACCOUNT;
    const feeUserId = WalletApplicationService.FEE_COLLECTION_USER_ID;

    // 获取手续费归集账户
    const feeAccount = await this.walletRepo.findByAccountSequence(feeAccountSequence, options);
    if (!feeAccount) {
      this.logger.error(`[FEE_COLLECTION] Fee collection account ${feeAccountSequence} not found!`);
      throw new Error(`Fee collection account ${feeAccountSequence} not found`);
    }

    // 增加手续费账户余额
    feeAccount.addAvailableBalance(fee);
    await this.walletRepo.save(feeAccount, options);

    // 记录手续费归集流水
    const feeEntry = LedgerEntry.create({
      accountSequence: feeAccountSequence,
      userId: UserId.create(feeUserId),
      entryType: LedgerEntryType.FEE_COLLECTION,
      amount: fee,
      balanceAfter: feeAccount.balances.usdt.available,
      refOrderId,
      memo: feeType === 'WITHDRAWAL_FEE'
        ? `区块链提现手续费归集: ${fee.value} 绿积分`
        : `法币提现手续费归集: ${fee.value} 绿积分`,
      payloadJson: {
        feeType,
        sourceOrderNo: refOrderId,
        collectedAt: new Date().toISOString(),
      },
    });
    await this.ledgerRepo.save(feeEntry, options);

    this.logger.log(`[FEE_COLLECTION] Fee collected: ${fee.value} USDT from order ${refOrderId}, type: ${feeType}`);
  }

  // =============== Queries ===============

  async getMyWallet(query: GetMyWalletQuery): Promise<WalletDTO> {
    const accountSequence = query.accountSequence;
    // userId 可能是 '0' (当仅通过 accountSequence 查询时)，此时跳过缓存和 BigInt 转换
    const userIdStr = query.userId;
    const hasValidUserId = userIdStr && userIdStr !== '0' && !userIdStr.startsWith('D');
    const userId = hasValidUserId ? BigInt(userIdStr) : BigInt(0);

    // Try to get from cache first (only if we have a valid userId)
    if (hasValidUserId) {
      const cached = await this.walletCacheService.getWallet(userId);
      if (cached) {
        this.logger.debug(`Returning cached wallet for user: ${userId}`);
        return {
          walletId: cached.walletId,
          userId: cached.userId,
          balances: cached.balances,
          hashpower: cached.hashpower,
          rewards: cached.rewards,
          status: cached.status,
        };
      }
    }

    // Cache miss or no valid userId - fetch from database (by accountSequence)
    const wallet = await this.walletRepo.getOrCreate(accountSequence, userId);

    const walletDTO: WalletDTO = {
      walletId: wallet.walletId.toString(),
      userId: wallet.userId.toString(),
      balances: {
        usdt: {
          available: wallet.balances.usdt.available.value,
          frozen: wallet.balances.usdt.frozen.value,
        },
        dst: {
          available: wallet.balances.dst.available.value,
          frozen: wallet.balances.dst.frozen.value,
        },
        bnb: {
          available: wallet.balances.bnb.available.value,
          frozen: wallet.balances.bnb.frozen.value,
        },
        og: {
          available: wallet.balances.og.available.value,
          frozen: wallet.balances.og.frozen.value,
        },
        rwad: {
          available: wallet.balances.rwad.available.value,
          frozen: wallet.balances.rwad.frozen.value,
        },
      },
      hashpower: wallet.hashpower.value,
      rewards: {
        pendingUsdt: wallet.rewards.pendingUsdt.value,
        pendingHashpower: wallet.rewards.pendingHashpower.value,
        pendingExpireAt: wallet.rewards.pendingExpireAt?.toISOString() ?? null,
        settleableUsdt: wallet.rewards.settleableUsdt.value,
        settleableHashpower: wallet.rewards.settleableHashpower.value,
        settledTotalUsdt: wallet.rewards.settledTotalUsdt.value,
        settledTotalHashpower: wallet.rewards.settledTotalHashpower.value,
        expiredTotalUsdt: wallet.rewards.expiredTotalUsdt.value,
        expiredTotalHashpower: wallet.rewards.expiredTotalHashpower.value,
      },
      status: wallet.status,
    };

    // Store in cache (fire and forget, don't block response)
    this.walletCacheService.setWallet(userId, walletDTO).catch((err) => {
      this.logger.warn(`Failed to cache wallet for user ${userId}: ${err.message}`);
    });

    return walletDTO;
  }

  async getMyLedger(query: GetMyLedgerQuery): Promise<PaginatedLedgerDTO> {
    // 使用 accountSequence 查询流水（废弃 userId 查询）
    const result = await this.ledgerRepo.findByAccountSequence(
      query.accountSequence,
      {
        entryType: query.entryType,
        assetType: query.assetType,
        startDate: query.startDate,
        endDate: query.endDate,
      },
      {
        page: query.page ?? 1,
        pageSize: query.pageSize ?? 20,
      },
    );

    // 调试日志：打印流水数据（只打印前5条）
    this.logger.debug(`[getMyLedger] ======== 流水数据调试 ========`);
    this.logger.debug(`[getMyLedger] accountSequence: ${query.accountSequence}, 共 ${result.data.length} 条, 总计 ${result.total} 条`);
    for (let i = 0; i < result.data.length && i < 5; i++) {
      const entry = result.data[i];
      const allocationType = (entry.payloadJson as Record<string, unknown>)?.allocationType;
      this.logger.debug(
        `[getMyLedger] [${i}] entryType=${entry.entryType}, payloadJson=${JSON.stringify(entry.payloadJson)}, allocationType=${allocationType}, memo=${entry.memo}`,
      );
    }
    this.logger.debug(`[getMyLedger] ================================`);

    return {
      data: result.data.map(entry => ({
        id: entry.id.toString(),
        entryType: entry.entryType,
        amount: entry.amount.value,
        assetType: entry.assetType,
        balanceAfter: entry.balanceAfter?.value ?? null,
        refOrderId: entry.refOrderId,
        refTxHash: entry.refTxHash,
        memo: entry.memo,
        ...extractPayloadInfo(entry.payloadJson),
        createdAt: entry.createdAt.toISOString(),
      })),
      total: result.total,
      page: result.page,
      pageSize: result.pageSize,
      totalPages: result.totalPages,
    };
  }

  // [2026-01-05] 新增：系统账户分类账查询
  /**
   * 获取单个系统账户分类账（按 accountSequence 查询）
   */
  async getSystemAccountLedger(params: {
    accountSequence: string;
    page?: number;
    pageSize?: number;
    entryType?: LedgerEntryType;
    startDate?: Date;
    endDate?: Date;
  }): Promise<PaginatedLedgerDTO> {
    const result = await this.ledgerRepo.findByAccountSequence(
      params.accountSequence,
      {
        entryType: params.entryType,
        startDate: params.startDate,
        endDate: params.endDate,
      },
      {
        page: params.page ?? 1,
        pageSize: params.pageSize ?? 20,
      },
    );

    return {
      data: result.data.map(entry => ({
        id: entry.id.toString(),
        entryType: entry.entryType,
        amount: entry.amount.value,
        assetType: entry.assetType,
        balanceAfter: entry.balanceAfter?.value ?? null,
        refOrderId: entry.refOrderId,
        refTxHash: entry.refTxHash,
        memo: entry.memo,
        ...extractPayloadInfo(entry.payloadJson),
        createdAt: entry.createdAt.toISOString(),
      })),
      total: result.total,
      page: result.page,
      pageSize: result.pageSize,
      totalPages: result.totalPages,
    };
  }

  /**
   * 获取所有系统账户的分类账明细
   * 返回每个系统账户的最近流水记录
   */
  async getAllSystemAccountsLedger(params?: {
    pageSize?: number;
    startDate?: Date;
    endDate?: Date;
  }): Promise<{
    fixedAccountsLedger: Array<{
      accountSequence: string;
      accountType: string;
      ledger: LedgerEntryDTO[];
      total: number;
    }>;
    provinceAccountsLedger: Array<{
      accountSequence: string;
      regionCode: string;
      regionName: string;
      ledger: LedgerEntryDTO[];
      total: number;
    }>;
    cityAccountsLedger: Array<{
      accountSequence: string;
      regionCode: string;
      regionName: string;
      ledger: LedgerEntryDTO[];
      total: number;
    }>;
  }> {
    const pageSize = params?.pageSize ?? 50; // 每个账户默认显示最近50条

    // 1. 获取所有系统账户
    const allAccounts = await this.getAllSystemAccounts();

    // 2. 并行获取所有账户的分类账
    const [fixedLedgers, provinceLedgers, cityLedgers] = await Promise.all([
      // 固定账户
      Promise.all(
        allAccounts.fixedAccounts.map(async account => {
          const ledgerResult = await this.ledgerRepo.findByAccountSequence(
            account.accountSequence,
            { startDate: params?.startDate, endDate: params?.endDate },
            { page: 1, pageSize },
          );
          return {
            accountSequence: account.accountSequence,
            accountType: account.accountType,
            ledger: ledgerResult.data.map(entry => ({
              id: entry.id.toString(),
              entryType: entry.entryType,
              amount: entry.amount.value,
              assetType: entry.assetType,
              balanceAfter: entry.balanceAfter?.value ?? null,
              refOrderId: entry.refOrderId,
              refTxHash: entry.refTxHash,
              memo: entry.memo,
              ...extractPayloadInfo(entry.payloadJson),
              createdAt: entry.createdAt.toISOString(),
            })),
            total: ledgerResult.total,
          };
        }),
      ),
      // 省账户
      Promise.all(
        allAccounts.provinceAccounts.map(async account => {
          const ledgerResult = await this.ledgerRepo.findByAccountSequence(
            account.accountSequence,
            { startDate: params?.startDate, endDate: params?.endDate },
            { page: 1, pageSize },
          );
          return {
            accountSequence: account.accountSequence,
            regionCode: account.regionCode,
            regionName: account.regionName,
            ledger: ledgerResult.data.map(entry => ({
              id: entry.id.toString(),
              entryType: entry.entryType,
              amount: entry.amount.value,
              assetType: entry.assetType,
              balanceAfter: entry.balanceAfter?.value ?? null,
              refOrderId: entry.refOrderId,
              refTxHash: entry.refTxHash,
              memo: entry.memo,
              ...extractPayloadInfo(entry.payloadJson),
              createdAt: entry.createdAt.toISOString(),
            })),
            total: ledgerResult.total,
          };
        }),
      ),
      // 市账户
      Promise.all(
        allAccounts.cityAccounts.map(async account => {
          const ledgerResult = await this.ledgerRepo.findByAccountSequence(
            account.accountSequence,
            { startDate: params?.startDate, endDate: params?.endDate },
            { page: 1, pageSize },
          );
          return {
            accountSequence: account.accountSequence,
            regionCode: account.regionCode,
            regionName: account.regionName,
            ledger: ledgerResult.data.map(entry => ({
              id: entry.id.toString(),
              entryType: entry.entryType,
              amount: entry.amount.value,
              assetType: entry.assetType,
              balanceAfter: entry.balanceAfter?.value ?? null,
              refOrderId: entry.refOrderId,
              refTxHash: entry.refTxHash,
              memo: entry.memo,
              ...extractPayloadInfo(entry.payloadJson),
              createdAt: entry.createdAt.toISOString(),
            })),
            total: ledgerResult.total,
          };
        }),
      ),
    ]);

    return {
      fixedAccountsLedger: fixedLedgers,
      provinceAccountsLedger: provinceLedgers,
      cityAccountsLedger: cityLedgers,
    };
  }

  // =============== Pending Rewards ===============

  /**
   * 查询用户的待领取奖励列表
   * 从 pending_rewards 表中读取，支持逐笔查看
   */
  async getPendingRewards(accountSequence: string): Promise<Array<{
    id: string;
    usdtAmount: number;
    hashpowerAmount: number;
    sourceOrderId: string;
    allocationType: string;
    expireAt: string;
    status: string;
    createdAt: string;
  }>> {
    // [2026-03-01] 修复：只返回 PENDING 状态的记录，排除已结算和已过期的
    const rewards = await this.pendingRewardRepo.findByAccountSequence(
      accountSequence,
      PendingRewardStatus.PENDING,
    );
    return rewards.map(r => ({
      id: r.id.toString(),
      usdtAmount: r.usdtAmount.value,
      hashpowerAmount: r.hashpowerAmount.value,
      sourceOrderId: r.sourceOrderId,
      allocationType: r.allocationType,
      expireAt: r.expireAt.toISOString(),
      status: r.status,
      createdAt: r.createdAt.toISOString(),
    }));
  }

  /**
   * 查询用户的可结算奖励列表
   * 从 pending_rewards 表中读取 status=SETTLED 的记录
   */
  async getSettleableRewards(accountSequence: string): Promise<Array<{
    id: string;
    usdtAmount: number;
    hashpowerAmount: number;
    sourceOrderId: string;
    allocationType: string;
    settledAt: string;
    createdAt: string;
  }>> {
    const rewards = await this.pendingRewardRepo.findByAccountSequence(
      accountSequence,
      PendingRewardStatus.SETTLED,
    );
    return rewards.map(r => ({
      id: r.id.toString(),
      usdtAmount: r.usdtAmount.value,
      hashpowerAmount: r.hashpowerAmount.value,
      sourceOrderId: r.sourceOrderId,
      allocationType: r.allocationType,
      settledAt: r.settledAt?.toISOString() ?? '',
      createdAt: r.createdAt.toISOString(),
    }));
  }

  /**
   * 查询用户的已过期奖励列表
   * 从 pending_rewards 表中读取 status=EXPIRED 的记录
   */
  async getExpiredRewards(accountSequence: string): Promise<Array<{
    id: string;
    usdtAmount: number;
    hashpowerAmount: number;
    sourceOrderId: string;
    allocationType: string;
    expiredAt: string;
    createdAt: string;
  }>> {
    const rewards = await this.pendingRewardRepo.findByAccountSequence(
      accountSequence,
      PendingRewardStatus.EXPIRED,
    );
    return rewards.map(r => ({
      id: r.id.toString(),
      usdtAmount: r.usdtAmount.value,
      hashpowerAmount: r.hashpowerAmount.value,
      sourceOrderId: r.sourceOrderId,
      allocationType: r.allocationType,
      expiredAt: r.expiredAt?.toISOString() ?? '',
      createdAt: r.createdAt.toISOString(),
    }));
  }

  /**
   * 结算用户所有待领取奖励
   * 当用户认种后调用，将 PENDING 状态的奖励转为 SETTLED
   * 同时将金额和算力转入钱包的可结算余额
   *
   * 使用数据库事务确保 pending_rewards 状态更新和 wallet_accounts 余额更新的原子性
   */
  async settleUserPendingRewards(accountSequence: string): Promise<{
    settledCount: number;
    totalUsdt: number;
    totalHashpower: number;
  }> {
    this.logger.log(`[settleUserPendingRewards] accountSequence=${accountSequence}`);

    // 查找所有 PENDING 状态的奖励
    const pendingRewards = await this.pendingRewardRepo.findByAccountSequence(
      accountSequence,
      PendingRewardStatus.PENDING,
    );

    if (pendingRewards.length === 0) {
      this.logger.debug(`[settleUserPendingRewards] No pending rewards for ${accountSequence}`);
      return { settledCount: 0, totalUsdt: 0, totalHashpower: 0 };
    }

    let totalUsdt = 0;
    let totalHashpower = 0;

    // 计算总金额
    for (const reward of pendingRewards) {
      totalUsdt += reward.usdtAmount.value;
      totalHashpower += reward.hashpowerAmount.value;
    }

    this.logger.log(`[settleUserPendingRewards] Found ${pendingRewards.length} pending rewards, total: ${totalUsdt} USDT, ${totalHashpower} hashpower`);

    // 使用事务确保原子性：pending_rewards 状态更新 + wallet_accounts 余额更新
    await this.prisma.$transaction(async (tx) => {
      // 1. 更新 pending_rewards 状态为 SETTLED
      const now = new Date();
      await tx.pendingReward.updateMany({
        where: {
          id: { in: pendingRewards.map(r => r.id) },
          status: PendingRewardStatus.PENDING, // 双重检查，防止并发问题
        },
        data: {
          status: PendingRewardStatus.SETTLED,
          settledAt: now,
        },
      });

      // 2. 更新 wallet_accounts 可结算余额
      const walletRecord = await tx.walletAccount.findUnique({
        where: { accountSequence },
      });

      if (walletRecord) {
        await tx.walletAccount.update({
          where: { accountSequence },
          data: {
            settleableUsdt: { increment: totalUsdt },
            settleableHashpower: { increment: totalHashpower },
            hashpower: { increment: totalHashpower },
            // 同时减少 pending 余额
            pendingUsdt: { decrement: totalUsdt },
            pendingHashpower: { decrement: totalHashpower },
            // [2026-03-01] 清除过期时间，避免前端残留倒计时
            pendingExpireAt: null,
          },
        });

        // 3. 记录流水
        if (totalUsdt > 0) {
          await tx.ledgerEntry.create({
            data: {
              accountSequence,
              userId: walletRecord.userId,
              entryType: LedgerEntryType.REWARD_PENDING_CONVERTED,
              amount: totalUsdt,
              assetType: 'USDT',
              memo: `[状态转换] ${pendingRewards.length}笔待领取奖励转为可结算`,
            },
          });
        }

        // 4. 为每笔 pending reward 创建 REWARD_TO_SETTLEABLE 流水（保留来源信息，供"分享收益"筛选）
        //    注意：payloadJson.convertedFromPending = true 用于统计时排除（避免与 REWARD_PENDING 双重计算）
        for (const reward of pendingRewards) {
          if (reward.usdtAmount.value <= 0) continue;
          // 查找原始 REWARD_PENDING 流水，获取来源信息
          const originalEntry = await tx.ledgerEntry.findFirst({
            where: {
              accountSequence,
              entryType: LedgerEntryType.REWARD_PENDING,
              refOrderId: reward.sourceOrderId,
            },
          });
          const originalPayload = (originalEntry?.payloadJson as Record<string, unknown>) || {};
          await tx.ledgerEntry.create({
            data: {
              accountSequence,
              userId: walletRecord.userId,
              entryType: LedgerEntryType.REWARD_TO_SETTLEABLE,
              amount: reward.usdtAmount.value,
              assetType: 'USDT',
              refOrderId: reward.sourceOrderId,
              memo: originalEntry?.memo?.replace('待领取', '可结算') || `[预种] ${reward.allocationType} | 可结算`,
              payloadJson: { ...originalPayload, convertedFromPending: true },
            },
          });
        }

        this.logger.log(`[settleUserPendingRewards] Transaction committed: ${pendingRewards.length} rewards settled for ${accountSequence}`);
      } else {
        this.logger.warn(`[settleUserPendingRewards] Wallet not found for ${accountSequence}, skipping wallet update`);
      }
    });

    // 清除钱包缓存
    const wallet = await this.walletRepo.findByAccountSequence(accountSequence);
    if (wallet) {
      await this.walletCacheService.invalidateWallet(wallet.userId.value);
    }

    this.logger.log(
      `[settleUserPendingRewards] Settled ${pendingRewards.length} rewards: ${totalUsdt} USDT, ${totalHashpower} hashpower`,
    );

    return {
      settledCount: pendingRewards.length,
      totalUsdt,
      totalHashpower,
    };
  }

  /**
   * 标记用户为已认种
   * 认种后的用户，分享权益直接进入可结算状态
   */
  async markUserAsPlanted(accountSequence: string): Promise<void> {
    const wallet = await this.walletRepo.findByAccountSequence(accountSequence);
    if (!wallet) {
      this.logger.warn(`[markUserAsPlanted] Wallet not found for ${accountSequence}`);
      return;
    }

    if (wallet.hasPlanted) {
      this.logger.debug(`[markUserAsPlanted] User ${accountSequence} already marked as planted`);
      return;
    }

    wallet.markAsPlanted();
    await this.walletRepo.save(wallet);
    await this.walletCacheService.invalidateWallet(wallet.userId.value);

    this.logger.log(`[markUserAsPlanted] User ${accountSequence} marked as planted`);
  }

  /**
   * [2026-03-01] 预种购买后触发待领取→可结算转换
   *
   * 用户购买预种份额后，planting-service 调用此方法，
   * 将该用户标记为 hasPlanted=true，并结算所有 PENDING 奖励为 SETTLED。
   * 内部复用已有幂等方法：markUserAsPlanted + settleUserPendingRewards。
   */
  async settleAfterPrePlanting(accountSequence: string): Promise<{
    markedAsPlanted: boolean;
    settledCount: number;
    totalUsdt: number;
    totalHashpower: number;
  }> {
    this.logger.log(`[settleAfterPrePlanting] Processing for ${accountSequence}`);

    // Step 1: 标记为已认种（幂等）
    const walletBefore = await this.walletRepo.findByAccountSequence(accountSequence);
    const wasAlreadyPlanted = walletBefore?.hasPlanted ?? false;

    await this.markUserAsPlanted(accountSequence);

    // Step 2: 结算所有 PENDING 奖励（幂等）
    const settleResult = await this.settleUserPendingRewards(accountSequence);

    this.logger.log(
      `[settleAfterPrePlanting] Done for ${accountSequence}: ` +
      `wasAlreadyPlanted=${wasAlreadyPlanted}, settled=${settleResult.settledCount} rewards, ` +
      `usdt=${settleResult.totalUsdt}, hashpower=${settleResult.totalHashpower}`,
    );

    return {
      markedAsPlanted: !wasAlreadyPlanted,
      settledCount: settleResult.settledCount,
      totalUsdt: settleResult.totalUsdt,
      totalHashpower: settleResult.totalHashpower,
    };
  }

  /**
   * 处理过期奖励
   * 定时任务调用，将已过期的 PENDING 奖励标记为 EXPIRED
   * 过期金额转入总部社区账户 (S0000000001)
   * [2026-01-07] 更新：按分配类型分别记录流水，便于明细查看
   */
  async processExpiredRewards(batchSize = 100): Promise<{
    processedCount: number;
    totalExpiredUsdt: number;
    transferredToHeadquarters: number;
  }> {
    const now = new Date();
    this.logger.log(`[processExpiredRewards] Processing expired rewards at ${now.toISOString()}`);

    // 查找已过期的 PENDING 奖励
    const expiredRewards = await this.pendingRewardRepo.findExpiredPending(now, batchSize);

    if (expiredRewards.length === 0) {
      return { processedCount: 0, totalExpiredUsdt: 0, transferredToHeadquarters: 0 };
    }

    let totalExpiredUsdt = 0;

    // 标记为已过期
    for (const reward of expiredRewards) {
      reward.markAsExpired();
      totalExpiredUsdt += reward.usdtAmount.value;
    }

    // 批量更新状态
    await this.pendingRewardRepo.updateAll(expiredRewards);

    // 将过期金额转入总部社区账户
    const headquartersAccountSequence = 'S0000000001';
    const hqWallet = await this.walletRepo.findByAccountSequence(headquartersAccountSequence);

    if (hqWallet && totalExpiredUsdt > 0) {
      hqWallet.addAvailableBalance(Money.USDT(totalExpiredUsdt));
      await this.walletRepo.save(hqWallet);

      // [2026-01-07] 更新：按分配类型分别记录流水，便于明细查看过期分享权益等
      // 按 allocationType 分组
      const groupedByType = new Map<string, { amount: number; count: number; orderIds: string[] }>();
      for (const reward of expiredRewards) {
        const type = reward.allocationType;
        const existing = groupedByType.get(type) || { amount: 0, count: 0, orderIds: [] };
        existing.amount += reward.usdtAmount.value;
        existing.count += 1;
        existing.orderIds.push(reward.sourceOrderId);
        groupedByType.set(type, existing);
      }

      // 为每种类型创建单独的流水记录
      for (const [allocationType, data] of groupedByType) {
        const ledgerEntry = LedgerEntry.create({
          accountSequence: headquartersAccountSequence,
          userId: hqWallet.userId,
          entryType: LedgerEntryType.SYSTEM_ALLOCATION,
          amount: Money.USDT(data.amount),
          memo: `过期${this.getAllocationTypeLabel(allocationType)}收入 (${data.count}笔)`,
          payloadJson: {
            allocationType,
            expiredCount: data.count,
            sourceOrderIds: data.orderIds.slice(0, 10), // 只保留前10个订单号
          },
        });
        await this.ledgerRepo.save(ledgerEntry);

        this.logger.log(
          `[processExpiredRewards] Recorded ${allocationType}: ${data.amount} USDT from ${data.count} expired rewards`,
        );
      }

      this.logger.log(
        `[processExpiredRewards] Transferred ${totalExpiredUsdt} USDT to headquarters from ${expiredRewards.length} expired rewards`,
      );
    }

    return {
      processedCount: expiredRewards.length,
      totalExpiredUsdt,
      transferredToHeadquarters: totalExpiredUsdt,
    };
  }

  /**
   * 获取分配类型的中文标签
   * [2026-01-07] 新增：用于过期奖励流水备注
   */
  private getAllocationTypeLabel(allocationType: string): string {
    const labels: Record<string, string> = {
      SHARE_RIGHT: '分享权益',
      PROVINCE_TEAM_RIGHT: '省团队权益',
      CITY_TEAM_RIGHT: '市团队权益',
      PROVINCE_AREA_RIGHT: '省区域权益',
      CITY_AREA_RIGHT: '市区域权益',
      COMMUNITY_RIGHT: '社区权益',
    };
    return labels[allocationType] || allocationType;
  }

  // =============== Ledger Statistics ===============

  /**
   * 获取流水统计信息
   */
  async getLedgerStatistics(accountSequence: string): Promise<{
    totalIncome: number;
    totalExpense: number;
    netAmount: number;
    totalCount: number;
    byEntryType: Array<{
      entryType: string;
      entryTypeName: string;
      totalAmount: number;
      count: number;
    }>;
    startDate: string;
    endDate: string;
  }> {
    // 使用 accountSequence 查询流水（废弃 userId 查询）
    const entries = await this.prisma.ledgerEntry.findMany({
      where: { accountSequence, assetType: 'USDT' },
      orderBy: { createdAt: 'asc' },
    });

    if (entries.length === 0) {
      return {
        totalIncome: 0,
        totalExpense: 0,
        netAmount: 0,
        totalCount: 0,
        byEntryType: [],
        startDate: new Date().toISOString(),
        endDate: new Date().toISOString(),
      };
    }

    // 计算收支
    // 排除临时性流水和状态转换类型，避免双重计算
    // 认种：PLANT_FREEZE(-100) + PLANT_PAYMENT(-100) → 只计 PLANT_PAYMENT
    // 奖励两条路径：
    //   未认种时：REWARD_PENDING(+720) → REWARD_PENDING_CONVERTED(+720) → REWARD_SETTLED(+720)
    //            只在 REWARD_PENDING 计入
    //   已认种时：REWARD_TO_SETTLEABLE(+720) → REWARD_SETTLED(+720)
    //            只在 REWARD_TO_SETTLEABLE 计入（这是该笔钱的首次入账）
    const excludeFromStats = new Set([
      'PLANT_FREEZE',              // 认种冻结（临时）
      'PLANT_UNFREEZE',            // 认种解冻（回滚）
      'FREEZE',                    // 通用冻结（临时）
      'UNFREEZE',                  // 通用解冻（回滚）
      'REWARD_PENDING_CONVERTED',  // 待领取批量转可结算（状态转换，REWARD_PENDING 已计入）
      'REWARD_SETTLED',            // 可结算→结算到余额（状态转换，首次入账已计入）
    ]);

    let totalIncome = 0;
    let totalExpense = 0;
    const byTypeMap = new Map<string, { totalAmount: number; count: number }>();

    for (const entry of entries) {
      const amount = Number(entry.amount);

      // 只计算非临时性流水的收支统计
      // 兼容历史数据：老的批量转换记录 entry_type 仍为 REWARD_TO_SETTLEABLE，
      // 通过 memo 中 "pending rewards settled" 或 payloadJson.convertedFromPending 识别并排除
      const isExcluded = excludeFromStats.has(entry.entryType) ||
        (entry.entryType === 'REWARD_TO_SETTLEABLE' && (
          entry.memo?.includes('pending rewards settled') ||
          (entry.payloadJson as Record<string, unknown>)?.convertedFromPending === true
        ));
      if (!isExcluded) {
        if (amount > 0) {
          totalIncome += amount;
        } else {
          totalExpense += Math.abs(amount);
        }
      }

      // 按类型汇总（包含所有类型，方便查看明细）
      const existing = byTypeMap.get(entry.entryType) || { totalAmount: 0, count: 0 };
      existing.totalAmount += amount;
      existing.count += 1;
      byTypeMap.set(entry.entryType, existing);
    }

    // 转换为数组
    const byEntryType = Array.from(byTypeMap.entries()).map(([entryType, data]) => ({
      entryType,
      entryTypeName: this.getEntryTypeName(entryType),
      totalAmount: data.totalAmount,
      count: data.count,
    }));

    return {
      totalIncome,
      totalExpense,
      netAmount: totalIncome - totalExpense,
      totalCount: entries.length,
      byEntryType,
      startDate: entries[0].createdAt.toISOString(),
      endDate: entries[entries.length - 1].createdAt.toISOString(),
    };
  }

  /**
   * 获取流水趋势
   */
  async getLedgerTrend(accountSequence: string, days: number = 30): Promise<{
    dailyTrend: Array<{
      date: string;
      income: number;
      expense: number;
      net: number;
      count: number;
    }>;
    days: number;
    periodIncome: number;
    periodExpense: number;
    periodNet: number;
  }> {
    // 使用 accountSequence 查询流水（废弃 userId 查询）
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - days);
    startDate.setHours(0, 0, 0, 0);

    const entries = await this.prisma.ledgerEntry.findMany({
      where: {
        accountSequence,
        assetType: 'USDT',
        createdAt: { gte: startDate },
      },
      orderBy: { createdAt: 'asc' },
    });

    // 按日期分组
    const dailyMap = new Map<string, { income: number; expense: number; count: number }>();

    // 初始化所有日期
    for (let i = 0; i < days; i++) {
      const date = new Date(startDate);
      date.setDate(date.getDate() + i);
      const dateStr = date.toISOString().split('T')[0];
      dailyMap.set(dateStr, { income: 0, expense: 0, count: 0 });
    }

    // 填充数据
    // 排除临时性流水和状态转换类型，避免双重计算（与上方 getLedgerStatistics 一致）
    const excludeFromStats = new Set([
      'PLANT_FREEZE',              // 认种冻结（临时）
      'PLANT_UNFREEZE',            // 认种解冻（回滚）
      'FREEZE',                    // 通用冻结（临时）
      'UNFREEZE',                  // 通用解冻（回滚）
      'REWARD_PENDING_CONVERTED',  // 待领取批量转可结算（状态转换）
      'REWARD_SETTLED',            // 可结算→结算到余额（状态转换）
    ]);

    let periodIncome = 0;
    let periodExpense = 0;

    for (const entry of entries) {
      const dateStr = entry.createdAt.toISOString().split('T')[0];
      const amount = Number(entry.amount);
      const existing = dailyMap.get(dateStr) || { income: 0, expense: 0, count: 0 };

      // 只计算非临时性流水的收支统计（兼容历史批量转换数据）
      const isExcluded = excludeFromStats.has(entry.entryType) ||
        (entry.entryType === 'REWARD_TO_SETTLEABLE' && (
          entry.memo?.includes('pending rewards settled') ||
          (entry.payloadJson as Record<string, unknown>)?.convertedFromPending === true
        ));
      if (!isExcluded) {
        if (amount > 0) {
          existing.income += amount;
          periodIncome += amount;
        } else {
          existing.expense += Math.abs(amount);
          periodExpense += Math.abs(amount);
        }
      }
      existing.count += 1;
      dailyMap.set(dateStr, existing);
    }

    // 转换为数组
    const dailyTrend = Array.from(dailyMap.entries())
      .sort(([a], [b]) => a.localeCompare(b))
      .map(([date, data]) => ({
        date,
        income: data.income,
        expense: data.expense,
        net: data.income - data.expense,
        count: data.count,
      }));

    return {
      dailyTrend,
      days,
      periodIncome,
      periodExpense,
      periodNet: periodIncome - periodExpense,
    };
  }

  /**
   * 获取流水类型中文名
   */
  private getEntryTypeName(entryType: string): string {
    const nameMap: Record<string, string> = {
      DEPOSIT_KAVA: '充值',
      DEPOSIT_BSC: '充值',
      PLANT_PAYMENT: '认种支付',
      PLANT_FREEZE: '认种冻结',
      PLANT_UNFREEZE: '认种解冻',
      REWARD_PENDING: '待领取奖励',
      REWARD_TO_SETTLEABLE: '分享收益',
      REWARD_PENDING_CONVERTED: '待领取转可结算',
      REWARD_EXPIRED: '奖励过期',
      REWARD_SETTLED: '提取',
      TRANSFER_TO_POOL: '转入矿池',
      SWAP_EXECUTED: '兑换执行',
      WITHDRAWAL: '提现',
      FIAT_WITHDRAWAL: '法币提现',
      SPECIAL_DEDUCTION: '特殊扣减',
      TRANSFER_IN: '转入',
      TRANSFER_OUT: '转出',
      FREEZE: '冻结',
      UNFREEZE: '解冻',
      SYSTEM_ALLOCATION: '系统分配',
    };
    return nameMap[entryType] || entryType;
  }

  // =============== 特殊扣减 (管理员操作) ===============

  /**
   * 特殊扣减命令
   */
  async executeSpecialDeduction(command: {
    accountSequence: string;
    amount: number;
    memo: string;
    operatorId: string;
  }): Promise<{ success: boolean; orderNo: string; newBalance: number }> {
    const MAX_RETRIES = 3;
    let retries = 0;

    while (retries < MAX_RETRIES) {
      try {
        return await this.doExecuteSpecialDeduction(command);
      } catch (error) {
        if (this.isOptimisticLockError(error)) {
          retries++;
          this.logger.warn(`[executeSpecialDeduction] Optimistic lock conflict for ${command.accountSequence}, retry ${retries}/${MAX_RETRIES}`);
          if (retries >= MAX_RETRIES) {
            this.logger.error(`[executeSpecialDeduction] Max retries exceeded for ${command.accountSequence}`);
            throw error;
          }
          await this.sleep(50 * retries);
        } else {
          throw error;
        }
      }
    }

    throw new Error('Unexpected: exited retry loop without result');
  }

  private async doExecuteSpecialDeduction(command: {
    accountSequence: string;
    amount: number;
    memo: string;
    operatorId: string;
  }): Promise<{ success: boolean; orderNo: string; newBalance: number }> {
    this.logger.log(`[executeSpecialDeduction] 执行特殊扣减: accountSequence=${command.accountSequence}, amount=${command.amount}`);

    // 验证金额
    if (command.amount <= 0) {
      throw new BadRequestException('扣减金额必须大于 0');
    }

    // 查找钱包
    const wallet = await this.walletRepo.findByAccountSequence(command.accountSequence);
    if (!wallet) {
      throw new WalletNotFoundError(`accountSequence: ${command.accountSequence}`);
    }

    // 验证余额是否足够
    const deductAmount = new (require('decimal.js').default)(command.amount);
    if (wallet.balances.usdt.available.value < command.amount) {
      throw new BadRequestException(
        `余额不足: 需要 ${command.amount} 绿积分, 当前可用 ${wallet.balances.usdt.available.value} 绿积分`,
      );
    }

    // 生成订单号
    const orderNo = this.generateSpecialDeductionOrderNo();

    // 使用事务处理
    let newBalance = 0;
    await this.prisma.$transaction(async (tx) => {
      const walletRecord = await tx.walletAccount.findUnique({
        where: { accountSequence: command.accountSequence },
      });

      if (!walletRecord) {
        throw new Error(`Wallet not found: ${command.accountSequence}`);
      }

      const currentAvailable = new (require('decimal.js').default)(walletRecord.usdtAvailable.toString());
      const currentVersion = walletRecord.version;

      if (currentAvailable.lessThan(deductAmount)) {
        throw new BadRequestException(`余额不足: 需要 ${command.amount} 绿积分, 当前可用 ${currentAvailable} 绿积分`);
      }

      const newAvailable = currentAvailable.minus(deductAmount);
      newBalance = newAvailable.toNumber();

      // 乐观锁更新
      const updateResult = await tx.walletAccount.updateMany({
        where: {
          id: walletRecord.id,
          version: currentVersion,
        },
        data: {
          usdtAvailable: newAvailable,
          version: currentVersion + 1,
          updatedAt: new Date(),
        },
      });

      if (updateResult.count === 0) {
        throw new OptimisticLockError(`Optimistic lock conflict for wallet ${walletRecord.id}`);
      }

      // 记录账本流水
      await tx.ledgerEntry.create({
        data: {
          accountSequence: command.accountSequence,
          userId: walletRecord.userId,
          entryType: LedgerEntryType.SPECIAL_DEDUCTION,
          amount: deductAmount.negated(),
          assetType: 'USDT',
          balanceAfter: newAvailable,
          refOrderId: orderNo,
          memo: `特殊扣减: ${command.memo}`,
          payloadJson: {
            operatorId: command.operatorId,
            originalAmount: command.amount,
            reason: command.memo,
            executedAt: new Date().toISOString(),
          },
        },
      });

      this.logger.log(`[executeSpecialDeduction] 成功扣减 ${command.amount} 绿积分, 新余额: ${newBalance}`);
    });

    // 清除缓存
    await this.walletCacheService.invalidateWallet(wallet.userId.value);

    return {
      success: true,
      orderNo,
      newBalance,
    };
  }

  /**
   * 生成特殊扣减订单号
   */
  private generateSpecialDeductionOrderNo(): string {
    const now = new Date();
    const dateStr = now.toISOString().slice(0, 10).replace(/-/g, '');
    const timeStr = now.toISOString().slice(11, 19).replace(/:/g, '');
    const random = Math.random().toString(36).substring(2, 8).toUpperCase();
    return `SD${dateStr}${timeStr}${random}`;
  }

  // =============== 全额线下结算扣减 ===============

  /**
   * 查询用户未处理的结算收益记录
   * 用于全额线下结算扣减模式：查询所有 REWARD_SETTLED 类型的流水，
   * 排除已被线下结算扣减过的记录
   */
  async getUnprocessedSettlements(accountSequence: string): Promise<{
    entries: Array<{
      id: string;  // BigInt 转换为 string 以支持 JSON 序列化
      amount: number;
      createdAt: Date;
      memo: string | null;
      allocationType: string | null;
    }>;
    totalAmount: number;
    count: number;
  }> {
    this.logger.log(`[getUnprocessedSettlements] 查询未处理结算记录: accountSequence=${accountSequence}`);

    // 查询所有 REWARD_SETTLED 类型的正向流水（入账）
    const settledEntries = await this.prisma.ledgerEntry.findMany({
      where: {
        accountSequence,
        entryType: LedgerEntryType.REWARD_SETTLED,
        amount: { gt: 0 }, // 只查询正向（入账）记录
      },
      orderBy: { createdAt: 'asc' },
    });

    // 查询已被线下结算扣减过的流水ID
    const processedEntryIds = await this.prisma.offlineSettlementDeduction.findMany({
      where: { accountSequence },
      select: { originalLedgerEntryId: true },
    });
    const processedIdSet = new Set(processedEntryIds.map(p => p.originalLedgerEntryId));

    // 过滤出未处理的记录
    const unprocessedEntries = settledEntries.filter(entry => !processedIdSet.has(entry.id));

    const Decimal = require('decimal.js').default;
    let totalAmount = new Decimal(0);
    const entries = unprocessedEntries.map(entry => {
      const amount = new Decimal(entry.amount.toString()).toNumber();
      totalAmount = totalAmount.plus(amount);
      // 从 payloadJson 中提取 allocationType
      const payloadJson = entry.payloadJson as Record<string, unknown> | null;
      const allocationType = payloadJson?.allocationType as string | null;
      return {
        id: entry.id.toString(),  // BigInt 需要转为 string 才能 JSON 序列化
        amount,
        createdAt: entry.createdAt,
        memo: entry.memo,
        allocationType,
      };
    });

    this.logger.log(`[getUnprocessedSettlements] 找到 ${entries.length} 条未处理记录, 总金额: ${totalAmount.toNumber()}`);

    return {
      entries,
      totalAmount: totalAmount.toNumber(),
      count: entries.length,
    };
  }

  /**
   * 执行全额线下结算扣减
   * 当 amount 为 0 或未提供时，自动扣减用户所有未处理的结算收益
   * 每笔结算记录生成一条扣减流水，并记录到 offline_settlement_deductions 表
   */
  async executeOfflineSettlementDeduction(command: {
    accountSequence: string;
    pendingActionId?: string;
    operatorId: string;
    customMemo?: string;  // 自定义备注，如果不提供则使用默认备注
  }): Promise<{
    success: boolean;
    totalDeducted: number;
    deductionCount: number;
    orderNos: string[];
    newBalance: number;
    details: Array<{
      originalEntryId: string;
      originalAmount: number;
      deductedAmount: number;
      orderNo: string;
    }>;
  }> {
    this.logger.log(`[executeOfflineSettlementDeduction] 执行全额线下结算扣减: accountSequence=${command.accountSequence}`);

    // 1. 查询未处理的结算记录
    const unprocessed = await this.getUnprocessedSettlements(command.accountSequence);

    if (unprocessed.count === 0) {
      this.logger.log(`[executeOfflineSettlementDeduction] 没有未处理的结算记录`);
      // 查询当前余额返回
      const wallet = await this.walletRepo.findByAccountSequence(command.accountSequence);
      return {
        success: true,
        totalDeducted: 0,
        deductionCount: 0,
        orderNos: [],
        newBalance: wallet?.balances.usdt.available.value ?? 0,
        details: [],
      };
    }

    // 2. 查找钱包
    const wallet = await this.walletRepo.findByAccountSequence(command.accountSequence);
    if (!wallet) {
      throw new WalletNotFoundError(`accountSequence: ${command.accountSequence}`);
    }

    const Decimal = require('decimal.js').default;
    const totalToDeduct = new Decimal(unprocessed.totalAmount);

    // 3. 验证余额是否足够（这里不做检查，因为是全额扣减，可能用户已经花费了部分）
    // 如果余额不足，扣减到0为止
    const currentAvailable = new Decimal(wallet.balances.usdt.available.value);
    const actualDeductTotal = Decimal.min(totalToDeduct, currentAvailable);

    if (actualDeductTotal.lte(0)) {
      this.logger.log(`[executeOfflineSettlementDeduction] 余额为0，无法扣减`);
      return {
        success: true,
        totalDeducted: 0,
        deductionCount: 0,
        orderNos: [],
        newBalance: 0,
        details: [],
      };
    }

    // 4. 生成主订单号（用于整批扣减）
    const batchOrderNo = this.generateOfflineSettlementBatchOrderNo();
    const defaultMemo = '该收益已由用户与上级间线下完成现金收付';
    const memo = command.customMemo || defaultMemo;

    // 5. 在事务中执行所有扣减
    const details: Array<{
      originalEntryId: string;
      originalAmount: number;
      deductedAmount: number;
      orderNo: string;
    }> = [];
    const orderNos: string[] = [];
    let newBalance = 0;
    let totalDeducted = new Decimal(0);
    let remainingBalance = currentAvailable;

    await this.prisma.$transaction(async (tx) => {
      // 获取钱包最新状态
      const walletRecord = await tx.walletAccount.findUnique({
        where: { accountSequence: command.accountSequence },
      });

      if (!walletRecord) {
        throw new Error(`Wallet not found: ${command.accountSequence}`);
      }

      remainingBalance = new Decimal(walletRecord.usdtAvailable.toString());
      const currentVersion = walletRecord.version;

      // 逐笔处理每条结算记录
      for (let i = 0; i < unprocessed.entries.length; i++) {
        const entry = unprocessed.entries[i];

        // 计算本笔实际扣减金额（不超过剩余余额）
        const entryAmount = new Decimal(entry.amount);
        const actualDeduct = Decimal.min(entryAmount, remainingBalance);

        if (actualDeduct.lte(0)) {
          this.logger.log(`[executeOfflineSettlementDeduction] 余额不足，跳过剩余记录`);
          break;
        }

        // 生成本笔扣减订单号
        const orderNo = `${batchOrderNo}-${String(i + 1).padStart(3, '0')}`;
        orderNos.push(orderNo);

        // 更新剩余余额
        remainingBalance = remainingBalance.minus(actualDeduct);
        totalDeducted = totalDeducted.plus(actualDeduct);

        // 构建扣减备注（包含原结算信息）
        const allocationTypeName = this.getAllocationTypeName(entry.allocationType);
        const deductionMemo = `线下结算扣减: ${memo} [原结算: ${allocationTypeName || '收益结算'}, 金额: ${entry.amount}, 时间: ${entry.createdAt.toISOString().slice(0, 10)}]`;

        // 创建扣减流水记录
        const ledgerEntry = await tx.ledgerEntry.create({
          data: {
            accountSequence: command.accountSequence,
            userId: walletRecord.userId,
            entryType: LedgerEntryType.SPECIAL_DEDUCTION,
            amount: actualDeduct.negated(), // 负数表示扣减
            assetType: 'USDT',
            balanceAfter: remainingBalance,
            refOrderId: orderNo,
            memo: deductionMemo,
            payloadJson: {
              operatorId: command.operatorId,
              pendingActionId: command.pendingActionId,
              originalLedgerEntryId: entry.id.toString(),
              originalAmount: entry.amount,
              deductedAmount: actualDeduct.toNumber(),
              reason: memo,
              isOfflineSettlement: true,
              batchOrderNo,
              executedAt: new Date().toISOString(),
            },
          },
        });

        // 创建线下结算扣减记录（标记该结算已被处理）
        await tx.offlineSettlementDeduction.create({
          data: {
            accountSequence: command.accountSequence,
            userId: walletRecord.userId,
            originalLedgerEntryId: BigInt(entry.id),  // 转回 BigInt 用于数据库存储
            originalAmount: entry.amount,
            deductedAmount: actualDeduct,
            deductionLedgerEntryId: ledgerEntry.id,
            deductionOrderNo: orderNo,
            pendingActionId: command.pendingActionId,
            operatorId: command.operatorId,
            memo: deductionMemo,
          },
        });

        details.push({
          originalEntryId: entry.id.toString(),
          originalAmount: entry.amount,
          deductedAmount: actualDeduct.toNumber(),
          orderNo,
        });
      }

      // 更新钱包余额
      newBalance = remainingBalance.toNumber();
      const updateResult = await tx.walletAccount.updateMany({
        where: {
          id: walletRecord.id,
          version: currentVersion,
        },
        data: {
          usdtAvailable: remainingBalance,
          version: currentVersion + 1,
          updatedAt: new Date(),
        },
      });

      if (updateResult.count === 0) {
        throw new OptimisticLockError(`Optimistic lock conflict for wallet ${walletRecord.id}`);
      }

      this.logger.log(`[executeOfflineSettlementDeduction] 成功扣减 ${details.length} 笔, 总金额: ${totalDeducted.toNumber()}, 新余额: ${newBalance}`);
    });

    // 6. 清除缓存
    await this.walletCacheService.invalidateWallet(wallet.userId.value);

    return {
      success: true,
      totalDeducted: totalDeducted.toNumber(),
      deductionCount: details.length,
      orderNos,
      newBalance,
      details,
    };
  }

  /**
   * 生成线下结算批次订单号
   */
  private generateOfflineSettlementBatchOrderNo(): string {
    const now = new Date();
    const dateStr = now.toISOString().slice(0, 10).replace(/-/g, '');
    const timeStr = now.toISOString().slice(11, 19).replace(/:/g, '');
    const random = Math.random().toString(36).substring(2, 6).toUpperCase();
    return `OSD${dateStr}${timeStr}${random}`;
  }

  /**
   * 获取权益分配类型的中文名称
   */
  private getAllocationTypeName(allocationType: string | null | undefined): string | null {
    if (!allocationType) return null;
    const nameMap: Record<string, string> = {
      'SHARE_RIGHT': '分享权益',
      'PROVINCE_AREA_RIGHT': '省区域权益',
      'PROVINCE_TEAM_RIGHT': '省团队权益',
      'CITY_AREA_RIGHT': '市区域权益',
      'CITY_TEAM_RIGHT': '市团队权益',
      'COMMUNITY_RIGHT': '社区权益',
    };
    return nameMap[allocationType] || allocationType;
  }

  // =============== 系统账户报表统计方法 ===============
  // [2026-01-04] 新增：用于 reporting-service 聚合系统账户报表数据
  // 回滚方式：删除以下方法即可

  /**
   * 获取面对面(线下)结算统计汇总
   * 用于系统账户报表展示面对面结算收益总额
   * [2026-01-06] 修改：从 wallet_ledger_entries 表查询 SPECIAL_DEDUCTION 类型
   * 回滚方式：恢复原来从 offline_settlement_deductions 表查询的逻辑
   */
  async getOfflineSettlementSummary(params: {
    startDate?: Date;
    endDate?: Date;
  }): Promise<{
    totalAmount: number;
    totalCount: number;
    byMonth: Array<{
      month: string;
      amount: number;
      count: number;
    }>;
  }> {
    this.logger.log(`[getOfflineSettlementSummary] 查询面对面结算统计（从 SPECIAL_DEDUCTION 流水）`);

    // 构建日期筛选条件，包含 entryType 过滤
    const whereClause: any = {
      entryType: 'SPECIAL_DEDUCTION',
    };
    if (params.startDate || params.endDate) {
      whereClause.createdAt = {};
      if (params.startDate) {
        whereClause.createdAt.gte = params.startDate;
      }
      if (params.endDate) {
        whereClause.createdAt.lte = params.endDate;
      }
    }

    // 查询总计（从 wallet_ledger_entries 表）
    const aggregateResult = await this.prisma.ledgerEntry.aggregate({
      where: whereClause,
      _sum: {
        amount: true,
      },
      _count: {
        id: true,
      },
    });

    // SPECIAL_DEDUCTION 的 amount 是负数，取绝对值
    const totalAmount = aggregateResult._sum.amount
      ? Math.abs(Number(aggregateResult._sum.amount))
      : 0;
    const totalCount = aggregateResult._count.id || 0;

    // 查询按月统计（从 wallet_ledger_entries 表，SPECIAL_DEDUCTION 类型）
    const byMonth = await this.prisma.$queryRaw<Array<{
      month: string;
      amount: any;
      count: any;
    }>>`
      SELECT
        TO_CHAR(created_at, 'YYYY-MM') as month,
        ABS(SUM(amount)) as amount,
        COUNT(*) as count
      FROM wallet_ledger_entries
      WHERE entry_type = 'SPECIAL_DEDUCTION'
      GROUP BY TO_CHAR(created_at, 'YYYY-MM')
      ORDER BY month DESC
      LIMIT 12
    `;

    this.logger.log(`[getOfflineSettlementSummary] 查询结果: totalAmount=${totalAmount}, totalCount=${totalCount}`);

    return {
      totalAmount,
      totalCount,
      byMonth: byMonth.map(row => ({
        month: row.month,
        amount: Number(row.amount) || 0,
        count: Number(row.count) || 0,
      })),
    };
  }

  // [2026-01-07] 新增：获取面对面结算明细列表
  // 回滚方式：删除此方法
  /**
   * 获取面对面结算明细列表
   * 查询 SPECIAL_DEDUCTION 类型的流水记录
   */
  async getOfflineSettlementEntries(params: {
    page?: number;
    pageSize?: number;
    startDate?: Date;
    endDate?: Date;
  }): Promise<{
    entries: Array<{
      id: string;
      accountSequence: string;
      amount: number;
      memo: string | null;
      refOrderId: string | null;
      createdAt: string;
    }>;
    total: number;
    page: number;
    pageSize: number;
    totalPages: number;
  }> {
    const page = params.page ?? 1;
    const pageSize = params.pageSize ?? 50;
    const skip = (page - 1) * pageSize;

    this.logger.log(`[getOfflineSettlementEntries] 查询面对面结算明细: page=${page}, pageSize=${pageSize}`);

    // 构建查询条件
    const where: any = {
      entryType: 'SPECIAL_DEDUCTION',
    };

    if (params.startDate || params.endDate) {
      where.createdAt = {};
      if (params.startDate) {
        where.createdAt.gte = params.startDate;
      }
      if (params.endDate) {
        where.createdAt.lte = params.endDate;
      }
    }

    // 查询总数
    const total = await this.prisma.ledgerEntry.count({ where });

    // 查询数据
    const entries = await this.prisma.ledgerEntry.findMany({
      where,
      orderBy: { createdAt: 'desc' },
      skip,
      take: pageSize,
      select: {
        id: true,
        accountSequence: true,
        amount: true,
        memo: true,
        refOrderId: true,
        createdAt: true,
      },
    });

    const totalPages = Math.ceil(total / pageSize);

    this.logger.log(`[getOfflineSettlementEntries] 查询结果: total=${total}, page=${page}, totalPages=${totalPages}`);

    return {
      entries: entries.map(entry => ({
        id: entry.id.toString(),
        accountSequence: entry.accountSequence,
        amount: Math.abs(Number(entry.amount)), // SPECIAL_DEDUCTION 金额是负数，取绝对值
        memo: entry.memo,
        refOrderId: entry.refOrderId,
        createdAt: entry.createdAt.toISOString(),
      })),
      total,
      page,
      pageSize,
      totalPages,
    };
  }

  /**
   * 批量查询系统账户余额
   * 用于系统账户报表展示固定账户(总部、成本、运营)的余额
   */
  async getSystemAccountsBalances(accountSequences: string[]): Promise<Array<{
    accountSequence: string;
    name: string;
    balance: number;
    totalReceived: number;
    createdAt: string;
  }>> {
    this.logger.log(`[getSystemAccountsBalances] 查询系统账户余额: ${accountSequences.join(', ')}`);

    if (accountSequences.length === 0) {
      return [];
    }

    // 系统账户名称映射
    const accountNames: Record<string, string> = {
      'S0000000001': '总部社区账户',
      'S0000000002': '成本费账户',
      'S0000000003': '运营费账户',
      'S0000000004': 'RWAD底池账户',
      'S0000000005': '分享权益池账户',
      'S0000000006': '手续费归集账户',
    };

    // 查询钱包账户
    const wallets = await this.prisma.walletAccount.findMany({
      where: {
        accountSequence: {
          in: accountSequences,
        },
      },
      select: {
        accountSequence: true,
        usdtAvailable: true,
        createdAt: true,
      },
    });

    // 查询每个账户的累计收入 (SYSTEM_ALLOCATION 类型的流水总和)
    const receivedStats = await this.prisma.ledgerEntry.groupBy({
      by: ['accountSequence'],
      where: {
        accountSequence: {
          in: accountSequences,
        },
        entryType: 'SYSTEM_ALLOCATION',
        amount: {
          gt: 0,
        },
      },
      _sum: {
        amount: true,
      },
    });

    // 构建结果
    const receivedMap = new Map(
      receivedStats.map(stat => [stat.accountSequence, Number(stat._sum.amount) || 0])
    );

    return wallets.map(wallet => ({
      accountSequence: wallet.accountSequence,
      name: accountNames[wallet.accountSequence] || `系统账户 ${wallet.accountSequence}`,
      balance: Number(wallet.usdtAvailable) || 0,
      totalReceived: receivedMap.get(wallet.accountSequence) || 0,
      createdAt: wallet.createdAt.toISOString(),
    }));
  }

  // =============== 系统账户报表统计 API - 增强版 ===============
  // [2026-01-05] 新增：获取所有系统账户列表（固定+区域）
  // [2026-01-07] 更新：账户余额改为 usdtAvailable + settleableUsdt，与累计收入统计保持一致
  // [2026-01-07] 更新：省/市区域账户增加 totalTransferred 字段，完善分类账统计
  // 回滚方式：删除以下方法

  async getAllSystemAccounts(): Promise<{
    fixedAccounts: Array<{ accountSequence: string; accountType: string; usdtBalance: string; totalReceived: string; totalTransferred: string; status: string; createdAt: string }>;
    provinceAccounts: Array<{ accountSequence: string; regionCode: string; regionName: string; usdtBalance: string; totalReceived: string; totalTransferred: string; status: string }>;
    cityAccounts: Array<{ accountSequence: string; regionCode: string; regionName: string; usdtBalance: string; totalReceived: string; totalTransferred: string; status: string }>;
  }> {
    this.logger.log('[getAllSystemAccounts] 查询所有系统账户...');
    const wallets = await this.prisma.walletAccount.findMany({
      where: { OR: [{ accountSequence: { startsWith: 'S' } }, { accountSequence: { startsWith: '9' } }, { accountSequence: { startsWith: '8' } }] },
      select: { accountSequence: true, usdtAvailable: true, settleableUsdt: true, status: true, createdAt: true },
    });
    const accountSeqs = wallets.map(w => w.accountSequence);
    const receivedStats2 = await this.prisma.ledgerEntry.groupBy({ by: ['accountSequence'], where: { accountSequence: { in: accountSeqs }, amount: { gt: 0 } }, _sum: { amount: true } });
    const transferredStats = await this.prisma.ledgerEntry.groupBy({ by: ['accountSequence'], where: { accountSequence: { in: accountSeqs }, amount: { lt: 0 } }, _sum: { amount: true } });
    const receivedMap2 = new Map(receivedStats2.map(s => [s.accountSequence, s._sum.amount]));
    const transferredMap = new Map(transferredStats.map(s => [s.accountSequence, s._sum.amount]));
    const fixedAccountTypes: Record<string, string> = { 'S0000000001': 'HQ_COMMUNITY', 'S0000000002': 'COST_ACCOUNT', 'S0000000003': 'OPERATION_ACCOUNT', 'S0000000004': 'RWAD_POOL_PENDING', 'S0000000005': 'SHARE_RIGHT_POOL', 'S0000000006': 'FEE_COLLECTION' };
    const fixedAccounts: Array<{ accountSequence: string; accountType: string; usdtBalance: string; totalReceived: string; totalTransferred: string; status: string; createdAt: string }> = [];
    const provinceAccounts: Array<{ accountSequence: string; regionCode: string; regionName: string; usdtBalance: string; totalReceived: string; totalTransferred: string; status: string }> = [];
    const cityAccounts: Array<{ accountSequence: string; regionCode: string; regionName: string; usdtBalance: string; totalReceived: string; totalTransferred: string; status: string }> = [];
    for (const wallet of wallets) {
      const seq = wallet.accountSequence;
      const received = Number(receivedMap2.get(seq) || 0);
      const transferred = Math.abs(Number(transferredMap.get(seq) || 0));
      // [2026-01-07] 账户余额 = usdtAvailable + settleableUsdt，与累计收入统计保持一致
      const totalBalance = Number(wallet.usdtAvailable || 0) + Number(wallet.settleableUsdt || 0);
      if (seq.startsWith('S')) {
        fixedAccounts.push({ accountSequence: seq, accountType: fixedAccountTypes[seq] || 'UNKNOWN', usdtBalance: String(totalBalance), totalReceived: String(received), totalTransferred: String(transferred), status: wallet.status, createdAt: wallet.createdAt.toISOString() });
      } else if (seq.startsWith('9')) {
        provinceAccounts.push({ accountSequence: seq, regionCode: seq.substring(1), regionName: '省区域 ' + seq.substring(1), usdtBalance: String(totalBalance), totalReceived: String(received), totalTransferred: String(transferred), status: wallet.status });
      } else if (seq.startsWith('8')) {
        cityAccounts.push({ accountSequence: seq, regionCode: seq.substring(1), regionName: '市区域 ' + seq.substring(1), usdtBalance: String(totalBalance), totalReceived: String(received), totalTransferred: String(transferred), status: wallet.status });
      }
    }
    this.logger.log('[getAllSystemAccounts] 固定: ' + fixedAccounts.length + ', 省: ' + provinceAccounts.length + ', 市: ' + cityAccounts.length);
    return { fixedAccounts, provinceAccounts, cityAccounts };
  }

  // =============== 手续费归集账户统计 API ===============
  // [2026-01-06] 新增：获取手续费归集账户 (S0000000006) 的统计数据
  // 用于系统账户报表中的"手续费账户汇总" Tab
  // 回滚方式：删除以下方法

  /**
   * 获取手续费归集账户汇总统计
   * 统计 S0000000006 账户收到的所有 FEE_COLLECTION 类型流水
   */
  async getFeeCollectionSummary(): Promise<{
    accountSequence: string;
    accountBalance: number;
    totalAmount: number;
    totalCount: number;
    breakdown: Array<{
      feeType: string;
      amount: number;
      count: number;
    }>;
    byMonth: Array<{
      month: string;
      amount: number;
      count: number;
    }>;
  }> {
    this.logger.log('[getFeeCollectionSummary] 查询手续费归集账户统计');

    const feeAccountSequence = WalletApplicationService.FEE_COLLECTION_ACCOUNT;

    // 1. 获取账户余额
    const wallet = await this.prisma.walletAccount.findUnique({
      where: { accountSequence: feeAccountSequence },
      select: { usdtAvailable: true },
    });
    const accountBalance = Number(wallet?.usdtAvailable) || 0;

    // 2. 统计所有 FEE_COLLECTION 类型的流水总额和总笔数
    const aggregateResult = await this.prisma.ledgerEntry.aggregate({
      where: {
        accountSequence: feeAccountSequence,
        entryType: 'FEE_COLLECTION',
      },
      _sum: {
        amount: true,
      },
      _count: {
        id: true,
      },
    });

    const totalAmount = Number(aggregateResult._sum.amount) || 0;
    const totalCount = aggregateResult._count.id || 0;

    // 3. 按手续费类型分组统计（从 payloadJson 中提取 feeType）
    // 由于 Prisma 不支持直接 groupBy jsonb 字段，使用原生 SQL
    const breakdownRaw = await this.prisma.$queryRaw<Array<{
      feeType: string;
      amount: any;
      count: any;
    }>>`
      SELECT
        COALESCE(payload_json->>'feeType', 'UNKNOWN') as "feeType",
        SUM(amount) as amount,
        COUNT(*) as count
      FROM wallet_ledger_entries
      WHERE account_sequence = ${feeAccountSequence}
        AND entry_type = 'FEE_COLLECTION'
      GROUP BY COALESCE(payload_json->>'feeType', 'UNKNOWN')
      ORDER BY amount DESC
    `;

    // 4. 按月统计
    const byMonthRaw = await this.prisma.$queryRaw<Array<{
      month: string;
      amount: any;
      count: any;
    }>>`
      SELECT
        TO_CHAR(created_at, 'YYYY-MM') as month,
        SUM(amount) as amount,
        COUNT(*) as count
      FROM wallet_ledger_entries
      WHERE account_sequence = ${feeAccountSequence}
        AND entry_type = 'FEE_COLLECTION'
      GROUP BY TO_CHAR(created_at, 'YYYY-MM')
      ORDER BY month DESC
      LIMIT 12
    `;

    this.logger.log(`[getFeeCollectionSummary] 统计完成: totalAmount=${totalAmount}, totalCount=${totalCount}`);

    // [2026-01-07] 兼容：如果 FEE_COLLECTION 流水为空，从提现订单表查询历史手续费
    // 回滚方式：删除此 fallback 代码块
    if (totalCount === 0) {
      this.logger.log('[getFeeCollectionSummary] FEE_COLLECTION 流水为空，使用提现订单表作为 fallback');
      return this.getFeeCollectionSummaryFromOrders(feeAccountSequence, accountBalance);
    }

    return {
      accountSequence: feeAccountSequence,
      accountBalance,
      totalAmount,
      totalCount,
      breakdown: breakdownRaw.map(row => ({
        feeType: row.feeType,
        amount: Number(row.amount) || 0,
        count: Number(row.count) || 0,
      })),
      byMonth: byMonthRaw.map(row => ({
        month: row.month,
        amount: Number(row.amount) || 0,
        count: Number(row.count) || 0,
      })),
    };
  }

  // [2026-01-07] 新增：从提现订单表查询历史手续费（兼容旧数据）
  // 回滚方式：删除此方法
  private async getFeeCollectionSummaryFromOrders(
    accountSequence: string,
    accountBalance: number,
  ): Promise<{
    accountSequence: string;
    accountBalance: number;
    totalAmount: number;
    totalCount: number;
    breakdown: Array<{ feeType: string; amount: number; count: number }>;
    byMonth: Array<{ month: string; amount: number; count: number }>;
  }> {
    // 1. 统计区块链提现订单手续费 (CONFIRMED 状态)
    const withdrawalStats = await this.prisma.withdrawalOrder.aggregate({
      where: { status: 'CONFIRMED' },
      _sum: { fee: true },
      _count: { id: true },
    });

    // 2. 统计法币提现订单手续费 (COMPLETED 状态)
    const fiatStats = await this.prisma.fiatWithdrawalOrder.aggregate({
      where: { status: 'COMPLETED' },
      _sum: { fee: true },
      _count: { id: true },
    });

    const withdrawalFeeAmount = Number(withdrawalStats._sum.fee) || 0;
    const withdrawalFeeCount = withdrawalStats._count.id || 0;
    const fiatFeeAmount = Number(fiatStats._sum.fee) || 0;
    const fiatFeeCount = fiatStats._count.id || 0;

    const totalAmount = withdrawalFeeAmount + fiatFeeAmount;
    const totalCount = withdrawalFeeCount + fiatFeeCount;

    // 3. 构建 breakdown
    const breakdown: Array<{ feeType: string; amount: number; count: number }> = [];
    if (withdrawalFeeCount > 0) {
      breakdown.push({
        feeType: 'WITHDRAWAL_FEE',
        amount: withdrawalFeeAmount,
        count: withdrawalFeeCount,
      });
    }
    if (fiatFeeCount > 0) {
      breakdown.push({
        feeType: 'FIAT_WITHDRAWAL_FEE',
        amount: fiatFeeAmount,
        count: fiatFeeCount,
      });
    }

    // 4. 按月统计（合并两种提现订单）
    const byMonthRaw = await this.prisma.$queryRaw<Array<{
      month: string;
      amount: any;
      count: any;
    }>>`
      SELECT month, SUM(amount) as amount, SUM(count) as count
      FROM (
        SELECT TO_CHAR(confirmed_at, 'YYYY-MM') as month, SUM(fee) as amount, COUNT(*) as count
        FROM withdrawal_orders
        WHERE status = 'CONFIRMED' AND confirmed_at IS NOT NULL
        GROUP BY TO_CHAR(confirmed_at, 'YYYY-MM')
        UNION ALL
        SELECT TO_CHAR(completed_at, 'YYYY-MM') as month, SUM(fee) as amount, COUNT(*) as count
        FROM fiat_withdrawal_orders
        WHERE status = 'COMPLETED' AND completed_at IS NOT NULL
        GROUP BY TO_CHAR(completed_at, 'YYYY-MM')
      ) combined
      GROUP BY month
      ORDER BY month DESC
      LIMIT 12
    `;

    this.logger.log(`[getFeeCollectionSummaryFromOrders] Fallback 统计完成: totalAmount=${totalAmount}, totalCount=${totalCount}`);

    return {
      accountSequence,
      accountBalance,
      totalAmount,
      totalCount,
      breakdown,
      byMonth: byMonthRaw.map(row => ({
        month: row.month,
        amount: Number(row.amount) || 0,
        count: Number(row.count) || 0,
      })),
    };
  }

  /**
   * 获取手续费归集账户的详细流水列表
   */
  async getFeeCollectionEntries(params: {
    page?: number;
    pageSize?: number;
    feeType?: string;
    startDate?: Date;
    endDate?: Date;
  }): Promise<{
    entries: Array<{
      id: string;
      feeType: string;
      amount: number;
      refOrderId: string | null;
      memo: string | null;
      createdAt: string;
    }>;
    total: number;
    page: number;
    pageSize: number;
    totalPages: number;
  }> {
    const page = params.page ?? 1;
    const pageSize = params.pageSize ?? 50;
    const skip = (page - 1) * pageSize;
    const feeAccountSequence = WalletApplicationService.FEE_COLLECTION_ACCOUNT;

    this.logger.log(`[getFeeCollectionEntries] 查询手续费归集流水: page=${page}, pageSize=${pageSize}, feeType=${params.feeType}`);

    // 构建查询条件
    const where: any = {
      accountSequence: feeAccountSequence,
      entryType: 'FEE_COLLECTION',
    };

    if (params.startDate || params.endDate) {
      where.createdAt = {};
      if (params.startDate) {
        where.createdAt.gte = params.startDate;
      }
      if (params.endDate) {
        where.createdAt.lte = params.endDate;
      }
    }

    // 查询总数
    const total = await this.prisma.ledgerEntry.count({ where });

    // 查询数据
    const entries = await this.prisma.ledgerEntry.findMany({
      where,
      orderBy: { createdAt: 'desc' },
      skip,
      take: pageSize,
      select: {
        id: true,
        amount: true,
        refOrderId: true,
        memo: true,
        payloadJson: true,
        createdAt: true,
      },
    });

    // 如果指定了 feeType，在应用层过滤（因为 Prisma 不支持 jsonb 字段的 where 条件）
    let filteredEntries = entries;
    if (params.feeType) {
      filteredEntries = entries.filter(entry => {
        const payload = entry.payloadJson as any;
        return payload?.feeType === params.feeType;
      });
    }

    // [2026-01-07] 兼容：如果 FEE_COLLECTION 流水为空，从提现订单表查询历史手续费
    // 回滚方式：删除此 fallback 代码块
    if (total === 0) {
      this.logger.log('[getFeeCollectionEntries] FEE_COLLECTION 流水为空，使用提现订单表作为 fallback');
      return this.getFeeCollectionEntriesFromOrders(params);
    }

    return {
      entries: filteredEntries.map(entry => {
        const payload = entry.payloadJson as any;
        return {
          id: entry.id.toString(),
          feeType: payload?.feeType || 'UNKNOWN',
          amount: Number(entry.amount),
          refOrderId: entry.refOrderId,
          memo: entry.memo,
          createdAt: entry.createdAt.toISOString(),
        };
      }),
      total,
      page,
      pageSize,
      totalPages: Math.ceil(total / pageSize),
    };
  }

  // [2026-01-07] 新增：从提现订单表查询历史手续费明细（兼容旧数据）
  // 回滚方式：删除此方法
  private async getFeeCollectionEntriesFromOrders(params: {
    page?: number;
    pageSize?: number;
    feeType?: string;
    startDate?: Date;
    endDate?: Date;
  }): Promise<{
    entries: Array<{
      id: string;
      feeType: string;
      amount: number;
      refOrderId: string | null;
      memo: string | null;
      createdAt: string;
    }>;
    total: number;
    page: number;
    pageSize: number;
    totalPages: number;
  }> {
    const page = params.page ?? 1;
    const pageSize = params.pageSize ?? 50;
    const skip = (page - 1) * pageSize;

    // 构建日期过滤条件
    const dateFilter: any = {};
    if (params.startDate) {
      dateFilter.gte = params.startDate;
    }
    if (params.endDate) {
      dateFilter.lte = params.endDate;
    }

    // 根据 feeType 决定查询哪些表
    const shouldQueryWithdrawal = !params.feeType || params.feeType === 'WITHDRAWAL_FEE';
    const shouldQueryFiat = !params.feeType || params.feeType === 'FIAT_WITHDRAWAL_FEE';

    // 从两种订单表查询数据
    const withdrawalEntries: Array<{
      id: string;
      feeType: string;
      amount: number;
      refOrderId: string | null;
      memo: string | null;
      createdAt: Date;
    }> = [];
    const fiatEntries: Array<{
      id: string;
      feeType: string;
      amount: number;
      refOrderId: string | null;
      memo: string | null;
      createdAt: Date;
    }> = [];

    if (shouldQueryWithdrawal) {
      const withdrawalWhere: any = { status: 'CONFIRMED' };
      if (Object.keys(dateFilter).length > 0) {
        withdrawalWhere.confirmedAt = dateFilter;
      }
      const withdrawals = await this.prisma.withdrawalOrder.findMany({
        where: withdrawalWhere,
        orderBy: { confirmedAt: 'desc' },
        select: {
          id: true,
          orderNo: true,
          fee: true,
          toAddress: true,
          chainType: true,
          confirmedAt: true,
        },
      });
      withdrawalEntries.push(...withdrawals.map(w => ({
        id: `W-${w.id.toString()}`,
        feeType: 'WITHDRAWAL_FEE',
        amount: Number(w.fee),
        refOrderId: w.orderNo,
        memo: `区块链提现手续费 (${w.chainType})`,
        createdAt: w.confirmedAt!,
      })));
    }

    if (shouldQueryFiat) {
      const fiatWhere: any = { status: 'COMPLETED' };
      if (Object.keys(dateFilter).length > 0) {
        fiatWhere.completedAt = dateFilter;
      }
      const fiatOrders = await this.prisma.fiatWithdrawalOrder.findMany({
        where: fiatWhere,
        orderBy: { completedAt: 'desc' },
        select: {
          id: true,
          orderNo: true,
          fee: true,
          paymentMethod: true,
          completedAt: true,
        },
      });
      fiatEntries.push(...fiatOrders.map(f => ({
        id: `F-${f.id.toString()}`,
        feeType: 'FIAT_WITHDRAWAL_FEE',
        amount: Number(f.fee),
        refOrderId: f.orderNo,
        memo: `法币提现手续费 (${f.paymentMethod})`,
        createdAt: f.completedAt!,
      })));
    }

    // 合并并按时间排序
    const allEntries = [...withdrawalEntries, ...fiatEntries]
      .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());

    const total = allEntries.length;
    const paginatedEntries = allEntries.slice(skip, skip + pageSize);

    this.logger.log(`[getFeeCollectionEntriesFromOrders] Fallback 查询完成: total=${total}, page=${page}`);

    return {
      entries: paginatedEntries.map(entry => ({
        ...entry,
        createdAt: entry.createdAt.toISOString(),
      })),
      total,
      page,
      pageSize,
      totalPages: Math.ceil(total / pageSize),
    };
  }

  // =============== 钱包预创建 API ===============
  // [2026-01-08] 新增：用户注册时预创建钱包，确保内部转账时接收方钱包一定存在
  // 回滚方式：删除此方法即可

  /**
   * 确保用户钱包存在（幂等操作）
   *
   * 此方法用于：
   * 1. 用户注册时预创建钱包（主要保障）
   * 2. 内部转账前预校验并创建接收方钱包（兜底保障）
   *
   * 使用 upsert 确保幂等性，避免并发创建冲突
   *
   * @param accountSequence 账户序列号（跨服务唯一标识）
   * @param userId 用户ID
   * @returns 钱包是否已存在（existed）和当前钱包ID
   */
  async ensureWalletExists(params: {
    accountSequence: string;
    userId: string;
  }): Promise<{
    existed: boolean;
    walletId: string;
    accountSequence: string;
  }> {
    const { accountSequence, userId } = params;
    const userIdBigint = BigInt(userId);

    this.logger.log(`[ensureWalletExists] 确保钱包存在: accountSequence=${accountSequence}, userId=${userId}`);

    // 先检查是否已存在
    const existingWallet = await this.prisma.walletAccount.findUnique({
      where: { accountSequence },
    });

    if (existingWallet) {
      this.logger.log(`[ensureWalletExists] 钱包已存在: id=${existingWallet.id}, accountSequence=${accountSequence}`);
      return {
        existed: true,
        walletId: existingWallet.id.toString(),
        accountSequence,
      };
    }

    // 使用 upsert 创建钱包（避免并发冲突）
    const wallet = await this.prisma.walletAccount.upsert({
      where: { accountSequence },
      create: {
        accountSequence,
        userId: userIdBigint,
        usdtAvailable: 0,
        usdtFrozen: 0,
        dstAvailable: 0,
        dstFrozen: 0,
        bnbAvailable: 0,
        bnbFrozen: 0,
        ogAvailable: 0,
        ogFrozen: 0,
        rwadAvailable: 0,
        rwadFrozen: 0,
        hashpower: 0,
        pendingUsdt: 0,
        pendingHashpower: 0,
        settleableUsdt: 0,
        settleableHashpower: 0,
        settledTotalUsdt: 0,
        settledTotalHashpower: 0,
        expiredTotalUsdt: 0,
        expiredTotalHashpower: 0,
        status: 'ACTIVE',
        hasPlanted: false,
        version: 0,
      },
      update: {}, // 如果已存在，不做任何更新
    });

    // 判断是新创建还是已存在
    const wasCreated = wallet.createdAt.getTime() > Date.now() - 1000; // 1秒内创建的认为是新创建

    this.logger.log(`[ensureWalletExists] ${wasCreated ? '新创建' : '已存在'}钱包: id=${wallet.id}, accountSequence=${accountSequence}`);

    return {
      existed: !wasCreated,
      walletId: wallet.id.toString(),
      accountSequence,
    };
  }

  /**
   * 批量确保钱包存在（用于批量操作场景）
   */
  async ensureWalletsExist(params: Array<{
    accountSequence: string;
    userId: string;
  }>): Promise<Array<{
    existed: boolean;
    walletId: string;
    accountSequence: string;
  }>> {
    const results: Array<{
      existed: boolean;
      walletId: string;
      accountSequence: string;
    }> = [];

    for (const param of params) {
      const result = await this.ensureWalletExists(param);
      results.push(result);
    }

    return results;
  }

  // ============================================================
  // [2026-02-19] 纯新增：树转让钱包操作（3 个方法）
  // 供 transfer-service Saga 编排器通过 HTTP 调用
  // 回滚方式：删除以下 3 对方法 + 移除 import
  // ============================================================

  /**
   * 转让冻结：冻结买方 USDT（从 available 转到 frozen）
   * 幂等：同一 transferOrderNo 重复调用返回成功
   */
  async freezeForTransfer(command: FreezeForTransferCommand): Promise<{ freezeId: string }> {
    const MAX_RETRIES = 3;
    let retries = 0;

    while (retries < MAX_RETRIES) {
      try {
        return await this.executeFreezeForTransfer(command);
      } catch (error) {
        if (this.isOptimisticLockError(error)) {
          retries++;
          this.logger.warn(`[freezeForTransfer] Optimistic lock conflict for ${command.transferOrderNo}, retry ${retries}/${MAX_RETRIES}`);
          if (retries >= MAX_RETRIES) throw error;
          await this.sleep(50 * retries);
        } else {
          throw error;
        }
      }
    }
    throw new Error('Unexpected: exited retry loop without result');
  }

  private async executeFreezeForTransfer(command: FreezeForTransferCommand): Promise<{ freezeId: string }> {
    const Decimal = (await import('decimal.js')).default;
    const amountDecimal = new Decimal(command.amount);
    let walletUserId: bigint | null = null;

    this.logger.log(`[freezeForTransfer] 开始处理: accountSequence=${command.accountSequence}, amount=${command.amount}, transferOrderNo=${command.transferOrderNo}`);

    await this.prisma.$transaction(async (tx) => {
      // 幂等性检查
      const existingEntry = await tx.ledgerEntry.findFirst({
        where: {
          refOrderId: command.transferOrderNo,
          entryType: LedgerEntryType.FREEZE,
        },
      });

      if (existingEntry) {
        this.logger.warn(`[freezeForTransfer] Transfer ${command.transferOrderNo} already frozen (idempotent)`);
        return;
      }

      // 查找钱包
      const walletRecord = await tx.walletAccount.findUnique({
        where: { accountSequence: command.accountSequence },
      });

      if (!walletRecord) {
        throw new WalletNotFoundError(`accountSequence: ${command.accountSequence}`);
      }

      walletUserId = walletRecord.userId;
      const currentAvailable = new Decimal(walletRecord.usdtAvailable.toString());
      const currentFrozen = new Decimal(walletRecord.usdtFrozen.toString());
      const currentVersion = walletRecord.version;

      // 余额检查
      if (currentAvailable.lessThan(amountDecimal)) {
        throw new BadRequestException(
          `余额不足: 需要 ${command.amount} USDT, 可用 ${currentAvailable.toString()} USDT`,
        );
      }

      const newAvailable = currentAvailable.minus(amountDecimal);
      const newFrozen = currentFrozen.plus(amountDecimal);

      // 乐观锁更新
      const updateResult = await tx.walletAccount.updateMany({
        where: { id: walletRecord.id, version: currentVersion },
        data: {
          usdtAvailable: newAvailable,
          usdtFrozen: newFrozen,
          version: currentVersion + 1,
          updatedAt: new Date(),
        },
      });

      if (updateResult.count === 0) {
        throw new OptimisticLockError(`Optimistic lock conflict for wallet ${walletRecord.id}`);
      }

      // 冻结流水
      await tx.ledgerEntry.create({
        data: {
          accountSequence: walletRecord.accountSequence,
          userId: walletRecord.userId,
          entryType: LedgerEntryType.FREEZE,
          amount: amountDecimal.negated(),
          assetType: 'USDT',
          balanceAfter: newAvailable,
          refOrderId: command.transferOrderNo,
          memo: `Transfer freeze: ${command.reason}`,
        },
      });

      this.logger.log(`[freezeForTransfer] 成功冻结 ${command.amount} USDT for ${command.transferOrderNo}`);
    });

    if (walletUserId) {
      await this.walletCacheService.invalidateWallet(walletUserId);
    }

    return { freezeId: command.transferOrderNo };
  }

  /**
   * 转让解冻：将冻结金额恢复到可用（补偿/回滚）
   * 幂等：同一 transferOrderNo 重复调用返回成功
   */
  async unfreezeForTransfer(command: UnfreezeForTransferCommand): Promise<void> {
    const MAX_RETRIES = 3;
    let retries = 0;

    while (retries < MAX_RETRIES) {
      try {
        await this.executeUnfreezeForTransfer(command);
        return;
      } catch (error) {
        if (this.isOptimisticLockError(error)) {
          retries++;
          this.logger.warn(`[unfreezeForTransfer] Optimistic lock conflict for ${command.transferOrderNo}, retry ${retries}/${MAX_RETRIES}`);
          if (retries >= MAX_RETRIES) throw error;
          await this.sleep(50 * retries);
        } else {
          throw error;
        }
      }
    }
    throw new Error('Unexpected: exited retry loop without result');
  }

  private async executeUnfreezeForTransfer(command: UnfreezeForTransferCommand): Promise<void> {
    const Decimal = (await import('decimal.js')).default;
    let walletUserId: bigint | null = null;

    this.logger.log(`[unfreezeForTransfer] 开始处理: accountSequence=${command.accountSequence}, transferOrderNo=${command.transferOrderNo}`);

    await this.prisma.$transaction(async (tx) => {
      // 幂等性检查
      const existingUnfreeze = await tx.ledgerEntry.findFirst({
        where: {
          refOrderId: command.transferOrderNo,
          entryType: LedgerEntryType.UNFREEZE,
        },
      });

      if (existingUnfreeze) {
        this.logger.warn(`[unfreezeForTransfer] Transfer ${command.transferOrderNo} already unfrozen (idempotent)`);
        return;
      }

      // 安全检查：已结算则不可解冻
      const existingSettle = await tx.ledgerEntry.findFirst({
        where: {
          refOrderId: command.transferOrderNo,
          entryType: LedgerEntryType.TRANSFER_OUT,
        },
      });

      if (existingSettle) {
        throw new BadRequestException(`转让 ${command.transferOrderNo} 已结算，无法解冻`);
      }

      // 查找原始冻结记录
      const freezeEntry = await tx.ledgerEntry.findFirst({
        where: {
          refOrderId: command.transferOrderNo,
          entryType: LedgerEntryType.FREEZE,
        },
      });

      if (!freezeEntry) {
        this.logger.warn(`[unfreezeForTransfer] Transfer ${command.transferOrderNo} no freeze record, returning success`);
        return;
      }

      const frozenAmount = new Decimal(freezeEntry.amount.toString()).abs();

      // 查找钱包
      const walletRecord = await tx.walletAccount.findUnique({
        where: { accountSequence: command.accountSequence },
      });

      if (!walletRecord) {
        throw new WalletNotFoundError(`accountSequence: ${command.accountSequence}`);
      }

      walletUserId = walletRecord.userId;
      const currentAvailable = new Decimal(walletRecord.usdtAvailable.toString());
      const currentFrozen = new Decimal(walletRecord.usdtFrozen.toString());
      const currentVersion = walletRecord.version;

      const newAvailable = currentAvailable.plus(frozenAmount);
      const newFrozen = currentFrozen.minus(frozenAmount);

      // 乐观锁更新
      const updateResult = await tx.walletAccount.updateMany({
        where: { id: walletRecord.id, version: currentVersion },
        data: {
          usdtAvailable: newAvailable,
          usdtFrozen: newFrozen,
          version: currentVersion + 1,
          updatedAt: new Date(),
        },
      });

      if (updateResult.count === 0) {
        throw new OptimisticLockError(`Optimistic lock conflict for wallet ${walletRecord.id}`);
      }

      // 解冻流水
      await tx.ledgerEntry.create({
        data: {
          accountSequence: walletRecord.accountSequence,
          userId: walletRecord.userId,
          entryType: LedgerEntryType.UNFREEZE,
          amount: frozenAmount,
          assetType: 'USDT',
          balanceAfter: newAvailable,
          refOrderId: command.transferOrderNo,
          memo: `Transfer unfreeze: ${command.reason}`,
        },
      });

      this.logger.log(`[unfreezeForTransfer] 成功解冻 ${frozenAmount.toString()} USDT for ${command.transferOrderNo}`);
    });

    if (walletUserId) {
      await this.walletCacheService.invalidateWallet(walletUserId);
    }
  }

  /**
   * 转让结算：买方冻结扣减 → 卖方入账 → 手续费归集
   * 单事务操作 3 个钱包（买方 + 卖方 + 手续费账户）
   * 幂等：同一 transferOrderNo 重复调用返回成功
   */
  async settleTransferPayment(command: SettleTransferPaymentCommand): Promise<void> {
    const MAX_RETRIES = 3;
    let retries = 0;

    while (retries < MAX_RETRIES) {
      try {
        await this.executeSettleTransferPayment(command);
        return;
      } catch (error) {
        if (this.isOptimisticLockError(error)) {
          retries++;
          this.logger.warn(`[settleTransferPayment] Optimistic lock conflict for ${command.transferOrderNo}, retry ${retries}/${MAX_RETRIES}`);
          if (retries >= MAX_RETRIES) throw error;
          await this.sleep(50 * retries);
        } else {
          throw error;
        }
      }
    }
    throw new Error('Unexpected: exited retry loop without result');
  }

  private async executeSettleTransferPayment(command: SettleTransferPaymentCommand): Promise<void> {
    const Decimal = (await import('decimal.js')).default;
    const sellerAmount = new Decimal(command.sellerReceiveAmount);
    const feeAmount = new Decimal(command.platformFeeAmount);
    const totalDeduct = sellerAmount.plus(feeAmount);
    const affectedUserIds: bigint[] = [];

    this.logger.log(`[settleTransferPayment] 开始结算: transferOrderNo=${command.transferOrderNo}`);
    this.logger.log(`[settleTransferPayment]   buyer=${command.buyerAccountSequence}, seller=${command.sellerAccountSequence}`);
    this.logger.log(`[settleTransferPayment]   sellerReceive=${command.sellerReceiveAmount}, fee=${command.platformFeeAmount}`);

    await this.prisma.$transaction(async (tx) => {
      // 幂等性检查
      const existingSettle = await tx.ledgerEntry.findFirst({
        where: {
          refOrderId: command.transferOrderNo,
          entryType: LedgerEntryType.TRANSFER_OUT,
        },
      });

      if (existingSettle) {
        this.logger.warn(`[settleTransferPayment] Transfer ${command.transferOrderNo} already settled (idempotent)`);
        return;
      }

      // ===== 1. 买方：从冻结余额扣减 =====
      const buyerWallet = await tx.walletAccount.findUnique({
        where: { accountSequence: command.buyerAccountSequence },
      });

      if (!buyerWallet) {
        throw new WalletNotFoundError(`buyer accountSequence: ${command.buyerAccountSequence}`);
      }

      const buyerFrozen = new Decimal(buyerWallet.usdtFrozen.toString());
      if (buyerFrozen.lessThan(totalDeduct)) {
        throw new BadRequestException(
          `买方冻结余额不足: 需要 ${totalDeduct.toString()} USDT, 冻结 ${buyerFrozen.toString()} USDT`,
        );
      }

      const newBuyerFrozen = buyerFrozen.minus(totalDeduct);
      const buyerUpdateResult = await tx.walletAccount.updateMany({
        where: { id: buyerWallet.id, version: buyerWallet.version },
        data: {
          usdtFrozen: newBuyerFrozen,
          version: buyerWallet.version + 1,
          updatedAt: new Date(),
        },
      });

      if (buyerUpdateResult.count === 0) {
        throw new OptimisticLockError(`Optimistic lock conflict for buyer wallet ${buyerWallet.id}`);
      }

      affectedUserIds.push(buyerWallet.userId);

      // 买方扣款流水
      await tx.ledgerEntry.create({
        data: {
          accountSequence: buyerWallet.accountSequence,
          userId: buyerWallet.userId,
          entryType: LedgerEntryType.TRANSFER_OUT,
          amount: totalDeduct.negated(),
          assetType: 'USDT',
          balanceAfter: new Decimal(buyerWallet.usdtAvailable.toString()),
          refOrderId: command.transferOrderNo,
          memo: `Tree transfer payment to ${command.sellerAccountSequence}`,
        },
      });

      // ===== 2. 卖方：可用余额增加 =====
      const sellerWallet = await tx.walletAccount.findUnique({
        where: { accountSequence: command.sellerAccountSequence },
      });

      if (!sellerWallet) {
        throw new WalletNotFoundError(`seller accountSequence: ${command.sellerAccountSequence}`);
      }

      const sellerAvailable = new Decimal(sellerWallet.usdtAvailable.toString());
      const newSellerAvailable = sellerAvailable.plus(sellerAmount);

      const sellerUpdateResult = await tx.walletAccount.updateMany({
        where: { id: sellerWallet.id, version: sellerWallet.version },
        data: {
          usdtAvailable: newSellerAvailable,
          version: sellerWallet.version + 1,
          updatedAt: new Date(),
        },
      });

      if (sellerUpdateResult.count === 0) {
        throw new OptimisticLockError(`Optimistic lock conflict for seller wallet ${sellerWallet.id}`);
      }

      affectedUserIds.push(sellerWallet.userId);

      // 卖方入账流水
      await tx.ledgerEntry.create({
        data: {
          accountSequence: sellerWallet.accountSequence,
          userId: sellerWallet.userId,
          entryType: LedgerEntryType.TRANSFER_IN,
          amount: sellerAmount,
          assetType: 'USDT',
          balanceAfter: newSellerAvailable,
          refOrderId: command.transferOrderNo,
          memo: `Tree transfer received from ${command.buyerAccountSequence}`,
        },
      });

      // ===== 3. 手续费归集账户 =====
      if (feeAmount.greaterThan(0)) {
        const feeAccountSequence = 'S0000000001'; // 总部账户
        const feeWallet = await tx.walletAccount.findUnique({
          where: { accountSequence: feeAccountSequence },
        });

        if (feeWallet) {
          const feeAvailable = new Decimal(feeWallet.usdtAvailable.toString());
          const newFeeAvailable = feeAvailable.plus(feeAmount);

          const feeUpdateResult = await tx.walletAccount.updateMany({
            where: { id: feeWallet.id, version: feeWallet.version },
            data: {
              usdtAvailable: newFeeAvailable,
              version: feeWallet.version + 1,
              updatedAt: new Date(),
            },
          });

          if (feeUpdateResult.count === 0) {
            throw new OptimisticLockError(`Optimistic lock conflict for fee wallet ${feeWallet.id}`);
          }

          affectedUserIds.push(feeWallet.userId);

          // 手续费流水
          await tx.ledgerEntry.create({
            data: {
              accountSequence: feeAccountSequence,
              userId: feeWallet.userId,
              entryType: LedgerEntryType.FEE_COLLECTION,
              amount: feeAmount,
              assetType: 'USDT',
              balanceAfter: newFeeAvailable,
              refOrderId: command.transferOrderNo,
              memo: `Transfer platform fee from ${command.buyerAccountSequence}`,
            },
          });
        } else {
          this.logger.warn(`[settleTransferPayment] Fee account ${feeAccountSequence} not found, skipping fee collection`);
        }
      }

      this.logger.log(`[settleTransferPayment] 结算成功: buyer frozen -${totalDeduct}, seller +${sellerAmount}, fee +${feeAmount}`);
    });

    // 事务成功后，使所有受影响钱包的缓存失效
    for (const userId of affectedUserIds) {
      await this.walletCacheService.invalidateWallet(userId);
    }

    this.logger.log(`[settleTransferPayment] 转让 ${command.transferOrderNo} 结算完成`);
  }
}
