- **Common Contracts**: Updated DTOs and models to support workflow history; removed legacy analysis result DTOs.
- **Data Persistence Service**:
- Removed `analysis_results` table logic and API endpoints.
- Implemented `workflow_history` API and DB access (`history.rs`).
- Fixed compilation errors and updated tests.
- Exposed Postgres port in `docker-compose.yml` for easier debugging/offline checks.
- **API Gateway**:
- Implemented `history` endpoints (get history list, get by ID).
- Removed legacy `analysis-results` endpoints.
- Fixed routing and handler logic in `api.rs`.
- **Report Generator Service**:
- Removed dependency on legacy `analysis-results` persistence calls.
- Fixed compilation errors.
- **Workflow Orchestrator**: Fixed warnings and minor logic issues.
- **Providers**: Updated provider services (alphavantage, tushare, finnhub, yfinance, mock) to align with contract changes.
- **Frontend**:
- Updated `ReportPage` and stores to use new workflow history.
- Added `RecentReportsDropdown` component.
- Cleaned up `RealtimeLogs` component.
- **Documentation**: Moved completed design tasks to `completed/` and added refactoring context docs.
Confirmed all services pass `cargo check`.
204 lines
5.8 KiB
Rust
204 lines
5.8 KiB
Rust
//!
|
|
//! 数据持久化服务客户端
|
|
//!
|
|
|
|
use crate::error::Result;
|
|
use common_contracts::config_models::{
|
|
AnalysisTemplateSets, DataSourcesConfig, LlmProvidersConfig,
|
|
};
|
|
use common_contracts::dtos::{CompanyProfileDto, TimeSeriesFinancialDto, WorkflowHistoryDto, WorkflowHistorySummaryDto};
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Clone)]
|
|
pub struct PersistenceClient {
|
|
client: reqwest::Client,
|
|
base_url: String,
|
|
}
|
|
|
|
impl PersistenceClient {
|
|
pub fn new(base_url: String) -> Self {
|
|
Self {
|
|
client: reqwest::Client::new(),
|
|
base_url,
|
|
}
|
|
}
|
|
|
|
pub async fn get_company_profile(&self, symbol: &str) -> Result<CompanyProfileDto> {
|
|
let url = format!("{}/companies/{}", self.base_url, symbol);
|
|
let profile = self
|
|
.client
|
|
.get(&url)
|
|
.send()
|
|
.await?
|
|
.error_for_status()?
|
|
.json::<CompanyProfileDto>()
|
|
.await?;
|
|
Ok(profile)
|
|
}
|
|
|
|
pub async fn get_financials(&self, symbol: &str) -> Result<Vec<TimeSeriesFinancialDto>> {
|
|
let url = format!(
|
|
"{}/market-data/financial-statements/{}",
|
|
self.base_url, symbol
|
|
);
|
|
let financials = self
|
|
.client
|
|
.get(&url)
|
|
.send()
|
|
.await?
|
|
.error_for_status()?
|
|
.json::<Vec<TimeSeriesFinancialDto>>()
|
|
.await?;
|
|
Ok(financials)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub async fn get_session_data(
|
|
&self,
|
|
request_id: Uuid,
|
|
provider: Option<&str>,
|
|
data_type: Option<&str>,
|
|
) -> Result<Vec<common_contracts::dtos::SessionDataDto>> {
|
|
let url = format!("{}/session-data/{}", self.base_url, request_id);
|
|
let mut req = self.client.get(&url);
|
|
|
|
if let Some(p) = provider {
|
|
req = req.query(&[("provider", p)]);
|
|
}
|
|
if let Some(d) = data_type {
|
|
req = req.query(&[("data_type", d)]);
|
|
}
|
|
|
|
let data = req
|
|
.send()
|
|
.await?
|
|
.error_for_status()?
|
|
.json::<Vec<common_contracts::dtos::SessionDataDto>>()
|
|
.await?;
|
|
Ok(data)
|
|
}
|
|
|
|
pub async fn get_workflow_histories(&self, symbol: Option<&str>, limit: Option<i64>) -> Result<Vec<WorkflowHistorySummaryDto>> {
|
|
let url = format!("{}/history", self.base_url);
|
|
let mut req = self.client.get(&url);
|
|
if let Some(s) = symbol {
|
|
req = req.query(&[("symbol", s)]);
|
|
}
|
|
if let Some(l) = limit {
|
|
req = req.query(&[("limit", l)]);
|
|
}
|
|
let resp = req.send().await?.error_for_status()?;
|
|
let results = resp.json().await?;
|
|
Ok(results)
|
|
}
|
|
|
|
pub async fn get_workflow_history_by_id(&self, request_id: Uuid) -> Result<WorkflowHistoryDto> {
|
|
let url = format!("{}/history/{}", self.base_url, request_id);
|
|
let resp = self.client.get(&url).send().await?.error_for_status()?;
|
|
let result = resp.json().await?;
|
|
Ok(result)
|
|
}
|
|
|
|
pub async fn clear_history(&self) -> Result<()> {
|
|
let url = format!("{}/system/history", self.base_url);
|
|
self.client
|
|
.delete(&url)
|
|
.send()
|
|
.await?
|
|
.error_for_status()?;
|
|
Ok(())
|
|
}
|
|
|
|
// --- Config Methods ---
|
|
|
|
pub async fn get_llm_providers_config(&self) -> Result<LlmProvidersConfig> {
|
|
let url = format!("{}/configs/llm_providers", self.base_url);
|
|
let config = self
|
|
.client
|
|
.get(&url)
|
|
.send()
|
|
.await?
|
|
.error_for_status()?
|
|
.json::<LlmProvidersConfig>()
|
|
.await?;
|
|
Ok(config)
|
|
}
|
|
|
|
pub async fn update_llm_providers_config(
|
|
&self,
|
|
payload: &LlmProvidersConfig,
|
|
) -> Result<LlmProvidersConfig> {
|
|
let url = format!("{}/configs/llm_providers", self.base_url);
|
|
let updated_config = self
|
|
.client
|
|
.put(&url)
|
|
.json(payload)
|
|
.send()
|
|
.await?
|
|
.error_for_status()?
|
|
.json::<LlmProvidersConfig>()
|
|
.await?;
|
|
Ok(updated_config)
|
|
}
|
|
|
|
pub async fn get_analysis_template_sets(&self) -> Result<AnalysisTemplateSets> {
|
|
let url = format!("{}/configs/analysis_template_sets", self.base_url);
|
|
let config = self
|
|
.client
|
|
.get(&url)
|
|
.send()
|
|
.await?
|
|
.error_for_status()?
|
|
.json::<AnalysisTemplateSets>()
|
|
.await?;
|
|
Ok(config)
|
|
}
|
|
|
|
pub async fn update_analysis_template_sets(
|
|
&self,
|
|
payload: &AnalysisTemplateSets,
|
|
) -> Result<AnalysisTemplateSets> {
|
|
let url = format!("{}/configs/analysis_template_sets", self.base_url);
|
|
let updated_config = self
|
|
.client
|
|
.put(&url)
|
|
.json(payload)
|
|
.send()
|
|
.await?
|
|
.error_for_status()?
|
|
.json::<AnalysisTemplateSets>()
|
|
.await?;
|
|
Ok(updated_config)
|
|
}
|
|
|
|
pub async fn get_data_sources_config(&self) -> Result<DataSourcesConfig> {
|
|
let url = format!("{}/configs/data_sources", self.base_url);
|
|
let config = self
|
|
.client
|
|
.get(&url)
|
|
.send()
|
|
.await?
|
|
.error_for_status()?
|
|
.json::<DataSourcesConfig>()
|
|
.await?;
|
|
Ok(config)
|
|
}
|
|
|
|
pub async fn update_data_sources_config(
|
|
&self,
|
|
payload: &DataSourcesConfig,
|
|
) -> Result<DataSourcesConfig> {
|
|
let url = format!("{}/configs/data_sources", self.base_url);
|
|
let updated_config = self
|
|
.client
|
|
.put(&url)
|
|
.json(payload)
|
|
.send()
|
|
.await?
|
|
.error_for_status()?
|
|
.json::<DataSourcesConfig>()
|
|
.await?;
|
|
Ok(updated_config)
|
|
}
|
|
}
|