fix(withdraw): 修复提取功能短信验证和手续费计算
- 修复 wallet-service 调用 identity-service 的 API 路径(添加 /user 前缀) - 修复 identity-client 默认端口从 3001 改为 3000 - 添加 docker-compose 中 IDENTITY_SERVICE_URL 环境变量配置 - 手续费改为按 0.1% 费率动态计算(前后端统一) - 最小提取金额从 10 改为 100 - 文案修改:Kava EVM 网络 → Kava安全网络,接收地址 → 接收账号 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
be09a8beac
commit
9bc7bb1200
|
|
@ -177,6 +177,8 @@ services:
|
|||
- KAFKA_BROKERS=kafka:29092
|
||||
- KAFKA_CLIENT_ID=wallet-service
|
||||
- KAFKA_GROUP_ID=wallet-service-group
|
||||
# Identity Service - 用于短信验证和密码验证
|
||||
- IDENTITY_SERVICE_URL=http://identity-service:3000
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
|
|
|||
|
|
@ -1253,14 +1253,14 @@ export class WalletApplicationService {
|
|||
// =============== Withdrawal ===============
|
||||
|
||||
/**
|
||||
* 提现手续费 (固定费用,可配置)
|
||||
* 提现手续费率 (0.1%)
|
||||
*/
|
||||
private readonly WITHDRAWAL_FEE = 1; // 1 USDT
|
||||
private readonly WITHDRAWAL_FEE_RATE = 0.001;
|
||||
|
||||
/**
|
||||
* 最小提现金额
|
||||
*/
|
||||
private readonly MIN_WITHDRAWAL_AMOUNT = 10; // 10 USDT
|
||||
private readonly MIN_WITHDRAWAL_AMOUNT = 100; // 100 USDT
|
||||
|
||||
/**
|
||||
* 请求提现
|
||||
|
|
@ -1281,10 +1281,12 @@ export class WalletApplicationService {
|
|||
}> {
|
||||
const userId = BigInt(command.userId);
|
||||
const amount = Money.USDT(command.amount);
|
||||
const fee = Money.USDT(this.WITHDRAWAL_FEE);
|
||||
// 计算手续费 = 金额 * 费率
|
||||
const feeAmount = command.amount * this.WITHDRAWAL_FEE_RATE;
|
||||
const fee = Money.USDT(feeAmount);
|
||||
const totalRequired = amount.add(fee);
|
||||
|
||||
this.logger.log(`Processing withdrawal request for user ${userId}: ${command.amount} USDT to ${command.toAddress}`);
|
||||
this.logger.log(`Processing withdrawal request for user ${userId}: ${command.amount} USDT to ${command.toAddress}, fee: ${feeAmount}`);
|
||||
|
||||
// 验证最小提现金额
|
||||
if (command.amount < this.MIN_WITHDRAWAL_AMOUNT) {
|
||||
|
|
@ -1303,7 +1305,7 @@ export class WalletApplicationService {
|
|||
// 验证余额是否足够
|
||||
if (wallet.balances.usdt.available.lessThan(totalRequired)) {
|
||||
throw new BadRequestException(
|
||||
`余额不足: 需要 ${totalRequired.value} USDT (金额 ${command.amount} + 手续费 ${this.WITHDRAWAL_FEE}), 当前可用 ${wallet.balances.usdt.available.value} USDT`,
|
||||
`余额不足: 需要 ${totalRequired.value} USDT (金额 ${command.amount} + 手续费 ${feeAmount.toFixed(2)}), 当前可用 ${wallet.balances.usdt.available.value} USDT`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1333,7 +1335,7 @@ export class WalletApplicationService {
|
|||
amount: Money.signed(-totalRequired.value, 'USDT'),
|
||||
balanceAfter: wallet.balances.usdt.available,
|
||||
refOrderId: savedOrder.orderNo,
|
||||
memo: `Withdrawal freeze: ${command.amount} USDT + ${this.WITHDRAWAL_FEE} USDT fee`,
|
||||
memo: `Withdrawal freeze: ${command.amount} USDT + ${feeAmount.toFixed(2)} USDT fee (${this.WITHDRAWAL_FEE_RATE * 100}%)`,
|
||||
});
|
||||
await this.ledgerRepo.save(freezeEntry);
|
||||
|
||||
|
|
@ -1344,8 +1346,8 @@ export class WalletApplicationService {
|
|||
userId: userId.toString(),
|
||||
walletId: wallet.walletId.toString(),
|
||||
amount: command.amount.toString(),
|
||||
fee: this.WITHDRAWAL_FEE.toString(),
|
||||
netAmount: (command.amount - this.WITHDRAWAL_FEE).toString(),
|
||||
fee: feeAmount.toString(),
|
||||
netAmount: (command.amount - feeAmount).toString(),
|
||||
assetType: 'USDT',
|
||||
chainType: command.chainType,
|
||||
toAddress: command.toAddress,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export class IdentityClientService {
|
|||
private readonly httpClient: AxiosInstance;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
const baseUrl = this.configService.get<string>('IDENTITY_SERVICE_URL', 'http://localhost:3001');
|
||||
const baseUrl = this.configService.get<string>('IDENTITY_SERVICE_URL', 'http://localhost:3000');
|
||||
|
||||
this.httpClient = axios.create({
|
||||
baseURL: baseUrl,
|
||||
|
|
@ -106,7 +106,7 @@ export class IdentityClientService {
|
|||
this.logger.log(`发送提取验证短信: userId=${userId}`);
|
||||
|
||||
await this.httpClient.post(
|
||||
'/sms/send-withdraw-code',
|
||||
'/user/sms/send-withdraw-code',
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
|
|
@ -141,7 +141,7 @@ export class IdentityClientService {
|
|||
this.logger.log(`验证提取短信验证码: userId=${userId}`);
|
||||
|
||||
const response = await this.httpClient.post(
|
||||
'/sms/verify-withdraw-code',
|
||||
'/user/sms/verify-withdraw-code',
|
||||
{ code: smsCode },
|
||||
{
|
||||
headers: {
|
||||
|
|
@ -183,7 +183,7 @@ export class IdentityClientService {
|
|||
this.logger.log(`验证登录密码: userId=${userId}`);
|
||||
|
||||
const response = await this.httpClient.post(
|
||||
'/verify-password',
|
||||
'/user/verify-password',
|
||||
{ password },
|
||||
{
|
||||
headers: {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class _WithdrawConfirmPageState extends ConsumerState<WithdrawConfirmPage> {
|
|||
String? _maskedPhoneNumber;
|
||||
|
||||
/// 手续费率
|
||||
final double _feeRate = 0.001;
|
||||
final double _feeRate = 0.001; // 0.1%
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -465,7 +465,7 @@ class _WithdrawConfirmPageState extends ConsumerState<WithdrawConfirmPage> {
|
|||
const SizedBox(height: 16),
|
||||
_buildDetailRow('提取网络', _getNetworkName(widget.params.network)),
|
||||
const SizedBox(height: 12),
|
||||
_buildDetailRow('接收地址', _formatAddress(widget.params.address)),
|
||||
_buildDetailRow('接收账号', _formatAddress(widget.params.address)),
|
||||
const SizedBox(height: 12),
|
||||
_buildDetailRow('提取数量', '${widget.params.amount.toStringAsFixed(2)} 绿积分'),
|
||||
const SizedBox(height: 12),
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class _WithdrawUsdtPageState extends ConsumerState<WithdrawUsdtPage> {
|
|||
final double _feeRate = 0.001; // 0.1%
|
||||
|
||||
/// 最小提款金额
|
||||
final double _minAmount = 10.0;
|
||||
final double _minAmount = 100.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -181,7 +181,7 @@ class _WithdrawUsdtPageState extends ConsumerState<WithdrawUsdtPage> {
|
|||
|
||||
// 验证地址
|
||||
if (address.isEmpty) {
|
||||
_showErrorSnackBar('请输入接收地址');
|
||||
_showErrorSnackBar('请输入接收账号');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -331,7 +331,7 @@ class _WithdrawUsdtPageState extends ConsumerState<WithdrawUsdtPage> {
|
|||
String _getNetworkDescription(WithdrawNetwork network) {
|
||||
switch (network) {
|
||||
case WithdrawNetwork.kava:
|
||||
return 'Kava EVM 网络';
|
||||
return 'Kava安全网络';
|
||||
case WithdrawNetwork.bsc:
|
||||
return 'BNB Smart Chain 网络';
|
||||
}
|
||||
|
|
@ -674,7 +674,7 @@ class _WithdrawUsdtPageState extends ConsumerState<WithdrawUsdtPage> {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'接收地址',
|
||||
'接收账号',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Inter',
|
||||
|
|
@ -966,7 +966,7 @@ class _WithdrawUsdtPageState extends ConsumerState<WithdrawUsdtPage> {
|
|||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildNoticeItem('请确保接收地址正确,错误地址将导致资产丢失'),
|
||||
_buildNoticeItem('请确保接收账号正确,错误账号将导致资产丢失'),
|
||||
_buildNoticeItem('请选择正确的网络,不同网络之间不可互转'),
|
||||
_buildNoticeItem('提取需要进行手机短信验证'),
|
||||
_buildNoticeItem('提取通常在 1-30 分钟内到账'),
|
||||
|
|
|
|||
Loading…
Reference in New Issue