rwadurian/backend/services/authorization-service/src/infrastructure/external/identity-service.client.ts

148 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}