Fundamental_Analysis/services/workflow-orchestrator-service/src/io_binder.rs
Lv, Qi 0c975bb8f1 Refactor: Remove legacy analysis results and implement workflow history
- **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`.
2025-11-29 14:46:44 +08:00

74 lines
2.2 KiB
Rust

use common_contracts::messages::TaskType;
pub struct IOBinder;
impl IOBinder {
pub fn new() -> Self {
Self
}
pub fn allocate_output_path(
&self,
task_type: TaskType,
symbol: &str,
task_id: &str,
display_name: Option<&str>,
) -> String {
// Convention based paths:
// DataFetch: raw/{provider_id}/{symbol}
// DataProcessing: processed/{processor_id}/{symbol}
// Analysis: analysis/{module_name_or_id}/{symbol}.md
let clean_task_id = task_id.split(':').last().unwrap_or(task_id);
match task_type {
TaskType::DataFetch => format!("raw/{}/{}", clean_task_id, symbol),
TaskType::DataProcessing => format!("processed/{}/{}", clean_task_id, symbol),
TaskType::Analysis => {
let folder_name = if let Some(name) = display_name {
self.sanitize_path_segment(name)
} else {
clean_task_id.to_string()
};
format!("analysis/{}/{}.md", folder_name, symbol)
},
}
}
pub fn allocate_trace_path(
&self,
task_type: TaskType,
symbol: &str,
task_id: &str,
display_name: Option<&str>,
) -> String {
let clean_task_id = task_id.split(':').last().unwrap_or(task_id);
match task_type {
TaskType::Analysis => {
let folder_name = if let Some(name) = display_name {
self.sanitize_path_segment(name)
} else {
clean_task_id.to_string()
};
format!("analysis/{}/{}_trace.md", folder_name, symbol)
},
_ => format!("debug/{}/{}_trace.md", clean_task_id, symbol),
}
}
fn sanitize_path_segment(&self, name: &str) -> String {
name.replace('/', "_")
.replace('\\', "_")
.replace(':', "_")
.replace('"', "_")
.replace('<', "_")
.replace('>', "_")
.replace('|', "_")
.replace('?', "_")
.replace('*', "_")
.trim()
.to_string()
}
}