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