Compare commits

..

No commits in common. "838bb0ef95658cad135f3327acdea0aaa9f688c1" and "e70c0602c8b82e779adae8188d173a32a9a407bf" have entirely different histories.

2 changed files with 16 additions and 25 deletions

View File

@ -87,11 +87,6 @@
- 统一颜色逻辑:值 `> 0` 应用 `text-red-500`(红色),值 `< 0` 应用 `text-green-500`(绿色),值 `=== 0` 使用默认文字颜色。 - 统一颜色逻辑:值 `> 0` 应用 `text-red-500`(红色),值 `< 0` 应用 `text-green-500`(绿色),值 `=== 0` 使用默认文字颜色。
- 括号内的百分比同步遵循相同逻辑,格式如 `$2447.48 (114.20%)` - 括号内的百分比同步遵循相同逻辑,格式如 `$2447.48 (114.20%)`
## 修复快照读取引擎中的 Drizzle 语法错误
- 修复快照读取引擎中的 Drizzle 语法错误,全面改用类型安全的 desc 和 gte 操作符进行查询。
- 在 `src/actions/snapshots.ts` 中引入 `desc``gte` 操作符,彻底替换原始 SQL 模板拼接(`sql`"${date}" DESC``),消除 `ReferenceError: date is not defined` 运行时错误。
- 使用 `desc(portfolioSnapshots.date)` 实现降序排列,使用 `gte(portfolioSnapshots.date, startDate)` 实现日期范围过滤,并添加 `.$dynamic()` 支持动态条件拼接。
## 持倉引擎 Native 幣種算法重構 (Task 38) ## 持倉引擎 Native 幣種算法重構 (Task 38)
- 重構底層盈虧引擎,全面轉向 Native 原生幣種計算,新增浮動/累計盈虧及百分比指標。 - 重構底層盈虧引擎,全面轉向 Native 原生幣種計算,新增浮動/累計盈虧及百分比指標。
- 徹底分離 Native 與 CNY 計算:單隻股票的成本與盈虧全部改用 Native (原幣種) 進行計算。 - 徹底分離 Native 與 CNY 計算:單隻股票的成本與盈虧全部改用 Native (原幣種) 進行計算。

View File

@ -3,8 +3,7 @@
import { db } from '@/db'; import { db } from '@/db';
import { portfolioSnapshots } from '@/db/schema'; import { portfolioSnapshots } from '@/db/schema';
import { getPortfolioPositions } from './portfolio'; import { getPortfolioPositions } from './portfolio';
import { desc, eq, gte, sql } from 'drizzle-orm'; import { eq, sql } from 'drizzle-orm';
import Big from 'big.js';
function getTodayInShanghai(): string { function getTodayInShanghai(): string {
const now = new Date(); const now = new Date();
@ -23,13 +22,13 @@ export async function recordDailySnapshot() {
const totalValueCny = positions.reduce( const totalValueCny = positions.reduce(
(sum, pos) => sum.plus(pos.cnyValue || '0'), (sum, pos) => sum.plus(pos.cnyValue || '0'),
new Big(0) 0 as number | string
).toString(); );
const totalCostCny = positions.reduce( const totalCostCny = positions.reduce(
(sum, pos) => sum.plus(pos.totalCostCny || '0'), (sum, pos) => sum.plus(pos.totalCostCny || '0'),
new Big(0) 0 as number | string
).toString(); );
const dateStr = getTodayInShanghai(); const dateStr = getTodayInShanghai();
@ -45,8 +44,8 @@ export async function recordDailySnapshot() {
await db await db
.update(portfolioSnapshots) .update(portfolioSnapshots)
.set({ .set({
totalValueCny, totalValueCny: String(totalValueCny),
totalCostCny, totalCostCny: String(totalCostCny),
updatedAt: now, updatedAt: now,
}) })
.where(eq(portfolioSnapshots.date, dateStr)); .where(eq(portfolioSnapshots.date, dateStr));
@ -55,8 +54,8 @@ export async function recordDailySnapshot() {
success: true, success: true,
action: 'updated', action: 'updated',
date: dateStr, date: dateStr,
totalValueCny, totalValueCny: String(totalValueCny),
totalCostCny, totalCostCny: String(totalCostCny),
}; };
} }
@ -64,8 +63,8 @@ export async function recordDailySnapshot() {
.insert(portfolioSnapshots) .insert(portfolioSnapshots)
.values({ .values({
date: dateStr, date: dateStr,
totalValueCny, totalValueCny: String(totalValueCny),
totalCostCny, totalCostCny: String(totalCostCny),
createdAt: now, createdAt: now,
updatedAt: now, updatedAt: now,
}); });
@ -74,8 +73,8 @@ export async function recordDailySnapshot() {
success: true, success: true,
action: 'inserted', action: 'inserted',
date: dateStr, date: dateStr,
totalValueCny, totalValueCny: String(totalValueCny),
totalCostCny, totalCostCny: String(totalCostCny),
}; };
} }
@ -89,16 +88,13 @@ export async function getSnapshots(params?: {
let query = db let query = db
.select() .select()
.from(portfolioSnapshots) .from(portfolioSnapshots)
.orderBy(desc(portfolioSnapshots.date)) .orderBy(sql`"${date}" DESC`);
.$dynamic();
if (startDate) { if (startDate) {
query = query.where(gte(portfolioSnapshots.date, startDate)); query = query.where(sql`"${date}" >= ${startDate}`);
} }
if (endDate) { if (endDate) {
query = query.where( query = query.where(sql`"${date}" <= ${endDate}`);
portfolioSnapshots.date.lte(endDate)
);
} }
const snapshots = await query.limit(limit); const snapshots = await query.limit(limit);