fix(trade_query): 优化时间筛选逻辑以支持创建时间的回退处理

在交易查询逻辑中,调整了时间筛选条件,确保在存在 `created_at` 列时使用 `COALESCE(created_at, entry_time)`,并在无该列时回退至 `entry_time`。同时,增强了对时间筛选的支持,确保在不同筛选条件下均能正确返回结果。这一改动旨在提升查询的准确性与一致性。
This commit is contained in:
薇薇安 2026-02-25 15:41:23 +08:00
parent 5c854290eb
commit 5a3888d905
4 changed files with 26 additions and 16 deletions

View File

@ -1104,7 +1104,7 @@ class Trade:
query += " AND (entry_reason IS NULL OR entry_reason != 'sync_recovered')"
query += " AND (exit_reason IS NULL OR exit_reason != 'sync')"
# 按创建时间筛选时若表无 created_at 则回退为 entry_time
# 按创建时间筛选:有 created_at 列则用 COALESCE(created_at, entry_time);无则回退为 entry_time保证「按创建时间」有结果
use_created = (time_filter == "created" and _table_has_column("trades", "created_at"))
time_col = "COALESCE(created_at, entry_time)" if use_created else None
@ -1118,6 +1118,9 @@ class Trade:
elif use_created:
query += " AND " + time_col + " >= %s AND " + time_col + " <= %s"
params.extend([start_timestamp, end_timestamp])
elif time_filter == "created":
query += " AND entry_time >= %s AND entry_time <= %s"
params.extend([start_timestamp, end_timestamp])
else:
query += " AND COALESCE(exit_time, entry_time) >= %s AND COALESCE(exit_time, entry_time) <= %s"
params.extend([start_timestamp, end_timestamp])
@ -1125,12 +1128,12 @@ class Trade:
if time_filter == "exit":
query += " AND ((status = 'closed' AND exit_time >= %s) OR (status != 'closed' AND entry_time >= %s))"
params.extend([start_timestamp, start_timestamp])
elif time_filter == "entry":
query += " AND entry_time >= %s"
params.append(start_timestamp)
elif use_created:
query += " AND " + time_col + " >= %s"
params.append(start_timestamp)
elif time_filter == "entry" or time_filter == "created":
query += " AND entry_time >= %s"
params.append(start_timestamp)
else:
query += " AND COALESCE(exit_time, entry_time) >= %s"
params.append(start_timestamp)
@ -1138,12 +1141,12 @@ class Trade:
if time_filter == "exit":
query += " AND ((status = 'closed' AND exit_time <= %s) OR (status != 'closed' AND entry_time <= %s))"
params.extend([end_timestamp, end_timestamp])
elif time_filter == "entry":
query += " AND entry_time <= %s"
params.append(end_timestamp)
elif use_created:
query += " AND " + time_col + " <= %s"
params.append(end_timestamp)
elif time_filter == "entry" or time_filter == "created":
query += " AND entry_time <= %s"
params.append(end_timestamp)
else:
query += " AND COALESCE(exit_time, entry_time) <= %s"
params.append(end_timestamp)
@ -1163,6 +1166,8 @@ class Trade:
if use_created:
query += " ORDER BY " + time_col + " DESC, id DESC"
elif time_filter == "created":
query += " ORDER BY entry_time DESC, id DESC"
else:
query += " ORDER BY COALESCE(exit_time, entry_time) DESC, id DESC"
# 未传 limit 时使用默认上限防止全表加载导致内存暴增2 CPU 4G 场景)

View File

@ -339,6 +339,14 @@
border-radius: 6px;
}
.position-time-row {
font-size: 0.85rem;
color: #212529;
margin-bottom: 0.5rem;
font-weight: 500;
}
.position-time-row:empty { display: none; }
.entry-time {
color: #999;
font-size: 0.8rem;

View File

@ -696,6 +696,11 @@ const StatsDashboard = () => {
{trade.side}
</div>
<div className="trade-info">
<div className="position-time-row">
开仓时间: {(trade.entry_time || trade.created_at) ? formatEntryTime(trade.entry_time || trade.created_at) : '—'}
{' · '}
创建时间: {(trade.created_at != null && trade.created_at !== '') ? formatEntryTime(trade.created_at) : '—'}
</div>
<div className="entry-type-line">
入场类型:{' '}
<span
@ -736,14 +741,6 @@ const StatsDashboard = () => {
</div>
{/* 止损止盈比例 */}
<div className="entry-time">
开仓时间: {(trade.entry_time || trade.created_at) ? formatEntryTime(trade.entry_time || trade.created_at) : '—'}
</div>
<div className="entry-time">
创建时间: {(trade.created_at != null && trade.created_at !== '') ? formatEntryTime(trade.created_at) : '—'}
</div>
</div>
<div className="trade-protection-col">
<div className="stop-take-info">

View File

@ -29,7 +29,7 @@ const TradeList = () => {
useEffect(() => {
loadData()
}, [accountId, reconciledOnly, timeFilter]) // accountId / 仅可对账 / 时间筛选方式 变化时重新加载
}, [accountId, reconciledOnly, timeFilter, period, useCustomDate, startDate, endDate, symbol, status, tradeType, exitReason]) //
const loadData = async () => {
setLoading(true)