fix: 修复仓位轮询检查逻辑和时区问题

- 修复仓位轮询检查:当仓位不存在时,只有当订单创建时间超过2分钟才认为仓位被出售,避免刚创建的订单被误判
- 修复时区问题:所有时间格式化使用系统时区,移除硬编码的时区配置
  - DateUtils 添加 formatDateTime() 函数,使用 ZoneId.systemDefault()
  - TelegramNotificationService 所有时间格式化使用 DateUtils.formatDateTime()
  - Dockerfile 和启动脚本移除时区配置,自动使用系统时区
This commit is contained in:
WrBug
2025-12-09 04:48:25 +08:00
parent 6e4d4f1ef5
commit cdadce467e
5 changed files with 38 additions and 13 deletions
+3 -3
View File
@@ -22,9 +22,9 @@ FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
# 安装必要的工具
# 安装必要的工具和时区数据
RUN apt-get update && \
apt-get install -y curl && \
apt-get install -y curl tzdata && \
rm -rf /var/lib/apt/lists/*
# 从构建阶段复制 JAR 文件
@@ -43,6 +43,6 @@ EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8000/api/system/health || exit 1
# 启动应用
# 启动应用(自动使用系统时区)
ENTRYPOINT ["java", "-jar", "app.jar"]
@@ -373,22 +373,22 @@ class PositionCheckService(
if (position == null) {
// 仓位不存在,检查订单创建时间
// 只有当订单创建时间超过30秒时,才认为仓位被出售了
// 只有当订单创建时间超过2分钟时,才认为仓位被出售了
// 这样可以避免刚创建的订单因为API延迟而被误判为已卖出
val now = System.currentTimeMillis()
val ordersToMarkAsSold = orders.filter { order ->
val orderAge = now - order.createdAt
orderAge > 30000 // 30秒 = 30000毫秒
orderAge > 120000 // 2分钟 = 120000毫秒
}
if (ordersToMarkAsSold.isNotEmpty()) {
// 有订单创建时间超过30秒,认为仓位已被出售
// 有订单创建时间超过2分钟,认为仓位已被出售
val currentPrice = getCurrentMarketPrice(marketId, outcomeIndex)
updateOrdersAsSold(ordersToMarkAsSold, currentPrice, copyTrading.id, marketId, outcomeIndex)
logger.debug("仓位不存在且订单创建时间超过30秒,标记为已卖出: marketId=$marketId, outcomeIndex=$outcomeIndex, orderCount=${ordersToMarkAsSold.size}")
logger.debug("仓位不存在且订单创建时间超过2分钟,标记为已卖出: marketId=$marketId, outcomeIndex=$outcomeIndex, orderCount=${ordersToMarkAsSold.size}")
} else {
// 订单创建时间不足30秒,可能是刚创建的订单,暂时不处理
logger.debug("仓位不存在但订单创建时间不足30秒,暂不标记为已卖出: marketId=$marketId, outcomeIndex=$outcomeIndex, orderCount=${orders.size}, oldestOrderAge=${orders.minOfOrNull { now - it.createdAt }?.let { "${it}ms" } ?: "N/A"}")
// 订单创建时间不足2分钟,可能是刚创建的订单,暂时不处理
logger.debug("仓位不存在但订单创建时间不足2分钟,暂不标记为已卖出: marketId=$marketId, outcomeIndex=$outcomeIndex, orderCount=${orders.size}, oldestOrderAge=${orders.minOfOrNull { now - it.createdAt }?.let { "${it}ms" } ?: "N/A"}")
}
} else {
// 有仓位,按订单下单顺序(FIFO)更新状态
@@ -7,6 +7,7 @@ import com.wrbug.polymarketbot.dto.NotificationConfigData
import com.wrbug.polymarketbot.dto.TelegramConfigData
import com.wrbug.polymarketbot.util.createClient
import com.wrbug.polymarketbot.util.toSafeBigDecimal
import com.wrbug.polymarketbot.util.DateUtils
import kotlinx.coroutines.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
@@ -308,7 +309,7 @@ class TelegramNotificationService(
}
}
val time = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(java.util.Date())
val time = DateUtils.formatDateTime()
// 转义 HTML 特殊字符
val escapedMarketTitle = marketTitle.replace("<", "&lt;").replace(">", "&gt;")
@@ -639,7 +640,7 @@ class TelegramNotificationService(
""
}
val time = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(java.util.Date())
val time = DateUtils.formatDateTime()
// 转义 HTML 特殊字符
val escapedMarketTitle = marketTitle.replace("<", "&lt;").replace(">", "&gt;")
@@ -760,7 +761,7 @@ class TelegramNotificationService(
}
}
val time = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(java.util.Date())
val time = DateUtils.formatDateTime()
// 错误信息已经是后端返回的 msg,不需要截断(但为了安全,限制长度)
val shortErrorMessage = if (errorMessage.length > 500) {
@@ -901,7 +902,7 @@ class TelegramNotificationService(
}
}
val time = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(java.util.Date())
val time = DateUtils.formatDateTime()
// 转义 HTML 特殊字符
val escapedAccountInfo = accountInfo.replace("<", "&lt;").replace(">", "&gt;")
@@ -1,6 +1,8 @@
package com.wrbug.polymarketbot.util
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeParseException
@@ -15,6 +17,13 @@ object DateUtils {
*/
private val isoFormatter = DateTimeFormatter.ISO_DATE_TIME
/**
* 使用系统时区的日期时间格式化器(用于显示)
* 格式:yyyy-MM-dd HH:mm:ss
*/
private val displayFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.withZone(ZoneId.systemDefault())
/**
* 将 ISO 8601 格式的日期字符串转换为时间戳(毫秒)
* @param dateString ISO 8601 格式的日期字符串,如 "2020-11-04T00:00:00Z"
@@ -61,5 +70,19 @@ object DateUtils {
null
}
}
/**
* 将时间戳(毫秒)格式化为可读的日期时间字符串(使用系统时区)
* @param timestamp 时间戳(毫秒),如果为 null 则使用当前时间
* @return 格式化的日期时间字符串,格式:yyyy-MM-dd HH:mm:ss
*/
fun formatDateTime(timestamp: Long? = null): String {
val instant = if (timestamp != null) {
Instant.ofEpochMilli(timestamp)
} else {
Instant.now()
}
return displayFormatter.format(instant)
}
}
+1
View File
@@ -54,6 +54,7 @@ trap cleanup SIGTERM SIGINT
# 启动后端服务(以 appuser 用户运行,后台运行)
echo "启动后端服务..."
# 自动使用系统时区
java -jar /app/app.jar --spring.profiles.active=${SPRING_PROFILES_ACTIVE:-prod} &
BACKEND_PID=$!