fix(identity-service): 添加注册流程详细日志和保存验证

问题:用户注册后 referral-service 有数据但 identity-service 没有
原因:save() 方法和 registerByPhone 方法缺少日志,无法追踪问题

修复:
- save() 方法添加完整日志和 try-catch
- registerByPhone 方法添加每个步骤的日志
- 添加关键验证:保存后检查 userId 是否为 0

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-21 23:19:24 -08:00
parent 45fcae5ef5
commit 1f15c494c1
2 changed files with 155 additions and 75 deletions

View File

@ -221,20 +221,32 @@ export class UserApplicationService {
): Promise<AutoCreateAccountResult> {
const phoneNumber = PhoneNumber.create(command.phoneNumber);
this.logger.log(
`[REGISTER] Starting phone registration: phone=${phoneNumber.value}, deviceId=${command.deviceId}`,
);
// 1. 验证短信验证码
const cachedCode = await this.redisService.get(
`sms:register:${phoneNumber.value}`,
);
if (cachedCode !== command.smsCode) {
this.logger.warn(
`[REGISTER] SMS code mismatch: phone=${phoneNumber.value}`,
);
throw new ApplicationError('验证码错误或已过期');
}
this.logger.log(`[REGISTER] SMS code verified`);
// 2. 检查手机号是否已注册
const phoneValidation =
await this.validatorService.validatePhoneNumber(phoneNumber);
if (!phoneValidation.isValid) {
this.logger.warn(
`[REGISTER] Phone validation failed: ${phoneValidation.errorMessage}`,
);
throw new ApplicationError(phoneValidation.errorMessage!);
}
this.logger.log(`[REGISTER] Phone number validated`);
// 3. 验证邀请码
let inviterSequence: AccountSequence | null = null;
@ -243,16 +255,25 @@ export class UserApplicationService {
const referralValidation =
await this.validatorService.validateReferralCode(referralCode);
if (!referralValidation.isValid) {
this.logger.warn(
`[REGISTER] Referral code invalid: ${command.inviterReferralCode}`,
);
throw new ApplicationError(referralValidation.errorMessage!);
}
const inviter =
await this.userRepository.findByReferralCode(referralCode);
inviterSequence = inviter!.accountSequence;
this.logger.log(
`[REGISTER] Inviter validated: ${inviterSequence.value}`,
);
}
// 4. 生成用户序列号
const accountSequence =
await this.sequenceGenerator.generateNextUserSequence();
this.logger.log(
`[REGISTER] Generated sequence: ${accountSequence.value}`,
);
// 5. 生成用户名和头像
const identity = generateIdentity(accountSequence.value);
@ -277,6 +298,9 @@ export class UserApplicationService {
deviceInfo: command.deviceName,
inviterSequence,
});
this.logger.log(
`[REGISTER] Account aggregate created (not saved yet): sequence=${accountSequence.value}`,
);
// 8. 设置随机用户名和头像
account.updateProfile({
@ -285,15 +309,29 @@ export class UserApplicationService {
});
// 9. 保存账户
this.logger.log(`[REGISTER] Saving account to database...`);
await this.userRepository.save(account);
this.logger.log(
`[REGISTER] Account saved: userId=${account.userId.value}, sequence=${account.accountSequence.value}`,
);
// 9.1 验证保存成功
if (account.userId.value === BigInt(0)) {
this.logger.error(
`[REGISTER] CRITICAL: userId is still 0 after save! sequence=${accountSequence.value}`,
);
throw new ApplicationError('账户保存失败未获取到用户ID');
}
// 10. 设置密码
this.logger.log(`[REGISTER] Setting password hash...`);
const bcrypt = await import('bcrypt');
const passwordHash = await bcrypt.hash(command.password, 10);
await this.prisma.userAccount.update({
where: { userId: account.userId.value },
data: { passwordHash },
});
this.logger.log(`[REGISTER] Password hash set`);
// 11. 删除验证码
await this.redisService.delete(`sms:register:${phoneNumber.value}`);
@ -304,10 +342,15 @@ export class UserApplicationService {
accountSequence: account.accountSequence.value,
deviceId: command.deviceId,
});
this.logger.log(`[REGISTER] Tokens generated`);
// 13. 发布领域事件
this.logger.log(
`[REGISTER] Publishing ${account.domainEvents.length} domain events...`,
);
await this.eventPublisher.publishAll(account.domainEvents);
account.clearDomainEvents();
this.logger.log(`[REGISTER] Domain events published`);
// 14. 发布 MPC Keygen 请求事件 (触发后台生成钱包)
const sessionId = crypto.randomUUID();
@ -324,7 +367,7 @@ export class UserApplicationService {
);
this.logger.log(
`Account registered by phone: sequence=${accountSequence.value}, phone=${phoneNumber.value}, MPC keygen requested`,
`[REGISTER] COMPLETE: sequence=${accountSequence.value}, phone=${phoneNumber.value}, userId=${account.userId.value}`,
);
return {

View File

@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common';
import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service';
import {
UserAccountRepository,
@ -27,87 +27,124 @@ import {
@Injectable()
export class UserAccountRepositoryImpl implements UserAccountRepository {
private readonly logger = new Logger(UserAccountRepositoryImpl.name);
constructor(private readonly prisma: PrismaService) {}
async save(account: UserAccount): Promise<void> {
const devices = account.getAllDevices();
const isNewAccount = account.userId.value === BigInt(0);
await this.prisma.$transaction(async (tx) => {
let savedUserId: bigint;
this.logger.log(
`[SAVE] Starting save: sequence=${account.accountSequence.value}, isNew=${isNewAccount}, phone=${account.phoneNumber?.value || 'null'}`,
);
if (isNewAccount) {
// 新账户让数据库自动生成userId
const created = await tx.userAccount.create({
data: {
accountSequence: account.accountSequence.value,
phoneNumber: account.phoneNumber?.value || null,
nickname: account.nickname,
avatarUrl: account.avatarUrl,
inviterSequence: account.inviterSequence?.value || null,
referralCode: account.referralCode.value,
kycStatus: account.kycStatus,
realName: account.kycInfo?.realName || null,
idCardNumber: account.kycInfo?.idCardNumber || null,
idCardFrontUrl: account.kycInfo?.idCardFrontUrl || null,
idCardBackUrl: account.kycInfo?.idCardBackUrl || null,
status: account.status,
registeredAt: account.registeredAt,
lastLoginAt: account.lastLoginAt,
},
});
savedUserId = created.userId;
// 更新聚合根的userId
(account as any)._userId = UserId.create(savedUserId);
} else {
// 已存在的账户,更新
await tx.userAccount.update({
where: { userId: BigInt(account.userId.value) },
data: {
phoneNumber: account.phoneNumber?.value || null,
nickname: account.nickname,
avatarUrl: account.avatarUrl,
kycStatus: account.kycStatus,
realName: account.kycInfo?.realName || null,
idCardNumber: account.kycInfo?.idCardNumber || null,
idCardFrontUrl: account.kycInfo?.idCardFrontUrl || null,
idCardBackUrl: account.kycInfo?.idCardBackUrl || null,
status: account.status,
lastLoginAt: account.lastLoginAt,
},
});
savedUserId = account.userId.value;
}
try {
await this.prisma.$transaction(async (tx) => {
let savedUserId: bigint;
// Sync devices
await tx.userDevice.deleteMany({ where: { userId: savedUserId } });
if (devices.length > 0) {
await tx.userDevice.createMany({
data: devices.map((d) => {
// 从 deviceInfo JSON 中提取冗余字段便于查询
const info = d.deviceInfo || {};
return {
userId: savedUserId,
deviceId: d.deviceId,
deviceName: d.deviceName,
deviceInfo: d.deviceInfo
? JSON.parse(JSON.stringify(d.deviceInfo))
: null, // 100% 保存完整 JSON
platform: (info as any).platform || null,
deviceModel: (info as any).model || null,
osVersion: (info as any).osVersion || null,
appVersion: (info as any).appVersion || null,
screenWidth: (info as any).screenWidth || null,
screenHeight: (info as any).screenHeight || null,
locale: (info as any).locale || null,
timezone: (info as any).timezone || null,
addedAt: d.addedAt,
lastActiveAt: d.lastActiveAt,
};
}),
});
}
});
if (isNewAccount) {
// 新账户让数据库自动生成userId
this.logger.log(`[SAVE] Creating new account in transaction...`);
const created = await tx.userAccount.create({
data: {
accountSequence: account.accountSequence.value,
phoneNumber: account.phoneNumber?.value || null,
nickname: account.nickname,
avatarUrl: account.avatarUrl,
inviterSequence: account.inviterSequence?.value || null,
referralCode: account.referralCode.value,
kycStatus: account.kycStatus,
realName: account.kycInfo?.realName || null,
idCardNumber: account.kycInfo?.idCardNumber || null,
idCardFrontUrl: account.kycInfo?.idCardFrontUrl || null,
idCardBackUrl: account.kycInfo?.idCardBackUrl || null,
status: account.status,
registeredAt: account.registeredAt,
lastLoginAt: account.lastLoginAt,
},
});
savedUserId = created.userId;
this.logger.log(
`[SAVE] Account created: userId=${savedUserId}, sequence=${created.accountSequence}`,
);
// 更新聚合根的userId
(account as any)._userId = UserId.create(savedUserId);
} else {
// 已存在的账户,更新
this.logger.log(
`[SAVE] Updating existing account: userId=${account.userId.value}`,
);
await tx.userAccount.update({
where: { userId: BigInt(account.userId.value) },
data: {
phoneNumber: account.phoneNumber?.value || null,
nickname: account.nickname,
avatarUrl: account.avatarUrl,
kycStatus: account.kycStatus,
realName: account.kycInfo?.realName || null,
idCardNumber: account.kycInfo?.idCardNumber || null,
idCardFrontUrl: account.kycInfo?.idCardFrontUrl || null,
idCardBackUrl: account.kycInfo?.idCardBackUrl || null,
status: account.status,
lastLoginAt: account.lastLoginAt,
},
});
savedUserId = account.userId.value;
this.logger.log(`[SAVE] Account updated: userId=${savedUserId}`);
}
// Sync devices
this.logger.log(
`[SAVE] Syncing ${devices.length} devices for userId=${savedUserId}`,
);
await tx.userDevice.deleteMany({ where: { userId: savedUserId } });
if (devices.length > 0) {
await tx.userDevice.createMany({
data: devices.map((d) => {
// 从 deviceInfo JSON 中提取冗余字段便于查询
const info = d.deviceInfo || {};
return {
userId: savedUserId,
deviceId: d.deviceId,
deviceName: d.deviceName,
deviceInfo: d.deviceInfo
? JSON.parse(JSON.stringify(d.deviceInfo))
: null, // 100% 保存完整 JSON
platform: (info as any).platform || null,
deviceModel: (info as any).model || null,
osVersion: (info as any).osVersion || null,
appVersion: (info as any).appVersion || null,
screenWidth: (info as any).screenWidth || null,
screenHeight: (info as any).screenHeight || null,
locale: (info as any).locale || null,
timezone: (info as any).timezone || null,
addedAt: d.addedAt,
lastActiveAt: d.lastActiveAt,
};
}),
});
}
this.logger.log(`[SAVE] Devices synced successfully`);
});
this.logger.log(
`[SAVE] Transaction committed: sequence=${account.accountSequence.value}, userId=${account.userId.value}`,
);
} catch (error) {
this.logger.error(
`[SAVE] Transaction FAILED: sequence=${account.accountSequence.value}, error=${error.message}`,
);
this.logger.error(`[SAVE] Stack: ${error.stack}`);
throw error; // 确保异常被抛出
}
}
async saveWallets(userId: UserId, wallets: WalletAddress[]): Promise<void> {