# 技术实现方案:15-归属候选纳入清仓持仓 > 迭代编号:15 | 依据:PLAN-016 + R-017 ## 1. 存储层(getAttributionCandidates 扩展,门面透传不变) ```sql SELECT holding_id, strategy_id, shares, closed_at FROM strategy_holdings WHERE code=? AND (closed_at IS NULL OR closed_at >= ?) -- ? = Date.now() - 7d ORDER BY (closed_at IS NULL) DESC, shares DESC, closed_at DESC ``` - 返回行新增 closed: boolean / closedAt: number|null(消费方:api strategies.js 同方法直接受益——注意 attribution-candidates 端点在 trades.js,调 DataStore.getTradeAttributionCandidates 透传,零改动); - 窗口常量 7 * 86400000(与 R-016 默认范围一致)。 ## 2. UI(TradeRecordsTab.jsx AttributionSelect 重构) - 锚点键:currentKey = order.holdingId != null ? String(order.holdingId) : ''(holdingId 是 R-009 定稿的归属锚点;strategyId 在同策略多轮持仓时会撞值); - handleSelect:按 String(holdingId) 找候选 → setAttribution(orderId, code, cand.strategyId, cand.holdingId);__unassigned__ → 双 null; - 占位选项:已归属但候选缺失(清仓超 7 天)→ 「当前归属({strategyId})」,value=currentKey,防 select 悬空显示第一项误导; - 清仓候选文本后缀「(已清仓 MM-DD)」(fmtClosed 本地 helper);select maxWidth 120→150。 ## 3. 数据修复记录(一次性,非功能) 2026-09-07 大连热电卖出委托 645001623 经 live set-attribution 通道回填 strategy_id=manual-t / holding_id=15(curl 实测 ok:true,orders/by-holding 回读确认)。当日归属曾被清为未关联的原因:候选下拉无做T项时老师误触「未关联」入口——本迭代修复后此路径消除。 ## 4. 验证链 test-r017-candidates.mjs 12 断言 + typecheck + build + 存量回归(r016 19 / position-sync 35 / r013 21 / quote-sync 27)。