fix(ledger): 修复价格变量泄漏与日期字符串比较陷阱,还原真实净值走势

This commit is contained in:
2026-04-30 13:15:30 +08:00
parent 9622e0d828
commit 91e7485259
2 changed files with 35 additions and 11 deletions
+26 -10
View File
@@ -6,16 +6,20 @@ import { getPortfolioPositions } from './portfolio';
import { asc, desc, eq, gte, lte, sql } from 'drizzle-orm';
import Big from 'big.js';
function formatDateString(date: Date): string {
const yyyy = date.getFullYear();
const mm = String(date.getMonth() + 1).padStart(2, '0');
const dd = String(date.getDate()).padStart(2, '0');
return `${yyyy}-${mm}-${dd}`;
}
function getTodayInShanghai(): string {
const now = new Date();
const utcStr = now.toLocaleString('en-US', { timeZone: 'UTC' });
const utcDate = new Date(utcStr);
const shanghaiOffset = 8 * 60 * 60 * 1000;
const shanghaiDate = new Date(utcDate.getTime() + shanghaiOffset);
const year = shanghaiDate.getFullYear();
const month = String(shanghaiDate.getMonth() + 1).padStart(2, '0');
const day = String(shanghaiDate.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
return formatDateString(shanghaiDate);
}
export async function recordDailySnapshot() {
@@ -113,7 +117,7 @@ interface HistoricalPosition {
}
export async function getHistoricalPositions(targetDate: Date): Promise<HistoricalPosition[]> {
const dateStr = targetDate.toISOString().split('T')[0];
const dateStr = formatDateString(targetDate);
const allTransactions = await db
.select({
@@ -183,7 +187,7 @@ export async function getEffectivePrice(
assetId: string,
targetDate: Date
): Promise<string | null> {
const dateStr = targetDate.toISOString().split('T')[0];
const dateStr = formatDateString(targetDate);
const [record] = await db
.select({
@@ -227,11 +231,14 @@ export async function reconstructPortfolioHistory() {
.select({
id: assets.id,
baseCurrency: assets.baseCurrency,
latestPrice: assets.latestPrice,
})
.from(assets);
const assetBaseCurrencyMap = new Map<string, string>();
const assetLatestPriceMap = new Map<string, string>();
for (const a of allAssets) {
assetBaseCurrencyMap.set(a.id, a.baseCurrency);
assetLatestPriceMap.set(a.id, a.latestPrice || '0');
}
const allRates = await db
@@ -273,8 +280,8 @@ export async function reconstructPortfolioHistory() {
let daysReconstructed = 0;
while (currentDate.toISOString().split('T')[0] <= todayStr) {
const dateStr = currentDate.toISOString().split('T')[0];
while (formatDateString(currentDate) <= todayStr) {
const dateStr = formatDateString(currentDate);
const positions = await getHistoricalPositions(currentDate);
@@ -284,12 +291,21 @@ export async function reconstructPortfolioHistory() {
for (const pos of positions) {
const priceStr = await getEffectivePrice(pos.assetId, currentDate);
const baseCurrency = assetBaseCurrencyMap.get(pos.assetId) || 'USD';
if (priceStr) {
const cnyPrice = convertPriceToCny(priceStr, baseCurrency);
if (!priceStr) {
const fallbackPrice = assetLatestPriceMap.get(pos.assetId) || '0';
const cnyPrice = convertPriceToCny(fallbackPrice, baseCurrency);
const price = new Big(cnyPrice);
const qty = new Big(pos.quantity);
totalValueCny = totalValueCny.plus(price.times(qty));
totalCostCny = totalCostCny.plus(pos.totalCost);
continue;
}
const cnyPrice = convertPriceToCny(priceStr, baseCurrency);
const price = new Big(cnyPrice);
const qty = new Big(pos.quantity);
totalValueCny = totalValueCny.plus(price.times(qty));
totalCostCny = totalCostCny.plus(pos.totalCost);
}