import { Injectable, Inject, Logger } from '@nestjs/common'; import { AutoCreateAccountCommand } from './auto-create-account.command'; import { UserAccountRepository, USER_ACCOUNT_REPOSITORY } from '@/domain/repositories/user-account.repository.interface'; import { UserAccount } from '@/domain/aggregates/user-account/user-account.aggregate'; import { AccountSequenceGeneratorService, UserValidatorService, WalletGeneratorService } from '@/domain/services'; import { ReferralCode, AccountSequence, ProvinceCode, CityCode, ChainType } from '@/domain/value-objects'; import { TokenService } from '@/application/services/token.service'; import { EventPublisherService } from '@/infrastructure/kafka/event-publisher.service'; import { ApplicationError } from '@/shared/exceptions/domain.exception'; import { AutoCreateAccountResult } from '../index'; import { MpcShareStorageService } from '@/infrastructure/external/backup/mpc-share-storage.service'; @Injectable() export class AutoCreateAccountHandler { private readonly logger = new Logger(AutoCreateAccountHandler.name); constructor( @Inject(USER_ACCOUNT_REPOSITORY) private readonly userRepository: UserAccountRepository, private readonly sequenceGenerator: AccountSequenceGeneratorService, private readonly validatorService: UserValidatorService, private readonly walletGenerator: WalletGeneratorService, private readonly tokenService: TokenService, private readonly eventPublisher: EventPublisherService, private readonly mpcShareStorage: MpcShareStorageService, ) {} async execute(command: AutoCreateAccountCommand): Promise { const deviceValidation = await this.validatorService.validateDeviceId(command.deviceId); if (!deviceValidation.isValid) throw new ApplicationError(deviceValidation.errorMessage!); let inviterSequence: AccountSequence | null = null; if (command.inviterReferralCode) { const referralCode = ReferralCode.create(command.inviterReferralCode); const referralValidation = await this.validatorService.validateReferralCode(referralCode); if (!referralValidation.isValid) throw new ApplicationError(referralValidation.errorMessage!); const inviter = await this.userRepository.findByReferralCode(referralCode); inviterSequence = inviter!.accountSequence; } const accountSequence = await this.sequenceGenerator.generateNext(); const account = UserAccount.createAutomatic({ accountSequence, initialDeviceId: command.deviceId, deviceName: command.deviceName, inviterSequence, province: ProvinceCode.create(command.provinceCode || 'DEFAULT'), city: CityCode.create(command.cityCode || 'DEFAULT'), }); // 使用 MPC 2-of-3 生成三链钱包 this.logger.log(`Generating MPC wallet for user=${account.userId.toString()}`); const mpcResult = await this.walletGenerator.generateMpcWalletSystem({ userId: account.userId.toString(), deviceId: command.deviceId, }); // 将 MPC 钱包信息转换为领域实体 const wallets = this.walletGenerator.convertToWalletEntities( account.userId, mpcResult.wallets, ); // 保存备份分片到备份服务 this.logger.log(`Storing backup share for user=${account.userId.toString()}`); await this.mpcShareStorage.storeBackupShare({ userId: account.userId.toString(), shareData: mpcResult.backupShareData, publicKey: mpcResult.publicKey, }); account.bindMultipleWalletAddresses(wallets); await this.userRepository.save(account); await this.userRepository.saveWallets(account.userId, Array.from(wallets.values())); const tokens = await this.tokenService.generateTokenPair({ userId: account.userId.toString(), accountSequence: account.accountSequence.value, deviceId: command.deviceId, }); await this.eventPublisher.publishAll(account.domainEvents); account.clearDomainEvents(); this.logger.log(`Account created successfully: userId=${account.userId.toString()}, seq=${account.accountSequence.value}`); return { userId: account.userId.toString(), accountSequence: account.accountSequence.value, referralCode: account.referralCode.value, mnemonic: '', // MPC 模式下不再使用助记词 clientShareData: mpcResult.clientShareData, // 客户端需安全存储此分片 publicKey: mpcResult.publicKey, walletAddresses: { kava: wallets.get(ChainType.KAVA)!.address, dst: wallets.get(ChainType.DST)!.address, bsc: wallets.get(ChainType.BSC)!.address, }, accessToken: tokens.accessToken, refreshToken: tokens.refreshToken, }; } }