rwadurian/backend/services/identity-service/src/application/commands/auto-create-account/auto-create-account.handler.ts

105 lines
4.6 KiB
TypeScript

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<AutoCreateAccountResult> {
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,
};
}
}