feat: 实现卖出订单价格轮询更新和订单状态管理
- 添加 price_updated 字段到 sell_match_record 表,用于标记价格是否已更新 - 创建 OrderStatusUpdateService 定时任务服务,每5秒轮询一次: - 更新卖出订单的实际成交价(通过 orderId 查询订单详情) - 清理已删除账户的订单记录 - 修改 processSellTrade:下单完成后不再立即查询价格,直接保存,等待定时任务更新 - 添加 orderId 格式验证:非0x开头的直接标记为已更新,0x开头的等待定时任务更新 - 启用 Spring 定时任务功能(@EnableScheduling) - 将 getActualExecutionPrice 方法改为 public,供定时任务调用
This commit is contained in:
@@ -2,8 +2,10 @@ package com.wrbug.polymarketbot
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
import org.springframework.scheduling.annotation.EnableScheduling
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
class PolymarketBotApplication
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
@@ -41,6 +41,9 @@ data class SellMatchRecord(
|
||||
@Column(name = "total_realized_pnl", nullable = false, precision = 20, scale = 8)
|
||||
val totalRealizedPnl: BigDecimal, // 总已实现盈亏
|
||||
|
||||
@Column(name = "price_updated", nullable = false)
|
||||
var priceUpdated: Boolean = false, // 价格是否已更新(从订单详情获取实际成交价)
|
||||
|
||||
@Column(name = "created_at", nullable = false)
|
||||
val createdAt: Long = System.currentTimeMillis()
|
||||
)
|
||||
|
||||
+5
@@ -24,5 +24,10 @@ interface SellMatchRecordRepository : JpaRepository<SellMatchRecord, Long> {
|
||||
* 根据Leader卖出交易ID查询记录
|
||||
*/
|
||||
fun findByLeaderSellTradeId(leaderSellTradeId: String): SellMatchRecord?
|
||||
|
||||
/**
|
||||
* 查询所有价格未更新的卖出记录
|
||||
*/
|
||||
fun findByPriceUpdatedFalse(): List<SellMatchRecord>
|
||||
}
|
||||
|
||||
|
||||
+140
-7
@@ -567,6 +567,12 @@ open class CopyOrderTrackingService(
|
||||
}
|
||||
|
||||
val realOrderId = createOrderResult.getOrNull() ?: continue
|
||||
|
||||
// 验证 orderId 格式(必须以 0x 开头的 16 进制)
|
||||
if (!isValidOrderId(realOrderId)) {
|
||||
logger.warn("买入订单ID格式无效,跳过保存: orderId=$realOrderId")
|
||||
continue
|
||||
}
|
||||
|
||||
// 创建买入订单跟踪记录(使用真实订单ID,使用outcomeIndex)
|
||||
val tracking = CopyOrderTracking(
|
||||
@@ -942,8 +948,20 @@ open class CopyOrderTrackingService(
|
||||
}
|
||||
|
||||
val realSellOrderId = createOrderResult.getOrNull() ?: return
|
||||
|
||||
// 12. 下单时直接使用下单价格保存,等待定时任务更新实际成交价
|
||||
// priceUpdated 统一由定时任务更新,下单时统一设置为 false(非0x开头的除外)
|
||||
val priceUpdated = !realSellOrderId.startsWith("0x", ignoreCase = true)
|
||||
if (priceUpdated) {
|
||||
logger.debug("卖出订单ID非0x开头,标记为已更新: orderId=$realSellOrderId")
|
||||
} else {
|
||||
logger.debug("卖出订单ID为0x开头,等待定时任务更新价格: orderId=$realSellOrderId")
|
||||
}
|
||||
|
||||
// 使用下单价格,等待定时任务更新实际成交价
|
||||
val actualSellPrice = sellPrice
|
||||
|
||||
// 12. 更新买入订单跟踪状态
|
||||
// 13. 更新买入订单跟踪状态
|
||||
for (order in unmatchedOrders) {
|
||||
val detail = matchDetails.find { it.trackingId == order.id }
|
||||
if (detail != null) {
|
||||
@@ -956,8 +974,17 @@ open class CopyOrderTrackingService(
|
||||
}
|
||||
}
|
||||
|
||||
// 13. 创建卖出匹配记录(使用真实订单ID,使用outcomeIndex)
|
||||
val totalRealizedPnl = matchDetails.sumOf { it.realizedPnl.toSafeBigDecimal() }
|
||||
// 14. 重新计算盈亏(使用实际成交价)
|
||||
val updatedMatchDetails = matchDetails.map { detail ->
|
||||
val updatedRealizedPnl = actualSellPrice.subtract(detail.buyPrice).multi(detail.matchedQuantity)
|
||||
detail.copy(
|
||||
sellPrice = actualSellPrice,
|
||||
realizedPnl = updatedRealizedPnl
|
||||
)
|
||||
}
|
||||
|
||||
// 15. 创建卖出匹配记录(使用真实订单ID和实际成交价)
|
||||
val totalRealizedPnl = updatedMatchDetails.sumOf { it.realizedPnl.toSafeBigDecimal() }
|
||||
|
||||
val matchRecord = SellMatchRecord(
|
||||
copyTradingId = copyTrading.id,
|
||||
@@ -967,14 +994,15 @@ open class CopyOrderTrackingService(
|
||||
side = leaderSellTrade.outcomeIndex.toString(), // 使用outcomeIndex作为side(兼容旧数据)
|
||||
outcomeIndex = leaderSellTrade.outcomeIndex, // 新增字段
|
||||
totalMatchedQuantity = totalMatched,
|
||||
sellPrice = sellPrice,
|
||||
totalRealizedPnl = totalRealizedPnl
|
||||
sellPrice = actualSellPrice, // 使用实际成交价(如果查询失败则为下单价格)
|
||||
totalRealizedPnl = totalRealizedPnl,
|
||||
priceUpdated = priceUpdated // 标记价格是否已更新
|
||||
)
|
||||
|
||||
val savedRecord = sellMatchRecordRepository.save(matchRecord)
|
||||
|
||||
// 14. 保存匹配明细
|
||||
for (detail in matchDetails) {
|
||||
// 16. 保存匹配明细(使用实际成交价)
|
||||
for (detail in updatedMatchDetails) {
|
||||
val savedDetail = detail.copy(matchRecordId = savedRecord.id!!)
|
||||
sellMatchDetailRepository.save(savedDetail)
|
||||
}
|
||||
@@ -1382,6 +1410,111 @@ open class CopyOrderTrackingService(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证订单ID格式
|
||||
* 订单ID必须以 0x 开头,且是有效的 16 进制字符串
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @return 如果格式有效返回 true,否则返回 false
|
||||
*/
|
||||
private fun isValidOrderId(orderId: String): Boolean {
|
||||
if (!orderId.startsWith("0x", ignoreCase = true)) {
|
||||
return false
|
||||
}
|
||||
// 验证是否为有效的 16 进制字符串(去除 0x 前缀后)
|
||||
val hexPart = orderId.substring(2)
|
||||
if (hexPart.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
// 检查是否只包含 0-9, a-f, A-F
|
||||
return hexPart.all { it in '0'..'9' || it in 'a'..'f' || it in 'A'..'F' }
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单的实际成交价
|
||||
* 通过查询订单详情和关联的交易记录,计算加权平均成交价
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @param clobApi CLOB API 客户端(已认证)
|
||||
* @param fallbackPrice 如果查询失败,使用此价格作为默认值
|
||||
* @return 实际成交价(加权平均),如果查询失败则返回 fallbackPrice
|
||||
*/
|
||||
suspend fun getActualExecutionPrice(
|
||||
orderId: String,
|
||||
clobApi: PolymarketClobApi,
|
||||
fallbackPrice: BigDecimal
|
||||
): BigDecimal {
|
||||
return try {
|
||||
// 1. 查询订单详情
|
||||
val orderResponse = clobApi.getOrder(orderId)
|
||||
if (!orderResponse.isSuccessful || orderResponse.body() == null) {
|
||||
logger.warn("查询订单详情失败: orderId=$orderId, code=${orderResponse.code()}")
|
||||
return fallbackPrice
|
||||
}
|
||||
|
||||
val order = orderResponse.body()!!
|
||||
|
||||
// 2. 如果订单未成交,使用下单价格
|
||||
if (order.status != "FILLED" && order.sizeMatched.toSafeBigDecimal() <= BigDecimal.ZERO) {
|
||||
logger.debug("订单未成交,使用下单价格: orderId=$orderId, status=${order.status}")
|
||||
return fallbackPrice
|
||||
}
|
||||
|
||||
// 3. 如果订单已成交,通过 associateTrades 获取交易记录
|
||||
val associateTrades = order.associateTrades
|
||||
if (associateTrades.isNullOrEmpty()) {
|
||||
logger.debug("订单无关联交易记录,使用下单价格: orderId=$orderId")
|
||||
return fallbackPrice
|
||||
}
|
||||
|
||||
// 4. 查询所有关联的交易记录
|
||||
val trades = mutableListOf<TradeResponse>()
|
||||
for (tradeId in associateTrades) {
|
||||
try {
|
||||
val tradesResponse = clobApi.getTrades(id = tradeId)
|
||||
if (tradesResponse.isSuccessful && tradesResponse.body() != null) {
|
||||
val tradesData = tradesResponse.body()!!.data
|
||||
trades.addAll(tradesData)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.warn("查询交易记录失败: tradeId=$tradeId, error=${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
if (trades.isEmpty()) {
|
||||
logger.debug("未找到交易记录,使用下单价格: orderId=$orderId")
|
||||
return fallbackPrice
|
||||
}
|
||||
|
||||
// 5. 计算加权平均成交价
|
||||
// 加权平均 = Σ(price * size) / Σ(size)
|
||||
var totalAmount = BigDecimal.ZERO
|
||||
var totalSize = BigDecimal.ZERO
|
||||
|
||||
for (trade in trades) {
|
||||
val tradePrice = trade.price.toSafeBigDecimal()
|
||||
val tradeSize = trade.size.toSafeBigDecimal()
|
||||
|
||||
if (tradeSize > BigDecimal.ZERO) {
|
||||
totalAmount = totalAmount.add(tradePrice.multiply(tradeSize))
|
||||
totalSize = totalSize.add(tradeSize)
|
||||
}
|
||||
}
|
||||
|
||||
if (totalSize > BigDecimal.ZERO) {
|
||||
val weightedAveragePrice = totalAmount.divide(totalSize, 8, java.math.RoundingMode.HALF_UP)
|
||||
logger.info("计算实际成交价成功: orderId=$orderId, 加权平均价=$weightedAveragePrice, 下单价格=$fallbackPrice, 交易笔数=${trades.size}")
|
||||
return weightedAveragePrice
|
||||
} else {
|
||||
logger.warn("交易记录数量为0,使用下单价格: orderId=$orderId")
|
||||
return fallbackPrice
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.error("获取实际成交价异常: orderId=$orderId, error=${e.message}", e)
|
||||
return fallbackPrice
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从trade中提取side(结果名称)
|
||||
*
|
||||
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
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.util.RetrofitFactory
|
||||
import com.wrbug.polymarketbot.util.CryptoUtils
|
||||
import com.wrbug.polymarketbot.util.toSafeBigDecimal
|
||||
import com.wrbug.polymarketbot.util.multi
|
||||
import kotlinx.coroutines.*
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent
|
||||
import org.springframework.context.event.EventListener
|
||||
import org.springframework.scheduling.annotation.Scheduled
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* 订单状态更新服务
|
||||
* 定时轮询更新卖出订单的实际成交价,并清理已删除账户的订单
|
||||
*/
|
||||
@Service
|
||||
class OrderStatusUpdateService(
|
||||
private val sellMatchRecordRepository: SellMatchRecordRepository,
|
||||
private val sellMatchDetailRepository: SellMatchDetailRepository,
|
||||
private val copyTradingRepository: CopyTradingRepository,
|
||||
private val accountRepository: AccountRepository,
|
||||
private val copyOrderTrackingRepository: CopyOrderTrackingRepository,
|
||||
private val retrofitFactory: RetrofitFactory,
|
||||
private val cryptoUtils: CryptoUtils,
|
||||
private val trackingService: CopyOrderTrackingService
|
||||
) {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(OrderStatusUpdateService::class.java)
|
||||
|
||||
private val updateScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
@EventListener(ApplicationReadyEvent::class)
|
||||
fun onApplicationReady() {
|
||||
logger.info("订单状态更新服务已启动,将每5秒轮询一次")
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时更新卖出订单价格
|
||||
* 每5秒执行一次
|
||||
*/
|
||||
@Scheduled(fixedDelay = 5000)
|
||||
fun updateSellOrderPrices() {
|
||||
updateScope.launch {
|
||||
try {
|
||||
// 1. 清理已删除账户的订单
|
||||
cleanupDeletedAccountOrders()
|
||||
|
||||
// 2. 更新卖出订单的实际成交价
|
||||
updatePendingSellOrderPrices()
|
||||
} catch (e: Exception) {
|
||||
logger.error("订单状态更新异常: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证订单ID格式
|
||||
* 订单ID必须以 0x 开头,且是有效的 16 进制字符串
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @return 如果格式有效返回 true,否则返回 false
|
||||
*/
|
||||
private fun isValidOrderId(orderId: String): Boolean {
|
||||
if (!orderId.startsWith("0x", ignoreCase = true)) {
|
||||
return false
|
||||
}
|
||||
// 验证是否为有效的 16 进制字符串(去除 0x 前缀后)
|
||||
val hexPart = orderId.substring(2)
|
||||
if (hexPart.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
// 检查是否只包含 0-9, a-f, A-F
|
||||
return hexPart.all { it in '0'..'9' || it in 'a'..'f' || it in 'A'..'F' }
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理已删除账户的订单
|
||||
*/
|
||||
@Transactional
|
||||
private suspend fun cleanupDeletedAccountOrders() {
|
||||
try {
|
||||
// 查询所有卖出记录
|
||||
val allRecords = sellMatchRecordRepository.findAll()
|
||||
|
||||
// 查询所有有效的账户ID
|
||||
val validAccountIds = accountRepository.findAll().mapNotNull { it.id }.toSet()
|
||||
|
||||
// 查询所有有效的跟单关系
|
||||
val validCopyTradingIds = copyTradingRepository.findAll()
|
||||
.filter { it.accountId in validAccountIds }
|
||||
.mapNotNull { it.id }
|
||||
.toSet()
|
||||
|
||||
// 找出需要删除的记录(关联的跟单关系已不存在或账户已删除)
|
||||
val recordsToDelete = allRecords.filter { record ->
|
||||
val copyTrading = copyTradingRepository.findById(record.copyTradingId).orElse(null)
|
||||
copyTrading == null || copyTrading.accountId !in validAccountIds
|
||||
}
|
||||
|
||||
if (recordsToDelete.isNotEmpty()) {
|
||||
logger.info("清理已删除账户的订单: ${recordsToDelete.size} 条记录")
|
||||
|
||||
// 删除匹配明细
|
||||
for (record in recordsToDelete) {
|
||||
val details = sellMatchDetailRepository.findByMatchRecordId(record.id!!)
|
||||
sellMatchDetailRepository.deleteAll(details)
|
||||
}
|
||||
|
||||
// 删除卖出记录
|
||||
sellMatchRecordRepository.deleteAll(recordsToDelete)
|
||||
|
||||
logger.info("已清理 ${recordsToDelete.size} 条已删除账户的订单记录")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.error("清理已删除账户订单异常: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新待更新的卖出订单价格
|
||||
*/
|
||||
@Transactional
|
||||
private suspend fun updatePendingSellOrderPrices() {
|
||||
try {
|
||||
// 查询所有价格未更新的卖出记录
|
||||
val pendingRecords = sellMatchRecordRepository.findByPriceUpdatedFalse()
|
||||
|
||||
if (pendingRecords.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
logger.debug("找到 ${pendingRecords.size} 条待更新价格的卖出订单")
|
||||
|
||||
for (record in pendingRecords) {
|
||||
try {
|
||||
// 获取跟单关系
|
||||
val copyTrading = copyTradingRepository.findById(record.copyTradingId).orElse(null)
|
||||
if (copyTrading == null) {
|
||||
logger.warn("跟单关系不存在,跳过更新: copyTradingId=${record.copyTradingId}")
|
||||
continue
|
||||
}
|
||||
|
||||
// 获取账户
|
||||
val account = accountRepository.findById(copyTrading.accountId).orElse(null)
|
||||
if (account == null) {
|
||||
logger.warn("账户不存在,跳过更新: accountId=${copyTrading.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
|
||||
)
|
||||
|
||||
// 如果 orderId 不是 0x 开头,直接标记为已更新(不需要通过API查询)
|
||||
if (!record.sellOrderId.startsWith("0x", ignoreCase = true)) {
|
||||
logger.debug("卖出订单ID非0x开头,直接标记为已更新: orderId=${record.sellOrderId}")
|
||||
val updatedRecord = SellMatchRecord(
|
||||
id = record.id,
|
||||
copyTradingId = record.copyTradingId,
|
||||
sellOrderId = record.sellOrderId,
|
||||
leaderSellTradeId = record.leaderSellTradeId,
|
||||
marketId = record.marketId,
|
||||
side = record.side,
|
||||
outcomeIndex = record.outcomeIndex,
|
||||
totalMatchedQuantity = record.totalMatchedQuantity,
|
||||
sellPrice = record.sellPrice,
|
||||
totalRealizedPnl = record.totalRealizedPnl,
|
||||
priceUpdated = true, // 标记为已更新
|
||||
createdAt = record.createdAt
|
||||
)
|
||||
sellMatchRecordRepository.save(updatedRecord)
|
||||
continue
|
||||
}
|
||||
|
||||
// 查询订单详情,获取实际成交价
|
||||
val actualSellPrice = trackingService.getActualExecutionPrice(
|
||||
orderId = record.sellOrderId,
|
||||
clobApi = clobApi,
|
||||
fallbackPrice = record.sellPrice
|
||||
)
|
||||
|
||||
// 如果价格已更新(与当前价格不同),更新数据库
|
||||
if (actualSellPrice != record.sellPrice) {
|
||||
// 重新计算盈亏
|
||||
val details = sellMatchDetailRepository.findByMatchRecordId(record.id!!)
|
||||
var totalRealizedPnl = BigDecimal.ZERO
|
||||
|
||||
for (detail in details) {
|
||||
val updatedRealizedPnl = actualSellPrice.subtract(detail.buyPrice).multi(detail.matchedQuantity)
|
||||
|
||||
// 更新明细的卖出价格和盈亏
|
||||
// 注意:SellMatchDetail 的字段都是 val,需要创建新对象
|
||||
val updatedDetail = SellMatchDetail(
|
||||
id = detail.id,
|
||||
matchRecordId = detail.matchRecordId,
|
||||
trackingId = detail.trackingId,
|
||||
buyOrderId = detail.buyOrderId,
|
||||
matchedQuantity = detail.matchedQuantity,
|
||||
buyPrice = detail.buyPrice,
|
||||
sellPrice = actualSellPrice, // 更新卖出价格
|
||||
realizedPnl = updatedRealizedPnl, // 更新盈亏
|
||||
createdAt = detail.createdAt
|
||||
)
|
||||
sellMatchDetailRepository.save(updatedDetail)
|
||||
|
||||
totalRealizedPnl = totalRealizedPnl.add(updatedRealizedPnl)
|
||||
}
|
||||
|
||||
// 更新卖出记录
|
||||
// 注意:SellMatchRecord 的字段都是 val,需要创建新对象
|
||||
val updatedRecord = SellMatchRecord(
|
||||
id = record.id,
|
||||
copyTradingId = record.copyTradingId,
|
||||
sellOrderId = record.sellOrderId,
|
||||
leaderSellTradeId = record.leaderSellTradeId,
|
||||
marketId = record.marketId,
|
||||
side = record.side,
|
||||
outcomeIndex = record.outcomeIndex,
|
||||
totalMatchedQuantity = record.totalMatchedQuantity,
|
||||
sellPrice = actualSellPrice, // 更新卖出价格
|
||||
totalRealizedPnl = totalRealizedPnl, // 更新总盈亏
|
||||
priceUpdated = true, // 标记为已更新
|
||||
createdAt = record.createdAt
|
||||
)
|
||||
sellMatchRecordRepository.save(updatedRecord)
|
||||
|
||||
logger.info("更新卖出订单价格成功: orderId=${record.sellOrderId}, 原价格=${record.sellPrice}, 新价格=$actualSellPrice")
|
||||
} else {
|
||||
// 价格相同,但可能已经查询过,标记为已更新
|
||||
val updatedRecord = SellMatchRecord(
|
||||
id = record.id,
|
||||
copyTradingId = record.copyTradingId,
|
||||
sellOrderId = record.sellOrderId,
|
||||
leaderSellTradeId = record.leaderSellTradeId,
|
||||
marketId = record.marketId,
|
||||
side = record.side,
|
||||
outcomeIndex = record.outcomeIndex,
|
||||
totalMatchedQuantity = record.totalMatchedQuantity,
|
||||
sellPrice = record.sellPrice,
|
||||
totalRealizedPnl = record.totalRealizedPnl,
|
||||
priceUpdated = true, // 标记为已更新
|
||||
createdAt = record.createdAt
|
||||
)
|
||||
sellMatchRecordRepository.save(updatedRecord)
|
||||
logger.debug("卖出订单价格无需更新: orderId=${record.sellOrderId}, price=$actualSellPrice")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.warn("更新卖出订单价格失败: orderId=${record.sellOrderId}, error=${e.message}", e)
|
||||
// 继续处理下一条记录
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.error("更新待更新卖出订单价格异常: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
-- 添加 price_updated 字段到 sell_match_record 表
|
||||
-- 用于标记卖出价格是否已从订单详情中更新
|
||||
ALTER TABLE sell_match_record
|
||||
ADD COLUMN price_updated BOOLEAN DEFAULT FALSE COMMENT '价格是否已更新(从订单详情获取实际成交价)';
|
||||
|
||||
-- 为已存在的记录设置默认值
|
||||
UPDATE sell_match_record SET price_updated = TRUE WHERE price_updated IS NULL;
|
||||
|
||||
Reference in New Issue
Block a user