fix(ui): 对齐图表末端与实时概览数据的精度,放开全量历史趋势查询限制

This commit is contained in:
kennethcheng 2026-04-30 18:42:08 +08:00
parent b131400aa9
commit 5269d697b7
3 changed files with 25 additions and 5 deletions

View File

@ -173,3 +173,10 @@
- 修复 `getEffectivePrice` 引擎中 Drizzle ORM 查询条件未使用 `and()` 复合操作符的致命逻辑漏网,确保历史断点结转价格精准匹配单一资产。
- 在 `src/actions/snapshots.ts` 顶部从 `drizzle-orm` 引入 `and` 操作符,将 `getEffectivePrice``where` 子句从两个独立的 `.where()` 链式调用重构为 `and(eq(assetPricesHistory.assetId, assetId), lte(assetPricesHistory.date, dateStr))` 的显式复合条件。
- 修复后 `assetId` 条件与日期条件被绝对锁定在同一个 `AND` 逻辑块中,彻底杜绝了周末等非交易日历史节点价格跨资产串联的荒谬市值问题(如海尔被错误匹配 BTC 价格导致 3700 万市值)。
## 通过前端运行时覆盖策略完美对齐图表今日快照与实时总资产的汇率时差 (Task 54)
- 修复了 Dashboard 图表末端Today 节点)与页面顶部超大号"总资产"数字之间存在时差误差的问题:图表依赖数据库里几小时前的快照,而概览数字基于实时计算,导致两者不完全一致。
- 在 `app/dashboard/page.tsx``loadSnapshots` 中引入前端运行时覆盖策略:先调用 `getPortfolioSummary()` 获取实时总览数据,再调用 `getSnapshots()` 获取历史快照,动态替换或追加今天的节点,确保图表末端与大数字 100% 严丝合缝。
- 具体逻辑:如果今天已有快照记录(`lastSnapshot.date === todayStr`),则将 `totalValueCny``totalCostCny` 覆盖为实时值;如果今天尚无快照,则直接追加一个实时点。
- 移除 `getSnapshots` 查询的视口限制:将 `src/actions/snapshots.ts``getSnapshots` 的默认 `limit` 从 365 改为无默认值,仅在显式传入 `limit` 参数时才应用 `.limit()`,前端调用处移除 `{ limit: 30 }` 参数,实现从第一笔交易至今的全量净值走势渲染。
- 前端 Dashboard 页面中两处 `getSnapshots` 调用(初始加载与重构历史后刷新)均已移除 `limit` 参数。

View File

@ -109,7 +109,20 @@ export default function DashboardPage() {
useEffect(() => {
async function loadSnapshots() {
await recordDailySnapshot();
const data = await getSnapshots({ limit: 30 });
const summary = await getPortfolioSummary();
const data = await getSnapshots();
const todayStr = new Date().toISOString().slice(0, 10);
const lastSnapshot = data[data.length - 1];
if (lastSnapshot && lastSnapshot.date === todayStr) {
lastSnapshot.totalValueCny = summary.totalCnyValue;
lastSnapshot.totalCostCny = summary.totalCnyValue;
} else {
data.push({
date: todayStr,
totalValueCny: summary.totalCnyValue,
totalCostCny: summary.totalCnyValue,
});
}
setSnapshots(data);
}
loadSnapshots();
@ -212,7 +225,7 @@ export default function DashboardPage() {
toast.dismiss();
if (result.success) {
toast.success(`重构成功,已填充 ${result.daysReconstructed} 天历史数据`);
setSnapshots(await getSnapshots({ limit: 30 }));
setSnapshots(await getSnapshots());
} else if (result.message) {
toast.info(result.message);
}

View File

@ -88,7 +88,7 @@ export async function getSnapshots(params?: {
startDate?: string;
endDate?: string;
}) {
const { limit = 365, startDate, endDate } = params || {};
const { limit, startDate, endDate } = params || {};
let query = db
.select()
@ -105,7 +105,7 @@ export async function getSnapshots(params?: {
);
}
const snapshots = await query.limit(limit);
const snapshots = limit ? await query.limit(limit) : await query;
return snapshots.reverse();
}