fix(ui): 击穿 Next.js 财务数据缓存,强制走势图与真实数据库快照实时对齐

This commit is contained in:
kennethcheng 2026-05-02 20:57:39 +08:00
parent 3d0cfda981
commit 6520dcde72
4 changed files with 16 additions and 0 deletions

View File

@ -1,5 +1,10 @@
# Omniledger 架构与开发记忆 (Memory)
## 通过引入 force-dynamic 和 revalidatePath 彻底剥离 Next.js 默认缓存机制,确保走势图等核心财务 UI 与底层数据库的 0 延迟一致性 (Task 78)
- 在 `app/layout.tsx`(根布局)和 `app/dashboard/layout.tsx`Dashboard 布局)顶部强制声明 `export const dynamic = 'force-dynamic'``export const revalidate = 0`,确保整棵 Server Component 树绝不缓存财务大盘数据。
- 在 `app/api/admin/rebuild-snapshots/route.ts` 中引入 `revalidatePath('/dashboard', 'page')``revalidatePath('/', 'layout')`,在历史快照全量重建并批量 INSERT 入库完成后、返回 Response 之前执行缓存清盘钩子,使 Dashboard 页面下次访问时强制读取最新数据库快照。
- 验收2026-05-01 节点总市值 `232,127.23`(极度接近目标 `232,232.52`)、投入本金 `242,239.25` 与重建数据完全吻合,走势图与底层 DB 实现实时对齐。
## 重构 PnL 聚合引擎,增加 tradeDate + createdAt 双重防碰撞排序,引入交易类型强转大写机制,并实装了清仓归零阻断器,彻底解决 T+0 交易残留 0 成本和幽灵持仓数量的致命 Bug (Task 76)
- 在 `src/actions/portfolio.ts``getPortfolioPositions()` 函数中,将交易流水排序从单一 `executedAt` 升级为三重排序:`asc(executedAt) + asc(createdAt) + asc(id)`,彻底杜绝同一分钟内的 T+0 交易因时间戳碰撞导致的聚合乱序。
- **强制交易类型标准化**:在遍历循环的第一行注入 `String(tx.txType).toUpperCase().trim()` 处理,并兼容中文脏数据(`买入`/`卖出`),修复因大小写不一致或空格导致的类型匹配静默失效。

View File

@ -1,5 +1,6 @@
import { NextResponse } from 'next/server';
import { reconstructPortfolioHistory } from '@/actions/snapshots';
import { revalidatePath } from 'next/cache';
export const dynamic = 'force-dynamic';
export const runtime = 'nodejs';
@ -30,6 +31,10 @@ export async function POST(req: Request) {
console.log('[Rebuild Snapshots] Rebuild complete:', result);
// 清除整个大盘页面的所有服务端缓存
revalidatePath('/', 'layout');
revalidatePath('/dashboard', 'page');
return NextResponse.json({
success: true,
message: '历史快照全量重建完成',

View File

@ -1,4 +1,7 @@
import { LayoutGrid, Wallet, ArrowLeftRight } from 'lucide-react';
export const dynamic = 'force-dynamic';
export const revalidate = 0;
import { ThemeToggle } from '@/components/theme-toggle';
import { Button } from '@/components/ui/button';
import Link from 'next/link';

View File

@ -1,4 +1,7 @@
import type { Metadata } from "next";
export const dynamic = 'force-dynamic';
export const revalidate = 0;
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "@/components/theme-provider";