5.7 KiB
5.7 KiB
技术设计:Home 市场数据概览
1. 边界与模块归属
市场数据概览属于现有 modules/market_data bounded context:
zhixing-server/src/zhixing_server/modules/market_data/
├── domain/overview.py # 无框架的读取模型和值
├── application/overview.py # 读取用例/端口编排
├── infrastructure/postgres.py # PostgreSQL 聚合读取适配器
└── presentation/home.py # Pydantic 模型与 /home/overview 路由
顶层 interfaces/http/router.py 只挂载 bounded context 的 presentation router。FastAPI 依赖和 Pydantic 只出现在 presentation;domain/application 不导入 FastAPI 或 Psycopg。
前端增加 features/home/ 垂直切片;shared/ui 仅承载无业务状态的 shadcn primitives。Home shell 和 overview card 的业务组合保留在 feature 内。
2. HTTP 契约
GET /api/v1/home/overview 返回:
{
"status": "success",
"stock_pool_count": 2,
"latest_update": {
"trade_date": "2026-08-06",
"completed_at": "2026-08-07T16:30:00+08:00",
"status": "success",
"target_count": 2,
"successful_count": 2,
"failed_count": 0,
"valid_count": 2,
"coverage_rate": 1.0,
"failures": [],
"batch_errors": []
}
}
约定:
- 顶层
status表示 Home 当前状态。没有任何批次时为no_data,latest_update为null。 latest_update.status只使用updating、success、partial_success、failed;无批次不创建虚假的 update 对象。completed_at对 running 批次为null。所有非空时间在 presentation 边界转换到Asia/Shanghai。failure包含ts_code、name、failure_type、reason、trade_date。batch_error包含item_key、error_type、reason;只聚合market_sync_item.item_kind = 'batch',不把日期级daily_basic错误伪造成股票级错误。- 覆盖率以
Decimal在读取层计算,Pydantic 在 JSON 边界输出数值;分母为 0 时返回 0。
3. 批次选择与事实计算
读取适配器在一个 PostgreSQL 连接/读取事务中完成聚合:
- 统计
market_stock.is_active = true的当前目标股票池。 - 找出最新
target_trade_date;该日期上的最新 running 批次优先,否则选该日期status != 'running'的最新已完成批次,排序使用finished_at DESC, created_at DESC。这保证同日 retry 只展示最后完成结果,且不会让旧日期 running 批次遮蔽更新日期。 - 若没有任何批次,返回
no_data。 - 对选中批次的目标交易日,使用 active 股票与
market_daily_bar、market_daily_basic的 left join;两张事实表均存在的股票构成有效/成功集合。 failed_count是 active 目标股票池减去有效集合;失败详情优先使用同批次market_sync_item中该代码的bar失败记录,否则按缺失的日线/估值事实生成可读原因。若两类事实都缺失,优先呈现已记录的股票级错误,再使用稳定的缺失类型。- 读取
item_kind = 'batch' AND status = 'failed'作为独立 batch errors。 target_count保留选中批次记录的目标数;stock_pool_count是当前 active 数;正常同步期间两者相同。successful_count/valid_count来自当前事实完整性计算,coverage_rate为成功数除以目标数,分母为 0 时为 0。
状态映射:
| 事实 | 状态 |
|---|---|
| 无同步批次 | no_data |
| 最新交易日仍有 running 批次 | updating |
| 已完成批次无失败且所有目标股票完整 | success |
| 已完成批次存在失败但至少一只股票完整 | partial_success |
| 已完成批次失败或没有任何完整股票 | failed |
这条读取路径不触发同步、不修改数据、不读取 CSV。数据库错误继续抛出 MarketDataRepositoryError,由现有 FastAPI 默认 500 边界暴露,避免把基础设施错误伪装成 no_data。
4. 依赖与测试 seam
- 增加
MarketDataOverviewReaderprotocol,HTTP 依赖工厂返回具体PostgresMarketDataRepository;测试通过app.dependency_overrides注入内存 reader,HTTP 测试仍调用TestClient(create_app())。 - 读取模型使用 frozen dataclass,Pydantic response model 负责日期/时间和字段名转换。
- 后端契约测试使用多个 active/inactive 股票、完整/缺失事实、running/completed/retry/batch error 记录构造 fake reader,验证用户可见 JSON,不断言 SQL。
- 前端 API 通过
requestJson保留AbortSignal;query 使用 feature 内as constkey。
5. shadcn/ui 组合策略
现有 Card、Badge、Button 作为基础,按当前 components.json 的 base-nova 约定补齐并复用:
- shell:
Avatar、Input、Separator、Button; - loading:
Skeleton; - status/data:
Card、Badge、Progress; - failure details:
Dialog+ScrollArea。
优先用 shadcn CLI 生成标准 primitive;如果生成器与锁定依赖版本不兼容,则采用 CLI 对应的当前源代码形状,在 shared/ui 保持同样的可访问属性、原生 props、cn 合并和组件组合接口。Home feature 不直接依赖第三方 modal/scroll implementation。
6. 兼容性、迁移与回滚
- 不新增数据库迁移;只读模型适配现有五张表。
- API 新增,不修改
/api/v1/system/status;根路由从 smoke 页面切换为 Home,但既有 system feature 测试文件保留。 - 若读取查询需要兼容已有迁移字段,使用当前 schema 中的字段和状态值,不变更同步写入语义。
- 回滚点是移除 Home route、overview presentation/reader 及新增前端 feature/shared primitives;数据库无需回滚。