refactor(identity-service): remove deposit/blockchain code, belongs to wallet-service

- Remove DepositController, DepositService, BlockchainQueryService
- Deposit address and balance queries should be in wallet-service
- identity-service now only handles user identity

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-06 23:22:06 -08:00
parent 9ae26d0f1f
commit cf308efecf
6 changed files with 1 additions and 461 deletions

View File

@ -2,11 +2,10 @@ import { Module } from '@nestjs/common';
import { UserAccountController } from './controllers/user-account.controller';
import { AuthController } from './controllers/auth.controller';
import { ReferralsController } from './controllers/referrals.controller';
import { DepositController } from './controllers/deposit.controller';
import { ApplicationModule } from '@/application/application.module';
@Module({
imports: [ApplicationModule],
controllers: [UserAccountController, AuthController, ReferralsController, DepositController],
controllers: [UserAccountController, AuthController, ReferralsController],
})
export class ApiModule {}

View File

@ -1,90 +0,0 @@
import { Controller, Get, UseGuards, Request, Logger } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '@/shared/guards/jwt-auth.guard';
import { DepositService } from '@/application/services/deposit.service';
/**
* DTO
*/
class DepositAddressResponseDto {
kavaAddress: string | null;
bscAddress: string | null;
isValid: boolean;
message?: string;
}
/**
* USDT DTO
*/
class UsdtBalanceDto {
chainType: string;
address: string;
balance: string;
rawBalance: string;
decimals: number;
}
class BalanceResponseDto {
kava: UsdtBalanceDto | null;
bsc: UsdtBalanceDto | null;
}
/**
*
*
* API
*/
@ApiTags('Deposit')
@Controller('api/deposit')
export class DepositController {
private readonly logger = new Logger(DepositController.name);
constructor(private readonly depositService: DepositService) {}
/**
*
*
* KAVA BSC
*
*/
@Get('addresses')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: '获取充值地址' })
@ApiResponse({
status: 200,
description: '成功获取充值地址',
type: DepositAddressResponseDto,
})
@ApiResponse({ status: 400, description: '获取失败' })
@ApiResponse({ status: 401, description: '未授权' })
async getDepositAddresses(@Request() req: any): Promise<DepositAddressResponseDto> {
const userId = req.user.userId;
this.logger.log(`获取充值地址: userId=${userId}`);
return this.depositService.getDepositAddresses(userId);
}
/**
* USDT
*
* KAVA BSC USDT
*/
@Get('balances')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: '查询 USDT 余额' })
@ApiResponse({
status: 200,
description: '成功获取余额',
type: BalanceResponseDto,
})
@ApiResponse({ status: 400, description: '查询失败' })
@ApiResponse({ status: 401, description: '未授权' })
async getUsdtBalances(@Request() req: any): Promise<BalanceResponseDto> {
const userId = req.user.userId;
this.logger.log(`查询 USDT 余额: userId=${userId}`);
return this.depositService.getUsdtBalances(userId);
}
}

View File

