/** * 神之一手(one_divine_lot)DSH 插件入口 * * 迭代 01:分仓管理工具 * - S2 数据源直连 QMT Bridge REST(技术约束-003) * - S3 设置管理(策略=标签 + 显示开关) * - S4 分仓数据本地存储 * - S5 分仓逻辑(份额分配/查询) * - S6 服务端 HTTP API(webServer /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'; // 依赖服务:settings(S3)、webServer(S6 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 qmtBaseUrl(Q4 不迁移) 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-006:JSON 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 API(R-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 };