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 { 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('AUTHORIZATION_SERVICE_URL') || 'http://localhost:3009'; this.timeoutMs = this.configService.get('AUTHORIZATION_SERVICE_TIMEOUT_MS') || 5000; } /** * 查找用户推荐链中最近的社区授权用户 * @param accountSequence 用户的 accountSequence (格式: D + YYMMDD + 5位序号) * @returns 最近社区授权用户的 accountSequence,如果没有则返回 null */ async findNearestCommunity(accountSequence: string): Promise { try { const response = await firstValueFrom( this.httpService .get>( `${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 { try { const response = await firstValueFrom( this.httpService .get>( `${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 { try { const response = await firstValueFrom( this.httpService .get>( `${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, }; } }