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

238 lines
7.6 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';
import {
ITeamStatisticsRepository,
TeamStatistics,
} from '../../domain/services/assessment-calculator.service';
/**
* 团队统计数据接口(来自 referral-service
*/
interface ReferralTeamStatsResponse {
userId: string;
accountSequence: string;
totalTeamPlantingCount: number;
provinceCityDistribution: Record<string, Record<string, number>> | null;
}
/**
* 推荐链数据接口
*/
interface ReferralChainResponse {
accountSequence: number;
userId: string | null;
ancestorPath: string[];
referrerId: string | null;
}
/**
* 团队成员数据接口
*/
interface TeamMembersResponse {
accountSequence: number;
teamMembers: number[];
}
/**
* 适配器类:将 referral-service 返回的数据转换为 authorization-service 需要的格式
*/
class TeamStatisticsAdapter implements TeamStatistics {
constructor(
public readonly userId: string,
public readonly accountSequence: bigint,
public readonly totalTeamPlantingCount: number,
private readonly provinceCityDistribution: Record<string, Record<string, number>> | null,
) {}
getProvinceTeamCount(provinceCode: string): number {
if (!this.provinceCityDistribution || !this.provinceCityDistribution[provinceCode]) {
return 0;
}
// 计算该省所有城市的总和
const provinceCities = this.provinceCityDistribution[provinceCode];
return Object.values(provinceCities).reduce((sum, count) => sum + count, 0);
}
getCityTeamCount(cityCode: string): number {
if (!this.provinceCityDistribution) {
return 0;
}
// 遍历所有省份找到该城市
for (const provinceCode of Object.keys(this.provinceCityDistribution)) {
const provinceCities = this.provinceCityDistribution[provinceCode];
if (provinceCities && cityCode in provinceCities) {
return provinceCities[cityCode];
}
}
return 0;
}
}
/**
* Referral Service HTTP 客户端
* 用于从 referral-service 获取团队统计数据
*/
@Injectable()
export class ReferralServiceClient implements ITeamStatisticsRepository, OnModuleInit {
private readonly logger = new Logger(ReferralServiceClient.name);
private httpClient: AxiosInstance;
private readonly baseUrl: string;
private readonly enabled: boolean;
constructor(private readonly configService: ConfigService) {
this.baseUrl = this.configService.get<string>('REFERRAL_SERVICE_URL') || 'http://referral-service:3004';
this.enabled = this.configService.get<boolean>('REFERRAL_SERVICE_ENABLED') !== false;
}
onModuleInit() {
this.httpClient = axios.create({
baseURL: this.baseUrl,
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
this.logger.log(`[INIT] ReferralServiceClient initialized: ${this.baseUrl}, enabled: ${this.enabled}`);
}
/**
* 根据 userId 查询团队统计
*/
async findByUserId(userId: string): Promise<TeamStatistics | null> {
if (!this.enabled) {
this.logger.debug('[DISABLED] Referral service integration is disabled');
return this.createEmptyStats(userId, BigInt(0));
}
try {
this.logger.debug(`[HTTP] GET /internal/team-statistics/user/${userId}`);
const response = await this.httpClient.get<ReferralTeamStatsResponse | null>(
`/api/v1/internal/team-statistics/user/${userId}`,
);
if (!response.data) {
this.logger.debug(`[HTTP] No stats found for userId: ${userId}`);
return this.createEmptyStats(userId, BigInt(0));
}
const data = response.data;
this.logger.debug(`[HTTP] Got stats for userId ${userId}: totalTeamPlantingCount=${data.totalTeamPlantingCount}`);
return new TeamStatisticsAdapter(
data.userId,
BigInt(data.accountSequence || 0),
data.totalTeamPlantingCount,
data.provinceCityDistribution,
);
} catch (error) {
this.logger.error(`[HTTP] Failed to get stats for userId ${userId}:`, error);
// 返回空数据而不是抛出错误,避免影响主流程
return this.createEmptyStats(userId, BigInt(0));
}
}
/**
* 根据 accountSequence 查询团队统计
*/
async findByAccountSequence(accountSequence: bigint): Promise<TeamStatistics | null> {
if (!this.enabled) {
this.logger.debug('[DISABLED] Referral service integration is disabled');
return this.createEmptyStats('', accountSequence);
}
try {
this.logger.debug(`[HTTP] GET /internal/team-statistics/account/${accountSequence}`);
const response = await this.httpClient.get<ReferralTeamStatsResponse | null>(
`/api/v1/internal/team-statistics/account/${accountSequence}`,
);
if (!response.data) {
this.logger.debug(`[HTTP] No stats found for accountSequence: ${accountSequence}`);
return this.createEmptyStats('', accountSequence);
}
const data = response.data;
this.logger.debug(`[HTTP] Got stats for accountSequence ${accountSequence}: totalTeamPlantingCount=${data.totalTeamPlantingCount}`);
return new TeamStatisticsAdapter(
data.userId,
BigInt(data.accountSequence || accountSequence.toString()),
data.totalTeamPlantingCount,
data.provinceCityDistribution,
);
} catch (error) {
this.logger.error(`[HTTP] Failed to get stats for accountSequence ${accountSequence}:`, error);
// 返回空数据而不是抛出错误,避免影响主流程
return this.createEmptyStats('', accountSequence);
}
}
/**
* 创建空的统计数据
*/
private createEmptyStats(userId: string, accountSequence: bigint): TeamStatistics {
return new TeamStatisticsAdapter(userId, accountSequence, 0, null);
}
/**
* 获取用户的祖先链(推荐链)
* 返回从直接推荐人到根节点的 accountSequence 列表
*/
async getReferralChain(accountSequence: bigint): Promise<number[]> {
if (!this.enabled) {
this.logger.debug('[DISABLED] Referral service integration is disabled');
return [];
}
try {
this.logger.debug(`[HTTP] GET /internal/referral-chain/${accountSequence}`);
const response = await this.httpClient.get<ReferralChainResponse>(
`/api/v1/internal/referral-chain/${accountSequence}`,
);
if (!response.data || !response.data.ancestorPath) {
return [];
}
// ancestorPath 存储的是 userId (bigint string),我们需要映射到 accountSequence
// 由于 referral-service 中 userId = BigInt(accountSequence),可以直接转换
return response.data.ancestorPath.map((id) => Number(id));
} catch (error) {
this.logger.error(`[HTTP] Failed to get referral chain for accountSequence ${accountSequence}:`, error);
return [];
}
}
/**
* 获取用户的团队成员 accountSequence 列表
*/
async getTeamMembers(accountSequence: bigint): Promise<number[]> {
if (!this.enabled) {
this.logger.debug('[DISABLED] Referral service integration is disabled');
return [];
}
try {
this.logger.debug(`[HTTP] GET /internal/referral-chain/${accountSequence}/team-members`);
const response = await this.httpClient.get<TeamMembersResponse>(
`/api/v1/internal/referral-chain/${accountSequence}/team-members`,
);
if (!response.data || !response.data.teamMembers) {
return [];
}
return response.data.teamMembers;
} catch (error) {
this.logger.error(`[HTTP] Failed to get team members for accountSequence ${accountSequence}:`, error);
return [];
}
}
}