refactor: 移除下单失败存储数据库的功能
- 删除 FailedTrade 实体类和 FailedTradeRepository - 从 CopyOrderTrackingService 中移除失败交易存储逻辑 - 移除 recordFailedTrade 方法 - 移除检查失败交易的代码 - 创建 Flyway migration V16 删除 failed_trade 表 - 从 V1__init_database.sql 中移除 failed_trade 表创建语句 下单失败时仅记录日志,不再存储到数据库
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
package com.wrbug.polymarketbot.entity
|
||||
|
||||
import jakarta.persistence.*
|
||||
|
||||
/**
|
||||
* 失败交易实体
|
||||
* 记录处理失败的交易信息
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "failed_trade")
|
||||
data class FailedTrade(
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
val id: Long? = null,
|
||||
|
||||
@Column(name = "leader_id", nullable = false)
|
||||
val leaderId: Long,
|
||||
|
||||
@Column(name = "leader_trade_id", nullable = false, length = 100)
|
||||
val leaderTradeId: String, // Leader 的交易ID
|
||||
|
||||
@Column(name = "trade_type", nullable = false, length = 10)
|
||||
val tradeType: String, // BUY 或 SELL
|
||||
|
||||
@Column(name = "copy_trading_id", nullable = false)
|
||||
val copyTradingId: Long,
|
||||
|
||||
@Column(name = "account_id", nullable = false)
|
||||
val accountId: Long,
|
||||
|
||||
@Column(name = "market_id", nullable = false, length = 100)
|
||||
val marketId: String,
|
||||
|
||||
@Column(name = "side", nullable = false, length = 10)
|
||||
val side: String, // YES/NO
|
||||
|
||||
@Column(name = "price", nullable = false, length = 50)
|
||||
val price: String, // 价格(字符串格式)
|
||||
|
||||
@Column(name = "size", nullable = false, length = 50)
|
||||
val size: String, // 数量(字符串格式)
|
||||
|
||||
@Column(name = "error_message", columnDefinition = "TEXT")
|
||||
val errorMessage: String? = null, // 错误信息
|
||||
|
||||
@Column(name = "retry_count", nullable = false)
|
||||
val retryCount: Int = 0, // 重试次数
|
||||
|
||||
@Column(name = "failed_at", nullable = false)
|
||||
val failedAt: Long = System.currentTimeMillis(),
|
||||
|
||||
@Column(name = "created_at", nullable = false)
|
||||
val createdAt: Long = System.currentTimeMillis()
|
||||
)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.wrbug.polymarketbot.repository
|
||||
|
||||
import com.wrbug.polymarketbot.entity.FailedTrade
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
/**
|
||||
* 失败交易Repository
|
||||
*/
|
||||
@Repository
|
||||
interface FailedTradeRepository : JpaRepository<FailedTrade, Long> {
|
||||
|
||||
/**
|
||||
* 根据Leader ID和交易ID查询
|
||||
*/
|
||||
fun findByLeaderIdAndLeaderTradeId(leaderId: Long, leaderTradeId: String): FailedTrade?
|
||||
|
||||
/**
|
||||
* 检查是否存在失败的交易
|
||||
*/
|
||||
fun existsByLeaderIdAndLeaderTradeId(leaderId: Long, leaderTradeId: String): Boolean
|
||||
}
|
||||
|
||||
+2
-139
@@ -37,7 +37,6 @@ open class CopyOrderTrackingService(
|
||||
private val sellMatchRecordRepository: SellMatchRecordRepository,
|
||||
private val sellMatchDetailRepository: SellMatchDetailRepository,
|
||||
private val processedTradeRepository: ProcessedTradeRepository,
|
||||
private val failedTradeRepository: FailedTradeRepository,
|
||||
private val filteredOrderRepository: FilteredOrderRepository,
|
||||
private val copyTradingRepository: CopyTradingRepository,
|
||||
private val accountRepository: AccountRepository,
|
||||
@@ -135,12 +134,6 @@ open class CopyOrderTrackingService(
|
||||
return@withLock Result.success(Unit)
|
||||
}
|
||||
|
||||
// 检查是否已记录为失败交易
|
||||
val failedTrade = failedTradeRepository.findByLeaderIdAndLeaderTradeId(leaderId, trade.id)
|
||||
if (failedTrade != null) {
|
||||
return@withLock Result.success(Unit)
|
||||
}
|
||||
|
||||
// 2. 处理交易逻辑
|
||||
val result = when (trade.side.uppercase()) {
|
||||
"BUY" -> processBuyTrade(leaderId, trade)
|
||||
@@ -491,27 +484,6 @@ open class CopyOrderTrackingService(
|
||||
if (createOrderResult.isFailure) {
|
||||
// 提取错误信息(只保留 code 和 errorBody)
|
||||
val exception = createOrderResult.exceptionOrNull()
|
||||
val errorMsg = buildFullErrorMessage(
|
||||
exception,
|
||||
"BUY",
|
||||
buyPrice.toString(),
|
||||
finalBuyQuantity.toString(),
|
||||
trade.id
|
||||
)
|
||||
|
||||
// 记录失败交易到数据库
|
||||
// retryCount = MAX_RETRY_ATTEMPTS - 1,表示已重试的次数
|
||||
recordFailedTrade(
|
||||
leaderId = leaderId,
|
||||
trade = trade,
|
||||
copyTradingId = copyTrading.id!!,
|
||||
accountId = copyTrading.accountId,
|
||||
side = "BUY",
|
||||
price = buyPrice.toString(),
|
||||
size = finalBuyQuantity.toString(),
|
||||
errorMessage = errorMsg,
|
||||
retryCount = MAX_RETRY_ATTEMPTS - 1 // 已重试次数
|
||||
)
|
||||
|
||||
// 发送订单失败通知(异步,不阻塞,仅在 pushFailedOrders 为 true 时发送)
|
||||
if (copyTrading.pushFailedOrders) {
|
||||
@@ -971,26 +943,9 @@ open class CopyOrderTrackingService(
|
||||
)
|
||||
|
||||
if (createOrderResult.isFailure) {
|
||||
// 创建订单失败,记录到失败表
|
||||
// 创建订单失败,记录错误日志
|
||||
val exception = createOrderResult.exceptionOrNull()
|
||||
val errorMsg = buildFullErrorMessage(
|
||||
exception,
|
||||
"SELL",
|
||||
sellPrice.toString(),
|
||||
totalMatched.toString(),
|
||||
leaderSellTrade.id
|
||||
)
|
||||
recordFailedTrade(
|
||||
leaderId = copyTrading.leaderId,
|
||||
trade = leaderSellTrade,
|
||||
copyTradingId = copyTrading.id!!,
|
||||
accountId = copyTrading.accountId,
|
||||
side = "SELL", // 订单方向是SELL
|
||||
price = sellPrice.toString(),
|
||||
size = totalMatched.toString(),
|
||||
errorMessage = errorMsg,
|
||||
retryCount = 1 // 已重试一次
|
||||
)
|
||||
logger.error("创建卖出订单失败: copyTradingId=${copyTrading.id}, tradeId=${leaderSellTrade.id}, error=${exception?.message}")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1248,98 +1203,6 @@ open class CopyOrderTrackingService(
|
||||
return "code=$code, errorBody=$errorBody"
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录失败交易到数据库
|
||||
* 注意:此方法在 @Transactional 方法中被调用,会自动继承事务
|
||||
*/
|
||||
private suspend fun recordFailedTrade(
|
||||
leaderId: Long,
|
||||
trade: TradeResponse,
|
||||
copyTradingId: Long,
|
||||
accountId: Long,
|
||||
side: String,
|
||||
price: String,
|
||||
size: String,
|
||||
errorMessage: String,
|
||||
retryCount: Int
|
||||
) {
|
||||
try {
|
||||
// 确保错误信息不超过数据库字段限制(TEXT类型通常支持65535字符)
|
||||
val maxErrorMessageLength = 50000 // 保留一些余量
|
||||
val finalErrorMessage = if (errorMessage.length > maxErrorMessageLength) {
|
||||
errorMessage.substring(0, maxErrorMessageLength) + "... (截断)"
|
||||
} else {
|
||||
errorMessage
|
||||
}
|
||||
|
||||
val failedTrade = FailedTrade(
|
||||
leaderId = leaderId,
|
||||
leaderTradeId = trade.id,
|
||||
tradeType = trade.side.uppercase(),
|
||||
copyTradingId = copyTradingId,
|
||||
accountId = accountId,
|
||||
marketId = trade.market,
|
||||
side = side,
|
||||
price = price,
|
||||
size = size,
|
||||
errorMessage = finalErrorMessage,
|
||||
retryCount = retryCount,
|
||||
failedAt = System.currentTimeMillis()
|
||||
)
|
||||
failedTradeRepository.save(failedTrade)
|
||||
|
||||
// 记录日志,确认已保存到数据库
|
||||
logger.info("失败交易已保存到数据库: leaderId=$leaderId, tradeId=${trade.id}, errorMessageLength=${finalErrorMessage.length}")
|
||||
|
||||
// 标记为已处理(失败状态),避免重复处理
|
||||
// 注意:并发情况下可能多个请求同时处理同一笔交易,需要处理唯一约束冲突
|
||||
try {
|
||||
val processed = ProcessedTrade(
|
||||
leaderId = leaderId,
|
||||
leaderTradeId = trade.id,
|
||||
tradeType = trade.side.uppercase(),
|
||||
source = "polling",
|
||||
status = "FAILED",
|
||||
processedAt = System.currentTimeMillis()
|
||||
)
|
||||
processedTradeRepository.save(processed)
|
||||
} catch (e: Exception) {
|
||||
// 检查是否是唯一键冲突异常
|
||||
if (isUniqueConstraintViolation(e)) {
|
||||
// 唯一约束冲突,说明已经处理过了(可能是并发请求)
|
||||
// 检查现有记录的状态
|
||||
val existing = processedTradeRepository.findByLeaderIdAndLeaderTradeId(leaderId, trade.id)
|
||||
if (existing != null) {
|
||||
if (existing.status == "SUCCESS") {
|
||||
logger.warn("交易已成功处理,但尝试记录为失败(并发冲突): leaderId=$leaderId, tradeId=${trade.id}")
|
||||
} else {
|
||||
logger.debug("交易已标记为失败(并发检测): leaderId=$leaderId, tradeId=${trade.id}")
|
||||
}
|
||||
} else {
|
||||
// 如果查询不到,等待一下再查询(可能是事务隔离级别问题)
|
||||
delay(100)
|
||||
val existingAfterDelay =
|
||||
processedTradeRepository.findByLeaderIdAndLeaderTradeId(leaderId, trade.id)
|
||||
if (existingAfterDelay != null) {
|
||||
logger.debug("延迟查询到记录(并发检测): leaderId=$leaderId, tradeId=${trade.id}, status=${existingAfterDelay.status}")
|
||||
} else {
|
||||
logger.warn(
|
||||
"保存ProcessedTrade失败记录时发生唯一约束冲突,但查询不到记录: leaderId=$leaderId, tradeId=${trade.id}",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 其他类型的异常,记录但不抛出(避免影响其他交易的处理)
|
||||
logger.warn("保存ProcessedTrade失败记录时发生异常: leaderId=$leaderId, tradeId=${trade.id}", e)
|
||||
}
|
||||
}
|
||||
|
||||
logger.warn("已记录失败交易: leaderId=$leaderId, tradeId=${trade.id}, error=$errorMessage")
|
||||
} catch (e: Exception) {
|
||||
logger.error("记录失败交易异常: leaderId=$leaderId, tradeId=${trade.id}", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新订单状态
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
-- ============================================
|
||||
-- V16: 删除失败交易记录表
|
||||
-- 移除下单失败存储到数据库的功能
|
||||
-- ============================================
|
||||
|
||||
-- 删除失败交易记录表
|
||||
DROP TABLE IF EXISTS failed_trade;
|
||||
|
||||
@@ -172,32 +172,8 @@ CREATE TABLE IF NOT EXISTS processed_trade (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='已处理交易表(用于去重)';
|
||||
|
||||
-- ============================================
|
||||
-- 9. 创建失败交易记录表
|
||||
-- ============================================
|
||||
CREATE TABLE IF NOT EXISTS failed_trade (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
leader_id BIGINT NOT NULL COMMENT 'Leader ID',
|
||||
leader_trade_id VARCHAR(100) NOT NULL COMMENT 'Leader 的交易ID',
|
||||
trade_type VARCHAR(10) NOT NULL COMMENT '交易类型:BUY 或 SELL',
|
||||
copy_trading_id BIGINT NOT NULL COMMENT '跟单关系ID',
|
||||
account_id BIGINT NOT NULL COMMENT '账户ID',
|
||||
market_id VARCHAR(100) NOT NULL COMMENT '市场地址',
|
||||
side VARCHAR(10) NOT NULL COMMENT '方向:YES/NO',
|
||||
price VARCHAR(50) NOT NULL COMMENT '价格',
|
||||
size VARCHAR(50) NOT NULL COMMENT '数量',
|
||||
error_message TEXT COMMENT '错误信息',
|
||||
retry_count INT NOT NULL DEFAULT 0 COMMENT '重试次数',
|
||||
failed_at BIGINT NOT NULL COMMENT '失败时间(毫秒时间戳)',
|
||||
created_at BIGINT NOT NULL COMMENT '创建时间(毫秒时间戳)',
|
||||
INDEX idx_leader_trade (leader_id, leader_trade_id),
|
||||
INDEX idx_copy_trading (copy_trading_id),
|
||||
INDEX idx_failed_at (failed_at),
|
||||
FOREIGN KEY (copy_trading_id) REFERENCES copy_trading(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (leader_id) REFERENCES copy_trading_leaders(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='失败交易记录表';
|
||||
|
||||
-- ============================================
|
||||
-- 10. 创建用户表(用于JWT登录鉴权)
|
||||
-- 9. 创建用户表(用于JWT登录鉴权)
|
||||
-- ============================================
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
|
||||
Reference in New Issue
Block a user