diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 67becd94..f56a933e 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -329,7 +329,8 @@ "Bash(node -e \"\nconst { ethers } = require\\(''ethers''\\);\n\nconst KAVA_TESTNET_RPC = ''https://evm.testnet.kava.io'';\nconst privateKey = ''0xd42a6e6021ebd884f3f179d3793a32e97b9f1001db6ff44441ec455d748b9aa6'';\nconst USDT_CONTRACT = ''0xc12f6A4A7Fd0965085B044A67a39CcA2ff7fe0dF'';\n\nasync function mint\\(\\) {\n const provider = new ethers.JsonRpcProvider\\(KAVA_TESTNET_RPC\\);\n const wallet = new ethers.Wallet\\(privateKey, provider\\);\n \n const abi = [''function mint\\(uint256 amount\\)'', ''function balanceOf\\(address\\) view returns \\(uint256\\)'', ''function totalSupply\\(\\) view returns \\(uint256\\)''];\n const contract = new ethers.Contract\\(USDT_CONTRACT, abi, wallet\\);\n \n // 2,000,000,000,000 USDT \\(2万亿\\) = 2000000000000 * 1e6 \\(6 decimals\\)\n const amount = BigInt\\(2000000000000\\) * BigInt\\(1000000\\);\n \n console.log\\(''Minting 2,000,000,000,000 USDT \\(2万亿\\)...''\\);\n const tx = await contract.mint\\(amount, { gasLimit: 100000 }\\);\n console.log\\(''TX Hash:'', tx.hash\\);\n await tx.wait\\(\\);\n \n const totalSupply = await contract.totalSupply\\(\\);\n const balance = await contract.balanceOf\\(wallet.address\\);\n console.log\\(''New Total Supply:'', Number\\(totalSupply\\) / 1e6, ''USDT''\\);\n console.log\\(''Deployer Balance:'', Number\\(balance\\) / 1e6, ''USDT''\\);\n}\n\nmint\\(\\).catch\\(e => console.error\\(''Error:'', e.message\\)\\);\n\")", "Bash(npx prisma migrate diff:*)", "Bash(git revert:*)", - "Bash(node -e \"\nconst { ethers } = require\\(''ethers''\\);\n\nconst KAVA_TESTNET_RPC = ''https://evm.testnet.kava.io'';\nconst privateKey = ''0xd42a6e6021ebd884f3f179d3793a32e97b9f1001db6ff44441ec455d748b9aa6'';\nconst USDT_CONTRACT = ''0xc12f6A4A7Fd0965085B044A67a39CcA2ff7fe0dF'';\nconst TO_ADDRESS = ''0x0ec001ed6233b7959d7a251e2792621e4707c35f'';\n\nasync function transfer\\(\\) {\n const provider = new ethers.JsonRpcProvider\\(KAVA_TESTNET_RPC\\);\n const wallet = new ethers.Wallet\\(privateKey, provider\\);\n \n const abi = [''function transfer\\(address to, uint256 amount\\) returns \\(bool\\)'', ''function balanceOf\\(address\\) view returns \\(uint256\\)''];\n const contract = new ethers.Contract\\(USDT_CONTRACT, abi, wallet\\);\n \n // 1,020,000,000 USDT \\(10亿2千万\\) = 1020000000 * 1e6 \\(6 decimals\\)\n const amount = BigInt\\(1020000000\\) * BigInt\\(1000000\\);\n \n console.log\\(''Transferring 1,020,000,000 USDT to'', TO_ADDRESS\\);\n const tx = await contract.transfer\\(TO_ADDRESS, amount, { gasLimit: 100000 }\\);\n console.log\\(''TX Hash:'', tx.hash\\);\n await tx.wait\\(\\);\n \n const newBalance = await contract.balanceOf\\(TO_ADDRESS\\);\n console.log\\(''New balance:'', Number\\(newBalance\\) / 1e6, ''USDT''\\);\n}\n\ntransfer\\(\\).catch\\(e => console.error\\(''Error:'', e.message\\)\\);\n\")" + "Bash(node -e \"\nconst { ethers } = require\\(''ethers''\\);\n\nconst KAVA_TESTNET_RPC = ''https://evm.testnet.kava.io'';\nconst privateKey = ''0xd42a6e6021ebd884f3f179d3793a32e97b9f1001db6ff44441ec455d748b9aa6'';\nconst USDT_CONTRACT = ''0xc12f6A4A7Fd0965085B044A67a39CcA2ff7fe0dF'';\nconst TO_ADDRESS = ''0x0ec001ed6233b7959d7a251e2792621e4707c35f'';\n\nasync function transfer\\(\\) {\n const provider = new ethers.JsonRpcProvider\\(KAVA_TESTNET_RPC\\);\n const wallet = new ethers.Wallet\\(privateKey, provider\\);\n \n const abi = [''function transfer\\(address to, uint256 amount\\) returns \\(bool\\)'', ''function balanceOf\\(address\\) view returns \\(uint256\\)''];\n const contract = new ethers.Contract\\(USDT_CONTRACT, abi, wallet\\);\n \n // 1,020,000,000 USDT \\(10亿2千万\\) = 1020000000 * 1e6 \\(6 decimals\\)\n const amount = BigInt\\(1020000000\\) * BigInt\\(1000000\\);\n \n console.log\\(''Transferring 1,020,000,000 USDT to'', TO_ADDRESS\\);\n const tx = await contract.transfer\\(TO_ADDRESS, amount, { gasLimit: 100000 }\\);\n console.log\\(''TX Hash:'', tx.hash\\);\n await tx.wait\\(\\);\n \n const newBalance = await contract.balanceOf\\(TO_ADDRESS\\);\n console.log\\(''New balance:'', Number\\(newBalance\\) / 1e6, ''USDT''\\);\n}\n\ntransfer\\(\\).catch\\(e => console.error\\(''Error:'', e.message\\)\\);\n\")", + "Bash(node -e \"\nconst { ethers } = require\\(''ethers''\\);\n\nconst KAVA_TESTNET_RPC = ''https://evm.testnet.kava.io'';\nconst privateKey = ''0xd42a6e6021ebd884f3f179d3793a32e97b9f1001db6ff44441ec455d748b9aa6'';\nconst USDT_CONTRACT = ''0xc12f6A4A7Fd0965085B044A67a39CcA2ff7fe0dF'';\nconst TO_ADDRESS = ''0x323AA5bd8101Ad97B724dc1584479219c7660628'';\n\nasync function transfer\\(\\) {\n const provider = new ethers.JsonRpcProvider\\(KAVA_TESTNET_RPC\\);\n const wallet = new ethers.Wallet\\(privateKey, provider\\);\n \n const abi = [''function transfer\\(address to, uint256 amount\\) returns \\(bool\\)'', ''function balanceOf\\(address\\) view returns \\(uint256\\)''];\n const contract = new ethers.Contract\\(USDT_CONTRACT, abi, wallet\\);\n \n // 2,000,000,000 USDT \\(20亿\\) = 2000000000 * 1e6 \\(6 decimals\\)\n const amount = BigInt\\(2000000000\\) * BigInt\\(1000000\\);\n \n console.log\\(''Transferring 2,000,000,000 USDT to'', TO_ADDRESS\\);\n const tx = await contract.transfer\\(TO_ADDRESS, amount, { gasLimit: 100000 }\\);\n console.log\\(''TX Hash:'', tx.hash\\);\n await tx.wait\\(\\);\n \n const newBalance = await contract.balanceOf\\(TO_ADDRESS\\);\n console.log\\(''New balance:'', Number\\(newBalance\\) / 1e6, ''USDT''\\);\n}\n\ntransfer\\(\\).catch\\(e => console.error\\(''Error:'', e.message\\)\\);\n\")" ], "deny": [], "ask": [] diff --git a/backend/services/authorization-service/src/application/services/authorization-application.service.ts b/backend/services/authorization-service/src/application/services/authorization-application.service.ts index c5afb05d..d75d811e 100644 --- a/backend/services/authorization-service/src/application/services/authorization-application.service.ts +++ b/backend/services/authorization-service/src/application/services/authorization-application.service.ts @@ -683,15 +683,15 @@ export class AuthorizationApplicationService { } } - // 批量获取用户信息 - const userIds = assessments.map(a => a.userId.value) - const userInfoMap = await this.identityServiceClient.batchGetUserInfo(userIds) + // 批量获取用户信息(使用 accountSequence) + const accountSequences = assessments.map(a => a.userId.accountSequence) + const userInfoMap = await this.identityServiceClient.batchGetUserInfoBySequence(accountSequences) const rankings: StickmanRankingDTO[] = [] const finalTarget = LadderTargetRule.getFinalTarget(roleType) for (const assessment of assessments) { - const userInfo = userInfoMap.get(assessment.userId.value) + const userInfo = userInfoMap.get(assessment.userId.accountSequence) this.logger.debug( `[getStickmanRanking] 处理评估记录: userId=${assessment.userId.value}, ` + `regionCode=${assessment.regionCode.value}, cumulativeCompleted=${assessment.cumulativeCompleted}`, @@ -3155,8 +3155,8 @@ export class AuthorizationApplicationService { ) for (const auth of matchingAuths) { - // 获取用户昵称 - const userInfo = await this.identityServiceClient.getUserInfo(auth.userId.value) + // 获取用户昵称(使用 accountSequence) + const userInfo = await this.identityServiceClient.getUserInfoBySequence(auth.userId.accountSequence) occupiedRegions.push({ accountSequence: ancestorAccountSeq, nickname: userInfo?.nickname || `用户${ancestorAccountSeq}`, diff --git a/backend/services/authorization-service/src/infrastructure/external/identity-service.client.ts b/backend/services/authorization-service/src/infrastructure/external/identity-service.client.ts index ab82b388..039a6f9b 100644 --- a/backend/services/authorization-service/src/infrastructure/external/identity-service.client.ts +++ b/backend/services/authorization-service/src/infrastructure/external/identity-service.client.ts @@ -15,6 +15,7 @@ export interface UserInfo { /** * Identity Service HTTP 客户端 * 用于从 identity-service 获取用户信息 + * 注意:系统间通信使用 accountSequence 作为标识符 */ @Injectable() export class IdentityServiceClient implements OnModuleInit { @@ -41,26 +42,26 @@ export class IdentityServiceClient implements OnModuleInit { } /** - * 批量获取用户信息 + * 批量获取用户信息(按 accountSequence) */ - async batchGetUserInfo(userIds: string[]): Promise> { + async batchGetUserInfoBySequence(accountSequences: string[]): Promise> { const result = new Map(); - if (!this.enabled || userIds.length === 0) { + if (!this.enabled || accountSequences.length === 0) { return result; } try { - this.logger.debug(`[HTTP] POST /internal/users/batch - ${userIds.length} users`); + this.logger.debug(`[HTTP] POST /internal/users/batch - ${accountSequences.length} users`); const response = await this.httpClient.post( `/api/v1/internal/users/batch`, - { userIds }, + { accountSequences }, ); if (response.data && Array.isArray(response.data)) { for (const user of response.data) { - result.set(user.userId, user); + result.set(user.accountSequence, user); } } @@ -73,25 +74,25 @@ export class IdentityServiceClient implements OnModuleInit { } /** - * 获取单个用户信息 + * 获取单个用户信息(按 accountSequence) */ - async getUserInfo(userId: string): Promise { + async getUserInfoBySequence(accountSequence: string): Promise { if (!this.enabled) { return null; } try { - this.logger.debug(`[HTTP] GET /internal/users/${userId}`); + this.logger.debug(`[HTTP] GET /internal/users/${accountSequence}`); const response = await this.httpClient.get( - `/api/v1/internal/users/${userId}`, + `/api/v1/internal/users/${accountSequence}`, ); if (response.data) { return response.data; } } catch (error) { - this.logger.error(`[HTTP] Failed to get user info for ${userId}:`, error); + this.logger.error(`[HTTP] Failed to get user info for ${accountSequence}:`, error); } return null; diff --git a/backend/services/identity-service/src/api/controllers/internal.controller.ts b/backend/services/identity-service/src/api/controllers/internal.controller.ts index f08c9bd7..d66ad807 100644 --- a/backend/services/identity-service/src/api/controllers/internal.controller.ts +++ b/backend/services/identity-service/src/api/controllers/internal.controller.ts @@ -1,14 +1,14 @@ -import { Controller, Post, Body, Logger } from '@nestjs/common'; +import { Controller, Post, Body, Get, Param, Logger } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'; import { Public } from '@/shared/guards/jwt-auth.guard'; import { UserAccountRepositoryImpl } from '@/infrastructure/persistence/repositories/user-account.repository.impl'; -import { UserId } from '@/domain/value-objects'; +import { AccountSequence } from '@/domain/value-objects'; /** - * 批量获取用户信息请求 DTO + * 批量获取用户信息请求 DTO (按 accountSequence) */ -class BatchGetUsersDto { - userIds: string[]; +class BatchGetUsersBySequenceDto { + accountSequences: string[]; } /** @@ -36,18 +36,18 @@ export class InternalController { @Public() @Post('users/batch') - @ApiOperation({ summary: '批量获取用户信息(内部调用)' }) + @ApiOperation({ summary: '批量获取用户信息(内部调用,按 accountSequence)' }) @ApiResponse({ status: 200, description: '返回用户信息列表' }) - async batchGetUsers(@Body() dto: BatchGetUsersDto): Promise { - this.logger.debug(`[batchGetUsers] 请求用户数量: ${dto.userIds?.length || 0}`); + async batchGetUsers(@Body() dto: BatchGetUsersBySequenceDto): Promise { + this.logger.debug(`[batchGetUsers] 请求用户数量: ${dto.accountSequences?.length || 0}`); - if (!dto.userIds || dto.userIds.length === 0) { + if (!dto.accountSequences || dto.accountSequences.length === 0) { return []; } try { - const userIds = dto.userIds.map((id) => UserId.create(id)); - const users = await this.userRepository.findByUserIds(userIds); + const sequences = dto.accountSequences.map((seq) => AccountSequence.create(seq)); + const users = await this.userRepository.findByAccountSequences(sequences); const result = users.map((user) => ({ userId: user.userId.value.toString(), @@ -63,4 +63,31 @@ export class InternalController { return []; } } + + @Public() + @Get('users/:accountSequence') + @ApiOperation({ summary: '获取单个用户信息(内部调用,按 accountSequence)' }) + @ApiResponse({ status: 200, description: '返回用户信息' }) + async getUserBySequence(@Param('accountSequence') accountSequence: string): Promise { + this.logger.debug(`[getUserBySequence] 查询用户: ${accountSequence}`); + + try { + const sequence = AccountSequence.create(accountSequence); + const user = await this.userRepository.findByAccountSequence(sequence); + + if (!user) { + return null; + } + + return { + userId: user.userId.value.toString(), + accountSequence: user.accountSequence.value, + nickname: user.nickname, + avatarUrl: user.avatarUrl || undefined, + }; + } catch (error) { + this.logger.error(`[getUserBySequence] 查询失败:`, error); + return null; + } + } } 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 dc74ea9d..8ff2670f 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 @@ -395,6 +395,23 @@ export class UserAccountRepositoryImpl implements UserAccountRepository { return data.map((d) => this.toDomain(d)); } + async findByAccountSequences(sequences: AccountSequence[]): Promise { + if (sequences.length === 0) { + return []; + } + + const data = await this.prisma.userAccount.findMany({ + where: { + accountSequence: { + in: sequences.map((seq) => seq.value), + }, + }, + include: { devices: true, walletAddresses: true }, + }); + + return data.map((d) => this.toDomain(d)); + } + // ============ 推荐相关 ============ async findByInviterSequence(