mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 00:35:32 +08:00
72412e7abd
Usage stats service: - Change get_request_logs() from limit/offset to page/page_size pagination - Return PaginatedLogs with total count, page, and page_size - Add appType and providerName filters with LIKE search - Add is_streaming, first_token_ms, duration_ms to RequestLogDetail - Join with providers table for provider name lookup Commands: - Update get_request_logs command signature for pagination params Module exports: - Export PaginatedLogs struct
179 lines
5.0 KiB
Rust
179 lines
5.0 KiB
Rust
//! 使用统计相关命令
|
|
|
|
use crate::error::AppError;
|
|
use crate::services::usage_stats::*;
|
|
use crate::store::AppState;
|
|
use tauri::State;
|
|
|
|
/// 获取使用量汇总
|
|
#[tauri::command]
|
|
pub fn get_usage_summary(
|
|
state: State<'_, AppState>,
|
|
start_date: Option<i64>,
|
|
end_date: Option<i64>,
|
|
) -> Result<UsageSummary, AppError> {
|
|
state.db.get_usage_summary(start_date, end_date)
|
|
}
|
|
|
|
/// 获取每日趋势
|
|
#[tauri::command]
|
|
pub fn get_usage_trends(
|
|
state: State<'_, AppState>,
|
|
days: u32,
|
|
) -> Result<Vec<DailyStats>, AppError> {
|
|
state.db.get_daily_trends(days)
|
|
}
|
|
|
|
/// 获取 Provider 统计
|
|
#[tauri::command]
|
|
pub fn get_provider_stats(state: State<'_, AppState>) -> Result<Vec<ProviderStats>, AppError> {
|
|
state.db.get_provider_stats()
|
|
}
|
|
|
|
/// 获取模型统计
|
|
#[tauri::command]
|
|
pub fn get_model_stats(state: State<'_, AppState>) -> Result<Vec<ModelStats>, AppError> {
|
|
state.db.get_model_stats()
|
|
}
|
|
|
|
/// 获取请求日志列表
|
|
#[tauri::command]
|
|
pub fn get_request_logs(
|
|
state: State<'_, AppState>,
|
|
filters: LogFilters,
|
|
page: u32,
|
|
page_size: u32,
|
|
) -> Result<PaginatedLogs, AppError> {
|
|
state.db.get_request_logs(&filters, page, page_size)
|
|
}
|
|
|
|
/// 获取单个请求详情
|
|
#[tauri::command]
|
|
pub fn get_request_detail(
|
|
state: State<'_, AppState>,
|
|
request_id: String,
|
|
) -> Result<Option<RequestLogDetail>, AppError> {
|
|
state.db.get_request_detail(&request_id)
|
|
}
|
|
|
|
/// 获取模型定价列表
|
|
#[tauri::command]
|
|
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 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(
|
|
"SELECT model_id, display_name, input_cost_per_million, output_cost_per_million,
|
|
cache_read_cost_per_million, cache_creation_cost_per_million
|
|
FROM model_pricing
|
|
ORDER BY display_name",
|
|
)?;
|
|
|
|
let rows = stmt.query_map([], |row| {
|
|
Ok(ModelPricingInfo {
|
|
model_id: row.get(0)?,
|
|
display_name: row.get(1)?,
|
|
input_cost_per_million: row.get(2)?,
|
|
output_cost_per_million: row.get(3)?,
|
|
cache_read_cost_per_million: row.get(4)?,
|
|
cache_creation_cost_per_million: row.get(5)?,
|
|
})
|
|
})?;
|
|
|
|
let mut pricing = Vec::new();
|
|
for row in rows {
|
|
pricing.push(row?);
|
|
}
|
|
|
|
log::info!("成功获取 {} 条模型定价数据", pricing.len());
|
|
Ok(pricing)
|
|
}
|
|
|
|
/// 更新模型定价
|
|
#[tauri::command]
|
|
pub fn update_model_pricing(
|
|
state: State<'_, AppState>,
|
|
model_id: String,
|
|
display_name: String,
|
|
input_cost: String,
|
|
output_cost: String,
|
|
cache_read_cost: String,
|
|
cache_creation_cost: String,
|
|
) -> Result<(), AppError> {
|
|
let db = state.db.clone();
|
|
let conn = crate::database::lock_conn!(db.conn);
|
|
|
|
conn.execute(
|
|
"INSERT OR REPLACE INTO model_pricing (
|
|
model_id, display_name, input_cost_per_million, output_cost_per_million,
|
|
cache_read_cost_per_million, cache_creation_cost_per_million
|
|
) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
|
rusqlite::params![
|
|
model_id,
|
|
display_name,
|
|
input_cost,
|
|
output_cost,
|
|
cache_read_cost,
|
|
cache_creation_cost
|
|
],
|
|
)
|
|
.map_err(|e| AppError::Database(format!("更新模型定价失败: {e}")))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// 检查 Provider 使用限额
|
|
#[tauri::command]
|
|
pub fn check_provider_limits(
|
|
state: State<'_, AppState>,
|
|
provider_id: String,
|
|
app_type: String,
|
|
) -> Result<crate::services::usage_stats::ProviderLimitStatus, AppError> {
|
|
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)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ModelPricingInfo {
|
|
pub model_id: String,
|
|
pub display_name: String,
|
|
pub input_cost_per_million: String,
|
|
pub output_cost_per_million: String,
|
|
pub cache_read_cost_per_million: String,
|
|
pub cache_creation_cost_per_million: String,
|
|
}
|