前端: 新增 RealTimeQuoteResponse 类型;新增 useRealtimeQuote Hook 并在报告页图表旁展示价格与时间戳(严格 TTL,无兜底)
FastAPI: 新增 GET /financials/{market}/{symbol}/realtime?max_age_seconds=.. 只读端点;通过 DataPersistenceClient 读取 Rust 缓存
Rust: 新增 realtime_quotes hypertable 迁移;新增 POST /api/v1/market-data/quotes 与 GET /api/v1/market-data/quotes/{symbol}?market=..;新增 DTO/Model/DB 函数;修正 #[api] 宏与路径参数;生成 SQLx 离线缓存 (.sqlx) 以支持离线构建
Python: DataPersistenceClient 新增 upsert/get 实时报价,并调整 GET 路径与参数
说明: TradingView 图表是第三方 websocket,不受我们缓存控制;页面数值展示走自有缓存通路,统一且可控。
199 lines
9.8 KiB
Markdown
199 lines
9.8 KiB
Markdown
# Rust 数据持久化服务设计 (`rust_data_service_design.md`)
|
||
|
||
## 1. 服务定位与核心职责
|
||
|
||
- **服务名称**: `data-persistence-service`
|
||
- **核心定位**: 本服务是整个微服务架构中**唯一的数据持久化层**。它是数据库的**独占所有者 (Sole Owner)**,负责管理所有与数据库的交互。
|
||
|
||
### 1.1. 职责边界:核心实体服务
|
||
|
||
本服务被设计为**核心实体数据服务**,而非一个包罗万象的、管理所有数据的“上帝服务”。它的职责被严格限定在管理那些**跨多个业务领域共享的核心数据实体**上。
|
||
|
||
这种设计遵循了一种务实的**混合微服务数据模式**:
|
||
- **核心数据集中管理**: 保证了通用数据的唯一性和一致性。我们定义的核心实体包括:
|
||
- 公司基本信息 (`company_profiles`)
|
||
- 标准化财务数据 (`time_series_financials`)
|
||
- 标准化市场数据 (`daily_market_data`)
|
||
- AI分析结果 (`analysis_results`) - 作为一种可被多方消费的核心产出物。
|
||
- **业务数据独立持久化**: 未来新增的、具有独立业务领域的微服务(例如“量化回测服务”)将被**允许并鼓励拥有和管理自己的数据库 Schema 或表**。这些新服务在需要核心实体数据时,应通过调用本服务提供的 API 来获取,而不是直接连接数据库。
|
||
|
||
这一策略确保了核心数据的一致性,同时为新服务的独立开发和快速迭代提供了最大的灵活性。
|
||
|
||
## 2. 技术选型与开发范式
|
||
|
||
### 2.1. 核心技术栈
|
||
- **语言**: **Rust**
|
||
- **开发套件**: **`service_kit`** (项目内置的一站式微服务开发套件)
|
||
- **Web 框架**: **`axum`**
|
||
- **数据库交互**: **`sqlx`**
|
||
- **序列化/反序列化**: **`serde`** (由 `service_kit` 自动集成)
|
||
|
||
### 2.2. 开发范式:API 规范驱动
|
||
|
||
我们将采纳 `service_kit` 提供的、以 **OpenAPI** 规范为核心的开发范式。
|
||
- **数据契约**: 所有的数据传输对象 (DTOs) 都将使用 `service_kit` 提供的 `#[api_dto]` 宏进行标注。此宏会自动派生 `serde` 和 `utoipa::ToSchema`,确保我们的 Rust 代码即是 API 规范的“唯一事实源”。
|
||
- **前后端协同**: 我们将使用 `cargo forge generate-types` 命令,从服务自动生成的 OpenAPI 规范中,为前端项目生成 TypeScript 类型定义,实现端到端的类型安全。
|
||
- **数据交换格式**: 服务间的数据交换格式依然是 **JSON**。
|
||
|
||
## 3. API 端点设计 (API Endpoint Design)
|
||
|
||
API 的设计严格服务于对核心实体的通用读写操作。
|
||
|
||
---
|
||
|
||
### 3.1. 公司信息 (`/companies`)
|
||
|
||
- **对应表**: `company_profiles`
|
||
|
||
| Method | Endpoint | 描述 |
|
||
| :--- | :--- | :--- |
|
||
| `PUT` | `/api/v1/companies` | 创建或更新(Upsert)一个公司的基本信息 |
|
||
| `GET` | `/api/v1/companies/{symbol}` | 获取指定公司的基本信息 |
|
||
|
||
---
|
||
|
||
### 3.2. 市场与财务数据 (`/market-data`)
|
||
|
||
- **对应表**: `time_series_financials`, `daily_market_data`
|
||
|
||
| Method | Endpoint | 描述 |
|
||
| :--- | :--- | :--- |
|
||
| `POST` | `/api/v1/market-data/financials/batch` | 批量写入多条时间序列财务指标 |
|
||
| `GET` | `/api/v1/market-data/financials/{symbol}` | 查询某公司的财务指标 (支持按 `metrics`, `start_date`, `end_date` 过滤) |
|
||
| `POST` | `/api/v1/market-data/daily/batch` | 批量写入多条每日市场行情数据 |
|
||
| `GET` | `/api/v1/market-data/daily/{symbol}` | 查询某公司的每日行情 (支持按 `start_date`, `end_date` 过滤) |
|
||
|
||
---
|
||
|
||
### 3.3. AI 分析结果 (`/analysis-results`)
|
||
|
||
- **对应表**: `analysis_results`
|
||
|
||
| Method | Endpoint | 描述 |
|
||
| :--- | :--- | :--- |
|
||
| `POST` | `/api/v1/analysis-results` | 保存一条新的 AI 分析结果 |
|
||
| `GET` | `/api/v1/analysis-results` | 查询分析结果列表 (支持按 `symbol`, `module_id` 过滤) |
|
||
| `GET` | `/api/v1/analysis-results/{id}` | 获取单条分析结果的详情 |
|
||
|
||
---
|
||
|
||
### 3.4. 系统配置 (`/system-config`)
|
||
|
||
- **对应表**: `system_config`
|
||
|
||
| Method | Endpoint | 描述 |
|
||
| :--- | :--- | :--- |
|
||
| `PUT` | `/api/v1/system-config/{key}` | 创建或更新一条键值对配置 |
|
||
| `GET` | `/api/v1/system-config/{key}` | 获取一条键值对配置 |
|
||
|
||
|
||
## 4. 数据传输对象 (DTOs)
|
||
|
||
所有 API 的请求体和响应体都将使用 `service_kit` 的 `#[api_dto]` 宏进行定义,以自动获得序列化、API Schema 生成和调试能力。
|
||
|
||
```rust
|
||
use service_kit::macros::api_dto;
|
||
|
||
// 示例:用于批量写入财务数据的 DTO
|
||
#[api_dto]
|
||
pub struct TimeSeriesFinancialDto {
|
||
pub symbol: String,
|
||
pub metric_name: String,
|
||
pub period_date: chrono::NaiveDate,
|
||
pub value: f64,
|
||
pub source: Option<String>,
|
||
}
|
||
|
||
// 示例:用于创建 AI 分析结果的 DTO
|
||
#[api_dto]
|
||
pub struct NewAnalysisResultDto {
|
||
pub symbol: String,
|
||
pub module_id: String,
|
||
pub model_name: Option<String>,
|
||
pub content: String,
|
||
pub meta_data: Option<serde_json::Value>,
|
||
}
|
||
```
|
||
|
||
## 5. 开发流程与工具链
|
||
|
||
本服务将完全遵循 `service_kit` 提供的标准化开发流程。
|
||
- **项目初始化**: 使用 `cargo generate --git <repo_url> service-template` 创建服务骨架。
|
||
- **质量保障**:
|
||
- 代码风格检查: `cargo forge lint`
|
||
- 单元与集成测试: `cargo forge test`
|
||
- **API 调试与交互**: 使用 `forge-cli` 工具,通过 `cargo forge <command>` 与正在运行的服务进行交互式 API 调用和调试。
|
||
- **前端协同**: 在 CI/CD 流程或本地开发中,通过 `cargo forge generate-types` 命令,自动将本服务的 API 类型同步到前端项目。
|
||
|
||
|
||
## 6. 项目结构(建议)
|
||
|
||
```
|
||
/data-persistence-service
|
||
├── Cargo.toml
|
||
└── src/
|
||
├── main.rs # 应用入口, 初始化数据库连接池, 定义路由
|
||
├── error.rs # 统一的错误处理类型
|
||
├── db.rs # 数据库交互逻辑 (使用 sqlx)
|
||
├── models.rs # 数据库表对应的结构体
|
||
├── dtos.rs # API 请求/响应对应的结构体
|
||
└── api/
|
||
├── mod.rs
|
||
├── companies.rs
|
||
├── market_data.rs
|
||
└── analysis.rs
|
||
```
|
||
|
||
## 7. 实施计划 (Implementation Plan & To-Do List)
|
||
|
||
本部分将开发 `data-persistence-service` 的过程分解为一系列可执行、可追踪的任务。
|
||
|
||
### Phase 1: 项目初始化与基础设置
|
||
|
||
- [x] **T1.1**: 使用 `cargo generate` 和 `service-template` 在 `services/data-persistence-service` 目录下初始化新项目。
|
||
- [x] **T1.2**: 清理模板中的示例代码(如 `hello` 模块)。
|
||
- [x] **T1.3**: 配置 `Cargo.toml`,添加 `sqlx` (with `postgres`, `runtime-tokio-rustls`, `chrono`, `uuid`, `json`), `axum`, `tokio`, `serde` 等核心依赖。
|
||
- [x] **T1.4**: 设置 `.env` 文件,用于管理 `DATABASE_URL` 等环境变量。
|
||
- [x] **T1.5**: 在 `main.rs` 中建立与 PostgreSQL 的数据库连接池 (`sqlx::PgPool`)。
|
||
|
||
### Phase 2: 数据库集成与迁移
|
||
|
||
- [x] **T2.1**: 安装 `sqlx-cli` (`cargo install sqlx-cli`)。
|
||
- [x] **T2.2**: 使用 `sqlx-cli` 初始化迁移目录 (`sqlx migrate add create_initial_tables`)。
|
||
- [x] **T2.3**: 在生成的迁移 SQL 文件中,编写 `CREATE TABLE` 语句,创建 `docs/database_schema_design.md` 中定义的所有表 (`company_profiles`, `time_series_financials` 等)。
|
||
- [x] **T2.4**: 在迁移 SQL 文件中,为时序表 (`time_series_financials`, `daily_market_data`) 添加 `create_hypertable` 命令。
|
||
- [x] **T2.5**: 运行 `sqlx migrate run` 应用迁移,并在数据库中验证表结构是否正确创建。
|
||
- [x] **T2.6**: 在 `src/models.rs` 中,根据数据库表结构,编写对应的 Rust 结构体。
|
||
|
||
### Phase 3: 核心 API 实现
|
||
|
||
- [x] **T3.1**: **Companies API**:
|
||
- [x] 在 `src/dtos.rs` 中创建 `CompanyProfileDto`。
|
||
- [x] 在 `src/db.rs` 中实现 `upsert_company` 和 `get_company_by_symbol` 数据库操作函数。
|
||
- [x] 在 `src/api/companies.rs` 中创建 `PUT /api/v1/companies` 和 `GET /api/v1/companies/{symbol}` 的 `axum` handler,并连接到 `db` 函数。
|
||
- [x] **T3.2**: **Market Data API**:
|
||
- [x] 在 `src/dtos.rs` 中创建 `TimeSeriesFinancialDto` 和 `DailyMarketDataDto`。
|
||
- [x] 在 `src/db.rs` 中实现 `batch_insert_financials` 和 `get_financials_by_symbol` 函数。
|
||
- [x] 在 `src/db.rs` 中实现 `batch_insert_daily_data` 和 `get_daily_data_by_symbol` 函数。
|
||
- [x] 在 `src/api/market_data.rs` 中创建对应的 `axum` handlers 和路由。
|
||
- [x] **T3.3**: **Analysis Results API**:
|
||
- [x] 在 `src/dtos.rs` 中创建 `NewAnalysisResultDto` 和 `AnalysisResultDto`。
|
||
- [x] 在 `src/db.rs` 中实现 `create_analysis_result` 和 `get_analysis_results` 函数。
|
||
- [x] 在 `src/api/analysis.rs` 中创建对应的 `axum` handlers 和路由。
|
||
- [x] **T3.4**: 在 `main.rs` 中,将所有 API 路由组合起来。
|
||
|
||
### Phase 4: 容器化与集成
|
||
|
||
- [x] **T4.1**: 编写多阶段 `Dockerfile`,优化镜像大小和构建速度。
|
||
- [x] **T4.2**: 在根目录的 `docker-compose.yml` 中,添加 `data-persistence-service` 的定义,并配置其依赖 `postgres-db`。
|
||
- [x] **T4.3**: 修改 `Tiltfile` 以包含新的 Rust 服务,确保 `tilt up` 可以成功构建并运行该服务。
|
||
- [x] **T4.4**: **(集成点)** 修改现有的 Python `backend` 服务,使其不再直接连接数据库,而是通过 HTTP 请求调用 `data-persistence-service` 的 API 来读写数据。
|
||
|
||
### Phase 5: 测试与文档
|
||
|
||
- [x] **T5.1**: 为 `db.rs` 中的每个数据库操作函数编写单元测试(需要 `sqlx` 的 test-macros 特性)。
|
||
- [x] **T5.2**: 为每个 API 端点编写集成测试。
|
||
- [ ] **T5.3**: 使用 `#[api_dto]` 宏确保所有 DTO 都已正确集成到 OpenAPI 规范中。
|
||
- [ ] **T5.4**: 运行 `cargo forge generate-types`,验证能否成功生成 TypeScript 类型文件。
|
||
- [ ] **T5.5**: 编写 `README.md`,说明如何本地启动、配置和测试该服务。
|