148 lines
4.3 KiB
TypeScript
148 lines
4.3 KiB
TypeScript
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<string>('IDENTITY_SERVICE_URL') || 'http://rwa-identity-service:3000';
|
||
this.enabled = this.configService.get<boolean>('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<Map<string, UserInfo>> {
|
||
const result = new Map<string, UserInfo>();
|
||
|
||
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<UserInfo | null> {
|
||
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<UserDetailInfo | null> {
|
||
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;
|
||
}
|
||
}
|