@ -1,7 +1,6 @@
import { Module } from '@nestjs/common';
import { UserApplicationService } from './services/user-application.service';
import { TokenService } from './services/token.service';
import { DepositService } from './services/deposit.service';
import { AutoCreateAccountHandler } from './commands/auto-create-account/auto-create-account.handler';
import { RecoverByMnemonicHandler } from './commands/recover-by-mnemonic/recover-by-mnemonic.handler';
import { RecoverByPhoneHandler } from './commands/recover-by-phone/recover-by-phone.handler';
@ -18,7 +17,6 @@ import { InfrastructureModule } from '@/infrastructure/infrastructure.module';
providers: [
UserApplicationService,
TokenService,
DepositService,
AutoCreateAccountHandler,
RecoverByMnemonicHandler,
RecoverByPhoneHandler,
@ -33,7 +31,6 @@ import { InfrastructureModule } from '@/infrastructure/infrastructure.module';
exports: [
UserApplicationService,
TokenService,
DepositService,
AutoCreateAccountHandler,
RecoverByMnemonicHandler,
RecoverByPhoneHandler,

View File

@ -1,194 +0,0 @@
import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import { UserAccountRepositoryImpl } from '@/infrastructure/persistence/repositories/user-account.repository.impl';
import { BlockchainQueryService, UsdtBalance } from '@/infrastructure/external/blockchain/blockchain-query.service';
import { UserId, ChainType } from '@/domain/value-objects';
import { WalletAddress } from '@/domain/entities/wallet-address.entity';
/**
*
*/
export interface DepositAddressResponse {
kavaAddress: string | null;
bscAddress: string | null;
isValid: boolean;
message?: string;
}
/**
*
*/
export interface BalanceResponse {
kava: UsdtBalance | null;
bsc: UsdtBalance | null;
}
/**
*
*
*
*/
@Injectable()
export class DepositService {
private readonly logger = new Logger(DepositService.name);
constructor(
private readonly userAccountRepository: UserAccountRepositoryImpl,
private readonly blockchainQueryService: BlockchainQueryService,
) {}
/**
*
*
* 使
*/
async getDepositAddresses(userId: string): Promise<DepositAddressResponse> {
this.logger.log(`获取充值地址: userId=${userId}`);
const userAccount = await this.userAccountRepository.findById(UserId.create(userId));
if (!userAccount) {
throw new BadRequestException('用户不存在');
}
const walletAddresses = userAccount.getAllWalletAddresses();
if (!walletAddresses || walletAddresses.length === 0) {
return {
kavaAddress: null,
bscAddress: null,
isValid: false,
message: '充值账户异常:未找到钱包地址,请联系客服',
};
}
// 查找 KAVA 和 BSC 地址
const kavaWallet = walletAddresses.find((w: WalletAddress) => w.chainType === ChainType.KAVA);
const bscWallet = walletAddresses.find((w: WalletAddress) => w.chainType === ChainType.BSC);
// 验证地址签名
const validationResults = await Promise.all([
kavaWallet ? this.validateWalletAddress(kavaWallet) : Promise.resolve(true),
bscWallet ? this.validateWalletAddress(bscWallet) : Promise.resolve(true),
]);
const [kavaValid, bscValid] = validationResults;
// 如果有任何一个地址验证失败,返回错误
if (!kavaValid || !bscValid) {
this.logger.warn(`地址验证失败: userId=${userId}, kavaValid=${kavaValid}, bscValid=${bscValid}`);
return {
kavaAddress: null,
bscAddress: null,
isValid: false,
message: '充值账户异常:地址验证失败,请重试或联系客服',
};
}
// 检查地址状态
if (kavaWallet && kavaWallet.status !== 'ACTIVE') {
this.logger.warn(`KAVA 地址状态异常: userId=${userId}, status=${kavaWallet.status}`);
return {
kavaAddress: null,
bscAddress: null,
isValid: false,
message: '充值账户异常KAVA 地址已禁用,请联系客服',
};
}
if (bscWallet && bscWallet.status !== 'ACTIVE') {
this.logger.warn(`BSC 地址状态异常: userId=${userId}, status=${bscWallet.status}`);
return {
kavaAddress: null,
bscAddress: null,
isValid: false,
message: '充值账户异常BSC 地址已禁用,请联系客服',
};
}
return {
kavaAddress: kavaWallet?.address || null,
bscAddress: bscWallet?.address || null,
isValid: true,
};
}
/**
* USDT
*/
async getUsdtBalances(userId: string): Promise<BalanceResponse> {
this.logger.log(`查询 USDT 余额: userId=${userId}`);
// 先获取充值地址
const depositAddresses = await this.getDepositAddresses(userId);
if (!depositAddresses.isValid) {
throw new BadRequestException(depositAddresses.message || '获取充值地址失败');
}
const results: BalanceResponse = {
kava: null,
bsc: null,
};
// 查询 KAVA 余额
if (depositAddresses.kavaAddress) {
try {
results.kava = await this.blockchainQueryService.getUsdtBalance(
'KAVA',
depositAddresses.kavaAddress,
);
} catch (error) {
this.logger.error(`查询 KAVA USDT 余额失败: ${error.message}`);
results.kava = {
chainType: 'KAVA',
address: depositAddresses.kavaAddress,
balance: '0',
rawBalance: '0',
decimals: 6,
};
}
}
// 查询 BSC 余额
if (depositAddresses.bscAddress) {
try {
results.bsc = await this.blockchainQueryService.getUsdtBalance(
'BSC',
depositAddresses.bscAddress,
);
} catch (error) {
this.logger.error(`查询 BSC USDT 余额失败: ${error.message}`);
results.bsc = {
chainType: 'BSC',
address: depositAddresses.bscAddress,
balance: '0',
rawBalance: '0',
decimals: 18,
};
}
}
return results;
}
/**
*
*/
private async validateWalletAddress(wallet: any): Promise<boolean> {
try {
// 如果没有签名数据 (旧版本创建的地址),暂时允许通过
if (!wallet.publicKey || !wallet.mpcSignature?.r) {
this.logger.warn(`钱包地址无签名数据: chainType=${wallet.chainType}`);
return true;
}
// 验证签名
const isValid = await wallet.verifySignature();
if (!isValid) {
this.logger.error(`钱包地址签名验证失败: chainType=${wallet.chainType}, address=${wallet.address}`);
}
return isValid;
} catch (error) {
this.logger.error(`验证钱包地址签名异常: ${error.message}`);
return false;
}
}
}

View File

@ -1,167 +0,0 @@
import { Injectable, Logger } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { firstValueFrom } from 'rxjs';
/**
* USDT
*/
export interface UsdtBalance {
chainType: string;
address: string;
balance: string; // 格式化后的余额 (带小数)
rawBalance: string; // 原始余额 (wei)
decimals: number;
}
/**
* blockchain-service
*/
interface BlockchainBalanceResponse {
chainType: string;
address: string;
usdtBalance: string;
nativeBalance: string;
nativeSymbol: string;
}
/**
* blockchain-service
*/
interface BlockchainMultiChainResponse {
address: string;
balances: BlockchainBalanceResponse[];
}
/**
*
*
* HTTP blockchain-service
*/
@Injectable()
export class BlockchainQueryService {
private readonly logger = new Logger(BlockchainQueryService.name);
private readonly blockchainServiceUrl: string;
// 链的 decimals 配置
private readonly chainDecimals: Record<string, number> = {
KAVA: 6,
BSC: 18,
};
constructor(
private readonly httpService: HttpService,
private readonly configService: ConfigService,
) {
this.blockchainServiceUrl = this.configService.get<string>(
'BLOCKCHAIN_SERVICE_URL',
'http://blockchain-service:3000',
);
this.logger.log(`BlockchainQueryService initialized, URL: ${this.blockchainServiceUrl}`);
}
/**
* USDT
*
* @param chainType (KAVA BSC)
* @param address EVM
*/
async getUsdtBalance(chainType: string, address: string): Promise<UsdtBalance> {
try {
this.logger.debug(`查询 ${chainType} USDT 余额: ${address}`);
const response = await firstValueFrom(
this.httpService.get<BlockchainBalanceResponse>(
`${this.blockchainServiceUrl}/balance`,
{
params: {
chainType,
address,
},
},
),
);
const data = response.data;
this.logger.debug(`${chainType} USDT 余额: ${data.usdtBalance}`);
return {
chainType: data.chainType,
address: data.address,
balance: data.usdtBalance,
rawBalance: this.toRawBalance(data.usdtBalance, chainType),
decimals: this.chainDecimals[chainType] || 6,
};
} catch (error) {
this.logger.error(`查询 ${chainType} USDT 余额失败: ${error.message}`);
throw new Error(`查询余额失败: ${error.message}`);
}
}
/**
* USDT
*
* @param addresses { chainType, address }[]
*/
async getMultipleUsdtBalances(
addresses: Array<{ chainType: string; address: string }>,
): Promise<UsdtBalance[]> {
const results = await Promise.allSettled(
addresses.map(({ chainType, address }) => this.getUsdtBalance(chainType, address)),
);
return results.map((result, index) => {
if (result.status === 'fulfilled') {
return result.value;
}
// 失败时返回零余额
this.logger.warn(`查询余额失败 [${addresses[index].chainType}:${addresses[index].address}]: ${result.reason}`);
return {
chainType: addresses[index].chainType,
address: addresses[index].address,
balance: '0',
rawBalance: '0',
decimals: this.chainDecimals[addresses[index].chainType] || 6,
};
});
}
/**
* (KAVA / BNB)
*/
async getNativeBalance(chainType: string, address: string): Promise<string> {
try {
const response = await firstValueFrom(
this.httpService.get<BlockchainBalanceResponse>(
`${this.blockchainServiceUrl}/balance`,
{
params: {
chainType,
address,
},
},
),
);
return response.data.nativeBalance;
} catch (error) {
this.logger.error(`查询 ${chainType} 原生代币余额失败: ${error.message}`);
throw new Error(`查询余额失败: ${error.message}`);
}
}
/**
* (wei)
*/
private toRawBalance(formattedBalance: string, chainType: string): string {
try {
const decimals = this.chainDecimals[chainType] || 6;
const [intPart, decPart = ''] = formattedBalance.split('.');
const paddedDecPart = decPart.padEnd(decimals, '0').slice(0, decimals);
const rawValue = BigInt(intPart + paddedDecPart);
return rawValue.toString();
} catch {
return '0';
}
}
}

View File

@ -1,6 +1,5 @@
import { Module, Global } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { ConfigModule } from '@nestjs/config';
import { PrismaService } from './persistence/prisma/prisma.service';
import { UserAccountRepositoryImpl } from './persistence/repositories/user-account.repository.impl';
import { MpcKeyShareRepositoryImpl } from './persistence/repositories/mpc-key-share.repository.impl';
@ -10,7 +9,6 @@ import { EventPublisherService } from './kafka/event-publisher.service';
import { MpcEventConsumerService } from './kafka/mpc-event-consumer.service';
import { SmsService } from './external/sms/sms.service';
import { WalletGeneratorServiceImpl } from './external/blockchain/wallet-generator.service.impl';
import { BlockchainQueryService } from './external/blockchain/blockchain-query.service';
import { MpcClientService, MpcWalletService } from './external/mpc';
import { MPC_KEY_SHARE_REPOSITORY } from '@/domain/repositories/mpc-key-share.repository.interface';
import { WalletGeneratorService } from '@/domain/services/wallet-generator.service';
@ -18,7 +16,6 @@ import { WalletGeneratorService } from '@/domain/services/wallet-generator.servi
@Global()
@Module({
imports: [
ConfigModule,
HttpModule.register({
timeout: 300000,
maxRedirects: 5,
@ -42,7 +39,6 @@ import { WalletGeneratorService } from '@/domain/services/wallet-generator.servi
provide: WalletGeneratorService,
useExisting: WalletGeneratorServiceImpl,
},
BlockchainQueryService,
MpcClientService,
MpcWalletService,
],
@ -60,7 +56,6 @@ import { WalletGeneratorService } from '@/domain/services/wallet-generator.servi
SmsService,
WalletGeneratorServiceImpl,
WalletGeneratorService,
BlockchainQueryService,
MpcClientService,
MpcWalletService,
],