188 lines
7.3 KiB
TypeScript
188 lines
7.3 KiB
TypeScript
import { Controller, Get, Post, Body, UseGuards, Headers, HttpException, HttpStatus } from '@nestjs/common';
|
||
import { ApiTags, ApiOperation, ApiBearerAuth, ApiResponse } from '@nestjs/swagger';
|
||
import { WalletApplicationService } from '@/application/services';
|
||
import { GetMyWalletQuery } from '@/application/queries';
|
||
import { ClaimRewardsCommand, SettleRewardsCommand, RequestWithdrawalCommand } from '@/application/commands';
|
||
import { CurrentUser, CurrentUserPayload } from '@/shared/decorators';
|
||
import { JwtAuthGuard } from '@/shared/guards/jwt-auth.guard';
|
||
import { SettleRewardsDTO, RequestWithdrawalDTO } from '@/api/dto/request';
|
||
import { WalletResponseDTO, WithdrawalResponseDTO, WithdrawalListItemDTO } from '@/api/dto/response';
|
||
import { IdentityClientService } from '@/infrastructure/external/identity';
|
||
|
||
@ApiTags('Wallet')
|
||
@Controller('wallet')
|
||
@UseGuards(JwtAuthGuard)
|
||
@ApiBearerAuth()
|
||
export class WalletController {
|
||
constructor(
|
||
private readonly walletService: WalletApplicationService,
|
||
private readonly identityClient: IdentityClientService,
|
||
) {}
|
||
|
||
@Get('my-wallet')
|
||
@ApiOperation({ summary: '查询我的钱包', description: '获取当前用户的钱包余额、算力和奖励信息' })
|
||
@ApiResponse({ status: 200, type: WalletResponseDTO })
|
||
async getMyWallet(@CurrentUser() user: CurrentUserPayload): Promise<WalletResponseDTO> {
|
||
const query = new GetMyWalletQuery(
|
||
user.accountSequence,
|
||
user.userId,
|
||
);
|
||
return this.walletService.getMyWallet(query);
|
||
}
|
||
|
||
@Post('claim-rewards')
|
||
@ApiOperation({ summary: '领取奖励', description: '将待领取的奖励转为可结算状态' })
|
||
@ApiResponse({ status: 200, description: '领取成功' })
|
||
async claimRewards(@CurrentUser() user: CurrentUserPayload): Promise<{ message: string }> {
|
||
const command = new ClaimRewardsCommand(user.userId);
|
||
await this.walletService.claimRewards(command);
|
||
return { message: 'Rewards claimed successfully' };
|
||
}
|
||
|
||
@Post('settle')
|
||
@ApiOperation({ summary: '结算收益', description: '将可结算的USDT兑换为指定币种' })
|
||
@ApiResponse({ status: 200, description: '结算成功' })
|
||
async settleRewards(
|
||
@CurrentUser() user: CurrentUserPayload,
|
||
@Body() dto: SettleRewardsDTO,
|
||
): Promise<{ settlementOrderId: string }> {
|
||
const command = new SettleRewardsCommand(
|
||
user.userId,
|
||
dto.usdtAmount,
|
||
dto.settleCurrency,
|
||
);
|
||
const orderId = await this.walletService.settleRewards(command);
|
||
return { settlementOrderId: orderId };
|
||
}
|
||
|
||
@Post('withdraw/send-sms')
|
||
@ApiOperation({ summary: '发送提取验证短信', description: '向用户手机发送提取验证码' })
|
||
@ApiResponse({ status: 200, description: '发送成功' })
|
||
async sendWithdrawSms(
|
||
@CurrentUser() user: CurrentUserPayload,
|
||
@Headers('authorization') authHeader: string,
|
||
): Promise<{ message: string }> {
|
||
const token = authHeader?.replace('Bearer ', '') || '';
|
||
|
||
// 调用 identity-service 发送短信验证码
|
||
await this.identityClient.sendWithdrawSmsCode(user.userId, token);
|
||
return { message: '验证码已发送' };
|
||
}
|
||
|
||
@Post('withdraw')
|
||
@ApiOperation({ summary: '申请提现', description: '将USDT提现到指定地址,需要短信验证和密码验证' })
|
||
@ApiResponse({ status: 201, type: WithdrawalResponseDTO })
|
||
async requestWithdrawal(
|
||
@CurrentUser() user: CurrentUserPayload,
|
||
@Body() dto: RequestWithdrawalDTO,
|
||
@Headers('authorization') authHeader: string,
|
||
): Promise<WithdrawalResponseDTO> {
|
||
// 提取 JWT token
|
||
const token = authHeader?.replace('Bearer ', '') || '';
|
||
|
||
// 验证短信验证码
|
||
if (!dto.smsCode) {
|
||
throw new HttpException('请输入短信验证码', HttpStatus.BAD_REQUEST);
|
||
}
|
||
|
||
const isSmsValid = await this.identityClient.verifyWithdrawSmsCode(user.userId, dto.smsCode, token);
|
||
if (!isSmsValid) {
|
||
throw new HttpException('短信验证码错误,请重试', HttpStatus.BAD_REQUEST);
|
||
}
|
||
|
||
// 验证登录密码
|
||
if (!dto.password) {
|
||
throw new HttpException('请输入登录密码', HttpStatus.BAD_REQUEST);
|
||
}
|
||
|
||
const isPasswordValid = await this.identityClient.verifyPassword(user.userId, dto.password, token);
|
||
if (!isPasswordValid) {
|
||
throw new HttpException('登录密码错误,请重试', HttpStatus.BAD_REQUEST);
|
||
}
|
||
|
||
// 处理 toAddress: 如果是 accountSequence 格式,转换为区块链地址
|
||
let actualAddress = dto.toAddress;
|
||
if (dto.toAddress.startsWith('D') && dto.toAddress.length === 12) {
|
||
// accountSequence 格式,需要查询对应的区块链地址
|
||
const resolvedAddress = await this.identityClient.resolveAccountSequenceToAddress(
|
||
dto.toAddress,
|
||
dto.chainType,
|
||
token,
|
||
);
|
||
if (!resolvedAddress) {
|
||
throw new HttpException('无效的充值ID,未找到对应地址', HttpStatus.BAD_REQUEST);
|
||
}
|
||
actualAddress = resolvedAddress;
|
||
}
|
||
|
||
const command = new RequestWithdrawalCommand(
|
||
user.userId,
|
||
dto.amount,
|
||
actualAddress,
|
||
dto.chainType,
|
||
);
|
||
return this.walletService.requestWithdrawal(command);
|
||
}
|
||
|
||
@Get('withdrawals')
|
||
@ApiOperation({ summary: '查询提现记录', description: '获取用户的提现订单列表' })
|
||
@ApiResponse({ status: 200, type: [WithdrawalListItemDTO] })
|
||
async getWithdrawals(
|
||
@CurrentUser() user: CurrentUserPayload,
|
||
): Promise<WithdrawalListItemDTO[]> {
|
||
return this.walletService.getWithdrawals(user.userId);
|
||
}
|
||
|
||
@Get('pending-rewards')
|
||
@ApiOperation({ summary: '查询待领取奖励列表', description: '获取用户的逐笔待领取奖励,包含过期时间' })
|
||
@ApiResponse({ status: 200, description: '待领取奖励列表' })
|
||
async getPendingRewards(
|
||
@CurrentUser() user: CurrentUserPayload,
|
||
): Promise<Array<{
|
||
id: string;
|
||
usdtAmount: number;
|
||
hashpowerAmount: number;
|
||
sourceOrderId: string;
|
||
allocationType: string;
|
||
expireAt: string;
|
||
status: string;
|
||
createdAt: string;
|
||
}>> {
|
||
return this.walletService.getPendingRewards(user.accountSequence);
|
||
}
|
||
|
||
@Get('settleable-rewards')
|
||
@ApiOperation({ summary: '查询可结算奖励列表', description: '获取用户的逐笔可结算奖励(已领取待结算)' })
|
||
@ApiResponse({ status: 200, description: '可结算奖励列表' })
|
||
async getSettleableRewards(
|
||
@CurrentUser() user: CurrentUserPayload,
|
||
): Promise<Array<{
|
||
id: string;
|
||
usdtAmount: number;
|
||
hashpowerAmount: number;
|
||
sourceOrderId: string;
|
||
allocationType: string;
|
||
settledAt: string;
|
||
createdAt: string;
|
||
}>> {
|
||
return this.walletService.getSettleableRewards(user.accountSequence);
|
||
}
|
||
|
||
@Get('expired-rewards')
|
||
@ApiOperation({ summary: '查询已过期奖励列表', description: '获取用户的逐笔已过期奖励(24h未领取)' })
|
||
@ApiResponse({ status: 200, description: '已过期奖励列表' })
|
||
async getExpiredRewards(
|
||
@CurrentUser() user: CurrentUserPayload,
|
||
): Promise<Array<{
|
||
id: string;
|
||
usdtAmount: number;
|
||
hashpowerAmount: number;
|
||
sourceOrderId: string;
|
||
allocationType: string;
|
||
expiredAt: string;
|
||
createdAt: string;
|
||
}>> {
|
||
return this.walletService.getExpiredRewards(user.accountSequence);
|
||
}
|
||
}
|