diff --git a/.env.example b/.env.example index d5a4388..76678bd 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ COMPOSE_PROJECT_NAME=zhixing-system-dev ZHIXING_SERVER_PORT=8000 -ZHIXING_WEB_PORT=5173 +ZHIXING_WEB_PORT=5555 ZHIXING_APP_ENV=development ZHIXING_LOG_LEVEL=INFO diff --git a/.gitignore b/.gitignore index 68200d0..e6b5d54 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,6 @@ htmlcov/ # Node.js node_modules/ +.pnpm-store/ dist/ coverage/ diff --git a/.scratch/home-market-overview/spec.md b/.scratch/home-market-overview/spec.md new file mode 100644 index 0000000..d4f320c --- /dev/null +++ b/.scratch/home-market-overview/spec.md @@ -0,0 +1,105 @@ +Status: ready-for-agent +Triage: ready-for-agent + +# Home 市场数据概览 + +## Problem Statement + +当前系统前端只有用于验证后端连通性的系统状态页面,用户无法在进入系统后快速了解市场数据的当前状态。 + +同步任务已经将股票主数据、日线行情、估值快照和同步批次结果写入 PostgreSQL,但这些结果尚未通过面向用户的 Home 页面聚合展示。用户需要在一个清晰、稳定的卡片中看到当前股票数量、最近更新交易日、同步完成时间、当日更新状态、数据覆盖率,以及更新成功和失败的股票情况。 + +## Solution + +新增 Home 页面和一个面向 Home 的市场数据概览查询接口。页面沿用参考原型的管理后台布局:左侧导航、顶部搜索占位框和用户头像、主内容区的组合卡片。 + +Home 的核心内容是一个“市场数据概览”卡片,展示当前目标股票池数量、最近更新交易日、同步完成时间、更新状态、目标数量、成功数量、失败数量和数据覆盖率。失败股票通过点击卡片中的失败区域打开弹窗,查看股票代码、名称、失败类型、失败原因和目标交易日。 + +后端以现有 PostgreSQL 市场数据和同步批次为事实源,提供一次聚合读取。前端通过同源 `/api/v1` 路径访问该接口,并根据返回状态处理正常、加载中、暂无数据和异常场景。 + +## User Stories + +1. As a 量化研究者, I want to enter the system and immediately see the Home page, so that I can understand the current market-data situation without navigating through multiple pages. +2. As a 量化研究者, I want to see the current target stock-pool count, so that I know the size of the universe currently available to the system. +3. As a 量化研究者, I want the stock count to exclude inactive historical stocks, so that the number reflects the current target stock pool rather than every row ever retained in the master data. +4. As a 量化研究者, I want to see the most recent update trading date, so that I know which trading day the displayed market data reaches. +5. As a 量化研究者, I want to see the synchronization completion time separately from the trading date, so that I can distinguish the data's business date from the job's wall-clock completion time. +6. As a 量化研究者, I want the page to interpret “当日” as the latest open trading day, so that weekends and market holidays are not incorrectly shown as missing updates. +7. As a 量化研究者, I want to see an updating state while a synchronization batch is running, so that I know the displayed result may still be changing. +8. As a 量化研究者, I want to see an updated state when every target stock has both required daily market data and valuation data, so that I can trust the batch as complete. +9. As a 量化研究者, I want to see a partial-update state when some stocks succeeded and others failed, so that I can distinguish incomplete data from a total synchronization failure. +10. As a 量化研究者, I want to see an unavailable state when the latest batch failed or produced no valid stock data, so that I do not mistake stale or absent data for a successful update. +11. As a 量化研究者, I want to see an empty-data state before any synchronization batch exists, so that the page explains why no update details are available. +12. As a 量化研究者, I want to see the target stock count, successful stock count, failed stock count, and coverage rate together, so that I can assess synchronization completeness at a glance. +13. As a 量化研究者, I want a stock to count as successful only when both its daily bar and valuation snapshot exist for the target trading day, so that partial per-stock data is not presented as complete. +14. As a 量化研究者, I want to click the failed-stock summary, so that I can inspect the affected stocks without leaving the Home page. +15. As a 量化研究者, I want the failure dialog to show each stock's code and name, so that I can identify the affected security quickly. +16. As a 量化研究者, I want the failure dialog to show failure type, readable reason, and target trading day, so that I can understand whether the issue concerns market data, valuation data, or another processing boundary. +17. As a 量化研究者, I want the failure dialog to support a long failure list through scrolling, so that every failed stock remains discoverable without making the main card unbounded. +18. As a 量化研究者, I want batch-level errors to be distinguished from stock-level failures, so that database or retention failures are not incorrectly attributed to a particular stock. +19. As a 量化研究者, I want a retried batch for the same trading day to be represented by the latest completed result, so that the Home page does not show duplicate competing summaries for one trading day. +20. As a 量化研究者, I want a loading placeholder while the Home query is pending, so that the page communicates progress instead of appearing broken. +21. As a 量化研究者, I want a retry action when the overview request fails, so that a temporary backend or network problem can be recovered from the page. +22. As a 量化研究者, I want the page to retain its layout when data is empty or partially unavailable, so that the navigation and page context remain usable. +23. As a 量化研究者, I want the sidebar to preserve the agreed navigation structure, so that future market-data and synchronization pages have a consistent place in the product. +24. As a 量化研究者, I want unfinished navigation entries to be visibly disabled, so that I do not enter blank or misleading pages. +25. As a 量化研究者, I want the search control to be visible as a visual placeholder, so that the header follows the reference layout while search behavior remains a separate feature. +26. As a 量化研究者, I want the user avatar to remain visible in the header, so that the page retains the reference product's account area without requiring authentication behavior in this slice. +27. As a 量化研究者, I want the main card to remain readable on desktop and tablet widths, so that the overview remains useful across the supported management-console screen sizes. +28. As a 量化研究者, I want the failure dialog to be dismissible with normal dialog controls and keyboard interaction, so that detailed failure inspection remains accessible. + +## Implementation Decisions + +- Add a market-data overview read model in the backend and expose it through one aggregated HTTP endpoint, `GET /api/v1/home/overview`. +- Use PostgreSQL as the runtime fact source, respecting the existing market-data storage ADRs and the distinction between the current target stock pool, valid selection stock pool, daily bars, valuation snapshots, and synchronization batches. +- Calculate the current stock-pool count from active stock master records only. +- Select the latest relevant synchronization result by trading date and batch state. A running batch for the latest target trading date takes precedence as “更新中”; once finished, the latest completed retry result for that trading date is the displayed result. +- Keep the business date and synchronization completion timestamp as separate response values. Format both for `Asia/Shanghai` at the presentation boundary. +- Define the overview status as a stable presentation contract with five states: `updating`, `success`, `partial_success`, `failed`, and `no_data`. The UI may render these as “更新中”“已更新”“部分更新”“未更新”“暂无数据”. +- Treat a stock as successful only when both its daily bar and valuation snapshot exist for the target trading date. Derive failed stocks from the active target stock pool minus that complete set. +- Return stock-level failure details with stock code, stock name, failure type, readable failure reason, and target trading date. Join stock master information for names and distinguish missing daily bars, missing valuation snapshots, and recorded per-stock failures where the data permits. +- Return batch-level errors separately from stock-level failure details. Batch-level errors affect the overall status and explanatory message but are not fabricated into a stock failure. +- Return target count, successful count, failed count, valid count, and coverage rate in the overview response. Coverage remains the valid stock count divided by the target count and follows the existing market-data definition. +- Return a nullable latest-update section when no synchronization batch exists. The response must let the frontend distinguish “暂无数据” from a transport or server error. +- Keep the endpoint read-only. This slice does not trigger synchronization, retry batches, or data repair from the Home page. +- Keep the browser transport same-origin under `/api/v1`, as required by the existing HTTP architecture decision. +- Add a vertical Home feature in the frontend containing the overview API adapter, typed response model, query hook, page composition, status presentation, and failure dialog behavior. +- Replace the current root smoke page with the Home page while preserving the existing application provider, router, query client, theme behavior, and shared UI conventions. +- Build a reusable management-console shell for this slice: sidebar navigation, top header, search placeholder, avatar area, main content region, and responsive card layout. Non-implemented navigation entries are disabled. +- Render all requested market-data facts in one composite “市场数据概览” card rather than splitting the overview across unrelated cards. +- Render successful and failed counts in the card. Clicking the failed summary opens a dialog containing the complete failure list; the list is scrollable and does not navigate away from Home. +- Provide distinct UI states for loading, no data, request error with retry, updating, success, partial success, and failed synchronization. +- Keep the search control visual-only in this slice. Stock search, search results, and search navigation are separate scope. +- Keep the avatar visual-only in this slice. Authentication, account menus, permissions, and profile behavior are separate scope. +- Optimize for desktop and tablet layouts. On narrow screens, stack the overview sections vertically while preserving access to the failure dialog. + +## Testing Decisions + +- Test external behavior at the highest practical seams. Do not assert SQL statements, repository implementation details, React Query internals, or CSS class composition. +- Test the backend through the Home overview HTTP contract using the existing FastAPI `TestClient` style. Fixtures should cover active stock counting, complete success, partial success, failed batch, running batch, no batch, retry selection, coverage calculation, stock-level failure details, and batch-level errors. +- Test the frontend through Home page rendering behavior using the existing Vitest, Testing Library, and query-hook mocking pattern used by the system status page. +- Frontend behavior tests should cover the normal overview, loading skeleton, no-data message, request error and retry action, each update status, coverage and count display, opening the failed-stock dialog, dialog fields, and dismissing the dialog. +- Add an accessibility-oriented assertion for the failure dialog's accessible name and normal close behavior where supported by the existing test setup. +- Prefer representative fixtures with multiple active stocks, inactive historical stocks, one missing daily bar, one missing valuation snapshot, and one batch-level error so that the success/failure boundary is observable. +- Preserve the existing system status tests and test style as prior art; the Home tests should verify user-visible text and interactions rather than implementation structure. + +## Out of Scope + +- Triggering a new market-data synchronization from Home. +- Retrying failed stocks from Home. +- Building a stock search experience or search result navigation. +- Building stock detail, synchronization history, strategy, or selection pages. +- Adding authentication, user profiles, account menus, or permission management. +- Adding real-time market data, intraday data, or live progress streaming. +- Changing the six-year retention policy, Tushare synchronization workflow, or existing market-data write semantics. +- Replacing PostgreSQL as the market-data fact source. +- Adding a new trading-calendar product surface or a separate calendar management feature. +- Full mobile-specific visual polish beyond a usable stacked layout. +- Adding unrelated prototype content such as courses, memberships, career paths, or promotional panels from the reference image. + +## Further Notes + +- The existing domain glossary uses “当前目标股票池” for the active, eligible market universe and “最近更新交易日” for the business date shown by Home. New API and UI naming should preserve those meanings. +- The UI should not imply that an older successful batch covers a newer trading day. When no relevant synchronization batch exists, the page must show the no-data or unavailable state implied by the response rather than inventing a current update. +- The overview is intentionally read-only and aggregation-oriented. Any future operational controls should be designed as separate actions with their own confirmation, permissions, and audit requirements. +- The agreed primary seam is the Home overview HTTP contract, with a frontend rendering seam for user-visible states and modal interaction. diff --git a/CONTEXT.md b/CONTEXT.md index d843328..1be0b48 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -13,6 +13,12 @@ _Avoid_: 用当前市值回填历史、把当前目标股票池当作无幸存 **当前目标股票池**:同步时仍处于上市状态的沪深 A 股集合,排除 ST/风险警示股和北交所股票,也不包含退市股票的长期历史成员资格。 +**股票数量**:当前目标股票池中的股票总数;不把已经不活跃的历史股票计入当前数量。 + +**最近更新交易日**:最近一个开市交易日中,系统尝试同步市场数据的目标日期;它描述数据覆盖到哪一天,不等同于同步任务的完成时间。 + +**日更新状态**:最近更新交易日的市场数据同步状态;“当日”按最近开市交易日理解,并区分更新中、已更新、部分更新、未更新和暂无数据。 + **有效选股股票池**:当前目标股票池中,行情和所需估值数据均已同步到目标交易日的股票集合;数据仍停留在更早交易日的股票不参与当天选股。 **日线行情**:以证券和交易日为粒度记录的开盘价、最高价、最低价、收盘价和成交量等交易结果。 @@ -25,4 +31,12 @@ _Avoid_: 用当前市值回填历史、把当前目标股票池当作无幸存 **同步批次**:一次面向当前目标股票池的市场数据同步运行;批次可以全部成功、部分成功或失败,并保留每只股票的处理结果。 +**重试批次**:针对已有同步批次中失败对象再次执行的同步批次;同一交易日的展示结果以最近一次已完成的批次为准。 + +**批次级错误**:无法归属于某一只股票的同步异常,例如数据库写入或数据保留处理失败;它影响同步批次状态,但不计入某只股票的失败明细。 + +**股票更新成功**:一只股票在目标交易日同时具备日线行情和估值快照时的状态。 + +**股票更新失败**:一只股票未能在目标交易日同时具备日线行情和估值快照时的状态,并应保留可读的失败原因。 + **数据覆盖率**:目标交易日内,有效选股股票数占当前目标股票池目标数的比例,用于判断选股结果是否具备足够完整性。 diff --git a/README.md b/README.md index 0cc9ad6..d54d7a0 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,10 @@ Zhixing System 是一个 Python + React 的量化系统基础工程。后端采 ```bash cp .env.example .env -./dev.sh up +./dev.sh dev ``` -打开 。前端通过 `/api/v1/system/status` 调用后端,后端健康检查位于 。 +打开 。前端通过 `/api/v1/system/status` 调用后端,后端健康检查位于 。 常用命令: @@ -33,4 +33,3 @@ cp .env.example .env ``` 后端和前端也可分别通过 `uv` 与 `pnpm` 在本机运行,具体命令见各子项目 README。 - diff --git a/dev.sh b/dev.sh index 287fc2a..6c096a7 100755 --- a/dev.sh +++ b/dev.sh @@ -6,13 +6,13 @@ PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" COMPOSE_FILE="${PROJECT_ROOT}/docker-compose.dev.yml" usage() { - printf 'Usage: %s {up|down|logs|check|test}\n' "$0" + printf 'Usage: %s {dev|up|down|logs|check|test}\n' "$0" } command="${1:-}" case "${command}" in - up) + dev|up) docker compose --file "${COMPOSE_FILE}" up --build --remove-orphans ;; down) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 68dd0e0..600dfc7 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -60,7 +60,7 @@ services: VITE_DEV_API_TARGET: http://server:8000 init: true ports: - - "${ZHIXING_WEB_PORT:-5173}:5173" + - "${ZHIXING_WEB_PORT:-5555}:5173" volumes: - ./zhixing-web:/app - web-node-modules:/app/node_modules