299 lines
8.2 KiB
TypeScript
299 lines
8.2 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
||
import { ConfigService } from '@nestjs/config';
|
||
import { HttpService } from '@nestjs/axios';
|
||
import { firstValueFrom } from 'rxjs';
|
||
|
||
// 省市公司授权信息
|
||
export interface RegionAuthorization {
|
||
authorizationId: string;
|
||
userId: string;
|
||
roleType: string;
|
||
regionCode: string;
|
||
regionName: string;
|
||
benefitActive: boolean;
|
||
// 算力百分比(正式省公司 1%,正式市公司 2%)
|
||
hashpowerPercent: number;
|
||
}
|
||
|
||
// 社区授权信息
|
||
export interface CommunityAuthorization {
|
||
authorizationId: string;
|
||
userId: string;
|
||
communityName: string;
|
||
benefitActive: boolean;
|
||
}
|
||
|
||
// 系统账户信息
|
||
export interface SystemAccountInfo {
|
||
accountId: string;
|
||
accountType: string;
|
||
regionCode: string | null;
|
||
walletAddress: string | null;
|
||
}
|
||
|
||
// 分配上下文 - 包含授权查询结果
|
||
export interface AllocationContext {
|
||
// 省公司授权(区域权益 15U + 1% 算力)
|
||
provinceCompanyAuth: RegionAuthorization | null;
|
||
// 授权省公司(团队权益 20U)
|
||
authProvinceCompanyAuth: RegionAuthorization | null;
|
||
// 市公司授权(区域权益 35U + 2% 算力)
|
||
cityCompanyAuth: RegionAuthorization | null;
|
||
// 授权市公司(团队权益 40U)
|
||
authCityCompanyAuth: RegionAuthorization | null;
|
||
// 社区授权(80U)
|
||
communityAuth: CommunityAuthorization | null;
|
||
// 系统账户(用于无授权时的fallback)
|
||
systemAccounts: {
|
||
costAccount: SystemAccountInfo;
|
||
operationAccount: SystemAccountInfo;
|
||
hqCommunityAccount: SystemAccountInfo;
|
||
rwadPoolAccount: SystemAccountInfo;
|
||
systemProvinceAccount: SystemAccountInfo;
|
||
systemCityAccount: SystemAccountInfo;
|
||
};
|
||
}
|
||
|
||
@Injectable()
|
||
export class AuthorizationServiceClient {
|
||
private readonly logger = new Logger(AuthorizationServiceClient.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:3005';
|
||
}
|
||
|
||
/**
|
||
* 获取分配上下文 - 查询省市社区授权状态和系统账户
|
||
*/
|
||
async getAllocationContext(
|
||
planterId: string,
|
||
provinceCode: string,
|
||
cityCode: string,
|
||
): Promise<AllocationContext> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService.get<AllocationContext>(
|
||
`${this.baseUrl}/api/v1/allocations/context`,
|
||
{
|
||
params: {
|
||
planterId,
|
||
provinceCode,
|
||
cityCode,
|
||
},
|
||
},
|
||
),
|
||
);
|
||
|
||
return response.data;
|
||
} catch (error) {
|
||
this.logger.error(
|
||
`Failed to get allocation context for planter ${planterId}`,
|
||
error,
|
||
);
|
||
|
||
// 开发环境返回默认数据
|
||
if (this.configService.get('NODE_ENV') === 'development') {
|
||
this.logger.warn(
|
||
'Development mode: returning default allocation context',
|
||
);
|
||
return this.getDefaultAllocationContext(provinceCode, cityCode);
|
||
}
|
||
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取省区域权益接收方
|
||
*/
|
||
async getProvinceAreaRightsRecipient(
|
||
provinceCode: string,
|
||
): Promise<{ recipientType: 'USER' | 'SYSTEM'; recipientId: string; hashpowerPercent: number }> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService.get(
|
||
`${this.baseUrl}/api/v1/authorizations/province/${provinceCode}/area-rights`,
|
||
),
|
||
);
|
||
return response.data;
|
||
} catch (error) {
|
||
this.logger.error(`Failed to get province area rights recipient for ${provinceCode}`, error);
|
||
// 返回系统省账户作为默认
|
||
return {
|
||
recipientType: 'SYSTEM',
|
||
recipientId: `SYSTEM_PROVINCE:${provinceCode}`,
|
||
hashpowerPercent: 0,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取省团队权益接收方
|
||
*/
|
||
async getProvinceTeamRightsRecipient(
|
||
provinceCode: string,
|
||
): Promise<{ recipientType: 'USER' | 'SYSTEM'; recipientId: string }> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService.get(
|
||
`${this.baseUrl}/api/v1/authorizations/province/${provinceCode}/team-rights`,
|
||
),
|
||
);
|
||
return response.data;
|
||
} catch (error) {
|
||
this.logger.error(`Failed to get province team rights recipient for ${provinceCode}`, error);
|
||
return {
|
||
recipientType: 'SYSTEM',
|
||
recipientId: `SYSTEM_PROVINCE:${provinceCode}`,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取市区域权益接收方
|
||
*/
|
||
async getCityAreaRightsRecipient(
|
||
cityCode: string,
|
||
): Promise<{ recipientType: 'USER' | 'SYSTEM'; recipientId: string; hashpowerPercent: number }> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService.get(
|
||
`${this.baseUrl}/api/v1/authorizations/city/${cityCode}/area-rights`,
|
||
),
|
||
);
|
||
return response.data;
|
||
} catch (error) {
|
||
this.logger.error(`Failed to get city area rights recipient for ${cityCode}`, error);
|
||
return {
|
||
recipientType: 'SYSTEM',
|
||
recipientId: `SYSTEM_CITY:${cityCode}`,
|
||
hashpowerPercent: 0,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取市团队权益接收方
|
||
*/
|
||
async getCityTeamRightsRecipient(
|
||
cityCode: string,
|
||
): Promise<{ recipientType: 'USER' | 'SYSTEM'; recipientId: string }> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService.get(
|
||
`${this.baseUrl}/api/v1/authorizations/city/${cityCode}/team-rights`,
|
||
),
|
||
);
|
||
return response.data;
|
||
} catch (error) {
|
||
this.logger.error(`Failed to get city team rights recipient for ${cityCode}`, error);
|
||
return {
|
||
recipientType: 'SYSTEM',
|
||
recipientId: `SYSTEM_CITY:${cityCode}`,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取社区权益接收方
|
||
*/
|
||
async getCommunityRightsRecipient(
|
||
planterId: string,
|
||
): Promise<{ recipientType: 'USER' | 'SYSTEM'; recipientId: string } | null> {
|
||
try {
|
||
const response = await firstValueFrom(
|
||
this.httpService.get(
|
||
`${this.baseUrl}/api/v1/authorizations/user/${planterId}/community-rights`,
|
||
),
|
||
);
|
||
return response.data;
|
||
} catch (error) {
|
||
this.logger.error(`Failed to get community rights recipient for ${planterId}`, error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 通知认种完成,更新考核进度
|
||
*/
|
||
async notifyPlantingCompleted(params: {
|
||
planterId: string;
|
||
treeCount: number;
|
||
provinceCode: string;
|
||
cityCode: string;
|
||
orderId: string;
|
||
}): Promise<void> {
|
||
try {
|
||
await firstValueFrom(
|
||
this.httpService.post(
|
||
`${this.baseUrl}/api/v1/allocations/planting-completed`,
|
||
params,
|
||
),
|
||
);
|
||
} catch (error) {
|
||
this.logger.error('Failed to notify planting completed', error);
|
||
// 不抛出错误,允许继续执行
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取默认分配上下文(开发环境用)
|
||
*/
|
||
private getDefaultAllocationContext(
|
||
provinceCode: string,
|
||
cityCode: string,
|
||
): AllocationContext {
|
||
return {
|
||
provinceCompanyAuth: null,
|
||
authProvinceCompanyAuth: null,
|
||
cityCompanyAuth: null,
|
||
authCityCompanyAuth: null,
|
||
communityAuth: null,
|
||
systemAccounts: {
|
||
costAccount: {
|
||
accountId: '1',
|
||
accountType: 'COST_ACCOUNT',
|
||
regionCode: null,
|
||
walletAddress: null,
|
||
},
|
||
operationAccount: {
|
||
accountId: '2',
|
||
accountType: 'OPERATION_ACCOUNT',
|
||
regionCode: null,
|
||
walletAddress: null,
|
||
},
|
||
hqCommunityAccount: {
|
||
accountId: '3',
|
||
accountType: 'HQ_COMMUNITY',
|
||
regionCode: null,
|
||
walletAddress: null,
|
||
},
|
||
rwadPoolAccount: {
|
||
accountId: '4',
|
||
accountType: 'RWAD_POOL_PENDING',
|
||
regionCode: null,
|
||
walletAddress: null,
|
||
},
|
||
systemProvinceAccount: {
|
||
accountId: `PROVINCE:${provinceCode}`,
|
||
accountType: 'SYSTEM_PROVINCE',
|
||
regionCode: provinceCode,
|
||
walletAddress: null,
|
||
},
|
||
systemCityAccount: {
|
||
accountId: `CITY:${cityCode}`,
|
||
accountType: 'SYSTEM_CITY',
|
||
regionCode: cityCode,
|
||
walletAddress: null,
|
||
},
|
||
},
|
||
};
|
||
}
|
||
}
|