Compare commits
2 Commits
e70c0602c8
...
838bb0ef95
| Author | SHA1 | Date | |
|---|---|---|---|
| 838bb0ef95 | |||
| 8f5ce4bc74 |
@ -87,6 +87,11 @@
|
|||||||
- 统一颜色逻辑:值 `> 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 (原幣種) 進行計算。
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
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 { eq, sql } from 'drizzle-orm';
|
import { desc, eq, gte, sql } from 'drizzle-orm';
|
||||||
|
import Big from 'big.js';
|
||||||
|
|
||||||
function getTodayInShanghai(): string {
|
function getTodayInShanghai(): string {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@ -22,13 +23,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'),
|
||||||
0 as number | string
|
new Big(0)
|
||||||
);
|
).toString();
|
||||||
|
|
||||||
const totalCostCny = positions.reduce(
|
const totalCostCny = positions.reduce(
|
||||||
(sum, pos) => sum.plus(pos.totalCostCny || '0'),
|
(sum, pos) => sum.plus(pos.totalCostCny || '0'),
|
||||||
0 as number | string
|
new Big(0)
|
||||||
);
|
).toString();
|
||||||
|
|
||||||
const dateStr = getTodayInShanghai();
|
const dateStr = getTodayInShanghai();
|
||||||
|
|
||||||
@ -44,8 +45,8 @@ export async function recordDailySnapshot() {
|
|||||||
await db
|
await db
|
||||||
.update(portfolioSnapshots)
|
.update(portfolioSnapshots)
|
||||||
.set({
|
.set({
|
||||||
totalValueCny: String(totalValueCny),
|
totalValueCny,
|
||||||
totalCostCny: String(totalCostCny),
|
totalCostCny,
|
||||||
updatedAt: now,
|
updatedAt: now,
|
||||||
})
|
})
|
||||||
.where(eq(portfolioSnapshots.date, dateStr));
|
.where(eq(portfolioSnapshots.date, dateStr));
|
||||||
@ -54,8 +55,8 @@ export async function recordDailySnapshot() {
|
|||||||
success: true,
|
success: true,
|
||||||
action: 'updated',
|
action: 'updated',
|
||||||
date: dateStr,
|
date: dateStr,
|
||||||
totalValueCny: String(totalValueCny),
|
totalValueCny,
|
||||||
totalCostCny: String(totalCostCny),
|
totalCostCny,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,8 +64,8 @@ export async function recordDailySnapshot() {
|
|||||||
.insert(portfolioSnapshots)
|
.insert(portfolioSnapshots)
|
||||||
.values({
|
.values({
|
||||||
date: dateStr,
|
date: dateStr,
|
||||||
totalValueCny: String(totalValueCny),
|
totalValueCny,
|
||||||
totalCostCny: String(totalCostCny),
|
totalCostCny,
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
updatedAt: now,
|
updatedAt: now,
|
||||||
});
|
});
|
||||||
@ -73,8 +74,8 @@ export async function recordDailySnapshot() {
|
|||||||
success: true,
|
success: true,
|
||||||
action: 'inserted',
|
action: 'inserted',
|
||||||
date: dateStr,
|
date: dateStr,
|
||||||
totalValueCny: String(totalValueCny),
|
totalValueCny,
|
||||||
totalCostCny: String(totalCostCny),
|
totalCostCny,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,13 +89,16 @@ export async function getSnapshots(params?: {
|
|||||||
let query = db
|
let query = db
|
||||||
.select()
|
.select()
|
||||||
.from(portfolioSnapshots)
|
.from(portfolioSnapshots)
|
||||||
.orderBy(sql`"${date}" DESC`);
|
.orderBy(desc(portfolioSnapshots.date))
|
||||||
|
.$dynamic();
|
||||||
|
|
||||||
if (startDate) {
|
if (startDate) {
|
||||||
query = query.where(sql`"${date}" >= ${startDate}`);
|
query = query.where(gte(portfolioSnapshots.date, startDate));
|
||||||
}
|
}
|
||||||
if (endDate) {
|
if (endDate) {
|
||||||
query = query.where(sql`"${date}" <= ${endDate}`);
|
query = query.where(
|
||||||
|
portfolioSnapshots.date.lte(endDate)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const snapshots = await query.limit(limit);
|
const snapshots = await query.limit(limit);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user