统一 Gson 使用,改为依赖注入方式
- 在 GsonConfig 中统一配置 Gson Bean(lenient 模式) - 所有 Service 类通过构造函数注入 Gson 实例 - RetrofitFactory、BlockchainService、PolymarketApiKeyService 等统一使用注入的 Gson - AccountService、PositionCheckService 添加 JsonUtils 注入 - OnChainWsUtils(object 单例)使用私有 Gson 实例(与 GsonConfig 配置一致) - 移除所有 GsonConverterFactory.create() 无参调用,统一使用注入的 Gson
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
package com.wrbug.polymarketbot.config
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
/**
|
||||
* Gson 配置类
|
||||
* 统一配置 Gson 实例,使用 lenient 模式允许解析格式不严格的 JSON
|
||||
*/
|
||||
@Configuration
|
||||
class GsonConfig {
|
||||
|
||||
/**
|
||||
* 创建 Gson Bean
|
||||
* 使用 lenient 模式,允许解析格式不严格的 JSON
|
||||
*/
|
||||
@Bean
|
||||
fun gson(): Gson {
|
||||
return GsonBuilder()
|
||||
.setLenient()
|
||||
.create()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.wrbug.polymarketbot.config
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.wrbug.polymarketbot.api.PolymarketClobApi
|
||||
import com.wrbug.polymarketbot.util.createClient
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
@@ -18,7 +19,9 @@ import retrofit2.converter.gson.GsonConverterFactory
|
||||
* - 账户 API Key 在调用时动态设置,不在此处配置
|
||||
*/
|
||||
@Configuration
|
||||
class RetrofitConfig {
|
||||
class RetrofitConfig(
|
||||
private val gson: Gson
|
||||
) {
|
||||
|
||||
@Value("\${polymarket.clob.base-url}")
|
||||
private lateinit var clobBaseUrl: String
|
||||
@@ -37,7 +40,7 @@ class RetrofitConfig {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(clobBaseUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build()
|
||||
.create(PolymarketClobApi::class.java)
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ class AccountService(
|
||||
private val orderSigningService: OrderSigningService,
|
||||
private val cryptoUtils: CryptoUtils,
|
||||
private val telegramNotificationService: TelegramNotificationService? = null, // 可选,避免循环依赖
|
||||
private val relayClientService: RelayClientService
|
||||
private val relayClientService: RelayClientService,
|
||||
private val jsonUtils: JsonUtils
|
||||
) {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(AccountService::class.java)
|
||||
@@ -1121,7 +1122,7 @@ class AccountService(
|
||||
// 如果目标 outcome 不是第一个(index != 0),需要转换价格
|
||||
// 对于二元市场:第二个 outcome 的价格 = 1 - 第一个 outcome 的价格
|
||||
if (outcomeIndex != null && outcomeIndex > 0) {
|
||||
val outcomes = JsonUtils.parseStringArray(market.outcomes)
|
||||
val outcomes = jsonUtils.parseStringArray(market.outcomes)
|
||||
// 只对二元市场进行价格转换
|
||||
if (outcomes.size == 2) {
|
||||
// 保存原始第一个 outcome 的价格
|
||||
|
||||
+3
-2
@@ -48,7 +48,8 @@ class PositionCheckService(
|
||||
private val accountRepository: AccountRepository,
|
||||
private val messageSource: MessageSource,
|
||||
private val retrofitFactory: RetrofitFactory,
|
||||
private val blockchainService: BlockchainService
|
||||
private val blockchainService: BlockchainService,
|
||||
private val jsonUtils: JsonUtils
|
||||
) {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(PositionCheckService::class.java)
|
||||
@@ -582,7 +583,7 @@ class PositionCheckService(
|
||||
// 优先使用 outcomePrices(结算价格数组)
|
||||
val outcomePrices = market.outcomePrices
|
||||
if (outcomePrices != null && outcomePrices.isNotBlank()) {
|
||||
val prices = JsonUtils.parseStringArray(outcomePrices)
|
||||
val prices = jsonUtils.parseStringArray(outcomePrices)
|
||||
if (outcomeIndex < prices.size) {
|
||||
val price = prices[outcomeIndex].toSafeBigDecimal()
|
||||
// 如果价格 >= 0.99,认为赢了
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.wrbug.polymarketbot.service.common
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.wrbug.polymarketbot.api.EthereumRpcApi
|
||||
import com.wrbug.polymarketbot.api.JsonRpcRequest
|
||||
import com.wrbug.polymarketbot.api.JsonRpcResponse
|
||||
@@ -29,7 +30,8 @@ class BlockchainService(
|
||||
private val dataApiBaseUrl: String,
|
||||
private val retrofitFactory: RetrofitFactory,
|
||||
private val relayClientService: RelayClientService,
|
||||
private val rpcNodeService: RpcNodeService
|
||||
private val rpcNodeService: RpcNodeService,
|
||||
private val gson: Gson
|
||||
) {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(BlockchainService::class.java)
|
||||
@@ -64,7 +66,7 @@ class BlockchainService(
|
||||
Retrofit.Builder()
|
||||
.baseUrl("$baseUrl/")
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build()
|
||||
.create(PolymarketDataApi::class.java)
|
||||
}
|
||||
|
||||
+5
-3
@@ -1,5 +1,6 @@
|
||||
package com.wrbug.polymarketbot.service.common
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.wrbug.polymarketbot.api.ApiKeyResponse
|
||||
import com.wrbug.polymarketbot.api.PolymarketClobApi
|
||||
import com.wrbug.polymarketbot.util.PolymarketL1AuthInterceptor
|
||||
@@ -19,7 +20,8 @@ import retrofit2.converter.gson.GsonConverterFactory
|
||||
@Service
|
||||
class PolymarketApiKeyService(
|
||||
@Value("\${polymarket.clob.base-url}")
|
||||
private val clobBaseUrl: String
|
||||
private val clobBaseUrl: String,
|
||||
private val gson: Gson
|
||||
) {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(PolymarketApiKeyService::class.java)
|
||||
@@ -222,7 +224,7 @@ class PolymarketApiKeyService(
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(clobBaseUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build()
|
||||
.create(PolymarketClobApi::class.java)
|
||||
}
|
||||
@@ -236,7 +238,7 @@ class PolymarketApiKeyService(
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(clobBaseUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build()
|
||||
.create(PolymarketClobApi::class.java)
|
||||
}
|
||||
|
||||
+2
-3
@@ -22,15 +22,14 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
@Service
|
||||
class CopyTradingWebSocketService(
|
||||
private val copyOrderTrackingService: CopyOrderTrackingService,
|
||||
private val templateRepository: CopyTradingTemplateRepository
|
||||
private val templateRepository: CopyTradingTemplateRepository,
|
||||
private val gson: Gson
|
||||
) {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(CopyTradingWebSocketService::class.java)
|
||||
|
||||
@Value("\${polymarket.websocket.url:wss://ws-live-data.polymarket.com}")
|
||||
private var websocketUrl: String = "wss://ws-live-data.polymarket.com"
|
||||
|
||||
private val gson = Gson()
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
// 存储每个Leader的WebSocket客户端:leaderId -> WebSocketClient
|
||||
|
||||
+30
-12
@@ -1,5 +1,7 @@
|
||||
package com.wrbug.polymarketbot.service.copytrading.monitor
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.wrbug.polymarketbot.api.*
|
||||
@@ -19,6 +21,29 @@ object OnChainWsUtils {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(OnChainWsUtils::class.java)
|
||||
|
||||
// 创建 Gson 实例(与 GsonConfig 中的配置一致,使用 lenient 模式)
|
||||
private val gson: Gson = GsonBuilder()
|
||||
.setLenient()
|
||||
.create()
|
||||
|
||||
/**
|
||||
* 解析 JSON 字符串数组
|
||||
* @param jsonString JSON 字符串,如 "[\"Yes\", \"No\"]"
|
||||
* @return 字符串列表,如果解析失败返回空列表
|
||||
*/
|
||||
private fun parseStringArray(jsonString: String?): List<String> {
|
||||
if (jsonString.isNullOrBlank()) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
return try {
|
||||
val listType = object : TypeToken<List<String>>() {}.type
|
||||
gson.fromJson<List<String>>(jsonString, listType) ?: emptyList()
|
||||
} catch (e: Exception) {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
// 合约地址
|
||||
const val USDC_CONTRACT = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"
|
||||
const val ERC1155_CONTRACT = "0x4d97dcd97ec945f40cf65f87097ace5ea0476045"
|
||||
@@ -240,24 +265,17 @@ object OnChainWsUtils {
|
||||
val clobTokenIds = when {
|
||||
clobTokenIdsRaw == null -> null
|
||||
else -> {
|
||||
try {
|
||||
// 尝试解析 JSON 字符串
|
||||
val gson = com.google.gson.Gson()
|
||||
val listType = object : com.google.gson.reflect.TypeToken<List<String>>() {}.type
|
||||
gson.fromJson<List<String>>(clobTokenIdsRaw, listType)
|
||||
} catch (e: Exception) {
|
||||
// 如果不是 JSON 字符串,可能是其他格式,返回 null
|
||||
null
|
||||
}
|
||||
// 解析 JSON 字符串
|
||||
parseStringArray(clobTokenIdsRaw)
|
||||
}
|
||||
}
|
||||
|
||||
// 解析 outcomes(可能是 JSON 字符串或数组)
|
||||
val outcomes = com.wrbug.polymarketbot.util.JsonUtils.parseStringArray(market.outcomes)
|
||||
val outcomes = parseStringArray(market.outcomes)
|
||||
|
||||
// 查找 tokenId 在 clobTokenIds 中的索引
|
||||
val outcomeIndex = clobTokenIds?.indexOfFirst {
|
||||
it.equals(tokenId, ignoreCase = true)
|
||||
val outcomeIndex = clobTokenIds?.indexOfFirst { token ->
|
||||
token.equals(tokenId, ignoreCase = true)
|
||||
}?.takeIf { it >= 0 }
|
||||
|
||||
// 获取 outcome 名称
|
||||
|
||||
+2
-4
@@ -27,14 +27,12 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
@Service
|
||||
class UnifiedOnChainWsService(
|
||||
private val rpcNodeService: RpcNodeService,
|
||||
private val retrofitFactory: RetrofitFactory
|
||||
private val retrofitFactory: RetrofitFactory,
|
||||
private val gson: Gson
|
||||
) {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(UnifiedOnChainWsService::class.java)
|
||||
|
||||
// Gson 实例,用于解析 JSON
|
||||
private val gson = Gson()
|
||||
|
||||
@Value("\${copy.trading.onchain.ws.reconnect.delay:3000}")
|
||||
private var reconnectDelay: Long = 3000 // 重连延迟(毫秒),默认3秒
|
||||
|
||||
|
||||
@@ -2,14 +2,16 @@ package com.wrbug.polymarketbot.util
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
/**
|
||||
* JSON 工具类
|
||||
* 用于解析 JSON 字符串
|
||||
*/
|
||||
object JsonUtils {
|
||||
|
||||
private val gson = Gson()
|
||||
@Component
|
||||
class JsonUtils(
|
||||
private val gson: Gson
|
||||
) {
|
||||
|
||||
/**
|
||||
* 解析 JSON 字符串数组
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.wrbug.polymarketbot.util
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.wrbug.polymarketbot.api.BuilderRelayerApi
|
||||
import com.wrbug.polymarketbot.api.EthereumRpcApi
|
||||
import com.wrbug.polymarketbot.api.GitHubApi
|
||||
@@ -34,7 +33,8 @@ class RetrofitFactory(
|
||||
@Value("\${polymarket.clob.base-url}")
|
||||
private val clobBaseUrl: String,
|
||||
@Value("\${polymarket.gamma.base-url}")
|
||||
private val gammaBaseUrl: String
|
||||
private val gammaBaseUrl: String,
|
||||
private val gson: Gson
|
||||
) {
|
||||
|
||||
/**
|
||||
@@ -61,11 +61,6 @@ class RetrofitFactory(
|
||||
.addInterceptor(responseLoggingInterceptor)
|
||||
.build()
|
||||
|
||||
// 创建 lenient 模式的 Gson,允许解析格式不严格的 JSON
|
||||
val gson = GsonBuilder()
|
||||
.setLenient()
|
||||
.create()
|
||||
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(clobBaseUrl)
|
||||
.client(okHttpClient)
|
||||
@@ -87,11 +82,6 @@ class RetrofitFactory(
|
||||
.addInterceptor(responseLoggingInterceptor)
|
||||
.build()
|
||||
|
||||
// 创建 lenient 模式的 Gson,允许解析格式不严格的 JSON
|
||||
val gson = GsonBuilder()
|
||||
.setLenient()
|
||||
.create()
|
||||
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(clobBaseUrl)
|
||||
.client(okHttpClient)
|
||||
@@ -129,11 +119,6 @@ class RetrofitFactory(
|
||||
.addInterceptor(urlReplaceInterceptor)
|
||||
.build()
|
||||
|
||||
// 创建 lenient 模式的 Gson
|
||||
val gson = GsonBuilder()
|
||||
.setLenient()
|
||||
.create()
|
||||
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(fixedBaseUrl)
|
||||
.client(okHttpClient)
|
||||
@@ -228,11 +213,6 @@ class RetrofitFactory(
|
||||
}
|
||||
val okHttpClient = createClient().build()
|
||||
|
||||
// 创建 lenient 模式的 Gson
|
||||
val gson = GsonBuilder()
|
||||
.setLenient()
|
||||
.create()
|
||||
|
||||
return Retrofit.Builder()
|
||||
.baseUrl("$baseUrl/")
|
||||
.client(okHttpClient)
|
||||
@@ -253,11 +233,6 @@ class RetrofitFactory(
|
||||
.followSslRedirects(true)
|
||||
.build()
|
||||
|
||||
// 创建 lenient 模式的 Gson
|
||||
val gson = GsonBuilder()
|
||||
.setLenient()
|
||||
.create()
|
||||
|
||||
return Retrofit.Builder()
|
||||
.baseUrl("$baseUrl/")
|
||||
.client(okHttpClient)
|
||||
@@ -292,10 +267,6 @@ class RetrofitFactory(
|
||||
.addInterceptor(builderAuthInterceptor)
|
||||
.build()
|
||||
|
||||
val gson = GsonBuilder()
|
||||
.setLenient()
|
||||
.create()
|
||||
|
||||
return Retrofit.Builder()
|
||||
.baseUrl("$baseUrl/")
|
||||
.client(okHttpClient)
|
||||
@@ -327,10 +298,6 @@ class RetrofitFactory(
|
||||
.addInterceptor(githubInterceptor)
|
||||
.build()
|
||||
|
||||
val gson = GsonBuilder()
|
||||
.setLenient()
|
||||
.create()
|
||||
|
||||
return Retrofit.Builder()
|
||||
.baseUrl("$baseUrl/")
|
||||
.client(okHttpClient)
|
||||
@@ -393,18 +360,32 @@ class ResponseLoggingInterceptor : Interceptor {
|
||||
val responseBody = response.peekBody(2048)
|
||||
val responseBodyString = responseBody.string()
|
||||
|
||||
// 检查是否是有效的 JSON
|
||||
val isJson = responseBodyString.trim().startsWith("{") ||
|
||||
responseBodyString.trim().startsWith("[")
|
||||
// 检查响应体是否为空
|
||||
val isEmpty = responseBodyString.isBlank()
|
||||
|
||||
if (!isJson || !response.isSuccessful) {
|
||||
// 检查是否是有效的 JSON
|
||||
val trimmedBody = responseBodyString.trim()
|
||||
val isJson = !isEmpty && (
|
||||
trimmedBody.startsWith("{") ||
|
||||
trimmedBody.startsWith("[")
|
||||
)
|
||||
|
||||
// 如果响应体为空或不是 JSON,记录警告
|
||||
if (isEmpty || !isJson) {
|
||||
val bodyPreview = if (isEmpty) {
|
||||
"(空响应体)"
|
||||
} else {
|
||||
trimmedBody.take(500)
|
||||
}
|
||||
logger.warn(
|
||||
"API 响应异常: method=${request.method}, url=${request.url}, " +
|
||||
"code=${response.code}, isJson=$isJson, " +
|
||||
"responseBody=${responseBodyString.take(500)}"
|
||||
"code=${response.code}, isJson=$isJson, isEmpty=$isEmpty, " +
|
||||
"responseBody=$bodyPreview"
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// 如果读取响应体失败,记录异常但不影响响应
|
||||
logger.debug("读取响应体失败: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user