改用API驗證
This commit is contained in:
203
migrations/fix_api_usage_stats.py
Normal file
203
migrations/fix_api_usage_stats.py
Normal file
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
執行 API Usage Stats 資料表修復 Migration
|
||||
|
||||
Usage:
|
||||
python migrations/fix_api_usage_stats.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加專案根目錄到 Python 路徑
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from app import create_app, db
|
||||
from sqlalchemy import text
|
||||
|
||||
|
||||
def run_migration():
|
||||
"""執行資料表結構修復"""
|
||||
app = create_app()
|
||||
|
||||
with app.app_context():
|
||||
print("=" * 60)
|
||||
print("API Usage Stats 資料表結構修復")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
# 1. 檢查當前結構
|
||||
print("\n[1/8] 檢查當前資料表結構...")
|
||||
result = db.session.execute(text('SHOW COLUMNS FROM dt_api_usage_stats'))
|
||||
current_columns = {row[0]: row for row in result}
|
||||
print(f" ✓ 當前欄位數量: {len(current_columns)}")
|
||||
|
||||
# 2. 備份現有資料
|
||||
print("\n[2/8] 建立資料備份...")
|
||||
db.session.execute(text('''
|
||||
CREATE TABLE IF NOT EXISTS dt_api_usage_stats_backup_20251001
|
||||
AS SELECT * FROM dt_api_usage_stats
|
||||
'''))
|
||||
db.session.commit()
|
||||
|
||||
backup_count = db.session.execute(
|
||||
text('SELECT COUNT(*) FROM dt_api_usage_stats_backup_20251001')
|
||||
).scalar()
|
||||
print(f" ✓ 已備份 {backup_count} 筆記錄")
|
||||
|
||||
# 3. 修改欄位名稱:api_name → api_endpoint
|
||||
if 'api_name' in current_columns:
|
||||
print("\n[3/8] 重新命名 api_name → api_endpoint...")
|
||||
db.session.execute(text('''
|
||||
ALTER TABLE dt_api_usage_stats
|
||||
CHANGE COLUMN api_name api_endpoint VARCHAR(200) NOT NULL COMMENT 'API端點'
|
||||
'''))
|
||||
db.session.commit()
|
||||
print(" ✓ 已重新命名 api_name → api_endpoint")
|
||||
else:
|
||||
print("\n[3/8] 跳過(api_name 已不存在或已是 api_endpoint)")
|
||||
|
||||
# 4. 新增 prompt_tokens 和 completion_tokens
|
||||
print("\n[4/8] 新增 prompt_tokens 和 completion_tokens...")
|
||||
if 'prompt_tokens' not in current_columns:
|
||||
db.session.execute(text('''
|
||||
ALTER TABLE dt_api_usage_stats
|
||||
ADD COLUMN prompt_tokens INT DEFAULT 0 COMMENT 'Prompt token數' AFTER api_endpoint
|
||||
'''))
|
||||
if 'completion_tokens' not in current_columns:
|
||||
db.session.execute(text('''
|
||||
ALTER TABLE dt_api_usage_stats
|
||||
ADD COLUMN completion_tokens INT DEFAULT 0 COMMENT 'Completion token數' AFTER prompt_tokens
|
||||
'''))
|
||||
db.session.commit()
|
||||
print(" ✓ 已新增 token 細分欄位")
|
||||
|
||||
# 5. 重新命名 token_used → total_tokens
|
||||
if 'token_used' in current_columns:
|
||||
print("\n[5/8] 重新命名 token_used → total_tokens...")
|
||||
db.session.execute(text('''
|
||||
ALTER TABLE dt_api_usage_stats
|
||||
CHANGE COLUMN token_used total_tokens INT DEFAULT 0 COMMENT '總token數'
|
||||
'''))
|
||||
db.session.commit()
|
||||
print(" ✓ 已重新命名 token_used → total_tokens")
|
||||
else:
|
||||
print("\n[5/8] 跳過(token_used 已不存在或已是 total_tokens)")
|
||||
|
||||
# 6. 新增計費相關欄位
|
||||
print("\n[6/8] 新增計費相關欄位...")
|
||||
if 'prompt_unit_price' not in current_columns:
|
||||
db.session.execute(text('''
|
||||
ALTER TABLE dt_api_usage_stats
|
||||
ADD COLUMN prompt_unit_price DECIMAL(10, 8) DEFAULT 0.00000000 COMMENT '單價' AFTER total_tokens
|
||||
'''))
|
||||
if 'prompt_price_unit' not in current_columns:
|
||||
db.session.execute(text('''
|
||||
ALTER TABLE dt_api_usage_stats
|
||||
ADD COLUMN prompt_price_unit VARCHAR(20) DEFAULT 'USD' COMMENT '價格單位' AFTER prompt_unit_price
|
||||
'''))
|
||||
db.session.commit()
|
||||
print(" ✓ 已新增計費欄位")
|
||||
|
||||
# 7. 替換 status 欄位為 success (BOOLEAN)
|
||||
print("\n[7/8] 更新 status 欄位...")
|
||||
if 'status' in current_columns and 'success' not in current_columns:
|
||||
# 先新增 success 欄位
|
||||
db.session.execute(text('''
|
||||
ALTER TABLE dt_api_usage_stats
|
||||
ADD COLUMN success BOOLEAN DEFAULT TRUE COMMENT '是否成功' AFTER response_time_ms
|
||||
'''))
|
||||
|
||||
# 將 status 資料轉換到 success
|
||||
db.session.execute(text('''
|
||||
UPDATE dt_api_usage_stats
|
||||
SET success = (status = 'SUCCESS')
|
||||
'''))
|
||||
|
||||
# 刪除舊的 status 欄位
|
||||
db.session.execute(text('''
|
||||
ALTER TABLE dt_api_usage_stats
|
||||
DROP COLUMN status
|
||||
'''))
|
||||
db.session.commit()
|
||||
print(" ✓ 已將 status 轉換為 success (BOOLEAN)")
|
||||
else:
|
||||
print(" ℹ 跳過(已完成或不需要轉換)")
|
||||
|
||||
# 8. 更新索引
|
||||
print("\n[8/8] 建立索引...")
|
||||
try:
|
||||
db.session.execute(text('''
|
||||
ALTER TABLE dt_api_usage_stats
|
||||
ADD INDEX IF NOT EXISTS idx_api_endpoint (api_endpoint)
|
||||
'''))
|
||||
except Exception as e:
|
||||
if 'Duplicate' not in str(e):
|
||||
raise
|
||||
|
||||
try:
|
||||
db.session.execute(text('''
|
||||
ALTER TABLE dt_api_usage_stats
|
||||
ADD INDEX IF NOT EXISTS idx_success (success)
|
||||
'''))
|
||||
except Exception as e:
|
||||
if 'Duplicate' not in str(e):
|
||||
raise
|
||||
|
||||
db.session.commit()
|
||||
print(" ✓ 已建立索引")
|
||||
|
||||
# 9. 驗證最終結構
|
||||
print("\n" + "=" * 60)
|
||||
print("驗證最終資料表結構")
|
||||
print("=" * 60)
|
||||
|
||||
result = db.session.execute(text('SHOW COLUMNS FROM dt_api_usage_stats'))
|
||||
final_columns = list(result)
|
||||
|
||||
print(f"\n最終欄位列表 (共 {len(final_columns)} 個):")
|
||||
for col in final_columns:
|
||||
print(f" - {col[0]:25} {col[1]:20} NULL={col[2]} Default={col[4]}")
|
||||
|
||||
# 10. 統計資料
|
||||
print("\n" + "=" * 60)
|
||||
print("資料統計")
|
||||
print("=" * 60)
|
||||
|
||||
total_records = db.session.execute(
|
||||
text('SELECT COUNT(*) FROM dt_api_usage_stats')
|
||||
).scalar()
|
||||
print(f"總記錄數: {total_records}")
|
||||
|
||||
if total_records > 0:
|
||||
stats = db.session.execute(text('''
|
||||
SELECT
|
||||
api_endpoint,
|
||||
COUNT(*) as count,
|
||||
SUM(total_tokens) as total_tokens,
|
||||
SUM(cost) as total_cost
|
||||
FROM dt_api_usage_stats
|
||||
GROUP BY api_endpoint
|
||||
''')).fetchall()
|
||||
|
||||
print("\nAPI 使用統計:")
|
||||
for stat in stats:
|
||||
print(f" {stat[0]:40} | {stat[1]:5} 次 | {stat[2]:10} tokens | ${stat[3]:.4f}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✅ Migration 執行完成!")
|
||||
print("=" * 60)
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
print(f"\n❌ Migration 失敗: {str(e)}")
|
||||
print("\n可以使用備份表還原資料:")
|
||||
print(" DROP TABLE dt_api_usage_stats;")
|
||||
print(" CREATE TABLE dt_api_usage_stats AS SELECT * FROM dt_api_usage_stats_backup_20251001;")
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_migration()
|
Reference in New Issue
Block a user