# 分析流程优化与数据缓存机制修复

## 1. 问题背景 (Problem Statement)

根据系统日志分析与代码排查，当前系统存在以下关键问题：

1.  **数据源重复请求 (Missing Cache Logic)**:
    *   `yfinance-provider-service` (及其他数据服务) 在接收到任务指令时，未检查本地数据库是否存在有效数据，而是直接向外部 API 发起请求。
    *   这导致每次用户点击，都会触发耗时约 1.5s 的外部抓取，既慢又浪费资源。

2.  **任务依赖与执行时序错乱 (Race Condition)**:
    *   `api-gateway` 在接收到请求时，**同时**触发了数据抓取 (`DATA_FETCH_QUEUE`) 和分析报告生成 (`ANALYSIS_COMMANDS_QUEUE`)。
    *   导致 `report-generator-service` 在数据还没抓回来时就启动了，读到空数据（或旧数据）后瞬间完成，导致 Token 消耗为 0，报告内容为空。

3.  **前端无法展示数据 (Frontend Data Visibility)**:
    *   **根本原因**: API Gateway 路由缺失与路径映射错误。
    *   前端 BFF (`frontend/src/app/api/financials/...`) 试图请求 `${BACKEND_BASE}/market-data/financial-statements/...`。
    *   然而，`api-gateway` **并未暴露** 此路由（仅暴露了 `/v1/companies/{symbol}/profile`）。
    *   因此，前端获取财务数据的请求全部 404 失败，导致界面始终显示 "暂无可展示的数据"，即使用户多次运行也无效。

## 2. 目标 (Goals)

1.  **实现"读写穿透"缓存策略**: 数据服务在抓取前必须先检查本地数据库数据的时效性。
2.  **构建事件驱动的依赖工作流**: 分析服务必须严格等待数据服务完成后触发（通过 NATS 事件链）。
3.  **修复数据访问层**: 确保 API Gateway 正确暴露并转发财务数据接口，使前端可见。
4.  **定义数据时效性标准**: 针对不同类型数据实施差异化的缓存过期策略。

## 3. 详细技术方案 (Technical Plan)

### 3.1. 数据时效性与缓存策略 (Data Freshness Policy)

针对基本面分析场景，不同数据的更新频率和时效性要求如下：

| 数据类型 | 内容示例 | 更新频率 | 建议 TTL (缓存有效期) | 更新策略 |
| :--- | :--- | :--- | :--- | :--- |
| **公司概况 (Profile)** | 名称、行业、简介、高管 | 极低 (年/不定期) | **30 天** | **Stale-While-Revalidate (SWR)**<br>过期后先返回旧数据，后台异步更新。 |
| **财务报表 (Financials)** | 营收、利润、资产负债 (季/年) | 季度 (4/8/10月) | **24 小时** | **Cache-Aside**<br>每次请求先查库。若 `updated_at > 24h`，则强制 Fetch；否则直接返回库中数据。<br>*注: 对于同一天内的重复请求，将直接命中缓存，0延迟。* |
| **市场数据 (Market Data)** | PE/PB/市值/价格 | 实时/日频 | **1 小时** | 基本面分析不需要秒级价格。取最近一小时内的快照即可。若需实时价格，使用专用实时接口。 |

### 3.2. 数据服务层 (Providers)
*   **涉及服务**: `yfinance-provider-service`, `alphavantage-provider-service`, `finnhub-provider-service`.
*   **逻辑变更**:
    1.  订阅 `FetchCommand`。
    2.  **Step 1 (Check DB)**: 调用 `PersistenceClient` 获取目标 Symbol 数据的 `updated_at`。
    3.  **Step 2 (Decision)**:
        *   若 `now - updated_at < TTL`: **Hit Cache**. Log "Cache Hit", 跳过外部请求，直接进入 Step 4。
        *   若数据不存在 或 `now - updated_at > TTL`: **Miss Cache**. Log "Cache Miss", 执行外部 API 抓取。
    4.  **Step 3 (Upsert)**: 将抓取的数据存入 DB (Update `updated_at` = now)。
    5.  **Step 4 (Publish Event)**: 发布 `CompanyDataPersistedEvent` (包含 symbol, data_types: ["profile", "financials"])。

### 3.3. 工作流编排 (Workflow Orchestration)
*   **API Gateway**:
    *   移除 `POST /data-requests` 中自动触发 Analysis 的逻辑。
    *   只发布 `FetchCompanyDataCommand`。
*   **Report Generator**:
    *   **不再监听** `StartAnalysisCommand` (作为触发源)。
    *   改为监听 `CompanyDataPersistedEvent`。
    *   收到事件后，检查事件中的 `request_id` 是否关联了待处理的分析任务（或者简单的：收到数据更新就检查是否有待跑的分析模板）。
    *   *临时方案*: 为了简化，可以在 API Gateway 发送 Fetch 命令时，在 payload 里带上 `trigger_analysis: true` 和 `template_id`。Data Provider 在发出的 `PersistedEvent` 里透传这些字段。Report Generator 看到 `trigger_analysis: true` 才执行。

### 3.4. API 修复 (Fixing Visibility)
*   **Backend (API Gateway)**:
    *   在 `create_v1_router` 中新增路由:
        *   `GET /v1/market-data/financial-statements/{symbol}` -> 转发至 Data Persistence Service。
        *   `GET /v1/market-data/quotes/{symbol}` -> 转发至 Data Persistence Service (可选)。
*   **Frontend (Next.js API Route)**:
    *   修改 `frontend/src/app/api/financials/[...slug]/route.ts`。
    *   将请求路径从 `${BACKEND_BASE}/market-data/...` 修正为 `${BACKEND_BASE}/v1/market-data/...` (匹配 Gateway 新路由)。
    *   或者直接修正为 Data Persistence Service 的正确路径 (但最佳实践是走 Gateway)。

## 4. 执行计划 (Action Items)

### Phase 1: API & Frontend 可见性修复 (立即执行)
1.  [x] **API Gateway**: 添加 `/v1/market-data/financial-statements/{symbol}` 路由。
2.  [x] **Frontend**: 修正 `route.ts` 中的后端请求路径。(通过修正 Gateway 路由适配前端)
3.  [ ] **验证**: 打开页面，应能看到（哪怕是旧的）财务图表数据，不再显示 404/无数据。

### Phase 2: 缓存与时效性逻辑 (核心)
4.  [x] **Data Providers**: 在 `worker.rs` 中实现 TTL 检查逻辑 (Profile: 30d, Financials: 24h)。(YFinance 已实现，其他 Provider 已适配事件)
5.  [x] **Persistence Service**: 确保 `get_company_profile` 和 `get_financials` 返回 `updated_at` 字段（如果还没有的话）。

### Phase 3: 事件驱动工作流 (解决 Race Condition)
6.  [x] **Contracts**: 定义新事件 `CompanyDataPersistedEvent` (含 `trigger_analysis` 标记)。
7.  [x] **API Gateway**: 停止直接发送 Analysis 命令，将其参数打包进 Fetch 命令。
8.  [x] **Data Providers**: 完成任务后发布 `PersistedEvent`。
9.  [x] **Report Generator**: 监听 `PersistedEvent` 触发分析。

## 5. 待确认
*   是否需要为每个数据源单独设置 TTL？(暂定统一策略)
*   前端是否需要显示数据的"上次更新时间"？(建议加上，增强用户信任)
