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:
parent
45fcae5ef5
commit
1f15c494c1
|
|
@ -221,20 +221,32 @@ export class UserApplicationService {
|
||||||
): Promise<AutoCreateAccountResult> {
|
): Promise<AutoCreateAccountResult> {
|
||||||
const phoneNumber = PhoneNumber.create(command.phoneNumber);
|
const phoneNumber = PhoneNumber.create(command.phoneNumber);
|
||||||
|
|
||||||
|
this.logger.log(
|
||||||
|
`[REGISTER] Starting phone registration: phone=${phoneNumber.value}, deviceId=${command.deviceId}`,
|
||||||
|
);
|
||||||
|
|
||||||
// 1. 验证短信验证码
|
// 1. 验证短信验证码
|
||||||
const cachedCode = await this.redisService.get(
|
const cachedCode = await this.redisService.get(
|
||||||
`sms:register:${phoneNumber.value}`,
|
`sms:register:${phoneNumber.value}`,
|
||||||
);
|
);
|
||||||
if (cachedCode !== command.smsCode) {
|
if (cachedCode !== command.smsCode) {
|
||||||
|
this.logger.warn(
|
||||||
|
`[REGISTER] SMS code mismatch: phone=${phoneNumber.value}`,
|
||||||
|
);
|
||||||
throw new ApplicationError('验证码错误或已过期');
|
throw new ApplicationError('验证码错误或已过期');
|
||||||
}
|
}
|
||||||
|
this.logger.log(`[REGISTER] SMS code verified`);
|
||||||
|
|
||||||
// 2. 检查手机号是否已注册
|
// 2. 检查手机号是否已注册
|
||||||
const phoneValidation =
|
const phoneValidation =
|
||||||
await this.validatorService.validatePhoneNumber(phoneNumber);
|
await this.validatorService.validatePhoneNumber(phoneNumber);
|
||||||
if (!phoneValidation.isValid) {
|
if (!phoneValidation.isValid) {
|
||||||
|
this.logger.warn(
|
||||||
|
`[REGISTER] Phone validation failed: ${phoneValidation.errorMessage}`,
|
||||||
|
);
|
||||||
throw new ApplicationError(phoneValidation.errorMessage!);
|
throw new ApplicationError(phoneValidation.errorMessage!);
|
||||||
}
|
}
|
||||||
|
this.logger.log(`[REGISTER] Phone number validated`);
|
||||||
|
|
||||||
// 3. 验证邀请码
|
// 3. 验证邀请码
|
||||||
let inviterSequence: AccountSequence | null = null;
|
let inviterSequence: AccountSequence | null = null;
|
||||||
|
|
@ -243,16 +255,25 @@ export class UserApplicationService {
|
||||||
const referralValidation =
|
const referralValidation =
|
||||||
await this.validatorService.validateReferralCode(referralCode);
|
await this.validatorService.validateReferralCode(referralCode);
|
||||||
if (!referralValidation.isValid) {
|
if (!referralValidation.isValid) {
|
||||||
|
this.logger.warn(
|
||||||
|
`[REGISTER] Referral code invalid: ${command.inviterReferralCode}`,
|
||||||
|
);
|
||||||
throw new ApplicationError(referralValidation.errorMessage!);
|
throw new ApplicationError(referralValidation.errorMessage!);
|
||||||
}
|
}
|
||||||
const inviter =
|
const inviter =
|
||||||
await this.userRepository.findByReferralCode(referralCode);
|
await this.userRepository.findByReferralCode(referralCode);
|
||||||
inviterSequence = inviter!.accountSequence;
|
inviterSequence = inviter!.accountSequence;
|
||||||
|
this.logger.log(
|
||||||
|
`[REGISTER] Inviter validated: ${inviterSequence.value}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 生成用户序列号
|
// 4. 生成用户序列号
|
||||||
const accountSequence =
|
const accountSequence =
|
||||||
await this.sequenceGenerator.generateNextUserSequence();
|
await this.sequenceGenerator.generateNextUserSequence();
|
||||||
|
this.logger.log(
|
||||||
|
`[REGISTER] Generated sequence: ${accountSequence.value}`,
|
||||||
|
);
|
||||||
|
|
||||||
// 5. 生成用户名和头像
|
// 5. 生成用户名和头像
|
||||||
const identity = generateIdentity(accountSequence.value);
|
const identity = generateIdentity(accountSequence.value);
|
||||||
|
|
@ -277,6 +298,9 @@ export class UserApplicationService {
|
||||||
deviceInfo: command.deviceName,
|
deviceInfo: command.deviceName,
|
||||||
inviterSequence,
|
inviterSequence,
|
||||||
});
|
});
|
||||||
|
this.logger.log(
|
||||||
|
`[REGISTER] Account aggregate created (not saved yet): sequence=${accountSequence.value}`,
|
||||||
|
);
|
||||||
|
|
||||||
// 8. 设置随机用户名和头像
|
// 8. 设置随机用户名和头像
|
||||||
account.updateProfile({
|
account.updateProfile({
|
||||||
|
|
@ -285,15 +309,29 @@ export class UserApplicationService {
|
||||||
});
|
});
|
||||||
|
|
||||||
// 9. 保存账户
|
// 9. 保存账户
|
||||||
|
this.logger.log(`[REGISTER] Saving account to database...`);
|
||||||
await this.userRepository.save(account);
|
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. 设置密码
|
// 10. 设置密码
|
||||||
|
this.logger.log(`[REGISTER] Setting password hash...`);
|
||||||
const bcrypt = await import('bcrypt');
|
const bcrypt = await import('bcrypt');
|
||||||
const passwordHash = await bcrypt.hash(command.password, 10);
|
const passwordHash = await bcrypt.hash(command.password, 10);
|
||||||
await this.prisma.userAccount.update({
|
await this.prisma.userAccount.update({
|
||||||
where: { userId: account.userId.value },
|
where: { userId: account.userId.value },
|
||||||
data: { passwordHash },
|
data: { passwordHash },
|
||||||
});
|
});
|
||||||
|
this.logger.log(`[REGISTER] Password hash set`);
|
||||||
|
|
||||||
// 11. 删除验证码
|
// 11. 删除验证码
|
||||||
await this.redisService.delete(`sms:register:${phoneNumber.value}`);
|
await this.redisService.delete(`sms:register:${phoneNumber.value}`);
|
||||||
|
|
@ -304,10 +342,15 @@ export class UserApplicationService {
|
||||||
accountSequence: account.accountSequence.value,
|
accountSequence: account.accountSequence.value,
|
||||||
deviceId: command.deviceId,
|
deviceId: command.deviceId,
|
||||||
});
|
});
|
||||||
|
this.logger.log(`[REGISTER] Tokens generated`);
|
||||||
|
|
||||||
// 13. 发布领域事件
|
// 13. 发布领域事件
|
||||||
|
this.logger.log(
|
||||||
|
`[REGISTER] Publishing ${account.domainEvents.length} domain events...`,
|
||||||
|
);
|
||||||
await this.eventPublisher.publishAll(account.domainEvents);
|
await this.eventPublisher.publishAll(account.domainEvents);
|
||||||
account.clearDomainEvents();
|
account.clearDomainEvents();
|
||||||
|
this.logger.log(`[REGISTER] Domain events published`);
|
||||||
|
|
||||||
// 14. 发布 MPC Keygen 请求事件 (触发后台生成钱包)
|
// 14. 发布 MPC Keygen 请求事件 (触发后台生成钱包)
|
||||||
const sessionId = crypto.randomUUID();
|
const sessionId = crypto.randomUUID();
|
||||||
|
|
@ -324,7 +367,7 @@ export class UserApplicationService {
|
||||||
);
|
);
|
||||||
|
|
||||||
this.logger.log(
|
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 {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service';
|
import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service';
|
||||||
import {
|
import {
|
||||||
UserAccountRepository,
|
UserAccountRepository,
|
||||||
|
|
@ -27,87 +27,124 @@ import {
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserAccountRepositoryImpl implements UserAccountRepository {
|
export class UserAccountRepositoryImpl implements UserAccountRepository {
|
||||||
|
private readonly logger = new Logger(UserAccountRepositoryImpl.name);
|
||||||
|
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
async save(account: UserAccount): Promise<void> {
|
async save(account: UserAccount): Promise<void> {
|
||||||
const devices = account.getAllDevices();
|
const devices = account.getAllDevices();
|
||||||
const isNewAccount = account.userId.value === BigInt(0);
|
const isNewAccount = account.userId.value === BigInt(0);
|
||||||
|
|
||||||
await this.prisma.$transaction(async (tx) => {
|
this.logger.log(
|
||||||
let savedUserId: bigint;
|
`[SAVE] Starting save: sequence=${account.accountSequence.value}, isNew=${isNewAccount}, phone=${account.phoneNumber?.value || 'null'}`,
|
||||||
|
);
|
||||||
|
|
||||||
if (isNewAccount) {
|
try {
|
||||||
// 新账户,让数据库自动生成userId
|
await this.prisma.$transaction(async (tx) => {
|
||||||
const created = await tx.userAccount.create({
|
let savedUserId: bigint;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync devices
|
if (isNewAccount) {
|
||||||
await tx.userDevice.deleteMany({ where: { userId: savedUserId } });
|
// 新账户,让数据库自动生成userId
|
||||||
if (devices.length > 0) {
|
this.logger.log(`[SAVE] Creating new account in transaction...`);
|
||||||
await tx.userDevice.createMany({
|
|
||||||
data: devices.map((d) => {
|
const created = await tx.userAccount.create({
|
||||||
// 从 deviceInfo JSON 中提取冗余字段便于查询
|
data: {
|
||||||
const info = d.deviceInfo || {};
|
accountSequence: account.accountSequence.value,
|
||||||
return {
|
phoneNumber: account.phoneNumber?.value || null,
|
||||||
userId: savedUserId,
|
nickname: account.nickname,
|
||||||
deviceId: d.deviceId,
|
avatarUrl: account.avatarUrl,
|
||||||
deviceName: d.deviceName,
|
inviterSequence: account.inviterSequence?.value || null,
|
||||||
deviceInfo: d.deviceInfo
|
referralCode: account.referralCode.value,
|
||||||
? JSON.parse(JSON.stringify(d.deviceInfo))
|
kycStatus: account.kycStatus,
|
||||||
: null, // 100% 保存完整 JSON
|
realName: account.kycInfo?.realName || null,
|
||||||
platform: (info as any).platform || null,
|
idCardNumber: account.kycInfo?.idCardNumber || null,
|
||||||
deviceModel: (info as any).model || null,
|
idCardFrontUrl: account.kycInfo?.idCardFrontUrl || null,
|
||||||
osVersion: (info as any).osVersion || null,
|
idCardBackUrl: account.kycInfo?.idCardBackUrl || null,
|
||||||
appVersion: (info as any).appVersion || null,
|
status: account.status,
|
||||||
screenWidth: (info as any).screenWidth || null,
|
registeredAt: account.registeredAt,
|
||||||
screenHeight: (info as any).screenHeight || null,
|
lastLoginAt: account.lastLoginAt,
|
||||||
locale: (info as any).locale || null,
|
},
|
||||||
timezone: (info as any).timezone || null,
|
});
|
||||||
addedAt: d.addedAt,
|
savedUserId = created.userId;
|
||||||
lastActiveAt: d.lastActiveAt,
|
|
||||||
};
|
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> {
|
async saveWallets(userId: UserId, wallets: WalletAddress[]): Promise<void> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue