From db833fdf457659c5c2be6fd5e72befcb0d77e105 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 7 Jan 2026 05:52:36 -0800 Subject: [PATCH] =?UTF-8?q?fix(wallet-service):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E5=88=92=E8=BD=AC=E8=AF=B7=E6=B1=82=20DTO=20?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为 SystemWithdrawalRequestDTO 添加 class-validator 装饰器 - 添加 @ApiProperty 装饰器用于 Swagger 文档 - 使用 @Type(() => Number) 自动转换 amount 类型 - 简化验证逻辑,移除冗余的手动验证 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../system-withdrawal.controller.ts | 85 +++++++------------ 1 file changed, 30 insertions(+), 55 deletions(-) diff --git a/backend/services/wallet-service/src/api/controllers/system-withdrawal.controller.ts b/backend/services/wallet-service/src/api/controllers/system-withdrawal.controller.ts index 86281cb6..b30569bb 100644 --- a/backend/services/wallet-service/src/api/controllers/system-withdrawal.controller.ts +++ b/backend/services/wallet-service/src/api/controllers/system-withdrawal.controller.ts @@ -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,