From 1f15c494c18d87394a8d971b0204a02a878beaa2 Mon Sep 17 00:00:00 2001 From: hailin Date: Sun, 21 Dec 2025 23:19:24 -0800 Subject: [PATCH] =?UTF-8?q?fix(identity-service):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E6=B5=81=E7=A8=8B=E8=AF=A6=E7=BB=86=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=92=8C=E4=BF=9D=E5=AD=98=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:用户注册后 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 --- .../services/user-application.service.ts | 45 ++++- .../user-account.repository.impl.ts | 185 +++++++++++------- 2 files changed, 155 insertions(+), 75 deletions(-) diff --git a/backend/services/identity-service/src/application/services/user-application.service.ts b/backend/services/identity-service/src/application/services/user-application.service.ts index c1bb756f..71f24baf 100644 --- a/backend/services/identity-service/src/application/services/user-application.service.ts +++ b/backend/services/identity-service/src/application/services/user-application.service.ts @@ -221,20 +221,32 @@ export class UserApplicationService { ): Promise { 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 { diff --git a/backend/services/identity-service/src/infrastructure/persistence/repositories/user-account.repository.impl.ts b/backend/services/identity-service/src/infrastructure/persistence/repositories/user-account.repository.impl.ts index 080890e9..85343702 100644 --- a/backend/services/identity-service/src/infrastructure/persistence/repositories/user-account.repository.impl.ts +++ b/backend/services/identity-service/src/infrastructure/persistence/repositories/user-account.repository.impl.ts @@ -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 { 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 {