# 迭代复盘:08-策略持仓行展开关联交易记录(Holding → 交易汇总) > 复盘日期:2026-09-02 | 迭代状态:**已完成(老师确认)** > 关联需求:R-010(策略持仓行展开关联交易记录,已定稿) > 关联计划:PLAN-009(计划-策略持仓行展开关联交易记录) ## 结果 迭代 08 达成:策略持仓 tab 每个持仓行(Holding)可展开,展开显示与该 holding 关联的交易记录(委托汇总,不分笔成交);持仓行新增成本价(avgPrice)+ 最后一笔成交价(lastTradePrice)两列;神之一手全部 tab 激活时隐藏 AI 对话输入框(纯 CSS)。实现「持仓 ↔ 交易」双向追溯。 ## 过程事实 1. **需求定稿(R-010)**:老师提出持仓行展开看关联交易(只要委托汇总)→ AI 登记 Q1-Q3(附加 holding_id / by-holding 端点 / 展开 UI)→ 老师确认 → 补充 Q4(成本价 + 最后一笔成交价,取最新有成交的 tradedPrice,无则默认成本价); 2. **服务端**:strategy-positions 附加 holding_id(PositionManager 查 strategy_holdings)+ 计算 avgPrice/lastTradePrice(查该 holding 关联委托,取最新有成交的 tradedPrice);SqliteStore.getOrdersByHolding + DataStore 委托;api trades/by-holding 端点; 3. **前端**:StrategyTab 持仓行展开(箭头 + 懒加载 + 内嵌委托汇总表,含交易日/时间列);成本价/最后一笔成交价两列; 4. **隐藏输入框**:借鉴 dsh-context 插件的纯 CSS 方案(:has(.odl-root) 命中时隐藏 composer)——神之一手 tab 激活时隐藏输入框,chat/其他 tab 正常显示; 5. **验证**:回归测试(by-holding 查询 6 项 + lastTradePrice 计算 4 项)通过、真实数据验证(001330.SZ holding 13 → 博纳影业卖出委托;成本价 6.84 / 最后成交 6.00)、构建 + typecheck 通过。 ## 经验教训(复盘沉淀) ### 1. 隐藏宿主 UI 优先借鉴成熟插件方案 - 曾深挖 DSH composer chain / selector 机制(复杂度高、依赖内部 store),后经老师提示参考 dsh-context 插件——它用一行纯 CSS(:has() 选择器)实现「特定 tab 激活时隐藏输入框」,零宿主改动、零风险; - **沉淀**:改宿主 UI 前先看同类插件怎么做的;纯 CSS :has() 是最优雅的 view 条件渲染方案。 ### 2. 前端声明顺序 TDZ(本次多次踩坑) - 迭代 07/08 多次遇到「Cannot access X before initialization」(TDZ):useEffect 依赖数组/回调在渲染时求值,引用了后声明的 const; - **沉淀**:React 组件内所有 useCallback/useEffect 的依赖引用必须在其声明之后;新增代码时严格核对声明顺序,构建后浏览器验证。 ### 3. 服务端 vs 客户端生效机制 - 服务端改动(PositionManager/API/SqliteStore)需重启 DSH 生效;客户端 bundle 刷新即载(rev 变化自动同步); - **沉淀**:改完先确认服务端端点/响应是新行为,再让老师重启。 ## 遗留/后续 1. **持仓展开的交易汇总**:当前只显示该 holding 关联的委托汇总;后续可按 holding 聚合复盘(目标-006); 2. **关注列表 tab**:仍为占位(PlaceholderTab),后续迭代实现; 3. **全部持仓 tab**:未做展开(老师只要求策略持仓 tab);如需可复用同样模式; 4. **迭代 07 遗留**:QMT Bridge 历史接口(老师完善后接入)、盘中行情写 SQLite 验证(开盘后确认)。