mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-29 09:37:37 +08:00
feat(db): rebuild Codex usage on upgrade and via maintenance action
Schema v16 wipes codex_session detail rows, _codex_session rollups and Codex rollout cursors inside the migration savepoint (cursor deletion uses pure shape matching so CODEX_HOME drift cannot orphan cursors); the next session sync re-imports history from source JSONL under the corrected importer. Fresh installs traverse the same branch as a no-op. Add a manual "Rebuild Codex usage" maintenance action (single-flight, hard-fail backup before reset, unconditional refresh notification even when reimport is empty or fails after reset) with a destructive confirm dialog, result toast and four-locale strings. Historical proxy-side duplicate rows are intentionally left untouched; history whose source JSONL was already deleted cannot be reconstructed.
This commit is contained in:
@@ -52,7 +52,7 @@ use std::sync::Mutex;
|
||||
|
||||
/// 当前 Schema 版本号
|
||||
/// 每次修改表结构时递增,并在 schema.rs 中添加相应的迁移逻辑
|
||||
pub(crate) const SCHEMA_VERSION: i32 = 15;
|
||||
pub(crate) const SCHEMA_VERSION: i32 = 16;
|
||||
|
||||
/// 安全地序列化 JSON,避免 unwrap panic
|
||||
pub(crate) fn to_json_string<T: Serialize>(value: &T) -> Result<String, AppError> {
|
||||
|
||||
@@ -506,6 +506,11 @@ impl Database {
|
||||
Self::migrate_v14_to_v15(conn)?;
|
||||
Self::set_user_version(conn, 15)?;
|
||||
}
|
||||
15 => {
|
||||
log::info!("迁移数据库从 v15 到 v16(重建 Codex 会话用量)");
|
||||
Self::migrate_v15_to_v16(conn)?;
|
||||
Self::set_user_version(conn, 16)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(AppError::Database(format!(
|
||||
"未知的数据库版本 {version},无法迁移到 {SCHEMA_VERSION}"
|
||||
@@ -1510,6 +1515,14 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// v15 -> v16: remove Codex session rows and cursors so startup sync can
|
||||
/// rebuild them with fork-history alignment. Must stay connection-level:
|
||||
/// schema migration already owns the Database connection mutex.
|
||||
fn migrate_v15_to_v16(conn: &Connection) -> Result<(), AppError> {
|
||||
let codex_dir = crate::codex_config::get_codex_config_dir();
|
||||
crate::services::session_usage_codex::reset_codex_usage_on_conn(conn, &codex_dir)
|
||||
}
|
||||
|
||||
/// 插入默认模型定价数据
|
||||
/// 格式: (model_id, display_name, input, output, cache_read, cache_creation)
|
||||
/// 注意: model_id 使用短横线格式(如 claude-haiku-4-5),与 API 返回的模型名称标准化后一致
|
||||
@@ -3025,4 +3038,44 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migrate_v15_to_v16_resets_only_codex_session_usage() -> Result<(), AppError> {
|
||||
let conn = Connection::open_in_memory()?;
|
||||
Database::create_tables_on_conn(&conn)?;
|
||||
conn.execute_batch(
|
||||
"INSERT INTO proxy_request_logs (
|
||||
request_id, provider_id, app_type, model, input_tokens,
|
||||
output_tokens, cache_read_tokens, latency_ms, status_code,
|
||||
created_at, data_source
|
||||
) VALUES
|
||||
('codex-row', '_codex_session', 'codex', 'gpt', 1, 1, 0, 0, 200, 1, 'codex_session'),
|
||||
('gemini-row', '_gemini_session', 'gemini', 'gemini', 1, 1, 0, 0, 200, 1, 'gemini_session');
|
||||
INSERT INTO usage_daily_rollups (date, app_type, provider_id, model)
|
||||
VALUES
|
||||
('2026-07-10', 'codex', '_codex_session', 'gpt'),
|
||||
('2026-07-10', 'gemini', '_gemini_session', 'gemini');
|
||||
INSERT INTO session_log_sync
|
||||
(file_path, last_modified, last_line_offset, last_synced_at)
|
||||
VALUES
|
||||
('/old/sessions/rollout-old-00000000-0000-4000-8000-000000000001.jsonl', 1, 1, 1),
|
||||
('/gemini/tmp/session-123.json', 1, 1, 1);",
|
||||
)?;
|
||||
Database::set_user_version(&conn, 15)?;
|
||||
|
||||
Database::apply_schema_migrations_on_conn(&conn)?;
|
||||
|
||||
assert_eq!(Database::get_user_version(&conn)?, 16);
|
||||
let counts: (i64, i64, i64, i64) = conn.query_row(
|
||||
"SELECT
|
||||
(SELECT COUNT(*) FROM proxy_request_logs WHERE data_source = 'codex_session'),
|
||||
(SELECT COUNT(*) FROM proxy_request_logs WHERE data_source = 'gemini_session'),
|
||||
(SELECT COUNT(*) FROM usage_daily_rollups WHERE provider_id = '_codex_session'),
|
||||
(SELECT COUNT(*) FROM session_log_sync)",
|
||||
[],
|
||||
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)),
|
||||
)?;
|
||||
assert_eq!(counts, (0, 1, 0, 1));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user