From b13aef944c95b492415a73e3df2b5b3055a828ba Mon Sep 17 00:00:00 2001 From: WrBug Date: Thu, 8 Jan 2026 18:33:49 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=BB=9F=E4=B8=80=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20MarketService=20=E5=B9=B6=E6=94=B9=E7=94=A8=20LRU?= =?UTF-8?q?=20=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主要改进: 1. 将 MarketService 缓存从 ConcurrentHashMap 改为 Caffeine LRU 缓存 - 最多缓存 200 条市场记录 - 自动淘汰最近最少使用的记录 - 提高缓存命中率和性能 2. 添加 Caffeine 依赖 (com.github.ben-manes.caffeine:caffeine:3.1.8) 3. 重构以下服务,统一使用 MarketService 获取市场信息: - OrderPushService: 删除 fetchMarketInfo() 方法,直接使用 MarketService - CopyOrderTrackingService: 移除直接调用 Gamma API 的代码 - AccountService: 订单通知中使用 MarketService - OrderStatusUpdateService: 买入/卖出订单通知使用 MarketService 优势: - 减少重复的 API 调用,利用三级缓存(内存 → 数据库 → API) - 提高代码一致性和可维护性 - 统一市场数据获取逻辑,便于后续扩展 --- backend/build.gradle.kts | 3 + .../service/accounts/AccountService.kt | 79 +++++-------------- .../service/common/MarketService.kt | 31 ++++---- .../copytrading/orders/OrderPushService.kt | 50 ++---------- .../statistics/CopyOrderTrackingService.kt | 54 +++---------- .../statistics/OrderStatusUpdateService.kt | 47 +++-------- 6 files changed, 71 insertions(+), 193 deletions(-) diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index 80afb33..05f5ea7 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -68,6 +68,9 @@ dependencies { // Logging implementation("org.slf4j:slf4j-api") + // Caffeine Cache (LRU) + implementation("com.github.ben-manes.caffeine:caffeine:3.1.8") + // Test testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test") diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/accounts/AccountService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/accounts/AccountService.kt index 13c2ae0..369cda6 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/accounts/AccountService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/accounts/AccountService.kt @@ -11,6 +11,7 @@ import com.wrbug.polymarketbot.util.JsonUtils import com.wrbug.polymarketbot.util.getEventSlug import com.wrbug.polymarketbot.service.common.PolymarketClobService import com.wrbug.polymarketbot.service.common.BlockchainService +import com.wrbug.polymarketbot.service.common.MarketService import com.wrbug.polymarketbot.service.common.PolymarketApiKeyService import com.wrbug.polymarketbot.service.copytrading.orders.OrderPushService import com.wrbug.polymarketbot.service.copytrading.orders.OrderSigningService @@ -37,6 +38,7 @@ class AccountService( private val orderPushService: OrderPushService, private val orderSigningService: OrderSigningService, private val cryptoUtils: CryptoUtils, + private val marketService: MarketService, // 市场信息服务 private val telegramNotificationService: TelegramNotificationService? = null, // 可选,避免循环依赖 private val relayClientService: RelayClientService, private val jsonUtils: JsonUtils @@ -935,35 +937,22 @@ class AccountService( notificationScope.launch { try { // 获取市场信息(标题和slug) - val marketInfo = withContext(Dispatchers.IO) { - try { - val gammaApi = retrofitFactory.createGammaApi() - val marketResponse = gammaApi.listMarkets(conditionIds = listOf(request.marketId)) - if (marketResponse.isSuccessful && marketResponse.body() != null) { - marketResponse.body()!!.firstOrNull() - } else { - null - } - } catch (e: Exception) { - logger.warn("获取市场信息失败: ${e.message}", e) - null - } - } - - val marketTitle = marketInfo?.question ?: request.marketId - + val market = marketService.getMarket(request.marketId) + val marketTitle = market?.title ?: request.marketId + val marketSlug = market?.eventSlug // 跳转用的 slug + // 获取当前语言设置(从 LocaleContextHolder) val locale = try { org.springframework.context.i18n.LocaleContextHolder.getLocale() } catch (e: Exception) { java.util.Locale("zh", "CN") // 默认简体中文 } - + telegramNotificationService?.sendOrderSuccessNotification( orderId = orderId, marketTitle = marketTitle, marketId = request.marketId, - marketSlug = marketInfo.getEventSlug(), // 跳转用的 slug + marketSlug = marketSlug, side = request.side, price = sellPrice, // 直接传递卖出价格 size = sellQuantity.toPlainString(), // 直接传递卖出数量 @@ -1002,34 +991,21 @@ class AccountService( notificationScope.launch { try { // 获取市场信息(标题和slug) - val marketInfo = withContext(Dispatchers.IO) { - try { - val gammaApi = retrofitFactory.createGammaApi() - val marketResponse = gammaApi.listMarkets(conditionIds = listOf(request.marketId)) - if (marketResponse.isSuccessful && marketResponse.body() != null) { - marketResponse.body()!!.firstOrNull() - } else { - null - } - } catch (e: Exception) { - logger.warn("获取市场信息失败: ${e.message}", e) - null - } - } - - val marketTitle = marketInfo?.question ?: request.marketId - + val market = marketService.getMarket(request.marketId) + val marketTitle = market?.title ?: request.marketId + val marketSlug = market?.eventSlug // 跳转用的 slug + // 获取当前语言设置(从 LocaleContextHolder) val locale = try { org.springframework.context.i18n.LocaleContextHolder.getLocale() } catch (e: Exception) { java.util.Locale("zh", "CN") // 默认简体中文 } - + telegramNotificationService?.sendOrderFailureNotification( marketTitle = marketTitle, marketId = request.marketId, - marketSlug = marketInfo.getEventSlug(), // 跳转用的 slug + marketSlug = marketSlug, side = request.side, outcome = null, // 失败时可能没有 outcome price = if (request.orderType == "LIMIT") sellPrice.toString() else "MARKET", @@ -1059,37 +1035,24 @@ class AccountService( notificationScope.launch { try { // 获取市场信息(标题和slug) - val marketInfo = withContext(Dispatchers.IO) { - try { - val gammaApi = retrofitFactory.createGammaApi() - val marketResponse = gammaApi.listMarkets(conditionIds = listOf(request.marketId)) - if (marketResponse.isSuccessful && marketResponse.body() != null) { - marketResponse.body()!!.firstOrNull() - } else { - null - } - } catch (e: Exception) { - logger.warn("获取市场信息失败: ${e.message}", e) - null - } - } - - val marketTitle = marketInfo?.question ?: request.marketId - + val market = marketService.getMarket(request.marketId) + val marketTitle = market?.title ?: request.marketId + val marketSlug = market?.eventSlug // 跳转用的 slug + // 获取当前语言设置(从 LocaleContextHolder) val locale = try { org.springframework.context.i18n.LocaleContextHolder.getLocale() } catch (e: Exception) { java.util.Locale("zh", "CN") // 默认简体中文 } - + // 只传递后端返回的 msg,不传递完整堆栈 val errorMsg = orderResponse.body()?.errorMsg ?: "创建订单失败" - + telegramNotificationService?.sendOrderFailureNotification( marketTitle = marketTitle, marketId = request.marketId, - marketSlug = marketInfo.getEventSlug(), // 跳转用的 slug + marketSlug = marketSlug, side = request.side, outcome = null, // 失败时可能没有 outcome price = if (request.orderType == "LIMIT") sellPrice.toString() else "MARKET", diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/common/MarketService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/common/MarketService.kt index 67df906..95446ab 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/common/MarketService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/common/MarketService.kt @@ -1,5 +1,7 @@ package com.wrbug.polymarketbot.service.common +import com.github.benmanes.caffeine.cache.Cache +import com.github.benmanes.caffeine.cache.Caffeine import com.wrbug.polymarketbot.api.MarketResponse import com.wrbug.polymarketbot.api.PolymarketGammaApi import com.wrbug.polymarketbot.entity.Market @@ -9,7 +11,6 @@ import com.wrbug.polymarketbot.util.getEventSlug import kotlinx.coroutines.runBlocking import org.slf4j.LoggerFactory import org.springframework.stereotype.Service -import java.util.concurrent.ConcurrentHashMap /** * 市场信息服务 @@ -20,11 +21,13 @@ class MarketService( val marketRepository: MarketRepository, // 改为 public,供 MarketPollingService 使用 private val retrofitFactory: RetrofitFactory ) { - + private val logger = LoggerFactory.getLogger(MarketService::class.java) - - // 内存缓存(避免频繁查询数据库) - private val marketCache = ConcurrentHashMap() + + // LRU 缓存(避免频繁查询数据库),最多缓存 200 条记录 + private val marketCache: Cache = Caffeine.newBuilder() + .maximumSize(200) // 最多缓存 200 条记录 + .build() /** * 根据市场ID获取市场信息 @@ -32,15 +35,15 @@ class MarketService( */ fun getMarket(marketId: String): Market? { // 1. 从缓存获取 - marketCache[marketId]?.let { return it } - + marketCache.getIfPresent(marketId)?.let { return it } + // 2. 从数据库查询 val market = marketRepository.findByMarketId(marketId) if (market != null) { - marketCache[marketId] = market + marketCache.put(marketId, market) return market } - + // 3. 从API获取(异步,不阻塞) runBlocking { try { @@ -49,10 +52,10 @@ class MarketService( logger.warn("获取市场信息失败: marketId=$marketId, error=${e.message}") } } - + // 再次从数据库查询(API可能已经保存) return marketRepository.findByMarketId(marketId)?.also { - marketCache[marketId] = it + marketCache.put(marketId, it) } } @@ -87,7 +90,7 @@ class MarketService( val savedMarkets = marketRepository.findByMarketIdIn(missingIds) for (market in savedMarkets) { result[market.marketId] = market - marketCache[market.marketId] = market + marketCache.put(market.marketId, market) } } @@ -192,7 +195,7 @@ class MarketService( } val savedMarket = marketRepository.save(market) - marketCache[marketId] = savedMarket + marketCache.put(marketId, savedMarket) savedMarket } catch (e: Exception) { logger.error("保存市场信息失败: marketId=$marketId, error=${e.message}", e) @@ -204,7 +207,7 @@ class MarketService( * 清除缓存(用于测试或手动刷新) */ fun clearCache() { - marketCache.clear() + marketCache.invalidateAll() } } diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/orders/OrderPushService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/orders/OrderPushService.kt index c68c3ba..de1d03b 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/orders/OrderPushService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/orders/OrderPushService.kt @@ -1,9 +1,7 @@ package com.wrbug.polymarketbot.service.copytrading.orders import com.fasterxml.jackson.databind.ObjectMapper -import com.wrbug.polymarketbot.api.MarketResponse import com.wrbug.polymarketbot.dto.OrderDetailDto -import com.wrbug.polymarketbot.util.getEventSlug import com.wrbug.polymarketbot.dto.OrderMessageDto import com.wrbug.polymarketbot.dto.OrderPushMessage import com.wrbug.polymarketbot.entity.Account @@ -20,6 +18,7 @@ import com.wrbug.polymarketbot.util.CryptoUtils import com.wrbug.polymarketbot.repository.CopyOrderTrackingRepository import com.wrbug.polymarketbot.repository.CopyTradingRepository import com.wrbug.polymarketbot.repository.LeaderRepository +import com.wrbug.polymarketbot.service.common.MarketService import org.springframework.stereotype.Service import java.util.concurrent.ConcurrentHashMap @@ -36,7 +35,8 @@ class OrderPushService( private val cryptoUtils: CryptoUtils, private val copyOrderTrackingRepository: CopyOrderTrackingRepository? = null, // 可选,避免循环依赖 private val copyTradingRepository: CopyTradingRepository? = null, // 可选,避免循环依赖 - private val leaderRepository: LeaderRepository? = null // 可选,避免循环依赖 + private val leaderRepository: LeaderRepository? = null, // 可选,避免循环依赖 + private val marketService: MarketService // 市场信息服务 ) { private val logger = LoggerFactory.getLogger(OrderPushService::class.java) @@ -423,8 +423,8 @@ class OrderPushService( result.fold( onSuccess = { openOrder -> - // 获取市场信息(通过 Gamma API) - val marketInfo = fetchMarketInfo(conditionId ?: openOrder.market) + // 获取市场信息(使用 MarketService,优先从数据库/缓存获取) + val market = marketService.getMarket(conditionId ?: openOrder.market) // 转换为 DTO // 注意:createdAt 是 unix timestamp (Long),需要转换为字符串 @@ -437,9 +437,9 @@ class OrderPushService( filled = openOrder.sizeMatched, // 使用 size_matched status = openOrder.status, createdAt = openOrder.createdAt.toString(), // unix timestamp 转换为字符串 - marketName = marketInfo?.question, - marketSlug = marketInfo?.slug, // 显示用的 slug - marketIcon = marketInfo?.icon + marketName = market?.title, + marketSlug = market?.slug, // 显示用的 slug + marketIcon = market?.icon ) }, onFailure = { e -> @@ -453,40 +453,6 @@ class OrderPushService( } } - /** - * 获取市场信息(通过 Gamma API) - * 文档: https://docs.polymarket.com/api-reference/markets/list-markets - * - * 使用 /markets 接口,通过 condition_ids 查询参数获取市场信息 - * 订单返回的 market 字段是 16 进制的 condition ID(如 "0x...") - */ - private suspend fun fetchMarketInfo(conditionId: String): MarketResponse? { - return try { - // 创建 Gamma API 客户端(公开 API,不需要认证) - val gammaApi = retrofitFactory.createGammaApi() - - // 调用 Gamma API 获取市场信息 - // 使用 /markets 接口,通过 condition_ids 查询参数 - val response = gammaApi.listMarkets( - conditionIds = listOf(conditionId), - includeTag = null - ) - if (response.isSuccessful && response.body() != null) { - val markets = response.body()!! - if (markets.isNotEmpty()) { - val market = markets.first() - return market - } else { - return null - } - } else { - null - } - } catch (e: Exception) { - null - } - } - /** * 订阅账户的订单推送(保留用于向后兼容) */ diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/CopyOrderTrackingService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/CopyOrderTrackingService.kt index 94d3808..d607deb 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/CopyOrderTrackingService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/CopyOrderTrackingService.kt @@ -7,7 +7,6 @@ import com.wrbug.polymarketbot.entity.* import com.wrbug.polymarketbot.repository.* import com.wrbug.polymarketbot.util.RetrofitFactory import com.wrbug.polymarketbot.util.* -import com.wrbug.polymarketbot.util.getEventSlug import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -20,6 +19,7 @@ import com.wrbug.polymarketbot.service.copytrading.configs.CopyTradingFilterServ import com.wrbug.polymarketbot.service.copytrading.configs.FilterStatus import com.wrbug.polymarketbot.service.copytrading.orders.OrderSigningService import com.wrbug.polymarketbot.service.common.BlockchainService +import com.wrbug.polymarketbot.service.common.MarketService import com.wrbug.polymarketbot.service.common.PolymarketClobService import com.wrbug.polymarketbot.service.system.TelegramNotificationService import com.wrbug.polymarketbot.util.CryptoUtils @@ -49,6 +49,7 @@ open class CopyOrderTrackingService( private val clobService: PolymarketClobService, private val retrofitFactory: RetrofitFactory, private val cryptoUtils: CryptoUtils, + private val marketService: MarketService, // 市场信息服务 private val telegramNotificationService: TelegramNotificationService? = null // 可选,避免循环依赖 ) { @@ -269,11 +270,8 @@ open class CopyOrderTrackingService( var marketTitle: String? = null if (copyTrading.keywordFilterMode != null && copyTrading.keywordFilterMode != "DISABLED") { try { - val gammaApi = retrofitFactory.createGammaApi() - val marketResponse = gammaApi.listMarkets(conditionIds = listOf(trade.market)) - if (marketResponse.isSuccessful && marketResponse.body() != null) { - marketTitle = marketResponse.body()!!.firstOrNull()?.question - } + val market = marketService.getMarket(trade.market) + marketTitle = market?.title } catch (e: Exception) { logger.warn("获取市场标题失败(关键字过滤需要): ${e.message}", e) } @@ -300,23 +298,9 @@ open class CopyOrderTrackingService( notificationScope.launch { try { // 获取市场信息(标题和slug) - val marketInfo = withContext(Dispatchers.IO) { - try { - val gammaApi = retrofitFactory.createGammaApi() - val marketResponse = gammaApi.listMarkets(conditionIds = listOf(trade.market)) - if (marketResponse.isSuccessful && marketResponse.body() != null) { - marketResponse.body()!!.firstOrNull() - } else { - null - } - } catch (e: Exception) { - logger.warn("获取市场信息失败: ${e.message}", e) - null - } - } - - val marketTitle = marketInfo?.question ?: trade.market - val marketSlug = marketInfo?.slug // 显示用的 slug + val market = marketService.getMarket(trade.market) + val marketTitle = market?.title ?: trade.market + val marketSlug = market?.slug // 显示用的 slug // 从过滤结果中提取 filterType val filterType = extractFilterType(filterResult.status, filterResult.reason) @@ -365,7 +349,7 @@ open class CopyOrderTrackingService( telegramNotificationService?.sendOrderFilteredNotification( marketTitle = marketTitle, marketId = trade.market, - marketSlug = marketInfo.getEventSlug(), // 跳转用的 slug + marketSlug = marketSlug, side = "BUY", outcome = trade.outcome, price = trade.price, @@ -571,23 +555,9 @@ open class CopyOrderTrackingService( notificationScope.launch { try { // 获取市场信息(标题和slug) - val marketInfo = withContext(Dispatchers.IO) { - try { - val gammaApi = retrofitFactory.createGammaApi() - val marketResponse = - gammaApi.listMarkets(conditionIds = listOf(trade.market)) - if (marketResponse.isSuccessful && marketResponse.body() != null) { - marketResponse.body()!!.firstOrNull() - } else { - null - } - } catch (e: Exception) { - logger.warn("获取市场信息失败: ${e.message}", e) - null - } - } - - val marketTitle = marketInfo?.question ?: trade.market + val market = marketService.getMarket(trade.market) + val marketTitle = market?.title ?: trade.market + val marketSlug = market?.eventSlug // 跳转用的 slug // 获取当前语言设置(从 LocaleContextHolder) val locale = try { @@ -599,7 +569,7 @@ open class CopyOrderTrackingService( telegramNotificationService?.sendOrderFailureNotification( marketTitle = marketTitle, marketId = trade.market, - marketSlug = marketInfo.getEventSlug(), // 跳转用的 slug + marketSlug = marketSlug, side = "BUY", outcome = null, // 失败时可能没有 outcome price = buyPrice.toString(), diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/OrderStatusUpdateService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/OrderStatusUpdateService.kt index f6a93ac..8ced0b7 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/OrderStatusUpdateService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/OrderStatusUpdateService.kt @@ -3,12 +3,12 @@ package com.wrbug.polymarketbot.service.copytrading.statistics import com.wrbug.polymarketbot.api.PolymarketClobApi import com.wrbug.polymarketbot.entity.* import com.wrbug.polymarketbot.repository.* +import com.wrbug.polymarketbot.service.common.MarketService import com.wrbug.polymarketbot.service.system.TelegramNotificationService import com.wrbug.polymarketbot.util.RetrofitFactory import com.wrbug.polymarketbot.util.CryptoUtils import com.wrbug.polymarketbot.util.toSafeBigDecimal import com.wrbug.polymarketbot.util.multi -import com.wrbug.polymarketbot.util.getEventSlug import kotlinx.coroutines.* import org.slf4j.LoggerFactory import org.springframework.boot.context.event.ApplicationReadyEvent @@ -34,6 +34,7 @@ class OrderStatusUpdateService( private val retrofitFactory: RetrofitFactory, private val cryptoUtils: CryptoUtils, private val trackingService: CopyOrderTrackingService, + private val marketService: MarketService, // 市场信息服务 private val telegramNotificationService: TelegramNotificationService? ) { @@ -713,24 +714,10 @@ class OrderStatusUpdateService( logger.warn("账户不存在,跳过发送通知: accountId=${order.accountId}") return } - + // 获取市场信息 - val marketInfo = withContext(Dispatchers.IO) { - try { - val gammaApi = retrofitFactory.createGammaApi() - val marketResponse = gammaApi.listMarkets(conditionIds = listOf(order.marketId)) - if (marketResponse.isSuccessful && marketResponse.body() != null) { - marketResponse.body()!!.firstOrNull() - } else { - null - } - } catch (e: Exception) { - logger.warn("获取市场信息失败: ${e.message}", e) - null - } - } - - val marketTitle = marketInfo?.question ?: order.marketId + val market = marketService.getMarket(order.marketId) + val marketTitle = market?.title ?: order.marketId // 获取 Leader 和跟单配置信息 val leader = leaderRepository.findById(order.leaderId).orElse(null) @@ -761,7 +748,7 @@ class OrderStatusUpdateService( orderId = order.buyOrderId, marketTitle = marketTitle, marketId = order.marketId, - marketSlug = marketInfo.getEventSlug(), // 跳转用的 slug + marketSlug = market?.eventSlug, // 跳转用的 slug side = "BUY", price = actualPrice ?: order.price.toString(), // 使用实际价格或临时价格 size = actualSize ?: order.quantity.toString(), // 使用实际数量或临时数量 @@ -817,24 +804,10 @@ class OrderStatusUpdateService( logger.warn("账户不存在,跳过发送通知: accountId=${finalCopyTrading.accountId}") return } - + // 获取市场信息 - val marketInfo = withContext(Dispatchers.IO) { - try { - val gammaApi = retrofitFactory.createGammaApi() - val marketResponse = gammaApi.listMarkets(conditionIds = listOf(record.marketId)) - if (marketResponse.isSuccessful && marketResponse.body() != null) { - marketResponse.body()!!.firstOrNull() - } else { - null - } - } catch (e: Exception) { - logger.warn("获取市场信息失败: ${e.message}", e) - null - } - } - - val marketTitle = marketInfo?.question ?: record.marketId + val market = marketService.getMarket(record.marketId) + val marketTitle = market?.title ?: record.marketId // 获取 Leader 和跟单配置信息 val leader = leaderRepository.findById(finalCopyTrading.leaderId).orElse(null) @@ -865,7 +838,7 @@ class OrderStatusUpdateService( orderId = record.sellOrderId, marketTitle = marketTitle, marketId = record.marketId, - marketSlug = marketInfo.getEventSlug(), // 跳转用的 slug + marketSlug = market?.eventSlug, // 跳转用的 slug side = "SELL", price = actualPrice ?: record.sellPrice.toString(), // 使用实际价格或临时价格 size = actualSize ?: record.totalMatchedQuantity.toString(), // 使用实际数量或临时数量