改用API驗證

This commit is contained in:
beabigegg
2025-10-02 17:13:24 +08:00
parent 0a89c19fc9
commit adecdf0cce
48 changed files with 6136 additions and 1239 deletions

View 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()