Files
one_divine_lot/src/index.js
T
kyugao 9b719f13ba 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 连接
2026-09-01 18:04:12 +08:00

96 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 神之一手(one_divine_lotDSH 插件入口
*
* 迭代 01:分仓管理工具
* - S2 数据源直连 QMT Bridge REST(技术约束-003
* - S3 设置管理(策略=标签 + 显示开关)
* - S4 分仓数据本地存储
* - S5 分仓逻辑(份额分配/查询)
* - S6 服务端 HTTP APIwebServer /odl/api/*
* - S7 客户端 tab 展示(客户端插件)
*
* 依据:docs/02-计划/计划-分仓管理工具.md、docs/04-迭代记录/01-分仓管理工具/
* 设计约束:技术约束-001/003、产品约束-001/002/003
*/
import { QmtBridgeRestDataSource } from './component/QmtBridgeRestDataSource.js';
import { registerSettings, resolveStartupConnection } from './settings.js';
import { DataStore } from './component/DataStore.js';
import { PositionManager } from './component/PositionManager.js';
import { registerApi } from './api/index.js';
import { MarketDataHub } from './component/MarketDataHub.js';
import { MarketFeed } from './component/MarketFeed.js';
import { QmtHealthMonitor } from './component/QmtHealthMonitor.js';
const name = 'one-divine-lot';
// 依赖服务:settingsS3)、webServerS6 api
const inject = ['settings', 'webServer'];
async function apply(ctx, config) {
const logger = ctx.logger;
// S3: 设置管理(策略=标签 + 显示开关 + QMT 连接配置)
const settings = registerSettings(ctx, config);
// S2: 数据源(REST 直连 QMT Bridge
// R-004(技术约束-008):启动激活地址 = 默认配置 → 上次激活 → 第一条;列表为空回退 cordis qmtBaseUrlQ4 不迁移)
const settingsScope = settings;
let startup;
try {
startup = await resolveStartupConnection(settingsScope, config);
} catch (e) {
logger.warn('[one-divine-lot] 启动连接解析失败,回退 cordis qmtBaseUrl: ' + e.message);
startup = { connection: null, baseUrl: config?.qmtBaseUrl ?? null, source: 'cordis-fallback' };
}
const dataSource = new QmtBridgeRestDataSource({
baseUrl: startup.baseUrl ?? config?.qmtBaseUrl,
timeoutMs: config?.qmtTimeoutMs,
});
logger.info(
'[one-divine-lot] QMT 连接激活: ' + (startup.connection ? startup.connection.name + ' (' + startup.connection.baseUrl + ')' : String(startup.baseUrl ?? '(未配置)')) +
' [来源: ' + startup.source + ']'
);
// S4: 分仓数据本地存储(R-006JSON data store,策略为中心)
const storage = new DataStore({
dataDir: config?.dataDir,
});
// R-005:行情数据(MarketDataHub 缓存 + MarketFeed 获取,页面轮询读取)
const marketHub = new MarketDataHub({ logger, dataStore: storage });
// 启动时从磁盘加载行情缓存(首屏快速展示,不依赖实盘订阅)
marketHub.warmup().catch((e) => logger.warn('[one-divine-lot] 行情预热失败: ' + e.message));
const marketFeed = new MarketFeed({ hub: marketHub, runtime: { settings, dataSource }, logger });
marketFeed.start(startup.baseUrl ?? config?.qmtBaseUrl);
// QMT 连接健康检查(服务端定时探测 + 缓存,2026-09-01
const qmtHealthMonitor = new QmtHealthMonitor({ runtime: { settings, dataSource }, logger });
qmtHealthMonitor.start();
// S5: 分仓逻辑(份额分配/查询)
const manager = new PositionManager({ dataSource, storage });
// S6: 服务端 HTTP APIR-004:注入 dataSource 以编排激活热切换)
registerApi(ctx, { manager, settings, dataSource, marketHub, marketFeed, qmtHealthMonitor });
// 启动时可用性检查(日志,不阻塞)
dataSource.isAvailable().then((ok) => {
logger.info(`[one-divine-lot] QMT Bridge REST 可用: ${ok}`);
}).catch((e) => {
logger.warn(`[one-divine-lot] QMT Bridge REST 检查失败: ${e.message}`);
});
ctx.effect(() => {
logger.info('[one-divine-lot] 插件已加载(S1-S6 就绪)');
return () => {
marketFeed.stop();
qmtHealthMonitor.stop();
storage.close(); // 关闭 SQLite 连接(迭代 06
logger.info('[one-divine-lot] 插件已释放');
};
}, 'one-divine-lot.dispose');
}
export { name, inject, apply };