From 64391503c2444e75cd09959473e27313c2e559ee Mon Sep 17 00:00:00 2001 From: WrBug Date: Wed, 14 Jan 2026 05:04:31 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E4=BB=93=E4=BD=8D=E9=87=91=E9=A2=9D=E6=9C=AA=E7=94=9F=E6=95=88?= =?UTF-8?q?=E5=92=8C=E4=BA=A4=E6=98=93receipt=E8=A7=A3=E6=9E=90=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复 CopyTradingFilterService 中最大仓位金额(maxPositionValue)未生效的问题 - 当只配置了最大仓位金额但未配置 maxSpread 或 minOrderDepth 时, 仓位检查被跳过导致过滤失效 - 将仓位检查移到 needOrderbook 判断之前,确保始终执行仓位检查 2. 修复 OnChainWsService 中交易 receipt 为 JsonNull 时的空指针问题 - 添加 JsonNull 检查,防止解析失败时出现异常 --- .../configs/CopyTradingFilterService.kt | 14 ++++- .../copytrading/monitor/OnChainWsService.kt | 60 ++++++++++--------- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/CopyTradingFilterService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/CopyTradingFilterService.kt index fb67765..d4c67ee 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/CopyTradingFilterService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/CopyTradingFilterService.kt @@ -71,12 +71,20 @@ class CopyTradingFilterService( } } - // 3. 检查是否需要获取订单簿 - // 只有在配置了需要订单簿的过滤条件时才获取 + // 3. 检查是否需要获取订单簿或需要执行仓位检查 + // 只有在配置了需要订单簿的过滤条件时才获取订单簿 val needOrderbook = copyTrading.maxSpread != null || copyTrading.minOrderDepth != null + // 3.5. 如果不需要订单簿,则跳过订单簿相关的检查,但仍然需要检查仓位限制 if (!needOrderbook) { - // 不需要订单簿,直接通过 + // 仓位检查(如果配置了最大仓位限制且提供了跟单金额和市场ID) + if (copyOrderAmount != null && marketId != null) { + val positionCheck = checkPositionLimits(copyTrading, copyOrderAmount, marketId) + if (!positionCheck.isPassed) { + return positionCheck + } + } + // 通过所有检查 return FilterResult.passed() } diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/monitor/OnChainWsService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/monitor/OnChainWsService.kt index 328ffb4..3665921 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/monitor/OnChainWsService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/monitor/OnChainWsService.kt @@ -1,5 +1,6 @@ package com.wrbug.polymarketbot.service.copytrading.monitor +import com.google.gson.JsonNull import com.wrbug.polymarketbot.api.* import com.wrbug.polymarketbot.entity.Leader import com.wrbug.polymarketbot.repository.LeaderRepository @@ -23,12 +24,12 @@ class OnChainWsService( private val copyOrderTrackingService: CopyOrderTrackingService, private val leaderRepository: LeaderRepository ) { - + private val logger = LoggerFactory.getLogger(OnChainWsService::class.java) - + // 存储需要监听的Leader:leaderId -> Leader private val monitoredLeaders = ConcurrentHashMap() - + /** * 启动链上 WebSocket 监听 * 通过统一服务订阅所有 Leader @@ -40,14 +41,14 @@ class OnChainWsService( stop() return } - + // 更新 Leader 列表 monitoredLeaders.clear() leaders.forEach { leader -> addLeader(leader) } } - + /** * 添加Leader监听 * 通过统一服务订阅该 Leader 的地址 @@ -57,17 +58,17 @@ class OnChainWsService( logger.warn("Leader ID为空,跳过: ${leader.leaderAddress}") return } - + val leaderId = leader.id!! - + // 如果已经在监听列表中,不重复添加 if (monitoredLeaders.containsKey(leaderId)) { logger.debug("Leader 已在监听列表中: ${leader.leaderName} (${leader.leaderAddress})") return } - + monitoredLeaders[leaderId] = leader - + // 通过统一服务订阅 val subscriptionId = "LEADER_$leaderId" unifiedOnChainWsService.subscribe( @@ -79,40 +80,45 @@ class OnChainWsService( handleLeaderTransaction(leaderId, txHash, httpClient, rpcApi) } ) - + logger.info("添加 Leader 监听: ${leader.leaderName} (${leader.leaderAddress})") } - + /** * 处理 Leader 的交易 */ - private suspend fun handleLeaderTransaction(leaderId: Long, txHash: String, httpClient: OkHttpClient, rpcApi: EthereumRpcApi) { + private suspend fun handleLeaderTransaction( + leaderId: Long, + txHash: String, + httpClient: OkHttpClient, + rpcApi: EthereumRpcApi + ) { val leader = monitoredLeaders[leaderId] ?: return - + logger.debug("开始处理 Leader 交易: leaderId=$leaderId, txHash=$txHash, leaderAddress=${leader.leaderAddress}") - + try { // 获取交易 receipt val receiptRequest = JsonRpcRequest( method = "eth_getTransactionReceipt", params = listOf(txHash) ) - + val receiptResponse = rpcApi.call(receiptRequest) if (!receiptResponse.isSuccessful || receiptResponse.body() == null) { logger.warn("获取交易 receipt 失败: leaderId=$leaderId, txHash=$txHash, code=${receiptResponse.code()}") return } - + val receiptRpcResponse = receiptResponse.body()!! - if (receiptRpcResponse.error != null || receiptRpcResponse.result == null) { + if (receiptRpcResponse.error != null || receiptRpcResponse.result == null || receiptRpcResponse.result is JsonNull) { logger.warn("交易 receipt 错误: leaderId=$leaderId, txHash=$txHash, error=${receiptRpcResponse.error}") return } - + // 使用 Gson 解析 receipt JSON val receiptJson = receiptRpcResponse.result.asJsonObject - + // 获取区块号和时间戳 val blockNumber = receiptJson.get("blockNumber")?.asString val blockTimestamp = if (blockNumber != null) { @@ -120,7 +126,7 @@ class OnChainWsService( } else { null } - + // 解析 receipt 中的 Transfer 日志 val logs = receiptJson.getAsJsonArray("logs") ?: run { logger.warn("交易 receipt 中没有日志: leaderId=$leaderId, txHash=$txHash") @@ -128,7 +134,7 @@ class OnChainWsService( } val (erc20Transfers, erc1155Transfers) = OnChainWsUtils.parseReceiptTransfers(logs) logger.debug("解析交易日志: leaderId=$leaderId, txHash=$txHash, erc20Transfers=${erc20Transfers.size}, erc1155Transfers=${erc1155Transfers.size}") - + // 解析交易信息 val trade = OnChainWsUtils.parseTradeFromTransfers( txHash = txHash, @@ -138,7 +144,7 @@ class OnChainWsService( erc1155Transfers = erc1155Transfers, retrofitFactory = retrofitFactory ) - + if (trade != null) { logger.info("成功解析交易: leaderId=$leaderId, txHash=$txHash, side=${trade.side}, market=${trade.market}, size=${trade.size}") // 调用 processTrade 处理交易 @@ -154,21 +160,21 @@ class OnChainWsService( logger.error("处理 Leader 交易失败: leaderId=$leaderId, txHash=$txHash, ${e.message}", e) } } - + /** * 移除Leader监听 * 取消该 Leader 的订阅 */ fun removeLeader(leaderId: Long) { monitoredLeaders.remove(leaderId) - + // 通过统一服务取消订阅 val subscriptionId = "LEADER_$leaderId" unifiedOnChainWsService.unsubscribe(subscriptionId) - + logger.info("移除 Leader 监听: leaderId=$leaderId") } - + /** * 停止监听 */ @@ -180,7 +186,7 @@ class OnChainWsService( } monitoredLeaders.clear() } - + @PreDestroy fun destroy() { stop()