其他代码修改
- CopyOrderTrackingRepository: 添加查询方法 - PolymarketClobService: 功能更新 - CopyOrderTrackingService: 逻辑优化 - OrderStatusUpdateService: 功能增强 - TelegramNotificationService: 通知优化
This commit is contained in:
+6
@@ -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>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-4
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-4
@@ -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) {
|
||||||
|
|||||||
+138
-5
@@ -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
|
||||||
|
|||||||
+19
-12
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user