# 《WaLiAPI - 本地 LLM API 网关》第2-5节:设置中心与打包部署
作者:小傅哥
博客:https://bugstack.cn (opens new window)
沉淀、分享、成长,让自己和他人都能有所收获!😄
大家好,我是技术UP主小傅哥。
这是本课程的最后一节。我们要把 WaLiAPI 打磨成一个"真正的桌面软件"——有系统托盘、能开机自启、配置持久化,最后打包成各平台的安装包发布出去。
# 一、本章诉求
- 实现基于 Tauri Store 的配置管理系统
- 实现系统托盘(托盘菜单、点击恢复窗口、关闭到托盘)
- 实现开机自启
- 实现主题切换
- 打包发布各平台安装包
# 二、配置管理系统
# 2.1 存储方案选型
WaLiAPI 中有两种配置存储,分别适用不同场景:
| 方案 | 存储位置 | 适用数据 |
|---|---|---|
| SQLite | app_data_dir/waliapi.db | 结构化业务数据(渠道、密钥、日志、规则) |
| Tauri Store | app_data_dir/settings.json | 键值对应用设置(端口、主题、开关) |
为什么设置不用 SQLite?因为设置是扁平键值对语义(server.port、ui.theme),用 SQL 表存 KV 反而别扭(要设计 key-value 表、做类型转换)。Tauri Store 插件就是为这个场景设计的:JSON 文件持久化 + 类型化读取 + 前后端都能访问。
# 2.2 后端读取配置
use tauri_plugin_store::StoreExt;
// 读取单个配置项的通用模式
fn get_server_port(app: &AppHandle) -> u16 {
if let Ok(store) = app.store("settings.json") {
if let Some(port) = store.get("server.port") {
if let Some(value) = port.as_u64() {
return value as u16;
}
}
}
8777 // 默认值
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
配置键的命名规范是 点分层级:server.port、security.enabled、retry.times、general.close_to_tray。
# 2.3 设置的命令层
src-tauri/src/commands/settings.rs 核心逻辑:
#[derive(Debug, Serialize, Deserialize)]
pub struct SettingsDto {
pub server_port: u16,
pub server_host: String,
pub ui_theme: String,
pub ui_language: String,
pub minimize_to_tray: bool,
pub close_to_tray: bool,
pub auto_start: bool,
pub retry_enabled: bool,
pub retry_times: i64,
pub security_enabled: bool,
pub security_mode: String,
// ... 安全相关开关
}
#[tauri::command]
pub async fn get_settings(app: AppHandle) -> Result<SettingsDto, String> {
let store = app.store("settings.json").map_err(|e| e.to_string())?;
Ok(SettingsDto {
server_port: store.get("server.port").and_then(|v| v.as_u64()).unwrap_or(8777) as u16,
server_host: store.get("server.host").and_then(|v| v.as_str().map(String::from))
.unwrap_or_else(|| "127.0.0.1".to_string()),
ui_theme: store.get("ui.theme").and_then(|v| v.as_str().map(String::from))
.unwrap_or_else(|| "dark".to_string()),
// ... 其余字段,全部带默认值
})
}
#[tauri::command]
pub async fn save_settings(app: AppHandle, settings: SettingsDto) -> Result<(), String> {
let store = app.store("settings.json").map_err(|e| e.to_string())?;
store.set("server.port", serde_json::json!(settings.server_port));
store.set("server.host", serde_json::json!(settings.server_host));
store.set("ui.theme", serde_json::json!(settings.ui_theme));
// ... 其余字段
store.save().map_err(|e| e.to_string())?;
Ok(())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
设计要点:所有配置读取都有默认值兜底——settings.json 损坏、缺失、字段不存在时,应用依然能以合理默认配置运行。这是"防御性配置读取"的标准实践。

