import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import axios, { AxiosInstance } from 'axios'; /** * 用户信息接口 */ export interface UserInfo { userId: string; accountSequence: string; nickname: string; avatarUrl?: string; } /** * 用户详情接口(包含敏感信息) */ export interface UserDetailInfo { userId: string; accountSequence: string; nickname: string; avatarUrl?: string; phoneNumber?: string; // 敏感信息 email?: string; // 敏感信息 registeredAt: string; inviterSequence?: string; // 推荐人序列号 kycStatus: string; realName?: string; // 敏感信息 } /** * Identity Service HTTP 客户端 * 用于从 identity-service 获取用户信息 * 注意:系统间通信使用 accountSequence 作为标识符 */ @Injectable() export class IdentityServiceClient implements OnModuleInit { private readonly logger = new Logger(IdentityServiceClient.name); private httpClient: AxiosInstance; private readonly baseUrl: string; private readonly enabled: boolean; constructor(private readonly configService: ConfigService) { this.baseUrl = this.configService.get('IDENTITY_SERVICE_URL') || 'http://rwa-identity-service:3000'; this.enabled = this.configService.get('IDENTITY_SERVICE_ENABLED') !== false; } onModuleInit() { this.httpClient = axios.create({ baseURL: this.baseUrl, timeout: 10000, headers: { 'Content-Type': 'application/json', }, }); this.logger.log(`[INIT] IdentityServiceClient initialized: ${this.baseUrl}, enabled: ${this.enabled}`); } /** * 批量获取用户信息(按 accountSequence) */ async batchGetUserInfoBySequence(accountSequences: string[]): Promise> { const result = new Map(); if (!this.enabled || accountSequences.length === 0) { return result; } try { this.logger.debug(`[HTTP] POST /internal/users/batch - ${accountSequences.length} users`); const response = await this.httpClient.post<{ success: boolean; data: UserInfo[] }>( `/api/v1/internal/users/batch`, { accountSequences }, ); // identity-service 使用 TransformInterceptor 包装响应为 { success, data } const users = response.data?.data || response.data; if (users && Array.isArray(users)) { for (const user of users) { result.set(user.accountSequence, user); } } this.logger.debug(`[HTTP] Got ${result.size} users info`); } catch (error) { this.logger.error(`[HTTP] Failed to batch get user info:`, error); } return result; } /** * 获取单个用户信息(按 accountSequence) */ async getUserInfoBySequence(accountSequence: string): Promise { if (!this.enabled) { return null; } try { this.logger.debug(`[HTTP] GET /internal/users/${accountSequence}`); const response = await this.httpClient.get<{ success: boolean; data: UserInfo } | UserInfo>( `/api/v1/internal/users/${accountSequence}`, ); // 处理可能的两种响应格式(带包装或不带包装) const data = (response.data as any)?.data || response.data; if (data) { return data as UserInfo; } } catch (error) { this.logger.error(`[HTTP] Failed to get user info for ${accountSequence}:`, error); } return null; } /** * 获取用户详情(包含敏感信息,按 accountSequence) */ async getUserDetailBySequence(accountSequence: string): Promise { if (!this.enabled) { return null; } try { this.logger.debug(`[HTTP] GET /internal/users/${accountSequence}/detail`); const response = await this.httpClient.get<{ success: boolean; data: UserDetailInfo } | UserDetailInfo>( `/api/v1/internal/users/${accountSequence}/detail`, ); // 处理可能的两种响应格式 const data = (response.data as any)?.data || response.data; if (data) { return data as UserDetailInfo; } } catch (error) { this.logger.error(`[HTTP] Failed to get user detail for ${accountSequence}:`, error); } return null; } }