Develop #37

Merged
sakibcc merged 8 commits from develop into main 2026-09-26 01:19:47 +08:00
42 changed files with 433 additions and 471 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ on:
jobs:
deploy:
runs-on: tencent-prod
timeout-minutes: 30
timeout-minutes: 60
env:
COMPOSE_PROJECT_NAME: zhixing-system
@@ -3,7 +3,7 @@
"name": "optimize-stock-list-radar",
"title": "优化选股列表与资金雷达看板布局",
"description": "",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
@@ -11,7 +11,7 @@
"creator": "codex",
"assignee": "codex",
"createdAt": "2026-09-01",
"completedAt": null,
"completedAt": "2026-09-25",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -3,7 +3,7 @@
"name": "add-gold-brick-strategy",
"title": "新增金砖共振选股策略",
"description": "按收盘后、qfq、沪深非ST口径新增金砖共振策略,复用B1指标并增加可复制诊断日志。",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
@@ -11,7 +11,7 @@
"creator": "yuxuanhui",
"assignee": "yuxuanhui",
"createdAt": "2026-09-04",
"completedAt": null,
"completedAt": "2026-09-25",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -3,7 +3,7 @@
"name": "selection-layout-sector-filter",
"title": "选股页面迭代:布局重构与板块筛选",
"description": "",
"status": "planning",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
@@ -11,7 +11,7 @@
"creator": "yuxuanhui",
"assignee": "yuxuanhui",
"createdAt": "2026-09-05",
"completedAt": null,
"completedAt": "2026-09-25",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -3,7 +3,7 @@
"name": "capital-radar-daily-detail",
"title": "资金雷达:单日榜单与板块详情",
"description": "",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
@@ -11,7 +11,7 @@
"creator": "yuxuanhui",
"assignee": "yuxuanhui",
"createdAt": "2026-09-06",
"completedAt": null,
"completedAt": "2026-09-25",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -23,4 +23,4 @@
"relatedFiles": [],
"notes": "实施及本地验证完成,详见 verification.md。代码按用户边界保持未提交,未自动归档;正常业务库尚未应用本次迁移。",
"meta": {}
}
}
@@ -3,7 +3,7 @@
"name": "api-performance-diagnosis",
"title": "接口性能诊断与缓存方案评估",
"description": "优化资金雷达详情、历史与榜单读取;无 Redis、无迁移,待用户发布验证。",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
@@ -11,7 +11,7 @@
"creator": "yuxuanhui",
"assignee": "yuxuanhui",
"createdAt": "2026-09-07",
"completedAt": null,
"completedAt": "2026-09-25",
"branch": "develop",
"base_branch": "develop",
"worktree_path": null,
@@ -23,4 +23,4 @@
"relatedFiles": [],
"notes": "用户已授权提交本地 develop。226 项测试通过;1 个既有集成测试失败和 14 个既有类型错误均在修改前复现。",
"meta": {}
}
}
+4 -3
View File
@@ -8,8 +8,8 @@
<!-- @@@auto:current-status -->
- **Active File**: `journal-1.md`
- **Total Sessions**: 16
- **Last Active**: 2026-09-21
- **Total Sessions**: 17
- **Last Active**: 2026-09-25
<!-- @@@/auto:current-status -->
---
@@ -19,7 +19,7 @@
<!-- @@@auto:active-documents -->
| File | Lines | Status |
|------|-------|--------|
| `journal-1.md` | ~387 | Active |
| `journal-1.md` | ~420 | Active |
<!-- @@@/auto:active-documents -->
---
@@ -29,6 +29,7 @@
<!-- @@@auto:session-history -->
| # | Date | Title | Commits | Branch |
|---|------|-------|---------|--------|
| 17 | 2026-09-25 | 部署运行器调整与全部存量任务收尾 | `f020362` | `develop` |
| 16 | 2026-09-21 | 雷达加权评分与排名变化上线准备 | `b9981aa` | `develop` |
| 15 | 2026-09-01 | 选股模块技术图表迭代 | `9145fe4`, `feb8fd4` | `-` |
| 14 | 2026-09-01 | 完成板块资金雷达双榜交互对齐 | `744cb76` | `develop` |
+33
View File
@@ -385,3 +385,36 @@
### Status
[OK] **Completed**
## Session 17: 部署运行器调整与全部存量任务收尾
**Date**: 2026-09-25
**Task**: 部署运行器调整与全部存量任务收尾
**Branch**: `develop`
### Summary
生产部署运行器改为 tencent-prod,本地 origin 更新至 my-home/zhixing-system;按用户明确要求将全部 5 个未归档任务标记 completed 并归档,清理空 Redis 任务目录及残留会话引用。
### Main Changes
- 归档:09-01-optimize-stock-list-radar、09-04-add-gold-brick-strategy、09-05-selection-layout-sector-filter、09-06-capital-radar-daily-detail、09-07-api-performance-diagnosis。
- 本次为用户要求的统一完结;保留原始 PRD、验收勾选、验证记录和历史限制,不将归档等同于全部功能重新验收通过。
- 两份 .zcode/plans/ 文件的既有删除未修改、未纳入提交;未推送远端或执行部署。
### Git Commits
| Hash | Message |
|------|---------|
| `f020362` | (see git log) |
### Testing
- [OK] 部署配置:YAML 解析、main 触发与 tencent-prod 标签断言、生产 Compose 默认及 jobs 配置校验、git diff --check 均通过。
- [OK] 归档核验:5 个任务元数据仅修改 status/completedAt;其余任务文件哈希与归档前一致;全部活动任务清空,残留当前任务引用已清除。
- [OK] 本轮未重跑产品测试或验证历史任务的线上验收;已有验证限制保留在归档文档中。
### Status
[OK] **Completed**
@@ -1,35 +0,0 @@
## 调研结论(可行性:具备)
**现状**
- 详情面板 `zhixing-web/src/features/selection/components/signal-detail-panel.tsx` 只展示名称/代码/价格/指标,无行业板块;`SelectionStockResult` 类型也无相关字段。
- 后端 `market_stock` 表没同步 tushare `stock_basic.industry`;但项目已有 **sector_radar 模块**,通过 tushare `dc_index`/`dc_member` 每日同步"行业板块 + 概念板块"的成分股快照到 PostgreSQL 表 `sector_radar_membership`(trade_date + sector_type=industry/concept + sector_code/name + stock_code),只是目前只暴露了板块级 rankings 端点,**没有"按股票反查所属板块"的接口**——这就是缺口的全部。
- 已确认决策:数据口径复用 sector_radar(DC 行业+概念);仅详情面板展示,列表行不动。
## 实施方案
### 第 0 步:数据验证(前置)
启动 docker compose 的 PostgreSQL,检查 `sector_radar_membership` 按交易日的覆盖情况。若选股目标日期无快照,运行现有 CLI `sector-radar-build` 回补。若 dc_member 接口因 tushare 积分不足失败,回退方案是给 `market_stock` 加 `stock_basic.industry` 列(Alembic 迁移),届时向你说明。
### 后端(sector_radar 模块内新增"按股票反查",不跨模块读表)
1. `modules/sector_radar/infrastructure/postgres.py`:新增读查询——按 `stock_code` 查 `membership_status='available'` 且 `trade_date <= 目标日` 的最近可用快照(带出实际 membership 的 trade_date,point-in-time 语义,不用最新日期冒充历史)。
2. `modules/sector_radar/application/read.py`:新增用例,输入 `ts_code + trade_date`,返回按类型分组的板块列表(industry 全部、concept 排序后默认截断 20 个)。
3. `modules/sector_radar/presentation/http.py`:新增端点
`GET /api/v1/sector-radar/stocks/{ts_code}/membership?trade_date=YYYY-MM-DD&limit=20`
响应:`{ ts_code, trade_date, membership_trade_date, industries: [{code,name}], concepts: [{code,name}], concept_total }`,遵循 http-api-contracts spec(Pydantic 边界模型)。
### 前端(selection feature)
4. 新增 API 调用 + TanStack Query hook(`enabled: !!stock`,按 `stock.target_trade_date` 查询,随详情面板选中切换)。
5. `signal-detail-panel.tsx`:股票名/代码/价格下方新增两行展示——
- **行业**:行业标签(通常 1 个)
- **所属板块**:概念板块 chips,最多显示 8 个 + "等 N 个"
加载中显示占位文案,无数据时静默隐藏(不打扰无板块数据的股票)。
### 测试与验证
- 后端:读用例单测(命中/无快照/截断)+ http 端点测试,参照 sector_radar 现有测试。
- 前端:vitest 组件测试 + typecheck/lint。
- 端到端:起 server + web,在 `/selection` 选一只股票确认行业与板块正确显示、无数据股票正常降级。
### 涉及文件
- `zhixing-server/.../modules/sector_radar/{application/read.py, infrastructure/postgres.py, presentation/http.py}`
- `zhixing-web/src/features/selection/api/*`(新 query)
- `zhixing-web/src/features/selection/components/signal-detail-panel.tsx`
@@ -1,42 +0,0 @@
# 选股页面迭代:上搜索 + 左列表/右详情布局 & 板块筛选
## 现状与关键结论
- 布局:`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`。
+5
View File
@@ -61,3 +61,8 @@ select = ["B", "E", "F", "I", "SIM", "UP"]
[tool.ruff.format]
quote-style = "double"
[[tool.uv.index]]
name = "tuna"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
default = true
+378 -378
View File
File diff suppressed because it is too large Load Diff