a
This commit is contained in:
parent
db2bd6a3b2
commit
2e672d1f25
|
|
@ -118,10 +118,22 @@ async def get_active_recommendations():
|
||||||
"""
|
"""
|
||||||
获取当前有效的推荐(未过期、未执行、未取消)
|
获取当前有效的推荐(未过期、未执行、未取消)
|
||||||
使用WebSocket实时价格更新推荐中的价格信息
|
使用WebSocket实时价格更新推荐中的价格信息
|
||||||
|
同一交易对只返回最新的推荐(已去重)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
recommendations = TradeRecommendation.get_active()
|
recommendations = TradeRecommendation.get_active()
|
||||||
|
|
||||||
|
# 确保时间格式正确(转换为ISO格式字符串,包含时区信息)
|
||||||
|
from datetime import datetime
|
||||||
|
for rec in recommendations:
|
||||||
|
if rec.get('recommendation_time'):
|
||||||
|
if isinstance(rec['recommendation_time'], datetime):
|
||||||
|
# 如果是datetime对象,转换为ISO格式字符串(UTC+8)
|
||||||
|
rec['recommendation_time'] = rec['recommendation_time'].isoformat()
|
||||||
|
elif isinstance(rec['recommendation_time'], str):
|
||||||
|
# 如果已经是字符串,确保格式正确
|
||||||
|
pass
|
||||||
|
|
||||||
# 尝试从WebSocket缓存获取实时价格更新推荐中的价格
|
# 尝试从WebSocket缓存获取实时价格更新推荐中的价格
|
||||||
try:
|
try:
|
||||||
import sys
|
import sys
|
||||||
|
|
|
||||||
|
|
@ -322,11 +322,19 @@ class TradeRecommendation:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_active():
|
def get_active():
|
||||||
"""获取当前有效的推荐(未过期、未执行、未取消)"""
|
"""获取当前有效的推荐(未过期、未执行、未取消)
|
||||||
|
同一交易对只返回最新的推荐(去重)
|
||||||
|
"""
|
||||||
return db.execute_query(
|
return db.execute_query(
|
||||||
"""SELECT * FROM trade_recommendations
|
"""SELECT t1.* FROM trade_recommendations t1
|
||||||
WHERE status = 'active' AND (expires_at IS NULL OR expires_at > NOW())
|
INNER JOIN (
|
||||||
ORDER BY signal_strength DESC, recommendation_time DESC"""
|
SELECT symbol, MAX(recommendation_time) as max_time
|
||||||
|
FROM trade_recommendations
|
||||||
|
WHERE status = 'active' AND (expires_at IS NULL OR expires_at > NOW())
|
||||||
|
GROUP BY symbol
|
||||||
|
) t2 ON t1.symbol = t2.symbol AND t1.recommendation_time = t2.max_time
|
||||||
|
WHERE t1.status = 'active' AND (t1.expires_at IS NULL OR t1.expires_at > NOW())
|
||||||
|
ORDER BY t1.signal_strength DESC, t1.recommendation_time DESC"""
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,56 @@ function Recommendations() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadRecommendations()
|
loadRecommendations()
|
||||||
|
|
||||||
// 如果是查看有效推荐,每10秒自动刷新一次(获取实时价格)
|
// 如果是查看有效推荐,每10秒静默更新价格(不触发loading状态)
|
||||||
let interval = null
|
let interval = null
|
||||||
if (statusFilter === 'active') {
|
if (statusFilter === 'active') {
|
||||||
interval = setInterval(loadRecommendations, 10000) // 每10秒刷新
|
interval = setInterval(async () => {
|
||||||
|
// 静默更新:只更新价格,不显示loading
|
||||||
|
try {
|
||||||
|
const result = await api.getActiveRecommendations()
|
||||||
|
const newData = result.data || []
|
||||||
|
|
||||||
|
// 使用setState直接更新,不触发loading状态
|
||||||
|
setRecommendations(prevRecommendations => {
|
||||||
|
// 如果新数据为空,保持原数据不变
|
||||||
|
if (newData.length === 0) {
|
||||||
|
return prevRecommendations
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个映射,用于快速查找
|
||||||
|
const newDataMap = new Map(newData.map(rec => [rec.id, rec]))
|
||||||
|
const prevMap = new Map(prevRecommendations.map(rec => [rec.id, rec]))
|
||||||
|
|
||||||
|
// 合并数据:优先使用新数据(包含实时价格更新)
|
||||||
|
const updated = prevRecommendations.map(prevRec => {
|
||||||
|
const newRec = newDataMap.get(prevRec.id)
|
||||||
|
if (newRec) {
|
||||||
|
// 如果新数据中有该推荐,使用新数据(包含价格更新)
|
||||||
|
return newRec
|
||||||
|
}
|
||||||
|
// 如果新数据中没有,保留旧数据(可能已过期或状态改变)
|
||||||
|
return prevRec
|
||||||
|
})
|
||||||
|
|
||||||
|
// 添加新出现的推荐
|
||||||
|
const newItems = newData.filter(newRec => !prevMap.has(newRec.id))
|
||||||
|
|
||||||
|
// 合并并去重(按id)
|
||||||
|
const merged = [...updated, ...newItems]
|
||||||
|
const uniqueMap = new Map()
|
||||||
|
merged.forEach(rec => {
|
||||||
|
if (!uniqueMap.has(rec.id)) {
|
||||||
|
uniqueMap.set(rec.id, rec)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return Array.from(uniqueMap.values())
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
// 静默失败,不显示错误
|
||||||
|
console.debug('静默更新价格失败:', err)
|
||||||
|
}
|
||||||
|
}, 10000) // 每10秒刷新
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
|
@ -104,14 +150,24 @@ function Recommendations() {
|
||||||
const formatTime = (timeStr) => {
|
const formatTime = (timeStr) => {
|
||||||
if (!timeStr) return '-'
|
if (!timeStr) return '-'
|
||||||
try {
|
try {
|
||||||
|
// 处理时区问题:如果时间字符串包含时区信息,直接解析
|
||||||
|
// 否则假设是UTC时间,转换为本地时间
|
||||||
const date = new Date(timeStr)
|
const date = new Date(timeStr)
|
||||||
|
|
||||||
|
// 检查日期是否有效
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
return timeStr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用本地时区格式化(显示北京时间,如果服务器返回的是UTC时间)
|
||||||
return date.toLocaleString('zh-CN', {
|
return date.toLocaleString('zh-CN', {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: '2-digit',
|
month: '2-digit',
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
second: '2-digit'
|
second: '2-digit',
|
||||||
|
timeZone: 'Asia/Shanghai' // 明确使用北京时间
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return timeStr
|
return timeStr
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user