223 lines
6.9 KiB
TypeScript
223 lines
6.9 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
||
import { ConfigService } from '@nestjs/config';
|
||
import { HttpService } from '@nestjs/axios';
|
||
import { firstValueFrom } from 'rxjs';
|
||
import { SYSTEM_ACCOUNTS } from '../../domain/value-objects/pre-planting-right-amounts';
|
||
|
||
export interface RewardDistributionResult {
|
||
recipientAccountSequence: string;
|
||
isFallback: boolean;
|
||
}
|
||
|
||
/**
|
||
* authorization-service 省/市区域 API 返回的 distributions 数组项
|
||
*/
|
||
interface AreaDistributionItem {
|
||
accountSequence: string;
|
||
treeCount: number;
|
||
reason: string;
|
||
isSystemAccount: boolean;
|
||
}
|
||
|
||
/**
|
||
* authorization-service 全局 TransformInterceptor 包装格式
|
||
* 所有响应都被包装为 { success: boolean, data: T, timestamp: string }
|
||
*/
|
||
interface WrappedResponse<T> {
|
||
success: boolean;
|
||
data: T;
|
||
timestamp: string;
|
||
}
|
||
|
||
@Injectable()
|
||
export class PrePlantingAuthorizationClient {
|
||
private readonly logger = new Logger(PrePlantingAuthorizationClient.name);
|
||
private readonly baseUrl: string;
|
||
|
||
constructor(
|
||
private readonly configService: ConfigService,
|
||
private readonly httpService: HttpService,
|
||
) {
|
||
this.baseUrl =
|
||
this.configService.get<string>('AUTHORIZATION_SERVICE_URL') ||
|
||
'http://localhost:3006';
|
||
}
|
||
|
||
/**
|
||
* 社区权益分配对象
|
||
*/
|
||
async getCommunityDistribution(
|
||
accountSequence: string,
|
||
): Promise<RewardDistributionResult> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService.get<WrappedResponse<{ accountSequence: string }>>(
|
||
`${this.baseUrl}/api/v1/authorization/community-reward-distribution`,
|
||
{ params: { accountSequence } },
|
||
),
|
||
);
|
||
return {
|
||
recipientAccountSequence: response.data.data.accountSequence,
|
||
isFallback: false,
|
||
};
|
||
} catch (error) {
|
||
this.logger.warn(
|
||
`Failed to get community distribution for ${accountSequence}, fallback to HQ`,
|
||
);
|
||
return {
|
||
recipientAccountSequence: SYSTEM_ACCOUNTS.HEADQUARTERS,
|
||
isFallback: true,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 省区域权益分配对象
|
||
*
|
||
* [2026-02-28] 修复:
|
||
* 1. 传 treeCount=0(预种阶段不计入省公司月度考核,等合成1棵树后再累计)
|
||
* 2. 正确解析 { success, data: { distributions: [...] } } 包装格式
|
||
* 3. fallback 路径使用 padEnd(6,'0') 右补零,确保生成 7 位标准格式(如 9440000)
|
||
*/
|
||
async getProvinceAreaDistribution(
|
||
provinceCode: string,
|
||
): Promise<RewardDistributionResult> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService.get<WrappedResponse<{ distributions: AreaDistributionItem[] }>>(
|
||
`${this.baseUrl}/api/v1/authorization/province-area-reward-distribution`,
|
||
{ params: { provinceCode, treeCount: 0 } },
|
||
),
|
||
);
|
||
|
||
const distributions = response.data.data.distributions;
|
||
if (!distributions || distributions.length === 0) {
|
||
throw new Error('Empty distributions returned');
|
||
}
|
||
|
||
// 预种不拆分金额,取第一个分配对象的 accountSequence
|
||
// 如果跨考核达标点有多个 distribution,优先取非系统账户(即省公司)
|
||
const target = distributions.find(d => !d.isSystemAccount) || distributions[0];
|
||
|
||
this.logger.debug(
|
||
`Province area distribution for ${provinceCode}: ${target.accountSequence} (${target.reason})`,
|
||
);
|
||
|
||
return {
|
||
recipientAccountSequence: target.accountSequence,
|
||
isFallback: false,
|
||
};
|
||
} catch (error) {
|
||
this.logger.warn(
|
||
`Failed to get province area distribution for ${provinceCode}, fallback to system province account`,
|
||
);
|
||
return {
|
||
recipientAccountSequence: `9${provinceCode.padEnd(6, '0')}`,
|
||
isFallback: true,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 省团队权益分配对象
|
||
*/
|
||
async getProvinceTeamDistribution(
|
||
accountSequence: string,
|
||
): Promise<RewardDistributionResult> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService.get<WrappedResponse<{ accountSequence: string }>>(
|
||
`${this.baseUrl}/api/v1/authorization/province-team-reward-distribution`,
|
||
{ params: { accountSequence } },
|
||
),
|
||
);
|
||
return {
|
||
recipientAccountSequence: response.data.data.accountSequence,
|
||
isFallback: false,
|
||
};
|
||
} catch (error) {
|
||
this.logger.warn(
|
||
`Failed to get province team distribution, fallback to HQ`,
|
||
);
|
||
return {
|
||
recipientAccountSequence: SYSTEM_ACCOUNTS.HEADQUARTERS,
|
||
isFallback: true,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 市区域权益分配对象
|
||
*
|
||
* [2026-02-28] 修复:与省区域同样的处理
|
||
* 1. 传 treeCount=0(预种阶段不计入市公司月度考核,等合成1棵树后再累计)
|
||
* 2. 正确解析 { success, data: { distributions: [...] } } 包装格式
|
||
* 3. fallback 路径使用 padEnd(6,'0') 右补零,确保生成 7 位标准格式(如 8440100)
|
||
*/
|
||
async getCityAreaDistribution(
|
||
cityCode: string,
|
||
): Promise<RewardDistributionResult> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService.get<WrappedResponse<{ distributions: AreaDistributionItem[] }>>(
|
||
`${this.baseUrl}/api/v1/authorization/city-area-reward-distribution`,
|
||
{ params: { cityCode, treeCount: 0 } },
|
||
),
|
||
);
|
||
|
||
const distributions = response.data.data.distributions;
|
||
if (!distributions || distributions.length === 0) {
|
||
throw new Error('Empty distributions returned');
|
||
}
|
||
|
||
// 预种不拆分金额,取第一个分配对象的 accountSequence
|
||
const target = distributions.find(d => !d.isSystemAccount) || distributions[0];
|
||
|
||
this.logger.debug(
|
||
`City area distribution for ${cityCode}: ${target.accountSequence} (${target.reason})`,
|
||
);
|
||
|
||
return {
|
||
recipientAccountSequence: target.accountSequence,
|
||
isFallback: false,
|
||
};
|
||
} catch (error) {
|
||
this.logger.warn(
|
||
`Failed to get city area distribution for ${cityCode}, fallback to system city account`,
|
||
);
|
||
return {
|
||
recipientAccountSequence: `8${cityCode.padEnd(6, '0')}`,
|
||
isFallback: true,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 市团队权益分配对象
|
||
*/
|
||
async getCityTeamDistribution(
|
||
accountSequence: string,
|
||
): Promise<RewardDistributionResult> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService.get<WrappedResponse<{ accountSequence: string }>>(
|
||
`${this.baseUrl}/api/v1/authorization/city-team-reward-distribution`,
|
||
{ params: { accountSequence } },
|
||
),
|
||
);
|
||
return {
|
||
recipientAccountSequence: response.data.data.accountSequence,
|
||
isFallback: false,
|
||
};
|
||
} catch (error) {
|
||
this.logger.warn(
|
||
`Failed to get city team distribution, fallback to HQ`,
|
||
);
|
||
return {
|
||
recipientAccountSequence: SYSTEM_ACCOUNTS.HEADQUARTERS,
|
||
isFallback: true,
|
||
};
|
||
}
|
||
}
|
||
}
|