189 lines
6.4 KiB
TypeScript
189 lines
6.4 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
||
import { ConfigService } from '@nestjs/config';
|
||
import { HttpService } from '@nestjs/axios';
|
||
import { firstValueFrom, timeout, catchError } from 'rxjs';
|
||
import { of } from 'rxjs';
|
||
|
||
// authorization-service 返回格式(经过 TransformInterceptor 包装)
|
||
export interface AuthorizationServiceResponse<T> {
|
||
success: boolean;
|
||
data: T;
|
||
timestamp: string;
|
||
}
|
||
|
||
export interface NearestAuthorizationResult {
|
||
accountSequence: string | null; // 格式: D + YYMMDD + 5位序号
|
||
}
|
||
|
||
/**
|
||
* Authorization Service 客户端
|
||
* 用于查询用户推荐链中最近的省/市/社区授权用户
|
||
*/
|
||
@Injectable()
|
||
export class AuthorizationServiceClient {
|
||
private readonly logger = new Logger(AuthorizationServiceClient.name);
|
||
private readonly baseUrl: string;
|
||
private readonly timeoutMs: number;
|
||
|
||
constructor(
|
||
private readonly configService: ConfigService,
|
||
private readonly httpService: HttpService,
|
||
) {
|
||
this.baseUrl =
|
||
this.configService.get<string>('AUTHORIZATION_SERVICE_URL') ||
|
||
'http://localhost:3009';
|
||
this.timeoutMs = this.configService.get<number>('AUTHORIZATION_SERVICE_TIMEOUT_MS') || 5000;
|
||
}
|
||
|
||
/**
|
||
* 查找用户推荐链中最近的社区授权用户
|
||
* @param accountSequence 用户的 accountSequence (格式: D + YYMMDD + 5位序号)
|
||
* @returns 最近社区授权用户的 accountSequence,如果没有则返回 null
|
||
*/
|
||
async findNearestCommunity(accountSequence: string): Promise<string | null> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService
|
||
.get<AuthorizationServiceResponse<NearestAuthorizationResult>>(
|
||
`${this.baseUrl}/api/v1/authorization/nearest-community`,
|
||
{
|
||
params: { accountSequence },
|
||
},
|
||
)
|
||
.pipe(
|
||
timeout(this.timeoutMs),
|
||
catchError((error) => {
|
||
this.logger.warn(
|
||
`Failed to find nearest community for accountSequence=${accountSequence}: ${error.message}`,
|
||
);
|
||
return of({ data: { success: false, data: { accountSequence: null }, timestamp: '' } });
|
||
}),
|
||
),
|
||
);
|
||
|
||
// authorization-service 返回格式: { success, data: { accountSequence }, timestamp }
|
||
return response.data.data?.accountSequence ?? null;
|
||
} catch (error) {
|
||
this.logger.error(
|
||
`Error finding nearest community for accountSequence=${accountSequence}`,
|
||
error,
|
||
);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查找用户推荐链中最近的省公司授权用户(匹配指定省份)
|
||
* @param accountSequence 用户的 accountSequence (格式: D + YYMMDD + 5位序号)
|
||
* @param provinceCode 省份代码
|
||
* @returns 最近省公司授权用户的 accountSequence,如果没有则返回 null
|
||
*/
|
||
async findNearestProvince(
|
||
accountSequence: string,
|
||
provinceCode: string,
|
||
): Promise<string | null> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService
|
||
.get<AuthorizationServiceResponse<NearestAuthorizationResult>>(
|
||
`${this.baseUrl}/api/v1/authorization/nearest-province`,
|
||
{
|
||
params: { accountSequence, provinceCode },
|
||
},
|
||
)
|
||
.pipe(
|
||
timeout(this.timeoutMs),
|
||
catchError((error) => {
|
||
this.logger.warn(
|
||
`Failed to find nearest province for accountSequence=${accountSequence}, provinceCode=${provinceCode}: ${error.message}`,
|
||
);
|
||
return of({ data: { success: false, data: { accountSequence: null }, timestamp: '' } });
|
||
}),
|
||
),
|
||
);
|
||
|
||
// authorization-service 返回格式: { success, data: { accountSequence }, timestamp }
|
||
return response.data.data?.accountSequence ?? null;
|
||
} catch (error) {
|
||
this.logger.error(
|
||
`Error finding nearest province for accountSequence=${accountSequence}, provinceCode=${provinceCode}`,
|
||
error,
|
||
);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查找用户推荐链中最近的市公司授权用户(匹配指定城市)
|
||
* @param accountSequence 用户的 accountSequence (格式: D + YYMMDD + 5位序号)
|
||
* @param cityCode 城市代码
|
||
* @returns 最近市公司授权用户的 accountSequence,如果没有则返回 null
|
||
*/
|
||
async findNearestCity(
|
||
accountSequence: string,
|
||
cityCode: string,
|
||
): Promise<string | null> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService
|
||
.get<AuthorizationServiceResponse<NearestAuthorizationResult>>(
|
||
`${this.baseUrl}/api/v1/authorization/nearest-city`,
|
||
{
|
||
params: { accountSequence, cityCode },
|
||
},
|
||
)
|
||
.pipe(
|
||
timeout(this.timeoutMs),
|
||
catchError((error) => {
|
||
this.logger.warn(
|
||
`Failed to find nearest city for accountSequence=${accountSequence}, cityCode=${cityCode}: ${error.message}`,
|
||
);
|
||
return of({ data: { success: false, data: { accountSequence: null }, timestamp: '' } });
|
||
}),
|
||
),
|
||
);
|
||
|
||
// authorization-service 返回格式: { success, data: { accountSequence }, timestamp }
|
||
return response.data.data?.accountSequence ?? null;
|
||
} catch (error) {
|
||
this.logger.error(
|
||
`Error finding nearest city for accountSequence=${accountSequence}, cityCode=${cityCode}`,
|
||
error,
|
||
);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 并行查询所有授权信息
|
||
* 优化性能:同时发起三个请求
|
||
*/
|
||
async findAllNearestAuthorizations(
|
||
accountSequence: string, // 格式: D + YYMMDD + 5位序号
|
||
provinceCode: string,
|
||
cityCode: string,
|
||
): Promise<{
|
||
nearestCommunity: string | null;
|
||
nearestProvinceAuth: string | null;
|
||
nearestCityAuth: string | null;
|
||
}> {
|
||
const [nearestCommunity, nearestProvinceAuth, nearestCityAuth] =
|
||
await Promise.all([
|
||
this.findNearestCommunity(accountSequence),
|
||
this.findNearestProvince(accountSequence, provinceCode),
|
||
this.findNearestCity(accountSequence, cityCode),
|
||
]);
|
||
|
||
this.logger.debug(
|
||
`Authorization lookup for accountSequence=${accountSequence}: ` +
|
||
`community=${nearestCommunity}, province=${nearestProvinceAuth}, city=${nearestCityAuth}`,
|
||
);
|
||
|
||
return {
|
||
nearestCommunity,
|
||
nearestProvinceAuth,
|
||
nearestCityAuth,
|
||
};
|
||
}
|
||
}
|