85 lines
3.9 KiB
TypeScript
85 lines
3.9 KiB
TypeScript
import { Injectable, Inject, Logger } from '@nestjs/common';
|
||
import { RecoverByMnemonicCommand } from './recover-by-mnemonic.command';
|
||
import { UserAccountRepository, USER_ACCOUNT_REPOSITORY } from '@/domain/repositories/user-account.repository.interface';
|
||
import { AccountSequence } from '@/domain/value-objects';
|
||
import { TokenService } from '@/application/services/token.service';
|
||
import { EventPublisherService } from '@/infrastructure/kafka/event-publisher.service';
|
||
import { BlockchainClientService } from '@/infrastructure/external/blockchain/blockchain-client.service';
|
||
import { ApplicationError } from '@/shared/exceptions/domain.exception';
|
||
import { RecoverAccountResult } from '../index';
|
||
import { generateRandomAvatarSvg } from '@/shared/utils/random-identity.util';
|
||
|
||
@Injectable()
|
||
export class RecoverByMnemonicHandler {
|
||
private readonly logger = new Logger(RecoverByMnemonicHandler.name);
|
||
|
||
constructor(
|
||
@Inject(USER_ACCOUNT_REPOSITORY)
|
||
private readonly userRepository: UserAccountRepository,
|
||
private readonly tokenService: TokenService,
|
||
private readonly eventPublisher: EventPublisherService,
|
||
private readonly blockchainClient: BlockchainClientService,
|
||
) {}
|
||
|
||
async execute(command: RecoverByMnemonicCommand): Promise<RecoverAccountResult> {
|
||
const accountSequence = AccountSequence.create(command.accountSequence);
|
||
const account = await this.userRepository.findByAccountSequence(accountSequence);
|
||
if (!account) throw new ApplicationError('账户序列号不存在');
|
||
if (!account.isActive) throw new ApplicationError('账户已冻结或注销');
|
||
|
||
// 调用 blockchain-service 验证助记词(blockchain-service 内部查询哈希并验证)
|
||
this.logger.log(`Verifying mnemonic for account ${command.accountSequence}`);
|
||
const verifyResult = await this.blockchainClient.verifyMnemonicByAccount({
|
||
accountSequence: command.accountSequence,
|
||
mnemonic: command.mnemonic,
|
||
});
|
||
|
||
if (!verifyResult.valid) {
|
||
this.logger.warn(`Mnemonic verification failed for account ${command.accountSequence}: ${verifyResult.message}`);
|
||
throw new ApplicationError(verifyResult.message || '助记词错误');
|
||
}
|
||
|
||
this.logger.log(`Mnemonic verified successfully for account ${command.accountSequence}`);
|
||
|
||
// 如果头像为空,重新生成一个
|
||
let avatarUrl = account.avatarUrl;
|
||
this.logger.log(`Account ${command.accountSequence} avatarUrl from DB: ${avatarUrl ? `长度=${avatarUrl.length}` : 'null'}`);
|
||
if (avatarUrl) {
|
||
this.logger.log(`Account ${command.accountSequence} avatarUrl前50字符: ${avatarUrl.substring(0, 50)}`);
|
||
}
|
||
if (!avatarUrl) {
|
||
this.logger.log(`Account ${command.accountSequence} has no avatar, generating new one`);
|
||
avatarUrl = generateRandomAvatarSvg();
|
||
account.updateProfile({ avatarUrl });
|
||
}
|
||
|
||
account.addDevice(command.newDeviceId, command.deviceName);
|
||
account.recordLogin();
|
||
await this.userRepository.save(account);
|
||
|
||
const tokens = await this.tokenService.generateTokenPair({
|
||
userId: account.userId.toString(),
|
||
accountSequence: account.accountSequence.value,
|
||
deviceId: command.newDeviceId,
|
||
});
|
||
|
||
await this.eventPublisher.publishAll(account.domainEvents);
|
||
account.clearDomainEvents();
|
||
|
||
const result = {
|
||
userId: account.userId.toString(),
|
||
accountSequence: account.accountSequence.value,
|
||
nickname: account.nickname,
|
||
avatarUrl,
|
||
referralCode: account.referralCode.value,
|
||
accessToken: tokens.accessToken,
|
||
refreshToken: tokens.refreshToken,
|
||
};
|
||
|
||
this.logger.log(`RecoverByMnemonic result - accountSequence: ${result.accountSequence}, nickname: ${result.nickname}`);
|
||
this.logger.log(`RecoverByMnemonic result - avatarUrl: ${result.avatarUrl ? `长度=${result.avatarUrl.length}` : 'null'}`);
|
||
|
||
return result;
|
||
}
|
||
}
|