rwadurian/backend/services/wallet-service/src/api/controllers/fiat-withdrawal.controller.ts

134 lines
5.3 KiB
TypeScript

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<FiatWithdrawalResponseDTO> {
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<FiatWithdrawalListItemDTO> {
// TODO: 验证订单属于当前用户
return this.fiatWithdrawalService.cancelFiatWithdrawal(orderNo, dto.reason);
}
@Get()
@ApiOperation({ summary: '查询法币提现记录', description: '获取用户的法币提现订单列表' })
@ApiResponse({ status: 200, type: [FiatWithdrawalListItemDTO] })
async getFiatWithdrawals(
@CurrentUser() user: CurrentUserPayload,
): Promise<FiatWithdrawalListItemDTO[]> {
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);
}
}
}