mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-02 18:41:35 +08:00
feat(commands): add usage statistics Tauri commands
Register usage commands for summary, trends, logs, and pricing. Expose usage stats service through Tauri command layer.
This commit is contained in:
@@ -12,6 +12,7 @@ mod provider;
|
|||||||
mod proxy;
|
mod proxy;
|
||||||
mod settings;
|
mod settings;
|
||||||
pub mod skill;
|
pub mod skill;
|
||||||
|
mod usage;
|
||||||
|
|
||||||
pub use config::*;
|
pub use config::*;
|
||||||
pub use deeplink::*;
|
pub use deeplink::*;
|
||||||
@@ -25,3 +26,4 @@ pub use provider::*;
|
|||||||
pub use proxy::*;
|
pub use proxy::*;
|
||||||
pub use settings::*;
|
pub use settings::*;
|
||||||
pub use skill::*;
|
pub use skill::*;
|
||||||
|
pub use usage::*;
|
||||||
|
|||||||
@@ -1,82 +1,89 @@
|
|||||||
//! 使用统计相关命令
|
//! 使用统计相关命令
|
||||||
|
|
||||||
use crate::database::Database;
|
|
||||||
use crate::error::AppError;
|
use crate::error::AppError;
|
||||||
use crate::services::usage_stats::*;
|
use crate::services::usage_stats::*;
|
||||||
|
use crate::store::AppState;
|
||||||
use tauri::State;
|
use tauri::State;
|
||||||
|
|
||||||
/// 获取使用量汇总
|
/// 获取使用量汇总
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_usage_summary(
|
pub fn get_usage_summary(
|
||||||
db: State<'_, Database>,
|
state: State<'_, AppState>,
|
||||||
start_date: Option<i64>,
|
start_date: Option<i64>,
|
||||||
end_date: Option<i64>,
|
end_date: Option<i64>,
|
||||||
) -> Result<UsageSummary, AppError> {
|
) -> Result<UsageSummary, AppError> {
|
||||||
db.get_usage_summary(start_date, end_date)
|
state.db.get_usage_summary(start_date, end_date)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取每日趋势
|
/// 获取每日趋势
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_usage_trends(
|
pub fn get_usage_trends(
|
||||||
db: State<'_, Database>,
|
state: State<'_, AppState>,
|
||||||
days: u32,
|
days: u32,
|
||||||
) -> Result<Vec<DailyStats>, AppError> {
|
) -> Result<Vec<DailyStats>, AppError> {
|
||||||
db.get_daily_trends(days)
|
state.db.get_daily_trends(days)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取 Provider 统计
|
/// 获取 Provider 统计
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_provider_stats(db: State<'_, Database>) -> Result<Vec<ProviderStats>, AppError> {
|
pub fn get_provider_stats(state: State<'_, AppState>) -> Result<Vec<ProviderStats>, AppError> {
|
||||||
db.get_provider_stats()
|
state.db.get_provider_stats()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取模型统计
|
/// 获取模型统计
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_model_stats(db: State<'_, Database>) -> Result<Vec<ModelStats>, AppError> {
|
pub fn get_model_stats(state: State<'_, AppState>) -> Result<Vec<ModelStats>, AppError> {
|
||||||
db.get_model_stats()
|
state.db.get_model_stats()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取请求日志列表
|
/// 获取请求日志列表
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_request_logs(
|
pub fn get_request_logs(
|
||||||
db: State<'_, Database>,
|
state: State<'_, AppState>,
|
||||||
provider_id: Option<String>,
|
filters: LogFilters,
|
||||||
model: Option<String>,
|
|
||||||
status_code: Option<u16>,
|
|
||||||
start_date: Option<i64>,
|
|
||||||
end_date: Option<i64>,
|
|
||||||
limit: u32,
|
limit: u32,
|
||||||
offset: u32,
|
offset: u32,
|
||||||
) -> Result<Vec<RequestLogDetail>, AppError> {
|
) -> Result<Vec<RequestLogDetail>, AppError> {
|
||||||
let filters = LogFilters {
|
state.db.get_request_logs(&filters, limit, offset)
|
||||||
provider_id,
|
|
||||||
model,
|
|
||||||
status_code,
|
|
||||||
start_date,
|
|
||||||
end_date,
|
|
||||||
};
|
|
||||||
db.get_request_logs(&filters, limit, offset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取单个请求详情
|
/// 获取单个请求详情
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_request_detail(
|
pub fn get_request_detail(
|
||||||
db: State<'_, Database>,
|
state: State<'_, AppState>,
|
||||||
request_id: String,
|
request_id: String,
|
||||||
) -> Result<Option<RequestLogDetail>, AppError> {
|
) -> Result<Option<RequestLogDetail>, AppError> {
|
||||||
db.get_request_detail(&request_id)
|
state.db.get_request_detail(&request_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取模型定价列表
|
/// 获取模型定价列表
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_model_pricing(db: State<'_, Database>) -> Result<Vec<ModelPricingInfo>, AppError> {
|
pub fn get_model_pricing(state: State<'_, AppState>) -> Result<Vec<ModelPricingInfo>, AppError> {
|
||||||
|
log::info!("获取模型定价列表");
|
||||||
|
state.db.ensure_model_pricing_seeded()?;
|
||||||
|
|
||||||
|
let db = state.db.clone();
|
||||||
let conn = crate::database::lock_conn!(db.conn);
|
let conn = crate::database::lock_conn!(db.conn);
|
||||||
|
|
||||||
|
// 检查表是否存在
|
||||||
|
let table_exists: bool = conn
|
||||||
|
.query_row(
|
||||||
|
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='model_pricing'",
|
||||||
|
[],
|
||||||
|
|row| row.get::<_, i64>(0).map(|count| count > 0),
|
||||||
|
)
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if !table_exists {
|
||||||
|
log::error!("model_pricing 表不存在,可能需要重启应用以触发数据库迁移");
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
let mut stmt = conn.prepare(
|
let mut stmt = conn.prepare(
|
||||||
"SELECT model_id, display_name, input_cost_per_million, output_cost_per_million,
|
"SELECT model_id, display_name, input_cost_per_million, output_cost_per_million,
|
||||||
cache_read_cost_per_million, cache_creation_cost_per_million
|
cache_read_cost_per_million, cache_creation_cost_per_million
|
||||||
FROM model_pricing
|
FROM model_pricing
|
||||||
ORDER BY display_name"
|
ORDER BY display_name",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let rows = stmt.query_map([], |row| {
|
let rows = stmt.query_map([], |row| {
|
||||||
@@ -95,13 +102,14 @@ pub fn get_model_pricing(db: State<'_, Database>) -> Result<Vec<ModelPricingInfo
|
|||||||
pricing.push(row?);
|
pricing.push(row?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log::info!("成功获取 {} 条模型定价数据", pricing.len());
|
||||||
Ok(pricing)
|
Ok(pricing)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 更新模型定价
|
/// 更新模型定价
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn update_model_pricing(
|
pub fn update_model_pricing(
|
||||||
db: State<'_, Database>,
|
state: State<'_, AppState>,
|
||||||
model_id: String,
|
model_id: String,
|
||||||
display_name: String,
|
display_name: String,
|
||||||
input_cost: String,
|
input_cost: String,
|
||||||
@@ -109,6 +117,7 @@ pub fn update_model_pricing(
|
|||||||
cache_read_cost: String,
|
cache_read_cost: String,
|
||||||
cache_creation_cost: String,
|
cache_creation_cost: String,
|
||||||
) -> Result<(), AppError> {
|
) -> Result<(), AppError> {
|
||||||
|
let db = state.db.clone();
|
||||||
let conn = crate::database::lock_conn!(db.conn);
|
let conn = crate::database::lock_conn!(db.conn);
|
||||||
|
|
||||||
conn.execute(
|
conn.execute(
|
||||||
@@ -133,15 +142,32 @@ pub fn update_model_pricing(
|
|||||||
/// 检查 Provider 使用限额
|
/// 检查 Provider 使用限额
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn check_provider_limits(
|
pub fn check_provider_limits(
|
||||||
db: State<'_, Database>,
|
state: State<'_, AppState>,
|
||||||
provider_id: String,
|
provider_id: String,
|
||||||
app_type: String,
|
app_type: String,
|
||||||
) -> Result<crate::services::usage_stats::ProviderLimitStatus, AppError> {
|
) -> Result<crate::services::usage_stats::ProviderLimitStatus, AppError> {
|
||||||
db.check_provider_limits(&provider_id, &app_type)
|
state.db.check_provider_limits(&provider_id, &app_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 删除模型定价
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn delete_model_pricing(state: State<'_, AppState>, model_id: String) -> Result<(), AppError> {
|
||||||
|
let db = state.db.clone();
|
||||||
|
let conn = crate::database::lock_conn!(db.conn);
|
||||||
|
|
||||||
|
conn.execute(
|
||||||
|
"DELETE FROM model_pricing WHERE model_id = ?1",
|
||||||
|
rusqlite::params![model_id],
|
||||||
|
)
|
||||||
|
.map_err(|e| AppError::Database(format!("删除模型定价失败: {e}")))?;
|
||||||
|
|
||||||
|
log::info!("已删除模型定价: {model_id}");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 模型定价信息
|
/// 模型定价信息
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ModelPricingInfo {
|
pub struct ModelPricingInfo {
|
||||||
pub model_id: String,
|
pub model_id: String,
|
||||||
pub display_name: String,
|
pub display_name: String,
|
||||||
|
|||||||
@@ -650,6 +650,17 @@ pub fn run() {
|
|||||||
commands::get_proxy_config,
|
commands::get_proxy_config,
|
||||||
commands::update_proxy_config,
|
commands::update_proxy_config,
|
||||||
commands::is_proxy_running,
|
commands::is_proxy_running,
|
||||||
|
// Usage statistics
|
||||||
|
commands::get_usage_summary,
|
||||||
|
commands::get_usage_trends,
|
||||||
|
commands::get_provider_stats,
|
||||||
|
commands::get_model_stats,
|
||||||
|
commands::get_request_logs,
|
||||||
|
commands::get_request_detail,
|
||||||
|
commands::get_model_pricing,
|
||||||
|
commands::update_model_pricing,
|
||||||
|
commands::delete_model_pricing,
|
||||||
|
commands::check_provider_limits,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let app = builder
|
let app = builder
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ pub mod provider;
|
|||||||
pub mod proxy;
|
pub mod proxy;
|
||||||
pub mod skill;
|
pub mod skill;
|
||||||
pub mod speedtest;
|
pub mod speedtest;
|
||||||
|
pub mod usage_stats;
|
||||||
|
|
||||||
pub use config::ConfigService;
|
pub use config::ConfigService;
|
||||||
pub use mcp::McpService;
|
pub use mcp::McpService;
|
||||||
@@ -15,3 +16,8 @@ pub use provider::{ProviderService, ProviderSortUpdate};
|
|||||||
pub use proxy::ProxyService;
|
pub use proxy::ProxyService;
|
||||||
pub use skill::{Skill, SkillRepo, SkillService};
|
pub use skill::{Skill, SkillRepo, SkillService};
|
||||||
pub use speedtest::{EndpointLatency, SpeedtestService};
|
pub use speedtest::{EndpointLatency, SpeedtestService};
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub use usage_stats::{
|
||||||
|
DailyStats, LogFilters, ModelStats, ProviderLimitStatus, ProviderStats, RequestLogDetail,
|
||||||
|
UsageSummary,
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user