feat(FEAT-MO-004): 实现指数板块详情分析页

This commit is contained in:
2026-08-25 00:16:43 +08:00
parent f8f4da93dc
commit 0131f863b6
32 changed files with 2262 additions and 8 deletions
@@ -0,0 +1,10 @@
# FEAT-MO-004 设计说明
设计基线为 `docs/superpowers/specs/2026-08-24-index-sector-detail-design.md`。
后端新增详情 Controller/Service/Mapper,复用现有 TDX Client 的超时、响应体上限和错误脱敏边界;指数和板块使用独立代码白名单和数据口径。前端新增详情路由、独立 Axios API 文件、共享详情页和轻量 SVG K 线图,概览按钮仅负责导航。
详情数据失败采用模块级降级,页面仍保留标题、来源、更新时间和 warning。分时能力不通过探测时不返回假数据,UI 只展示明确的不可用边界。
安全策略:Controller/Service 双层参数校验、代码白名单、固定周期、数量上限、响应字段白名单和错误脱敏。性能策略:单次最多 120 点、固定图表尺寸、页面卸载释放图表状态,不新增数据库和缓存热点。
@@ -0,0 +1,28 @@
# GitNexus 开发前基线
- 功能编号:FEAT-MO-004
- 记录时间:2026年8月24日 GMT+8 23:37:42
- 分支:main
- 基线提交:f8f4da93dc8f
## 工作区
```text
?? "SRMS3.6.0\345\270\270\350\247\201\351\227\256\351\242\230\346\265\213\350\257\204\351\242\230.2csv"
?? docs/features/FEAT-MO-004-index-sector-detail/
?? docs/superpowers/plans/2026-08-24-index-sector-detail.md
```
## GitNexus 状态
```text
Repository: /Users/citrons/Documents/Codex/2026-08-12/wo-x/a-share-analysis
Indexed: 8/24/2026, 11:37:41 PM
Indexed commit: f8f4da9
Current commit: f8f4da9
Status: ✅ up-to-date
```
## 后续人工分析
在修改代码前,将 GitNexus query、context、impact 的结论写入 `gitnexus/impact-plan.md`。高风险结果必须先获得用户确认。
@@ -0,0 +1,37 @@
# GitNexus 开发前影响计划
- 功能编号:FEAT-MO-004
- 总体风险:MEDIUM(既有概览服务扩展会触及 Controller、模块、服务测试和 OpenAPI 导出;无 HIGH/CRITICAL)
## 直接与间接影响
### 查询与上下文
- GitNexus 仓库:`a-share-analysis`,开发前索引与 `f8f4da9` 一致且 up-to-date。
- 影响目标:`MarketOverviewController`、`MarketOverviewService`、`createTdxClient`、`createAppRouter`。
- `MarketOverviewController` upstream:3 个直接文件消费者(概览 E2E、OpenAPI 导出、模块注册),总影响 7 个文件,风险 LOW。
- `MarketOverviewService` upstream:5 个直接文件消费者(概览 E2E、服务测试、OpenAPI 导出、Controller、模块),总影响 9 个文件,风险 MEDIUM。
- `createTdxClient` upstream:TDX Client 单元测试 1 个直接消费者,风险 LOW;详情复用其稳定 `request<T>()` 边界,不修改协议实现。
- `createAppRouter` upstream:`App.test.ts` 1 个直接消费者,风险 LOW;新增路由需要补充路由测试和根路径回归。
- `MarketOverviewPage` 未被索引为符号,按文件级人工检查确认只消费概览 composable 和两个选择事件;新增导航不改变概览请求契约。
- 执行流:详情是新增只读 HTTP 流程;既有首页五个概览流程仅通过模块注册和共享 TDX Client 间接关联,需回归验证。
## 跨模块同步修改
| 范围 | 判断 | 同步动作 |
| --- | --- | --- |
| HTTP/OpenAPI | 需要 | 新增两个详情路由、DTO、生成 OpenAPI 并校验 |
| Web API | 需要 | 新增详情 Axios 文件与类型适配 |
| 消息契约 | 不需要 | 只读同步请求,不产生消息 |
| 数据库/Redis | 不需要 | 不持久化、不缓存,且不触碰生产库 |
| API 进程 | 需要 | 注册详情 Controller/Service,复用已校验 TDX Client |
| Scheduler/Worker | 不需要 | 无异步任务或定时采集 |
| 配置/部署 | 不需要 | 继续使用现有 TDX 配置和内网隧道,不新增环境变量 |
| 测试 | 需要 | Mapper、Service、HTTP、Web 路由/状态、视觉和全量门禁 |
| 文档 | 需要 | 功能档案、TDX 探测、开发总结、Review、QA、GitNexus 前后对比 |
### 设计决策
1. 不扩展首页接口大对象,详情接口独立,降低既有消费者的契约风险。
2. 不修改 `createTdxClient`,只复用其超时、响应体上限和错误脱敏;分时能力未验证时明确返回不可用。
3. 指数和板块共享页面外壳与 K 线点类型,但保留各自白名单和业务摘要映射。
@@ -0,0 +1,6 @@
# FEAT-MO-004 实施记录
详细实施计划见 [`docs/superpowers/plans/2026-08-24-index-sector-detail.md`](../../../superpowers/plans/2026-08-24-index-sector-detail.md)。
执行顺序:开发前 Schema/GitNexus 门禁 → TDX 能力探测 → Mapper → Service/Controller → Web 路由和详情页 → 契约、测试、视觉验证、Review 和治理收尾。
@@ -0,0 +1,9 @@
featureId: FEAT-MO-004
slug: index-sector-detail
name: 指数与板块详情分析页
status: in_progress
release: unreleased
source: tdx
scope: internal_lan_only
createdAt: 2026-08-24
@@ -0,0 +1,29 @@
# FEAT-MO-004 指数与板块详情分析页 PRD
## 原始需求
市场综合概览中的指数、板块可点击进入详细分析页,支持使用 TDX 真实数据进行复盘查看。
## 用户与场景
- 个人用户在概览页发现指数或强势板块后,进入详情确认交易日期、价格变化和 K 线走势。
- 内网开发环境优先,TDX 通过当前 Mac 本机隧道访问;不把 TDX 暴露到公网。
## V1 范围
- 指数详情和一级行业板块详情。
- 共享详情页外壳、数据状态、更新时间、来源、警告和 K 线图。
- 分时视图由 TDX 能力探测结果决定,未通过时明确不可用。
- 事实型数据和规则计算结果,不提供买卖建议、目标价、仓位建议或交易操作。
## 非目标
成分股排名、主力净流入、盘中新闻、AI 结论、用户体系、订阅付费和数据库/Redis/RocketMQ 持久化均不在本版本。
## 验收标准
1. 点击概览中的指数或板块可进入对应详情 URL,非法代码不触发 TDX 请求。
2. API 返回 `instrument`、`quote`、`series`、`availableViews` 和统一元数据,K 线不超过 120 点。
3. TDX 不可达、交易日延迟、字段缺失和空序列分别显示明确状态,不伪造图表。
4. Web 显示上海时间,桌面和 390px 视口无横向溢出,图表容器尺寸稳定。
@@ -0,0 +1,23 @@
# FEAT-MO-004 需求与约束
## 功能需求
- FR-001:提供指数详情 HTTP API。
- FR-002:提供一级行业板块详情 HTTP API。
- FR-003:详情 API 只接受登记代码、`period=1d` 和 `1..120` 的 `count`。
- FR-004:详情响应包含 K 线序列、报价、交易日期、状态、来源和警告。
- FR-005:概览指数和板块按钮导航到详情页,详情页可返回概览。
- FR-006:分时仅在 TDX 探测通过后启用,未启用时返回 `intraday=false`。
## 非功能需求
- NFR-001:TDX 原始异常和地址不得暴露给 Web。
- NFR-002:本机 TDX 可达时详情首屏 P95 目标小于 2 秒;不可达时 10 秒内返回不可用状态。
- NFR-003:详情请求失败不影响首页五个概览模块。
- NFR-004:API `asOf` 使用 UTC ISO,Web 使用 `Asia/Shanghai` 展示。
- NFR-005:前端请求只能经过 Axios 封装,后端 DTO 作为 OpenAPI 契约来源。
## 约束判断
消息、数据库、Redis、Scheduler、Worker 和部署配置无需同步修改;详情为只读实时查询,不产生异步任务或持久化数据。OpenAPI、Web API、前端路由、测试和文档需要同步更新。
@@ -0,0 +1,6 @@
# FEAT-MO-004 回退说明
详情功能为无数据库结构变更的只读模块。若发布后出现问题,先回退前端详情路由和概览导航,再回退后端详情 Controller/Service 注册;首页已有五个概览接口不依赖详情模块。回退不执行生产数据库操作、不删除 TDX 配置、不修改用户数据。
回退验证包括:`GET /api/v1/market-overview/indices`、`sectors` 仍返回 200;Web 根路径可加载;OpenAPI 与生成契约重新生成并通过边界测试。
@@ -0,0 +1,29 @@
# FEAT-MO-004 TDX 能力探测报告
- 探测日期:2026-08-24(开发环境)
- 数据源:TDX 开发隧道(地址和请求体按安全规则不记录)
- 探测方式:只读 JSON-RPC 请求,输出仅保留状态码、耗时、字段名和脱敏代码。
## 已通过能力
| 能力 | 方法 | 结果 | 观测 |
| --- | --- | --- | --- |
| 指数日 K 线 | `get_market_data` | 通过 | HTTP 200,业务 `ErrorId=0`,返回 `Value` 及分页元数据;两条核心指数请求耗时约 56ms |
| 一级行业目录 | `get_stock_list` | 通过 | HTTP 200,业务 `ErrorId=0`,返回数组,首个脱敏记录代码为 `881001.SH` |
| 一级行业日 K 线 | `get_market_data` | 通过 | 对目录首个代码请求 5 条日线,HTTP 200,业务 `ErrorId=0`,返回对应 `Value`;耗时约 47ms |
## 未通过能力
| 能力 | 候选方法 | 结果 | 处理 |
| --- | --- | --- | --- |
| 分时 | `get_minute_data` | 未通过 | 服务返回协议错误,未暴露原始错误内容;V1 返回 `availableViews.intraday=false` |
| 分时 | `get_minute_data_ex` | 未通过 | 服务返回协议错误;不继续猜测参数或伪造分时序列 |
| 历史分时 | `get_history_minute_data` | 未通过 | 服务返回协议错误;延期到独立能力验证 |
## 安全和稳定性结论
- 所有探测均为固定只读方法,未调用交易、账户、自选或写入接口。
- 日 K 线字段沿用已验证的 `Date/Open/High/Low/Close/Amount/Volume`;详情服务仍会二次校验字段长度、日期和数量上限。
- 真实响应只用于能力判断,不作为提交到仓库的原始样本;报告不包含地址、请求体、凭据或原始异常。
- 本次观测不代表生产容量证明;详情页仍按单请求最多 120 点和现有 TDX 超时配置执行。
@@ -0,0 +1,163 @@
# 指数与板块详情分析页实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 在市场综合概览中实现可点击的指数详情页,并在同一外壳上接入一级行业板块详情;只展示经过 TDX 校验的事实数据和规则计算结果。
**Architecture:** 后端新增详情业务模块,复用现有 TDX Client 和概览元数据,但不把详情数据拼入首页接口。详情 Controller 负责参数白名单与查询上限,Service 按指数/板块分别读取和映射日 K 线,Web 通过独立 Axios API 和共享详情页面外壳展示状态、K 线与可用视图;分时能力只有在探测报告通过后才启用。
**Tech Stack:** NestJS 11、TypeScript 5.9、Vue 3、Vue Router 4、Axios、Naive UI、原生 SVG/Canvas(除非依赖评估证明需要新增图表库)、TDX JSON-RPC。
**Spec:** `docs/superpowers/specs/2026-08-24-index-sector-detail-design.md`
## Global Constraints
- Node 使用 `22.x`,MySQL 仅保留 `5.7.x` 兼容约束,排序规则为 `utf8mb4_general_ci`。
- 详情接口只读 TDX,不启用 MySQL、Redis、RocketMQ、AI 或用户体系。
- TDX 原始地址、请求体、凭据和原始异常不得进入 Web 响应;错误必须分类并脱敏。
- API 响应 `asOf` 保持 UTC ISO,Web 统一按 `Asia/Shanghai` 展示。
- 每个详情请求最多返回 120 个 K 线点;`period` 仅接受已验证的 `1d`。
- 不提交密钥、密码、`.env`、生产数据或 `.gitnexus/`;继续使用项目级 Gitea。
- 修改代码前完成 GitNexus upstream impact;提交前运行 `gitnexus_detect_changes`。
- 中文 Conventional Commit,提交验证通过后立即推送远程。
## 文件边界
### 新增
- `server/apps/api/src/market-overview/detail/market-detail.dto.ts`:详情响应、报价、K 线点和可用视图 OpenAPI DTO。
- `server/apps/api/src/market-overview/detail/market-detail.mapper.ts`:指数/板块 K 线的安全字段映射、日期排序、数量裁剪和状态辅助。
- `server/apps/api/src/market-overview/detail/market-detail.service.ts`:详情查询、业务代码白名单、TDX 错误降级和指数/板块口径分流。
- `server/apps/api/src/market-overview/detail/market-detail.controller.ts`:两个详情路由、查询参数校验和 Swagger 声明。
- `server/apps/api/src/market-overview/detail/market-detail.service.test.ts`、`market-detail.mapper.test.ts`:服务与映射单元测试。
- `server/apps/api/test/market-detail.e2e-spec.ts`:HTTP 参数、响应状态和脱敏测试。
- `web/src/api/market-overview/detail.ts`:详情 API Axios 封装。
- `web/src/views/market-overview/detail/MarketDetailPage.vue`:共享详情页外壳和加载/错误/延迟状态。
- `web/src/views/market-overview/detail/MarketDetailChart.vue`:固定尺寸的轻量 SVG K 线图和不可用边界。
- `web/src/views/market-overview/detail/market-detail.ts`:前端详情响应类型适配与展示辅助。
- `web/src/views/market-overview/detail/MarketDetailPage.test.ts`、`MarketDetailChart.test.ts`:路由状态、时区和图表输入测试。
- `docs/features/FEAT-MO-004-index-sector-detail/`:PRD、需求、设计、计划、测试、回退、开发总结、GitNexus、Review 和 QA 证据。
### 修改
- `server/apps/api/src/market-overview/market-overview.module.ts`:注册详情 Controller/Service。
- `server/apps/api/src/market-overview/tdx/tdx.types.ts`:仅在探测确认后补充分时响应类型;日 K 线已有类型保持兼容。
- `web/src/app/router.ts`:新增 `/market-overview/indices/:code` 与 `/market-overview/sectors/:code` 路由。
- `web/src/views/market-overview/MarketOverviewPage.vue`:把指数/板块选择事件改为真实路由导航。
- `server/openapi/openapi.json`、`web/src/api/generated/schema.ts`:通过既有生成脚本更新契约,不手写漂移。
## 实施任务
### Task 1:建立功能档案、基线与影响分析
**Files:**
- Create: `docs/features/FEAT-MO-004-index-sector-detail/manifest.yaml`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/prd.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/requirements.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/design.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/rollback.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/gitnexus/impact-plan.md`
- [ ] 运行 `npm run schema:baseline`、`npm run schema:verify`;如果开发库为空,记录 `captured_empty`,不得伪造 SQL。
- [ ] 运行 `npm run governance:start -- FEAT-MO-004 index-sector-detail`,确认基线已提交、已推送且 GitNexus up-to-date。
- [ ] 对 `MarketOverviewController`、`MarketOverviewService`、`createTdxClient`、`createAppRouter` 和 `MarketOverviewPage` 执行 upstream impact,记录直接调用方、执行流、模块影响和风险。
- [ ] 将 HTTP/OpenAPI、Web API、消息、数据库、Redis、Scheduler、Worker、部署、测试和文档的同步判断写入 `gitnexus/impact-plan.md`。
### Task 2:TDX 能力探测和脱敏证据
**Files:**
- Create: `docs/features/FEAT-MO-004-index-sector-detail/tdx-capability-probe.md`
- Test: `server/apps/api/src/market-overview/tdx/tdx.client.test.ts`(只补充固定方法/参数边界测试,如确有必要)
- [ ] 使用当前开发环境 TDX 隧道探测 `get_market_data` 的日 K 线、候选分时方法、时间字段、响应上限和一级行业代码历史 K 线。
- [ ] 记录方法名、参数形状、返回字段、耗时、是否可重复和脱敏失败原因;禁止把地址、请求体凭据或原始异常写入文档。
- [ ] 若分时协议或字段完整性未通过,只登记 `intraday=false`,不改造协议、不生成假数据。
### Task 3:先写详情 Mapper 失败测试并实现安全 K 线映射
**Files:**
- Create: `server/apps/api/src/market-overview/detail/market-detail.mapper.test.ts`
- Create: `server/apps/api/src/market-overview/detail/market-detail.mapper.ts`
- Create: `server/apps/api/src/market-overview/detail/market-detail.dto.ts`
**Interfaces:**
- `mapDetailSeries(series: TdxKlineSeries, count: number): DetailSeriesPoint[]`
- `mapDetailQuote(series: TdxKlineSeries, definition: DetailInstrumentDefinition): DetailQuoteDto | null`
- `trimAndSortSeries(points: DetailSeriesPoint[], count: number): DetailSeriesPoint[]`
- [ ] 为空序列、日期非法、字段长度不一致、倒序日期、缺少金额/成交量、超过 120 条和相邻收盘价为零编写失败测试。
- [ ] 实现只读取 `Date/Open/High/Low/Close/Amount/Volume`,按合法交易日期升序排序,单点字段缺失时返回 `null` 或降级状态,不抛出原始数据。
- [ ] 实现价格、涨跌幅和方向计算;板块报价只暴露设计中确认的强弱字段。
- [ ] 运行 mapper 定向测试并确认通过。
### Task 4:实现详情 Service、Controller 和 OpenAPI DTO
**Files:**
- Create: `server/apps/api/src/market-overview/detail/market-detail.service.ts`
- Create: `server/apps/api/src/market-overview/detail/market-detail.controller.ts`
- Create: `server/apps/api/src/market-overview/detail/market-detail.service.test.ts`
- Modify: `server/apps/api/src/market-overview/market-overview.module.ts`
**Interfaces:**
- `MarketDetailService.getIndexDetail(code: string, period: DetailPeriod, count: number): Promise<IndexDetailResponseDto>`
- `MarketDetailService.getSectorDetail(code: string, period: DetailPeriod, count: number): Promise<SectorDetailResponseDto>`
- `GET /api/v1/market-overview/indices/:code/detail?period=1d&count=120`
- `GET /api/v1/market-overview/sectors/:code/detail?period=1d&count=120`
- [ ] 先写成功、非法代码、非法 period/count、TDX 超时、协议错误、空序列、部分字段缺失和延迟交易日测试。
- [ ] Controller 只接受白名单指数/一级行业代码、`period=1d` 和 `1..120` 的整数 count;非法参数由 Nest 标准 400 返回。
- [ ] Service 调用固定 `get_market_data`,不把方法名或原始 params 从 HTTP 传入;指数和板块使用独立定义表和 warning 文案。
- [ ] 以 `fresh/delayed/partial/unavailable` 生成元数据;详情失败只影响本次请求,不影响首页接口。
- [ ] 注册模块并生成 OpenAPI;运行 API 单元测试和 E2E 定向测试。
### Task 5:接入 Web 路由、Axios API 和共享详情页面
**Files:**
- Create: `web/src/api/market-overview/detail.ts`
- Create: `web/src/views/market-overview/detail/market-detail.ts`
- Create: `web/src/views/market-overview/detail/MarketDetailPage.vue`
- Create: `web/src/views/market-overview/detail/MarketDetailChart.vue`
- Create: `web/src/views/market-overview/detail/MarketDetailPage.test.ts`
- Create: `web/src/views/market-overview/detail/MarketDetailChart.test.ts`
- Modify: `web/src/app/router.ts`
- Modify: `web/src/views/market-overview/MarketOverviewPage.vue`
- Modify: `web/src/styles/index.scss`
**Interfaces:**
- `createMarketDetailApi(requestClient): { getIndexDetail(code, params), getSectorDetail(code, params) }`
- `MarketDetailPage` consumes route `type/code`, calls only the Axios API wrapper, and renders `instrument/quote/series/availableViews`.
- [ ] 先写路由参数、加载、空、错误、延迟、上海时区和 390px 视口测试。
- [ ] 实现页面固定结构:返回概览、名称/代码/来源/更新时间、核心行情、K 线、数据质量和事实型复盘摘要。
- [ ] 实现轻量 SVG 图表,固定容器尺寸,点数多于 120 时不额外扩张;`intraday=false` 只显示边界说明。
- [ ] 修改概览点击事件使用 `router.push`,不在页面直接调用 Axios 或 TDX。
- [ ] 运行 Web 定向测试、typecheck 和生产构建;确认移动端无横向溢出。
### Task 6:契约、全量验证与 Review
**Files:**
- Modify: `server/openapi/openapi.json`
- Modify: `web/src/api/generated/schema.ts`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/test-plan.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/implementation-plan.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/development-summary.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/review/code-review.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/review/standards-review.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/qa/test-results.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/qa/visual-qa.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/gitnexus/after.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/gitnexus/detected-changes.md`
- Create: `docs/features/FEAT-MO-004-index-sector-detail/gitnexus/comparison.md`
- [ ] 运行 `npm run check:contracts`,确认后端 DTO、OpenAPI 和 Web 类型一致。
- [ ] 运行定向测试、`npm run verify`、开发服务器 Playwright 桌面/390px 截图和非空图表检查。
- [ ] 执行代码 Review、开发规范符合性 Review,记录发现、处置和残余风险。
- [ ] 运行 `npm run governance:finish -- FEAT-MO-004 index-sector-detail`,刷新 GitNexus 并保存前后对比。
- [ ] 运行 `gitnexus_detect_changes`,确认只影响详情模块、路由、契约和预期文档。
- [ ] 使用中文 Conventional Commit 提交并立即推送项目 Gitea;推送失败不得标记完成。
## 计划自检
- 设计覆盖:指数/板块双切片、TDX 能力门、状态降级、路由、契约、时区、安全、性能、测试、回退均有对应任务。
- 占位检查:没有以 TBD/TODO 或“适当处理”代替具体行为;分时未验证时明确返回 `intraday=false`。
- 类型一致性:Service 参数、DTO 字段、Web API 方法和页面消费字段与设计文档一致。