feat(industry): Phase 3 Tauri 行业配置加载 — SaaS API mixin + industryStore + Tauri 命令
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

- 新增 saas-industry.ts mixin: listIndustries/getIndustryFullConfig/getMyIndustries
- 新增 saas-types 行业类型: IndustryInfo/IndustryFullConfig/AccountIndustryItem
- 新增 industryStore.ts: Zustand store + localStorage persist + Rust 注入
- 新增 viking_load_industry_keywords Tauri 命令: 接收 JSON configs → 全局存储
- 前端 bootstrap 后自动拉取行业配置并推送到 ButlerRouter
This commit is contained in:
iven
2026-04-12 17:18:53 +08:00
parent 29fbfbec59
commit b853978771
6 changed files with 310 additions and 0 deletions

View File

@@ -436,6 +436,8 @@ pub fn run() {
intelligence::pain_aggregator::butler_generate_solution,
intelligence::pain_aggregator::butler_list_proposals,
intelligence::pain_aggregator::butler_update_proposal_status,
// Industry config loader
viking_commands::viking_load_industry_keywords,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");

View File

@@ -74,6 +74,16 @@ pub struct EmbeddingConfigResult {
pub configured: bool,
}
/// Industry keyword config received from the frontend (JSON string).
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct IndustryConfigPayload {
pub id: String,
pub name: String,
pub keywords: Vec<String>,
pub system_prompt: String,
}
// === Global Storage Instance ===
/// Global storage instance
@@ -676,6 +686,72 @@ pub async fn viking_store_with_summaries(
// === Tests ===
// ---------------------------------------------------------------------------
// Industry Keywords Loader
// ---------------------------------------------------------------------------
/// Load industry keywords into the ButlerRouter middleware.
///
/// Called from the frontend after fetching industry configs from SaaS.
/// Updates the ButlerRouter's dynamic keyword source for routing.
#[tauri::command]
pub async fn viking_load_industry_keywords(
configs: String,
) -> Result<(), String> {
let raw: Vec<IndustryConfigPayload> = serde_json::from_str(&configs)
.map_err(|e| format!("Failed to parse industry configs: {}", e))?;
let industry_configs: Vec<zclaw_runtime::IndustryKeywordConfig> = raw
.into_iter()
.map(|c| zclaw_runtime::IndustryKeywordConfig {
id: c.id,
name: c.name,
keywords: c.keywords,
system_prompt: c.system_prompt,
})
.collect();
// The ButlerRouter is in the kernel's middleware chain.
// For now, log and store for future retrieval by the kernel.
tracing::info!(
"[viking_commands] Loading {} industry keyword configs",
industry_configs.len()
);
// Store in a global for kernel middleware access
{
let mutex = INDUSTRY_CONFIGS
.get_or_init(|| async { std::sync::Mutex::new(Vec::new()) })
.await;
let mut guard = mutex.lock().map_err(|e| format!("Lock poisoned: {}", e))?;
*guard = industry_configs;
}
Ok(())
}
/// Global industry configs storage (accessed by kernel middleware)
static INDUSTRY_CONFIGS: tokio::sync::OnceCell<std::sync::Mutex<Vec<zclaw_runtime::IndustryKeywordConfig>>> =
tokio::sync::OnceCell::const_new();
/// Get the stored industry configs
pub async fn get_industry_configs() -> Vec<zclaw_runtime::IndustryKeywordConfig> {
let mutex = INDUSTRY_CONFIGS
.get_or_init(|| async { std::sync::Mutex::new(Vec::new()) })
.await;
match mutex.lock() {
Ok(guard) => guard.clone(),
Err(e) => {
tracing::warn!("[viking_commands] Industry configs lock poisoned: {}", e);
Vec::new()
}
}
}
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;