fix(wallet-service): 修复系统划转请求 DTO 验证错误

- 为 SystemWithdrawalRequestDTO 添加 class-validator 装饰器
- 添加 @ApiProperty 装饰器用于 Swagger 文档
- 使用 @Type(() => Number) 自动转换 amount 类型
- 简化验证逻辑,移除冗余的手动验证

🤖 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 2026-01-07 05:52:36 -08:00
parent 02fa87f6c8
commit db833fdf45
1 changed files with 30 additions and 55 deletions

View File

@ -19,18 +19,43 @@ import {
ApiOperation,
ApiResponse,
ApiQuery,
ApiBody,
ApiProperty,
} from '@nestjs/swagger';
import { IsString, IsNotEmpty, IsOptional, IsNumber } from 'class-validator';
import { Type } from 'class-transformer';
import { Public } from '@/shared/decorators';
import { SystemWithdrawalApplicationService } from '@/application/services';
// DTO 定义
class SystemWithdrawalRequestDTO {
@ApiProperty({ description: '转出方系统账户序列号', example: 'S0000000003' })
@IsString()
@IsNotEmpty()
fromAccountSequence: string;
@ApiProperty({ description: '接收方用户充值ID', example: 'D25122800032' })
@IsString()
@IsNotEmpty()
toAccountSequence: string;
amount: number | string; // 支持字符串或数字
@ApiProperty({ description: '转出金额(绿积分)', example: 1000 })
@Type(() => Number)
@IsNumber()
amount: number;
@ApiProperty({ description: '备注', required: false, example: '补发奖励' })
@IsString()
@IsOptional()
memo?: string;
@ApiProperty({ description: '操作管理员ID', example: 'admin_001' })
@IsString()
@IsNotEmpty()
operatorId: string;
@ApiProperty({ description: '操作管理员姓名', required: false, example: '管理员张三' })
@IsString()
@IsOptional()
operatorName?: string;
}
@ -56,70 +81,20 @@ export class SystemWithdrawalController {
summary: '发起系统账户转出内部API',
description: '从系统账户(总部、运营、区域等)转出资金到用户账户',
})
@ApiBody({
schema: {
type: 'object',
required: ['fromAccountSequence', 'toAccountSequence', 'amount', 'operatorId'],
properties: {
fromAccountSequence: {
type: 'string',
description: '转出方系统账户序列号',
example: 'S0000000003',
},
toAccountSequence: {
type: 'string',
description: '接收方用户充值ID',
example: 'D25122800032',
},
amount: {
type: 'number',
description: '转出金额(绿积分)',
example: 1000,
},
memo: {
type: 'string',
description: '备注',
example: '补发奖励',
},
operatorId: {
type: 'string',
description: '操作管理员ID',
example: 'admin_001',
},
operatorName: {
type: 'string',
description: '操作管理员姓名',
example: '管理员张三',
},
},
},
})
@ApiResponse({ status: 200, description: '转出订单创建成功' })
@ApiResponse({ status: 400, description: '参数错误或余额不足' })
async requestSystemWithdrawal(@Body() dto: SystemWithdrawalRequestDTO) {
this.logger.log(`[REQUEST] 系统账户转出请求: ${JSON.stringify(dto)}`);
// 验证必填参数
if (!dto.fromAccountSequence) {
throw new BadRequestException('转出账户不能为空');
}
if (!dto.toAccountSequence) {
throw new BadRequestException('接收账户不能为空');
}
// 将 amount 转换为数字(前端可能传入字符串)
const amount = typeof dto.amount === 'string' ? parseFloat(dto.amount) : dto.amount;
if (!amount || isNaN(amount) || amount <= 0) {
// class-validator 已处理基本验证,这里只做业务验证
if (dto.amount <= 0) {
throw new BadRequestException('转出金额必须大于0');
}
if (!dto.operatorId) {
throw new BadRequestException('操作员ID不能为空');
}
const result = await this.systemWithdrawalService.requestSystemWithdrawal({
fromAccountSequence: dto.fromAccountSequence,
toAccountSequence: dto.toAccountSequence,
amount: amount,
amount: dto.amount,
memo: dto.memo,
operatorId: dto.operatorId,
operatorName: dto.operatorName,