Files
zhixing-system/.zcode/plans/plan-sess_55e3d246-e875-4283-bdfc-929f8f26a73f.md
T
yuxuanhui 7f93d6b0f5 feat(sector_radar): enhance sector radar functionality with active moneyflow and detailed metrics
- Introduced ActiveMoneyflowSource to fetch optional active-order flow, enhancing the sector radar's data capabilities.
- Updated StockFactRecord and DailyAggregateRecord to include pct_change and active_buy_net_amount_yuan for improved financial insights.
- Modified the build process to incorporate active moneyflow data without invalidating main rankings on failure.
- Enhanced the HTTP API to return detailed sector history and metrics, including pct_change and active buy metrics for members.
- Updated tests to validate the new functionality and ensure data integrity across various scenarios.
2026-09-06 16:06:17 +08:00

42 lines
5.3 KiB
Markdown
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.
# 选股页面迭代:上搜索 + 左列表/右详情布局 & 板块筛选
## 现状与关键结论
- 布局:`selection-results-workbench.tsx` 目前是左右两栏 grid(左 320px 筛选+列表 / 右详情),筛选栏嵌在左栏顶部。
- 板块数据:选股结果表(`selection_run_item`)没有板块字段;板块归属在 `sector_radar_membership` 表,现有 membership 接口只支持单股查询。**板块聚合必须由后端新增**(前端逐股请求既慢又只覆盖已加载分页,计数不准)。
- 已确认口径(用户未答,按推荐执行):概念板块(`sector_type='concept'`,与详情面板"板块"标签一致),单选;接口保留 `sector_type` 参数,后续扩展行业零成本。排序 = 按 `stock_count` 倒序(选项旁展示数量)。
## 一、后端(zhixing-server)
遵循 bounded-context-first(ADR 0001):selection 不直接 join sector_radar 表,通过应用层端口调用 sector_radar 的读服务。
1. **sector_radar 模块**(`application/read.py` + `infrastructure/postgres.py`):
- 仓储新增两个只读方法(复用已有的 `get_last_good_publication` 解析快照日):
- `load_sector_counts(stock_codes, snapshot_date, sector_type)`:`SELECT sector_code, sector_name, COUNT(*) FROM sector_radar_membership WHERE stock_code = ANY(%s) AND trade_date = %s AND sector_type = %s AND membership_status='available' GROUP BY 1,2`
- `load_sector_member_codes(snapshot_date, sector_code, sector_type)`:返回该板块成员股票代码列表
- 读服务 `ReadSectorRadar` 新增:`sector_counts(stock_codes, trade_date, sector_type)`(内部解析 last-good publication,返回按 stock_count 降序、名称升序)和 `sector_member_codes(trade_date, sector_code, sector_type)`。
2. **selection 模块**:
- `domain/runs.py`:`SelectionResultQuery` 增加 `sector: str | None`;新增 `SelectionSectorCount(sector_code, sector_name, stock_count)` 值对象;定义端口协议 `SectorMembershipReader`(`sector_counts` / `sector_member_codes` 两个方法)。
- `application/run.py`:新增 `list_sector_counts(strategy, target_trade_date, sector_type)` —— 取 latest run,收集 `status='selected' AND signal_count>0` 的 ts_code,调端口聚合;`get_latest`/`get` 结果查询在 `query.sector` 有值时先调端口取成员代码,空则直接返回空页,否则把代码数组传入仓储。
- `infrastructure/postgres_runs.py`:`_stock_filter` 增加子句 `item.ts_code = ANY(%s)`(参数由应用层传入)。
- `presentation/http.py`:
- 新端点 `GET /api/v1/selection/sectors?strategy=&target_trade_date=&sector_type=` → `{ sector_type, snapshot_trade_date, sectors: [{sector_code, sector_name, stock_count}] }`
- `GET /results` 与 `GET /runs/{id}` 增加 `sector` query 参数校验(去空格、限长)。
- 组合根(router/依赖装配处)把 sector_radar 的读服务适配为 selection 的端口注入。
3. **测试**:`tests/unit/sector_radar/`(新仓储方法)、`tests/unit/selection/test_postgres_runs.py`(sector 过滤、ANY 数组、空成员空页)、`tests/test_selection_http.py`(新端点契约 + results 带 sector)。跑 `./dev.sh check`(ruff/pyright/pytest)。
## 二、前端(zhixing-web)
1. **布局重构** `selection-results-workbench.tsx`:
- 外层改为 `flex flex-col`:顶部一个全宽 section 放搜索栏(关键词 / 信号分类 / **板块(新增)** / 排序 / 筛选结果计数),带 `rounded-md border bg-card` 与现有一致;
- 下方 `md:grid md:grid-cols-[320px_minmax(0,1fr)]` 左列表右详情;移动端纵向堆叠为 搜索 → 列表 → 详情。
2. **类型与 API**(`selection.types.ts` / `selection.api.ts`):新增 `SelectionSectorAggregate`;`SelectionResultsQuery` 加 `sector?`;`getSelectionResultSectors()`;`buildSelectionQueryParams` 带 sector。
3. **查询层**(`selection.query.ts`):`useSelectionResultSectors`(key:strategy+date,结果就绪后启用);`selectionResultsQueryKey`/`selectionRunQueryKey` 加入 sector 使筛选变化触发重新请求。
4. **路由**(`routes/route-tree.tsx`):selectionRoute `validateSearch` 增加 `sector`(string,限长,默认 undefined)。
5. **页面接线**(`selection-results-page.tsx`):把 URL 中的 sector 传入 `useSelectionResults`;页面层调用 `useSelectionResultSectors` 并把聚合结果传给 workbench。
6. **workbench 板块下拉**:选项 = "全部板块" + 聚合数据,item 渲染 `名称 + 数量徽标`(数量 tabular-nums,倒序由后端保证);选择写 URL;聚合加载后若当前 sector 不在列表中(如切换策略/日期)自动重置为全部;板块无数据时下拉仅剩"全部板块"并禁用。
## 三、执行方式
- 按仓库 Trellis 工作流建任务目录并加载 `.trellis/spec/backend`(selection.md 契约:查询不触发重算、错误矩阵等)与前端规范后再动手;先后端(接口+测试)再前端接线,最后 `./dev.sh check` + 前端 lint/tsc 全量质量门禁。
- 语义说明:板块数量 = 当次 run 全部选中股票中归属该板块的数量(不随关键词/信号分类变化);列表"筛选结果 N 只" = 包含板块在内的全部过滤叠加后的 `stocks_total`。