From 1368db488f93d34a792f27e9d9b2ab5d3877d250 Mon Sep 17 00:00:00 2001 From: "fengchuanhn@gmail.com" Date: Fri, 22 May 2026 18:09:09 +0800 Subject: [PATCH] 1 --- src-tauri/resources/data/agent | 1 + src-tauri/resources/data/oem | 1 + src-tauri/src/agent_id.rs | 115 +++++++++++++++++++++++++++++++++ src-tauri/src/lib.rs | 10 ++- src-tauri/src/oem_context.rs | 4 +- src-tauri/src/oem_id.rs | 63 ++++++++++++++++-- 6 files changed, 183 insertions(+), 11 deletions(-) create mode 100644 src-tauri/resources/data/agent create mode 100644 src-tauri/resources/data/oem create mode 100644 src-tauri/src/agent_id.rs diff --git a/src-tauri/resources/data/agent b/src-tauri/resources/data/agent new file mode 100644 index 0000000..c227083 --- /dev/null +++ b/src-tauri/resources/data/agent @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/src-tauri/resources/data/oem b/src-tauri/resources/data/oem new file mode 100644 index 0000000..e440e5c --- /dev/null +++ b/src-tauri/resources/data/oem @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/src-tauri/src/agent_id.rs b/src-tauri/src/agent_id.rs new file mode 100644 index 0000000..cc403dd --- /dev/null +++ b/src-tauri/src/agent_id.rs @@ -0,0 +1,115 @@ +//! 从应用目录 `resources/data/agent` 读取代理编号。 + +use std::path::{Path, PathBuf}; + +use serde_json::Value; +use tauri::{AppHandle, Manager, path::BaseDirectory}; + +/// 相对可执行文件目录的代理配置路径:`resources/data/agent`。 +pub const AGENT_RESOURCE_RELATIVE: &[&str] = &["resources", "data", "agent"]; + +/// 开发时源码树中的代理配置路径。 +pub const AGENT_DEV_RELATIVE: &str = "src-tauri/resources/data/agent"; + +pub fn read_agent_id_file(path: &Path) -> Option { + let content = std::fs::read_to_string(path).ok()?; + parse_agent_id_content(&content) +} + +/// 解析代理配置文件路径(打包资源 → 应用目录 → 开发目录)。 +pub fn resolve_agent_id_path(handle: &AppHandle) -> Option { + if let Ok(path) = handle.path().resolve("data/agent", BaseDirectory::Resource) { + if path.is_file() { + return Some(path); + } + } + locate_agent_id_file() +} + +/// 读取当前应用的代理编号;无配置文件时返回 `None`。 +pub fn load_agent_id(handle: &AppHandle) -> Option { + resolve_agent_id_path(handle).and_then(|path| read_agent_id_file(&path)) +} + +/// 按可执行文件旁 `resources/data/agent` 或开发目录定位配置文件。 +pub fn locate_agent_id_file() -> Option { + if let Ok(path) = std::env::var("AICLIENT_AGENT_FILE") { + let custom = PathBuf::from(path); + if custom.is_file() { + return Some(custom); + } + } + + if let Ok(exe) = std::env::current_exe() { + if let Some(dir) = exe.parent() { + let bundled = dir.join(path_from_parts(AGENT_RESOURCE_RELATIVE)); + if bundled.is_file() { + return Some(bundled); + } + } + } + + let dev = PathBuf::from(AGENT_DEV_RELATIVE); + if dev.is_file() { + return Some(dev); + } + + None +} + +fn path_from_parts(parts: &[&str]) -> PathBuf { + let mut path = PathBuf::new(); + for part in parts { + path.push(part); + } + path +} + +fn parse_agent_id_content(content: &str) -> Option { + let trimmed = content.trim(); + if trimmed.is_empty() { + return None; + } + + if let Ok(value) = serde_json::from_str::(trimmed) { + if let Some(id) = value.get("agent_id").and_then(|v| v.as_i64()) { + if id >= 1 { + return Some(id); + } + } + if let Some(id) = value.as_i64() { + if id >= 1 { + return Some(id); + } + } + } + + let first_line = trimmed.lines().next()?.trim(); + let id: i64 = first_line.parse().ok()?; + if id >= 1 { + Some(id) + } else { + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_integer_line() { + assert_eq!(parse_agent_id_content("5\n"), Some(5)); + } + + #[test] + fn parse_json_object() { + assert_eq!(parse_agent_id_content(r#"{"agent_id":7}"#), Some(7)); + } + + #[test] + fn invalid_returns_none() { + assert_eq!(parse_agent_id_content("abc"), None); + assert_eq!(parse_agent_id_content(""), None); + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 436dfb2..4471906 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -18,6 +18,7 @@ pub mod ffmpeg; pub mod http; pub mod js_runtime; pub mod nodejs; +pub mod agent_id; pub mod oem_context; pub mod oem_id; pub mod oss; @@ -40,12 +41,9 @@ pub fn run() { .setup(|app| { let handle = app.handle().clone(); let heartbeat_handle = handle.clone(); - let oem_id = handle - .path() - .app_data_dir() - .map(|dir| oem_id::read_oem_id_file(&dir.join(oem_id::OEM_FILE_NAME))) - .unwrap_or(oem_id::DEFAULT_OEM_ID); - app.manage(oem_context::OemContext { oem_id }); + let oem_id = oem_id::load_oem_id(&handle); + let agent_id = agent_id::load_agent_id(&handle); + app.manage(oem_context::OemContext { oem_id, agent_id }); tauri::async_runtime::block_on(async move { let app_config = handle.state::(); diff --git a/src-tauri/src/oem_context.rs b/src-tauri/src/oem_context.rs index e4d7856..1c7f6cb 100644 --- a/src-tauri/src/oem_context.rs +++ b/src-tauri/src/oem_context.rs @@ -1,6 +1,8 @@ -//! 启动时解析的 OEM 上下文。 +//! 启动时从 `resources/data/oem`、`resources/data/agent` 解析的安装配置。 #[derive(Debug, Clone)] pub struct OemContext { pub oem_id: i64, + /// 代理版安装包配置;无 `agent` 文件时为 `None`。 + pub agent_id: Option, } diff --git a/src-tauri/src/oem_id.rs b/src-tauri/src/oem_id.rs index 93f224a..c95a0d6 100644 --- a/src-tauri/src/oem_id.rs +++ b/src-tauri/src/oem_id.rs @@ -1,13 +1,17 @@ -//! 从应用数据目录的 `oem` 文件读取 OEM 编号。 +//! 从应用目录 `resources/data/oem` 读取 OEM 编号。 -use std::path::Path; +use std::path::{Path, PathBuf}; use serde_json::Value; +use tauri::{AppHandle, Manager, path::BaseDirectory}; pub const DEFAULT_OEM_ID: i64 = 1; -/// 应用数据目录下的 OEM 配置文件名。 -pub const OEM_FILE_NAME: &str = "oem"; +/// 相对可执行文件目录的 OEM 配置路径:`resources/data/oem`。 +pub const OEM_RESOURCE_RELATIVE: &[&str] = &["resources", "data", "oem"]; + +/// 开发时源码树中的 OEM 配置路径。 +pub const OEM_DEV_RELATIVE: &str = "src-tauri/resources/data/oem"; pub fn read_oem_id_file(path: &Path) -> i64 { let content = match std::fs::read_to_string(path) { @@ -17,6 +21,57 @@ pub fn read_oem_id_file(path: &Path) -> i64 { parse_oem_id_content(&content).unwrap_or(DEFAULT_OEM_ID) } +/// 解析 OEM 配置文件路径(打包资源 → 应用目录 → 开发目录)。 +pub fn resolve_oem_id_path(handle: &AppHandle) -> Option { + if let Ok(path) = handle.path().resolve("data/oem", BaseDirectory::Resource) { + if path.is_file() { + return Some(path); + } + } + locate_oem_id_file() +} + +/// 读取当前应用的 OEM 编号。 +pub fn load_oem_id(handle: &AppHandle) -> i64 { + resolve_oem_id_path(handle) + .map(|path| read_oem_id_file(&path)) + .unwrap_or(DEFAULT_OEM_ID) +} + +/// 按可执行文件旁 `resources/data/oem` 或开发目录定位配置文件。 +pub fn locate_oem_id_file() -> Option { + if let Ok(path) = std::env::var("AICLIENT_OEM_FILE") { + let custom = PathBuf::from(path); + if custom.is_file() { + return Some(custom); + } + } + + if let Ok(exe) = std::env::current_exe() { + if let Some(dir) = exe.parent() { + let bundled = dir.join(path_from_parts(OEM_RESOURCE_RELATIVE)); + if bundled.is_file() { + return Some(bundled); + } + } + } + + let dev = PathBuf::from(OEM_DEV_RELATIVE); + if dev.is_file() { + return Some(dev); + } + + None +} + +fn path_from_parts(parts: &[&str]) -> PathBuf { + let mut path = PathBuf::new(); + for part in parts { + path.push(part); + } + path +} + fn parse_oem_id_content(content: &str) -> Option { let trimmed = content.trim(); if trimmed.is_empty() {