From 7cdee75bb96d4f53dbd6042f422bffa0ed9f25f0 Mon Sep 17 00:00:00 2001 From: kennethcheng Date: Sat, 2 May 2026 17:22:32 +0800 Subject: [PATCH] =?UTF-8?q?fix(ledger):=20=E4=BF=AE=E5=A4=8D=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E5=87=80=E5=80=BC=E5=8F=8C=E9=87=8D=E6=B1=87=E7=8E=87?= =?UTF-8?q?=E6=8A=98=E7=AE=97=20bug=20=E4=B8=8E=E6=B1=87=E7=8E=87=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=9F=A5=E8=AF=A2=E8=BE=B9=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Memory.md | 8 ++++++++ app/api/debug/snapshot/route.ts | 6 +++--- src/actions/snapshots.ts | 8 +++----- src/utils/finance.ts | 5 ++++- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Memory.md b/Memory.md index 384f99a..9054a5d 100644 --- a/Memory.md +++ b/Memory.md @@ -1,5 +1,13 @@ # Omniledger 架构与开发记忆 (Memory) +## 修复时光机引擎:1. 将汇率查询条件延展至 23:59:59 以解决跨日边界导致的数据穿透失败;2. 修复对已结算法币成本进行双重汇率乘法的严重财务逻辑 Bug (Task 74) +- **汇率时间边界修复**:在 `src/actions/snapshots.ts` 和 `app/api/debug/snapshot/route.ts` 的 `buildDailyRatesMap` 函数中,将 `getClosestRateForDate` 内部的时间比较边界从 `targetDateStr + 'T00:00:00Z'` 延展至 `targetDateStr + 'T23:59:59.999'`。修复根因:初始 SQL 查询使用 `lte(fetchTime, 23:59:59)` 拉取全天数据,但内层循环用次日 00:00:00 做 `<=` 截断,导致跨日时区边界下汇率数据穿透失败,美元汇率回退到 7.22 兜底值而非数据库真实的 6.82。 +- **双重汇率乘法修复**:在 `src/utils/finance.ts` 的 `calculateAssetMetrics` 返回值中新增 `accumulatedCost` 字段(`totalInvested - totalRealized`),代表持仓净成本(Base Currency)。 +- **架构红线**:`accumulatedCost` / `totalCost` 在底层数据库已是法币 (CNY) 本位(交易录入时已乘以 `exchangeRate`),**严禁再与 `snapshotFxRate` 相乘**。修复 `reconstructPortfolioHistory()` 中的 `posCostCny` 计算:从 `(metrics.marketValue - metrics.accumulatedPnl) * snapshotFxRate` 改为直接取 `metrics.accumulatedCost`,彻底消除 USD→CNY 再×FXRate 的双重折算暴击。 +- 同步修复 `app/api/debug/snapshot/route.ts` X光机接口:`calcCostCny` 从 `holding.totalCost * fxNum` 改为 `holding.totalCost`,确保调试接口与时光机引擎逻辑完全一致。 +- **验收**:META 的 `calculatedCostCny` 从虚高的 61 万量级回落到 4255 左右真实水平,美股 `snapshotFxRate` 成功抓取数据库 6.82 而非 7.22 兜底值。 + + ## 重构时光机底层引擎,引入基于 lte 的历史价格/汇率向后穿透查询,解决数据断层导致的 0 价格黑洞与汇率串用 Bug (Task 72) - 在 `src/actions/snapshots.ts` 的 `reconstructPortfolioHistory()` 中,将汇率获取从"一次性全量加载"重构为"按天循环顶部动态构建":每天 `targetDate` 循环开始时调用 `buildDailyRatesMap(dateStr)`,查询 `exchange_rates_history` 中 `fetch_time <= targetDate` 的所有记录,按 `(fromCurrency, toCurrency)` 分组构建当日汇率字典,O(1) 内存访问。 - **汇率兜底安全值**:USD → 7.22,HKD → 0.92,CNY → 1,确保新系统建的老账单查不到历史汇率时不会崩溃。 diff --git a/app/api/debug/snapshot/route.ts b/app/api/debug/snapshot/route.ts index 99cdc78..82eb1da 100644 --- a/app/api/debug/snapshot/route.ts +++ b/app/api/debug/snapshot/route.ts @@ -48,10 +48,10 @@ async function buildDailyRatesMap(targetDateStr: string): Promise