refactor: 移除刷新代理钱包接口,增加copyRatio精度支持
- 移除刷新代理钱包相关接口和方法 - AccountController: 移除 /refresh-proxy 和 /refresh-all-proxies 接口 - AccountService: 移除 refreshProxyAddress 和 refreshAllProxyAddresses 方法 - 增加 copyRatio 字段精度支持 - CopyTrading: copyRatio 精度从 DECIMAL(10,2) 增加到 DECIMAL(20,8) - CopyTradingTemplate: copyRatio 精度从 DECIMAL(10,2) 增加到 DECIMAL(20,8) - 新增数据库迁移脚本 V18__increase_copy_ratio_precision.sql - 前端工具函数 - 添加 formatNumber 函数用于格式化数字显示 修复的文件: - AccountController.kt - AccountService.kt - CopyTrading.kt - CopyTradingTemplate.kt - frontend/src/utils/index.ts - V18__increase_copy_ratio_precision.sql
This commit is contained in:
-59
@@ -95,65 +95,6 @@ class AccountController(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新账户的代理地址
|
||||
* 使用最新的代理地址计算逻辑(支持 Magic 和 Safe 两种类型)
|
||||
*/
|
||||
@PostMapping("/refresh-proxy")
|
||||
fun refreshProxyAddress(@RequestBody request: AccountDetailRequest): ResponseEntity<ApiResponse<AccountDto>> {
|
||||
return try {
|
||||
if (request.accountId == null || request.accountId <= 0) {
|
||||
return ResponseEntity.ok(ApiResponse.error(ErrorCode.PARAM_ACCOUNT_ID_INVALID, messageSource = messageSource))
|
||||
}
|
||||
|
||||
val result = accountService.refreshProxyAddress(request.accountId)
|
||||
result.fold(
|
||||
onSuccess = { account ->
|
||||
ResponseEntity.ok(ApiResponse.success(account))
|
||||
},
|
||||
onFailure = { e ->
|
||||
logger.error("刷新代理地址失败: ${e.message}", e)
|
||||
when (e) {
|
||||
is IllegalArgumentException -> ResponseEntity.ok(
|
||||
ApiResponse.error(
|
||||
ErrorCode.PARAM_ERROR,
|
||||
e.message,
|
||||
messageSource
|
||||
)
|
||||
)
|
||||
|
||||
else -> ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_ERROR, e.message, messageSource))
|
||||
}
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
logger.error("刷新代理地址异常: ${e.message}", e)
|
||||
ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_ERROR, e.message, messageSource))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量刷新所有账户的代理地址
|
||||
*/
|
||||
@PostMapping("/refresh-all-proxies")
|
||||
fun refreshAllProxyAddresses(): ResponseEntity<ApiResponse<List<AccountDto>>> {
|
||||
return try {
|
||||
val result = accountService.refreshAllProxyAddresses()
|
||||
result.fold(
|
||||
onSuccess = { accounts ->
|
||||
ResponseEntity.ok(ApiResponse.success(accounts))
|
||||
},
|
||||
onFailure = { e ->
|
||||
logger.error("批量刷新代理地址失败: ${e.message}", e)
|
||||
ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_ERROR, e.message, messageSource))
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
logger.error("批量刷新代理地址异常: ${e.message}", e)
|
||||
ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_ERROR, e.message, messageSource))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除账户
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,7 @@ data class CopyTrading(
|
||||
@Column(name = "copy_mode", nullable = false, length = 10)
|
||||
val copyMode: String = "RATIO", // "RATIO" 或 "FIXED"
|
||||
|
||||
@Column(name = "copy_ratio", nullable = false, precision = 10, scale = 2)
|
||||
@Column(name = "copy_ratio", nullable = false, precision = 20, scale = 8)
|
||||
val copyRatio: BigDecimal = BigDecimal.ONE, // 仅在 copyMode="RATIO" 时生效
|
||||
|
||||
@Column(name = "fixed_amount", precision = 20, scale = 8)
|
||||
|
||||
@@ -20,7 +20,7 @@ data class CopyTradingTemplate(
|
||||
@Column(name = "copy_mode", nullable = false, length = 10)
|
||||
val copyMode: String = "RATIO", // "RATIO" 或 "FIXED"
|
||||
|
||||
@Column(name = "copy_ratio", nullable = false, precision = 10, scale = 2)
|
||||
@Column(name = "copy_ratio", nullable = false, precision = 20, scale = 8)
|
||||
val copyRatio: BigDecimal = BigDecimal.ONE, // 仅在 copyMode="RATIO" 时生效
|
||||
|
||||
@Column(name = "fixed_amount", precision = 20, scale = 8)
|
||||
|
||||
@@ -202,85 +202,6 @@ class AccountService(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新账户的代理地址
|
||||
* 使用最新的代理地址计算逻辑(支持 Magic 和 Safe 两种类型)
|
||||
*/
|
||||
@Transactional
|
||||
fun refreshProxyAddress(accountId: Long): Result<AccountDto> {
|
||||
return try {
|
||||
val account = accountRepository.findById(accountId)
|
||||
.orElse(null) ?: return Result.failure(IllegalArgumentException("账户不存在"))
|
||||
|
||||
// 重新获取代理地址(使用保存的钱包类型)
|
||||
val proxyAddress = runBlocking {
|
||||
val proxyResult = blockchainService.getProxyAddress(account.walletAddress, account.walletType)
|
||||
if (proxyResult.isSuccess) {
|
||||
proxyResult.getOrNull()
|
||||
?: throw IllegalStateException("获取代理地址返回空值")
|
||||
} else {
|
||||
val error = proxyResult.exceptionOrNull()
|
||||
throw IllegalStateException("获取代理地址失败: ${error?.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// 更新账户
|
||||
val updated = account.copy(
|
||||
proxyAddress = proxyAddress,
|
||||
updatedAt = System.currentTimeMillis()
|
||||
)
|
||||
val saved = accountRepository.save(updated)
|
||||
|
||||
logger.info("刷新代理地址成功: accountId=${accountId}, oldProxy=${account.proxyAddress}, newProxy=${proxyAddress}")
|
||||
Result.success(toDto(saved))
|
||||
} catch (e: Exception) {
|
||||
logger.error("刷新代理地址失败: accountId=${accountId}", e)
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新所有账户的代理地址
|
||||
*/
|
||||
@Transactional
|
||||
fun refreshAllProxyAddresses(): Result<List<AccountDto>> {
|
||||
return try {
|
||||
val accounts = accountRepository.findAll()
|
||||
val updatedAccounts = mutableListOf<AccountDto>()
|
||||
|
||||
accounts.forEach { account ->
|
||||
try {
|
||||
val proxyAddress = runBlocking {
|
||||
val proxyResult = blockchainService.getProxyAddress(account.walletAddress, account.walletType)
|
||||
if (proxyResult.isSuccess) {
|
||||
proxyResult.getOrNull()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (proxyAddress != null && proxyAddress != account.proxyAddress) {
|
||||
val updated = account.copy(
|
||||
proxyAddress = proxyAddress,
|
||||
updatedAt = System.currentTimeMillis()
|
||||
)
|
||||
val saved = accountRepository.save(updated)
|
||||
logger.info("刷新代理地址成功: accountId=${account.id}, oldProxy=${account.proxyAddress}, newProxy=${proxyAddress}")
|
||||
updatedAccounts.add(toDto(saved))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.warn("刷新账户 ${account.id} 代理地址失败: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("批量刷新代理地址完成: 更新了 ${updatedAccounts.size} 个账户")
|
||||
Result.success(updatedAccounts)
|
||||
} catch (e: Exception) {
|
||||
logger.error("批量刷新代理地址失败", e)
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除账户
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
-- ============================================
|
||||
-- V18: 增加 copy_ratio 字段的精度,支持更小的小数值
|
||||
-- ============================================
|
||||
|
||||
-- 修改 copy_trading 表的 copy_ratio 字段精度
|
||||
-- 从 DECIMAL(10, 2) 改为 DECIMAL(20, 8),支持更小的跟单比例值(如 0.001)
|
||||
ALTER TABLE copy_trading
|
||||
MODIFY COLUMN copy_ratio DECIMAL(20, 8) NOT NULL DEFAULT 1.00000000 COMMENT '跟单比例(仅在copyMode=RATIO时生效)';
|
||||
|
||||
-- 修改 copy_trading_templates 表的 copy_ratio 字段精度
|
||||
-- 从 DECIMAL(10, 2) 改为 DECIMAL(20, 8),支持更小的跟单比例值(如 0.001)
|
||||
ALTER TABLE copy_trading_templates
|
||||
MODIFY COLUMN copy_ratio DECIMAL(20, 8) NOT NULL DEFAULT 1.00000000 COMMENT '跟单比例(仅在copyMode=RATIO时生效)';
|
||||
|
||||
@@ -1,3 +1,34 @@
|
||||
/**
|
||||
* 格式化数字,自动去除尾随零
|
||||
* @param value - 数字值(字符串或数字)
|
||||
* @param maxDecimals - 最大小数位数(默认不限制)
|
||||
* @returns 格式化后的字符串,如果值为空或无效则返回 ''
|
||||
* @example
|
||||
* formatNumber(100.00) => "100"
|
||||
* formatNumber(100.50) => "100.5"
|
||||
* formatNumber(100.55) => "100.55"
|
||||
*/
|
||||
export const formatNumber = (value: string | number | undefined | null, maxDecimals?: number): string => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return ''
|
||||
}
|
||||
|
||||
const num = typeof value === 'string' ? parseFloat(value) : value
|
||||
if (isNaN(num)) {
|
||||
return ''
|
||||
}
|
||||
|
||||
// 如果有最大小数位数限制,先截断
|
||||
if (maxDecimals !== undefined) {
|
||||
const multiplier = Math.pow(10, maxDecimals)
|
||||
const truncated = Math.floor(num * multiplier) / multiplier
|
||||
return truncated.toFixed(maxDecimals).replace(/\.?0+$/, '')
|
||||
}
|
||||
|
||||
// 直接转换为字符串,然后去除尾随零
|
||||
return num.toString().replace(/\.?0+$/, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化 USDC 金额
|
||||
* 最多显示 4 位小数,自动去除尾随零(截断,不四舍五入)
|
||||
|
||||
Reference in New Issue
Block a user