134 lines
5.3 KiB
TypeScript
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);
|
|
}
|
|
}
|
|
}
|