feat(存储): JSON data store 升级 SQLite(迭代 06,R-008)

- 新增 SqliteStore(node:sqlite 封装):strategy_holdings 持仓生命周期表 + market_quotes_cache 行情缓存表
- DataStore 改造为门面委托 SqliteStore,保留兼容 API,启动检测自动迁移(幂等)
- PositionManager 适配单票生命周期(openHolding/addShares/reduceShares/closeHolding),清仓转历史保留
- 新增一次性迁移脚本 scripts/migrate-json-to-sqlite.mjs + pnpm migrate 命令
- 修复 addToStrategy 语义 Bug(新增量 vs 绝对目标量),真实 API CRUD 回归通过
- 移除策略 tab 一键清零按钮及 remove-all-shares 端点(clearStrategyShares 保留供删除策略联动)
- 插件 dispose 补充 storage.close() 关闭 SQLite 连接
This commit is contained in:
2026-09-01 18:04:12 +08:00
parent aef27dcd3f
commit 9b719f13ba
10 changed files with 555 additions and 299 deletions
+44
View File
@@ -0,0 +1,44 @@
/**
* 一次性迁移脚本:旧 JSON data store → SQLiteR-008 / 迭代 06D6
*
* 用途:
* - 独立执行(手动 / CI),逻辑与 DataStore 启动自动迁移共用(都调用 SqliteStore.migrateJson
* - 手动/CI 环境下可显式触发迁移,无需等插件启动
*
* 用法:
* node scripts/migrate-json-to-sqlite.mjs # 默认数据目录 ~/.dsh/one-divine-lot
* ODL_TEST_DATA_DIR=/tmp/xxx node scripts/migrate-json-to-sqlite.mjs # 指定数据目录(测试隔离,技术约束-011)
*
* 行为(与启动自动迁移一致,幂等):
* 1. 检测 SQLite 是否为空(strategy_holdings / market_quotes_cache 均无数据)——非空则跳过;
* 2. store.json → strategy_holdings(策略为中心平铺,created_at=迁移时间戳);
* 3. store.market.json → market_quotes_cache(抽取 lastPrice/lastClose 两列);
* 4. 旧 allocations.json(若存在)→ strategy_holdingscode 为中心聚合,跳过重复);
* 5. 迁移前备份 JSON 为 *.bak;迁移失败不破坏原文件。
*/
import { SqliteStore } from '../lib/component/SqliteStore.js';
async function main() {
const store = new SqliteStore({}); // dataDir 默认 ~/.dsh/one-divine-lot,或 ODL_TEST_DATA_DIR
try {
if (!store.isEmpty()) {
console.log('[migrate] SQLite 已有数据,跳过迁移(幂等)');
return;
}
const result = await store.migrateJson();
console.log('[migrate] 迁移完成:', JSON.stringify(result));
if (result.holdings === 0 && result.quotes === 0) {
console.log('[migrate] 无数据可迁移(store.json / store.market.json / allocations.json 均不存在或为空)');
} else {
console.log('[migrate] 旧 JSON 已备份为 *.bak,可安全删除或保留');
}
} catch (err) {
console.error('[migrate] 迁移失败:', err.message);
process.exitCode = 1;
} finally {
store.close();
}
}
main();