其他代码修改

- CopyOrderTrackingRepository: 添加查询方法
- PolymarketClobService: 功能更新
- CopyOrderTrackingService: 逻辑优化
- OrderStatusUpdateService: 功能增强
- TelegramNotificationService: 通知优化
This commit is contained in:
WrBug
2026-01-01 12:03:05 +08:00
parent 5c18cbd95d
commit fe2db11b75
5 changed files with 187 additions and 25 deletions
@@ -55,5 +55,11 @@ interface CopyOrderTrackingRepository : JpaRepository<CopyOrderTracking, Long> {
* 查询未发送通知的买入订单(用于轮询更新) * 查询未发送通知的买入订单(用于轮询更新)
*/ */
fun findByNotificationSentFalse(): List<CopyOrderTracking> fun findByNotificationSentFalse(): List<CopyOrderTracking>
/**
* 查询指定时间之前创建的订单(用于检查30秒后未成交的订单)
*/
@Query("SELECT t FROM CopyOrderTracking t WHERE t.createdAt <= :beforeTime")
fun findByCreatedAtBefore(beforeTime: Long): List<CopyOrderTracking>
} }
@@ -300,13 +300,23 @@ class PolymarketClobService(
) )
val response = authenticatedClobApi.getOrder(orderId) val response = authenticatedClobApi.getOrder(orderId)
if (response.isSuccessful && response.body() != null) { if (response.isSuccessful) {
Result.success(response.body()!!) val body = response.body()
if (body != null) {
Result.success(body)
} else {
// 响应体为空,可能是订单不存在或已过期
logger.warn("获取订单详情失败: 响应体为空, orderId=$orderId, code=${response.code()}")
Result.failure(Exception("订单不存在或已过期: orderId=$orderId"))
}
} else { } else {
Result.failure(Exception("获取订单详情失败: ${response.code()} ${response.message()}")) // HTTP 状态码不是 2xx
val errorBody = response.errorBody()?.string()?.take(200) ?: "无错误详情"
logger.warn("获取订单详情失败: HTTP ${response.code()}, orderId=$orderId, errorBody=$errorBody")
Result.failure(Exception("获取订单详情失败: HTTP ${response.code()} ${response.message()}"))
} }
} catch (e: Exception) { } catch (e: Exception) {
logger.error("获取订单详情异常: ${e.message}", e) logger.error("获取订单详情异常: orderId=$orderId, ${e.message}", e)
Result.failure(e) Result.failure(e)
} }
} }
@@ -1496,12 +1496,18 @@ open class CopyOrderTrackingService(
return try { return try {
// 1. 查询订单详情 // 1. 查询订单详情
val orderResponse = clobApi.getOrder(orderId) val orderResponse = clobApi.getOrder(orderId)
if (!orderResponse.isSuccessful || orderResponse.body() == null) { if (!orderResponse.isSuccessful) {
logger.warn("查询订单详情失败: orderId=$orderId, code=${orderResponse.code()}") val errorBody = orderResponse.errorBody()?.string()?.take(200) ?: "无错误详情"
logger.warn("查询订单详情失败: orderId=$orderId, code=${orderResponse.code()}, errorBody=$errorBody")
return fallbackPrice
}
val order = orderResponse.body()
if (order == null) {
// 响应体为空,可能是订单不存在或已过期
logger.warn("查询订单详情失败: 响应体为空, orderId=$orderId, code=${orderResponse.code()}")
return fallbackPrice return fallbackPrice
} }
val order = orderResponse.body()!!
// 2. 如果订单未成交,使用下单价格 // 2. 如果订单未成交,使用下单价格
if (order.status != "FILLED" && order.sizeMatched.toSafeBigDecimal() <= BigDecimal.ZERO) { if (order.status != "FILLED" && order.sizeMatched.toSafeBigDecimal() <= BigDecimal.ZERO) {
@@ -56,10 +56,13 @@ class OrderStatusUpdateService(
// 1. 清理已删除账户的订单 // 1. 清理已删除账户的订单
cleanupDeletedAccountOrders() cleanupDeletedAccountOrders()
// 2. 更新卖出订单的实际成交价并发送通知(priceUpdated 共用字段) // 2. 检查30秒前创建的订单,如果未成交则删除
checkAndDeleteUnfilledOrders()
// 3. 更新卖出订单的实际成交价并发送通知(priceUpdated 共用字段)
updatePendingSellOrderPrices() updatePendingSellOrderPrices()
// 3. 更新买入订单的实际数据并发送通知 // 4. 更新买入订单的实际数据并发送通知
updatePendingBuyOrders() updatePendingBuyOrders()
} catch (e: Exception) { } catch (e: Exception) {
logger.error("订单状态更新异常: ${e.message}", e) logger.error("订单状态更新异常: ${e.message}", e)
@@ -130,6 +133,124 @@ class OrderStatusUpdateService(
} }
} }
/**
* 检查30秒前创建的订单,如果未成交则删除
* 首次检测但加入缓存中30s后还没有成交,则删除
*/
@Transactional
private suspend fun checkAndDeleteUnfilledOrders() {
try {
// 计算30秒前的时间戳
val thirtySecondsAgo = System.currentTimeMillis() - 30000
// 查询30秒前创建的订单
val ordersToCheck = copyOrderTrackingRepository.findByCreatedAtBefore(thirtySecondsAgo)
if (ordersToCheck.isEmpty()) {
return
}
logger.debug("检查 ${ordersToCheck.size} 个30秒前创建的订单是否成交")
// 按账户分组,避免重复创建 API 客户端
val ordersByAccount = ordersToCheck.groupBy { it.accountId }
for ((accountId, orders) in ordersByAccount) {
try {
// 获取账户
val account = accountRepository.findById(accountId).orElse(null)
if (account == null) {
logger.warn("账户不存在,跳过检查: accountId=$accountId")
continue
}
// 检查账户是否配置了 API 凭证
if (account.apiKey == null || account.apiSecret == null || account.apiPassphrase == null) {
logger.debug("账户未配置 API 凭证,跳过检查: accountId=${account.id}")
continue
}
// 解密 API 凭证
val apiSecret = try {
cryptoUtils.decrypt(account.apiSecret!!)
} catch (e: Exception) {
logger.warn("解密 API Secret 失败: accountId=${account.id}, error=${e.message}")
continue
}
val apiPassphrase = try {
cryptoUtils.decrypt(account.apiPassphrase!!)
} catch (e: Exception) {
logger.warn("解密 API Passphrase 失败: accountId=${account.id}, error=${e.message}")
continue
}
// 创建带认证的 CLOB API 客户端
val clobApi = retrofitFactory.createClobApi(
account.apiKey!!,
apiSecret,
apiPassphrase,
account.walletAddress
)
// 检查每个订单
for (order in orders) {
try {
// 查询订单详情
val orderResponse = clobApi.getOrder(order.buyOrderId)
if (!orderResponse.isSuccessful) {
// HTTP 错误,可能是订单不存在,删除
logger.info("订单查询失败(HTTP错误),删除本地订单: orderId=${order.buyOrderId}, copyOrderTrackingId=${order.id}, code=${orderResponse.code()}")
try {
copyOrderTrackingRepository.deleteById(order.id!!)
logger.info("已删除本地订单: orderId=${order.buyOrderId}, copyOrderTrackingId=${order.id}")
} catch (e: Exception) {
logger.error("删除本地订单失败: orderId=${order.buyOrderId}, copyOrderTrackingId=${order.id}, error=${e.message}", e)
}
continue
}
val orderDetail = orderResponse.body()
if (orderDetail == null) {
// HTTP 200 但响应体为空,表示订单不存在,删除
logger.info("订单不存在(响应体为空),删除本地订单: orderId=${order.buyOrderId}, copyOrderTrackingId=${order.id}, code=${orderResponse.code()}")
try {
copyOrderTrackingRepository.deleteById(order.id!!)
logger.info("已删除本地订单: orderId=${order.buyOrderId}, copyOrderTrackingId=${order.id}")
} catch (e: Exception) {
logger.error("删除本地订单失败: orderId=${order.buyOrderId}, copyOrderTrackingId=${order.id}, error=${e.message}", e)
}
continue
}
// 检查订单是否成交
// 如果订单状态不是 FILLED 且已成交数量为0,说明未成交,删除
val sizeMatched = orderDetail.sizeMatched?.toSafeBigDecimal() ?: BigDecimal.ZERO
if (orderDetail.status != "FILLED" && sizeMatched <= BigDecimal.ZERO) {
logger.info("订单30秒后仍未成交,删除本地订单: orderId=${order.buyOrderId}, copyOrderTrackingId=${order.id}, status=${orderDetail.status}, sizeMatched=$sizeMatched")
try {
copyOrderTrackingRepository.deleteById(order.id!!)
logger.info("已删除未成交订单: orderId=${order.buyOrderId}, copyOrderTrackingId=${order.id}")
} catch (e: Exception) {
logger.error("删除未成交订单失败: orderId=${order.buyOrderId}, copyOrderTrackingId=${order.id}, error=${e.message}", e)
}
} else {
logger.debug("订单已成交或部分成交,保留: orderId=${order.buyOrderId}, status=${orderDetail.status}, sizeMatched=$sizeMatched")
}
} catch (e: Exception) {
logger.error("检查订单失败: orderId=${order.buyOrderId}, error=${e.message}", e)
}
}
} catch (e: Exception) {
logger.error("检查账户订单失败: accountId=$accountId, error=${e.message}", e)
}
}
} catch (e: Exception) {
logger.error("检查未成交订单异常: ${e.message}", e)
}
}
/** /**
* 更新待更新的卖出订单价格 * 更新待更新的卖出订单价格
* 注意:priceUpdated 现在同时表示价格已更新和通知已发送(共用字段) * 注意:priceUpdated 现在同时表示价格已更新和通知已发送(共用字段)
@@ -456,12 +577,24 @@ class OrderStatusUpdateService(
// 查询订单详情 // 查询订单详情
val orderResponse = clobApi.getOrder(order.buyOrderId) val orderResponse = clobApi.getOrder(order.buyOrderId)
if (!orderResponse.isSuccessful || orderResponse.body() == null) { if (!orderResponse.isSuccessful) {
logger.debug("查询订单详情失败,等待下次轮询: orderId=${order.buyOrderId}, code=${orderResponse.code()}") val errorBody = orderResponse.errorBody()?.string()?.take(200) ?: "无错误详情"
logger.debug("查询订单详情失败,等待下次轮询: orderId=${order.buyOrderId}, code=${orderResponse.code()}, errorBody=$errorBody")
continue continue
} }
val orderDetail = orderResponse.body()!! val orderDetail = orderResponse.body()
if (orderDetail == null) {
// HTTP 200 但响应体为空,表示订单不存在(没有交易成功),删除本地订单
logger.info("订单不存在(响应体为空),删除本地订单: orderId=${order.buyOrderId}, copyOrderTrackingId=${order.id}, code=${orderResponse.code()}")
try {
copyOrderTrackingRepository.deleteById(order.id!!)
logger.info("已删除本地订单: orderId=${order.buyOrderId}, copyOrderTrackingId=${order.id}")
} catch (e: Exception) {
logger.error("删除本地订单失败: orderId=${order.buyOrderId}, copyOrderTrackingId=${order.id}, error=${e.message}", e)
}
continue
}
// 获取实际价格和数量 // 获取实际价格和数量
val actualPrice = orderDetail.price?.toSafeBigDecimal() ?: order.price val actualPrice = orderDetail.price?.toSafeBigDecimal() ?: order.price
@@ -97,21 +97,28 @@ class TelegramNotificationService(
if ((actualPrice == null || actualSize == null) && orderId != null && clobApi != null && apiKey != null && apiSecret != null && apiPassphrase != null && walletAddressForApi != null) { if ((actualPrice == null || actualSize == null) && orderId != null && clobApi != null && apiKey != null && apiSecret != null && apiPassphrase != null && walletAddressForApi != null) {
try { try {
val orderResponse = clobApi.getOrder(orderId) val orderResponse = clobApi.getOrder(orderId)
if (orderResponse.isSuccessful && orderResponse.body() != null) { if (orderResponse.isSuccessful) {
val order = orderResponse.body()!! val order = orderResponse.body()
if (actualPrice == null) { if (order != null) {
actualPrice = order.price if (actualPrice == null) {
} actualPrice = order.price
if (actualSize == null) { }
actualSize = order.originalSize // 使用 originalSize 作为订单数量 if (actualSize == null) {
} actualSize = order.originalSize // 使用 originalSize 作为订单数量
actualSide = order.side // 使用订单详情中的 side }
if (actualOutcome == null) { actualSide = order.side // 使用订单详情中的 side
actualOutcome = order.outcome // 使用订单详情中的 outcome(市场方向) if (actualOutcome == null) {
actualOutcome = order.outcome // 使用订单详情中的 outcome(市场方向)
}
} else {
logger.debug("查询订单详情失败: 响应体为空, orderId=$orderId")
} }
} else {
val errorBody = orderResponse.errorBody()?.string()?.take(200) ?: "无错误详情"
logger.debug("查询订单详情失败: orderId=$orderId, code=${orderResponse.code()}, errorBody=$errorBody")
} }
} catch (e: Exception) { } catch (e: Exception) {
logger.warn("查询订单详情失败: ${e.message}", e) logger.warn("查询订单详情失败: orderId=$orderId, ${e.message}", e)
} }
} }