Files
kyugao 2aacd8a25d docs(迭代07/08): 交易记录本地存储 + 策略持仓展开全量文档 + 需求归档(R-009/R-010)
- PLAN-008/PLAN-009 计划(已完成)
- 迭代 07/08 四件套(目标/技术方案/验收标准/复盘)
- 设计约束:数据存储设计 §10/§10.1、技术约束-012/013、产品约束-008
- R-009/R-010 需求归档(含二次定稿:手动归属、Q4 成本价/成交价)
- 需求池索引同步
2026-09-02 01:21:10 +08:00

2.3 KiB
Raw Permalink Blame History

技术实现方案:08-策略持仓行展开关联交易记录(Holding → 交易汇总)

依据:PLAN-009 需求:R-010(Q1-Q3 定稿)| 设计约束:技术约束-012、技术约束-011(测试隔离)

技术选型

  • 复用 R-009 的 holding_id 关联(trade_orders.holding_id);
  • 服务端:strategy-positions 附加 holding_id + trades/by-holding 端点;
  • 前端:StrategyTab 持仓行展开(懒加载内嵌汇总表)。

数据流

策略持仓 tab
  load() → strategy-positions(每行含 holding_id
  用户点击持仓行 → 展开 → trades/by-holding?holdingId=13 → 委托汇总列表
  懒加载:不展开不请求

实现细节

1. PositionManager.getStrategyPositions 附加 holding_id

async getStrategyPositions(strategyId) {
  const [positions, dataset, holdings] = await Promise.all([
    this.getAllPositions(),
    this.storage.getDataset(strategyId),
    this.storage.getCurrentHoldings(strategyId),  // 含 holding_id
  ]);
  const shareMap = new Map(dataset.map(d => [d.code, d.shares]));
  const holdingMap = new Map(holdings.map(h => [h.code, h.holdingId]));
  return positions.map(p => {
    const shares = shareMap.get(p.code) ?? 0;
    return shares > 0 ? { ...p, shares, holdingId: holdingMap.get(p.code) ?? null } : null;
  }).filter(Boolean);
}

2. SqliteStore.getOrdersByHolding

getOrdersByHolding(holdingId) {
  this.init();
  return this.db.prepare(
    'SELECT * FROM trade_orders WHERE holding_id=? ORDER BY insert_ts DESC'
  ).all(holdingId).map(r => this._mapOrderRow(r));
}

3. api/trades.jstrades/by-holding 端点

case 'trades/by-holding':
  return await storage.getTradeOrdersByHolding(args.holdingId);

4. StrategyTab 展开 UI

  • 持仓行加展开箭头(类似交易记录 tab);
  • 展开时调 trades/by-holding,内嵌小表格显示委托汇总: 时间 / 方向 / 状态 / 委托量 / 成交量 / 均价 / 金额 / 费用;
  • 只显示委托汇总(ORDER_STATUS 中文映射复用);
  • 懒加载:展开时才请求,折叠清空。

涉及设计约束

约束 内容
技术约束-012 数据存储遵循数据存储设计.md(复用 trade_orders.holding_id
技术约束-011 测试用独立数据目录(ODL_TEST_DATA_DIR