import { Controller, Get, Post, Body, Param, UseGuards, Headers, HttpException, HttpStatus } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth, ApiResponse } from '@nestjs/swagger'; import { FiatWithdrawalApplicationService, RequestFiatWithdrawalCommand } from '@/application/services'; import { CurrentUser, CurrentUserPayload } from '@/shared/decorators'; import { JwtAuthGuard } from '@/shared/guards/jwt-auth.guard'; import { RequestFiatWithdrawalDTO, CancelFiatWithdrawalDTO, } from '@/api/dto/request'; import { FiatWithdrawalResponseDTO, FiatWithdrawalListItemDTO } from '@/api/dto/response'; import { IdentityClientService } from '@/infrastructure/external/identity/identity-client.service'; import { PaymentMethod } from '@/domain/value-objects/fiat-withdrawal-status.enum'; @ApiTags('Fiat Withdrawal (法币提现)') @Controller('wallet/fiat-withdrawal') @UseGuards(JwtAuthGuard) @ApiBearerAuth() export class FiatWithdrawalController { constructor( private readonly fiatWithdrawalService: FiatWithdrawalApplicationService, private readonly identityClient: IdentityClientService, ) {} @Post('send-sms') @ApiOperation({ summary: '发送法币提现验证短信', description: '向用户手机发送法币提现验证码' }) @ApiResponse({ status: 200, description: '发送成功' }) async sendFiatWithdrawSms( @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() @ApiOperation({ summary: '申请法币提现', description: '将绿积分提现到银行卡/支付宝/微信,需要短信验证和密码验证' }) @ApiResponse({ status: 201, type: FiatWithdrawalResponseDTO }) async requestFiatWithdrawal( @CurrentUser() user: CurrentUserPayload, @Body() dto: RequestFiatWithdrawalDTO, @Headers('authorization') authHeader: string, ): Promise { 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); } // 验证收款账户信息完整性 this.validatePaymentAccount(dto); const command: RequestFiatWithdrawalCommand = { accountSequence: user.accountSequence, userId: user.userId, amount: dto.amount, paymentMethod: dto.paymentMethod, bankName: dto.bankName, bankCardNo: dto.bankCardNo, cardHolderName: dto.cardHolderName, alipayAccount: dto.alipayAccount, alipayRealName: dto.alipayRealName, wechatAccount: dto.wechatAccount, wechatRealName: dto.wechatRealName, }; return this.fiatWithdrawalService.requestFiatWithdrawal(command); } @Post(':orderNo/cancel') @ApiOperation({ summary: '取消法币提现', description: '取消未审核的法币提现订单' }) @ApiResponse({ status: 200, type: FiatWithdrawalListItemDTO }) async cancelFiatWithdrawal( @CurrentUser() user: CurrentUserPayload, @Param('orderNo') orderNo: string, @Body() dto: CancelFiatWithdrawalDTO, ): Promise { // TODO: 验证订单属于当前用户 return this.fiatWithdrawalService.cancelFiatWithdrawal(orderNo, dto.reason); } @Get() @ApiOperation({ summary: '查询法币提现记录', description: '获取用户的法币提现订单列表' }) @ApiResponse({ status: 200, type: [FiatWithdrawalListItemDTO] }) async getFiatWithdrawals( @CurrentUser() user: CurrentUserPayload, ): Promise { return this.fiatWithdrawalService.getFiatWithdrawals(user.userId); } /** * 验证收款账户信息完整性 */ private validatePaymentAccount(dto: RequestFiatWithdrawalDTO): void { switch (dto.paymentMethod) { case PaymentMethod.BANK_CARD: if (!dto.bankName || !dto.bankCardNo || !dto.cardHolderName) { throw new HttpException('请填写完整的银行卡信息', HttpStatus.BAD_REQUEST); } break; case PaymentMethod.ALIPAY: if (!dto.alipayAccount || !dto.alipayRealName) { throw new HttpException('请填写完整的支付宝信息', HttpStatus.BAD_REQUEST); } break; case PaymentMethod.WECHAT: if (!dto.wechatAccount || !dto.wechatRealName) { throw new HttpException('请填写完整的微信信息', HttpStatus.BAD_REQUEST); } break; default: throw new HttpException('不支持的收款方式', HttpStatus.BAD_REQUEST); } } }