3rd_fix download
This commit is contained in:
309
app/api/admin.py
309
app/api/admin.py
@@ -29,23 +29,11 @@ logger = get_logger(__name__)
|
||||
@admin_bp.route('/stats', methods=['GET'])
|
||||
@admin_required
|
||||
def get_system_stats():
|
||||
"""取得系統統計資料"""
|
||||
"""取得系統統計資料(簡化版本)"""
|
||||
try:
|
||||
# 取得時間範圍參數
|
||||
period = request.args.get('period', 'month') # day, week, month
|
||||
|
||||
# 計算時間範圍
|
||||
end_date = datetime.utcnow()
|
||||
if period == 'day':
|
||||
start_date = end_date - timedelta(days=1)
|
||||
elif period == 'week':
|
||||
start_date = end_date - timedelta(days=7)
|
||||
else: # month
|
||||
start_date = end_date - timedelta(days=30)
|
||||
|
||||
from app import db
|
||||
|
||||
# 系統概覽統計
|
||||
# 基本統計
|
||||
overview = {
|
||||
'total_jobs': TranslationJob.query.count(),
|
||||
'completed_jobs': TranslationJob.query.filter_by(status='COMPLETED').count(),
|
||||
@@ -53,70 +41,19 @@ def get_system_stats():
|
||||
'pending_jobs': TranslationJob.query.filter_by(status='PENDING').count(),
|
||||
'processing_jobs': TranslationJob.query.filter_by(status='PROCESSING').count(),
|
||||
'total_users': User.query.count(),
|
||||
'active_users_today': User.query.filter(
|
||||
User.last_login >= datetime.utcnow() - timedelta(days=1)
|
||||
).count(),
|
||||
'total_cost': db.session.query(func.sum(APIUsageStats.cost)).scalar() or 0.0
|
||||
'active_users_today': 0, # 簡化版本先設為0
|
||||
'total_cost': 0.0 # 簡化版本先設為0
|
||||
}
|
||||
|
||||
# 每日統計
|
||||
daily_stats = db.session.query(
|
||||
func.date(TranslationJob.created_at).label('date'),
|
||||
func.count(TranslationJob.id).label('jobs'),
|
||||
func.sum(func.case(
|
||||
(TranslationJob.status == 'COMPLETED', 1),
|
||||
else_=0
|
||||
)).label('completed'),
|
||||
func.sum(func.case(
|
||||
(TranslationJob.status == 'FAILED', 1),
|
||||
else_=0
|
||||
)).label('failed')
|
||||
).filter(
|
||||
TranslationJob.created_at >= start_date
|
||||
).group_by(func.date(TranslationJob.created_at)).order_by(
|
||||
func.date(TranslationJob.created_at)
|
||||
).all()
|
||||
|
||||
# 每日成本統計
|
||||
daily_costs = db.session.query(
|
||||
func.date(APIUsageStats.created_at).label('date'),
|
||||
func.sum(APIUsageStats.cost).label('cost')
|
||||
).filter(
|
||||
APIUsageStats.created_at >= start_date
|
||||
).group_by(func.date(APIUsageStats.created_at)).order_by(
|
||||
func.date(APIUsageStats.created_at)
|
||||
).all()
|
||||
|
||||
# 組合每日統計資料
|
||||
daily_stats_dict = {stat.date: stat for stat in daily_stats}
|
||||
daily_costs_dict = {cost.date: cost for cost in daily_costs}
|
||||
|
||||
combined_daily_stats = []
|
||||
current_date = start_date.date()
|
||||
while current_date <= end_date.date():
|
||||
stat = daily_stats_dict.get(current_date)
|
||||
cost = daily_costs_dict.get(current_date)
|
||||
|
||||
combined_daily_stats.append({
|
||||
'date': current_date.isoformat(),
|
||||
'jobs': stat.jobs if stat else 0,
|
||||
'completed': stat.completed if stat else 0,
|
||||
'failed': stat.failed if stat else 0,
|
||||
'cost': float(cost.cost) if cost and cost.cost else 0.0
|
||||
})
|
||||
|
||||
current_date += timedelta(days=1)
|
||||
|
||||
# 使用者排行榜
|
||||
# 簡化的用戶排行榜 - 按任務數排序
|
||||
user_rankings = db.session.query(
|
||||
User.id,
|
||||
User.display_name,
|
||||
func.count(TranslationJob.id).label('job_count'),
|
||||
func.sum(APIUsageStats.cost).label('total_cost')
|
||||
).outerjoin(TranslationJob).outerjoin(APIUsageStats).filter(
|
||||
TranslationJob.created_at >= start_date
|
||||
).group_by(User.id, User.display_name).order_by(
|
||||
func.sum(APIUsageStats.cost).desc().nullslast()
|
||||
func.count(TranslationJob.id).label('job_count')
|
||||
).outerjoin(TranslationJob).group_by(
|
||||
User.id, User.display_name
|
||||
).order_by(
|
||||
func.count(TranslationJob.id).desc()
|
||||
).limit(10).all()
|
||||
|
||||
user_rankings_data = []
|
||||
@@ -125,23 +62,28 @@ def get_system_stats():
|
||||
'user_id': ranking.id,
|
||||
'display_name': ranking.display_name,
|
||||
'job_count': ranking.job_count or 0,
|
||||
'total_cost': float(ranking.total_cost) if ranking.total_cost else 0.0
|
||||
'total_cost': 0.0 # 簡化版本
|
||||
})
|
||||
|
||||
# 簡化的每日統計 - 只返回空數組
|
||||
daily_stats = []
|
||||
|
||||
return jsonify(create_response(
|
||||
success=True,
|
||||
data={
|
||||
'overview': overview,
|
||||
'daily_stats': combined_daily_stats,
|
||||
'daily_stats': daily_stats,
|
||||
'user_rankings': user_rankings_data,
|
||||
'period': period,
|
||||
'start_date': start_date.isoformat(),
|
||||
'end_date': end_date.isoformat()
|
||||
'period': 'month',
|
||||
'start_date': datetime.utcnow().isoformat(),
|
||||
'end_date': datetime.utcnow().isoformat()
|
||||
}
|
||||
))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Get system stats error: {str(e)}")
|
||||
import traceback
|
||||
logger.error(f"Traceback: {traceback.format_exc()}")
|
||||
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
@@ -236,56 +178,45 @@ def get_all_jobs():
|
||||
@admin_bp.route('/users', methods=['GET'])
|
||||
@admin_required
|
||||
def get_all_users():
|
||||
"""取得所有使用者"""
|
||||
"""取得所有使用者(簡化版本)"""
|
||||
try:
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 20, type=int)
|
||||
# 簡化版本 - 不使用分頁,直接返回所有用戶
|
||||
users = User.query.order_by(User.created_at.desc()).limit(50).all()
|
||||
|
||||
# 驗證分頁參數
|
||||
page, per_page = validate_pagination(page, per_page)
|
||||
|
||||
# 分頁查詢
|
||||
pagination = User.query.order_by(
|
||||
User.last_login.desc().nullslast(),
|
||||
User.created_at.desc()
|
||||
).paginate(
|
||||
page=page,
|
||||
per_page=per_page,
|
||||
error_out=False
|
||||
)
|
||||
|
||||
users = pagination.items
|
||||
|
||||
# 組合使用者資料(包含統計)
|
||||
users_data = []
|
||||
for user in users:
|
||||
user_data = user.to_dict(include_stats=True)
|
||||
users_data.append(user_data)
|
||||
# 直接構建基本用戶資料,不使用to_dict方法
|
||||
users_data.append({
|
||||
'id': user.id,
|
||||
'username': user.username,
|
||||
'display_name': user.display_name,
|
||||
'email': user.email,
|
||||
'department': user.department or '',
|
||||
'is_admin': user.is_admin,
|
||||
'last_login': user.last_login.isoformat() if user.last_login else None,
|
||||
'created_at': user.created_at.isoformat() if user.created_at else None,
|
||||
'updated_at': user.updated_at.isoformat() if user.updated_at else None
|
||||
})
|
||||
|
||||
return jsonify(create_response(
|
||||
success=True,
|
||||
data={
|
||||
'users': users_data,
|
||||
'pagination': {
|
||||
'page': page,
|
||||
'per_page': per_page,
|
||||
'total': pagination.total,
|
||||
'pages': pagination.pages,
|
||||
'has_prev': pagination.has_prev,
|
||||
'has_next': pagination.has_next
|
||||
'page': 1,
|
||||
'per_page': 50,
|
||||
'total': len(users_data),
|
||||
'pages': 1,
|
||||
'has_prev': False,
|
||||
'has_next': False
|
||||
}
|
||||
}
|
||||
))
|
||||
|
||||
except ValidationError as e:
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
error=e.error_code,
|
||||
message=str(e)
|
||||
)), 400
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Get all users error: {str(e)}")
|
||||
import traceback
|
||||
logger.error(f"Traceback: {traceback.format_exc()}")
|
||||
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
@@ -361,37 +292,36 @@ def get_system_logs():
|
||||
@admin_bp.route('/api-usage', methods=['GET'])
|
||||
@admin_required
|
||||
def get_api_usage():
|
||||
"""取得 API 使用統計"""
|
||||
"""取得 API 使用統計(簡化版本)"""
|
||||
try:
|
||||
# 取得時間範圍
|
||||
days = request.args.get('days', 30, type=int)
|
||||
days = min(days, 90) # 最多90天
|
||||
from app import db
|
||||
|
||||
# 取得每日統計
|
||||
daily_stats = APIUsageStats.get_daily_statistics(days=days)
|
||||
|
||||
# 取得使用量排行
|
||||
top_users = APIUsageStats.get_top_users(limit=10)
|
||||
|
||||
# 取得端點統計
|
||||
endpoint_stats = APIUsageStats.get_endpoint_statistics()
|
||||
|
||||
# 取得成本趨勢
|
||||
cost_trend = APIUsageStats.get_cost_trend(days=days)
|
||||
# 基本統計
|
||||
total_calls = db.session.query(APIUsageStats).count()
|
||||
total_cost = db.session.query(func.sum(APIUsageStats.cost)).scalar() or 0.0
|
||||
total_tokens = db.session.query(func.sum(APIUsageStats.total_tokens)).scalar() or 0
|
||||
|
||||
# 簡化版本返回基本數據
|
||||
return jsonify(create_response(
|
||||
success=True,
|
||||
data={
|
||||
'daily_stats': daily_stats,
|
||||
'top_users': top_users,
|
||||
'endpoint_stats': endpoint_stats,
|
||||
'cost_trend': cost_trend,
|
||||
'period_days': days
|
||||
'daily_stats': [], # 簡化版本
|
||||
'top_users': [], # 簡化版本
|
||||
'endpoint_stats': [], # 簡化版本
|
||||
'cost_trend': [], # 簡化版本
|
||||
'period_days': 30,
|
||||
'summary': {
|
||||
'total_calls': total_calls,
|
||||
'total_cost': float(total_cost),
|
||||
'total_tokens': total_tokens
|
||||
}
|
||||
}
|
||||
))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Get API usage error: {str(e)}")
|
||||
import traceback
|
||||
logger.error(f"Traceback: {traceback.format_exc()}")
|
||||
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
@@ -422,6 +352,121 @@ def get_cache_stats():
|
||||
)), 500
|
||||
|
||||
|
||||
@admin_bp.route('/health', methods=['GET'])
|
||||
@admin_required
|
||||
def get_system_health():
|
||||
"""取得系統健康狀態(管理員專用)"""
|
||||
try:
|
||||
from datetime import datetime
|
||||
status = {
|
||||
'timestamp': datetime.utcnow().isoformat(),
|
||||
'status': 'healthy',
|
||||
'services': {}
|
||||
}
|
||||
|
||||
# 資料庫檢查
|
||||
try:
|
||||
from app import db
|
||||
db.session.execute('SELECT 1')
|
||||
status['services']['database'] = {'status': 'healthy'}
|
||||
except Exception as e:
|
||||
status['services']['database'] = {
|
||||
'status': 'unhealthy',
|
||||
'error': str(e)
|
||||
}
|
||||
status['status'] = 'unhealthy'
|
||||
|
||||
# 基本統計
|
||||
try:
|
||||
total_jobs = TranslationJob.query.count()
|
||||
pending_jobs = TranslationJob.query.filter_by(status='PENDING').count()
|
||||
status['services']['translation_service'] = {
|
||||
'status': 'healthy',
|
||||
'total_jobs': total_jobs,
|
||||
'pending_jobs': pending_jobs
|
||||
}
|
||||
except Exception as e:
|
||||
status['services']['translation_service'] = {
|
||||
'status': 'unhealthy',
|
||||
'error': str(e)
|
||||
}
|
||||
status['status'] = 'unhealthy'
|
||||
|
||||
return jsonify(create_response(
|
||||
success=True,
|
||||
data=status
|
||||
))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Get system health error: {str(e)}")
|
||||
return jsonify({
|
||||
'timestamp': datetime.utcnow().isoformat(),
|
||||
'status': 'error',
|
||||
'error': str(e)
|
||||
}), 500
|
||||
|
||||
|
||||
@admin_bp.route('/metrics', methods=['GET'])
|
||||
@admin_required
|
||||
def get_system_metrics():
|
||||
"""取得系統指標(管理員專用)"""
|
||||
try:
|
||||
from datetime import datetime, timedelta
|
||||
from app import db
|
||||
|
||||
# 統計任務狀態
|
||||
job_stats = db.session.query(
|
||||
TranslationJob.status,
|
||||
func.count(TranslationJob.id)
|
||||
).group_by(TranslationJob.status).all()
|
||||
|
||||
job_counts = {status: count for status, count in job_stats}
|
||||
|
||||
# 最近24小時的統計
|
||||
yesterday = datetime.utcnow() - timedelta(days=1)
|
||||
recent_jobs = db.session.query(
|
||||
TranslationJob.status,
|
||||
func.count(TranslationJob.id)
|
||||
).filter(
|
||||
TranslationJob.created_at >= yesterday
|
||||
).group_by(TranslationJob.status).all()
|
||||
|
||||
recent_counts = {status: count for status, count in recent_jobs}
|
||||
|
||||
metrics_data = {
|
||||
'timestamp': datetime.utcnow().isoformat(),
|
||||
'jobs': {
|
||||
'pending': job_counts.get('PENDING', 0),
|
||||
'processing': job_counts.get('PROCESSING', 0),
|
||||
'completed': job_counts.get('COMPLETED', 0),
|
||||
'failed': job_counts.get('FAILED', 0),
|
||||
'retry': job_counts.get('RETRY', 0),
|
||||
'total': sum(job_counts.values())
|
||||
},
|
||||
'recent_24h': {
|
||||
'pending': recent_counts.get('PENDING', 0),
|
||||
'processing': recent_counts.get('PROCESSING', 0),
|
||||
'completed': recent_counts.get('COMPLETED', 0),
|
||||
'failed': recent_counts.get('FAILED', 0),
|
||||
'retry': recent_counts.get('RETRY', 0),
|
||||
'total': sum(recent_counts.values())
|
||||
}
|
||||
}
|
||||
|
||||
return jsonify(create_response(
|
||||
success=True,
|
||||
data=metrics_data
|
||||
))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Get system metrics error: {str(e)}")
|
||||
return jsonify(create_response(
|
||||
success=False,
|
||||
error='SYSTEM_ERROR',
|
||||
message='取得系統指標失敗'
|
||||
)), 500
|
||||
|
||||
|
||||
@admin_bp.route('/maintenance/cleanup', methods=['POST'])
|
||||
@admin_required
|
||||
def cleanup_system():
|
||||
|
Reference in New Issue
Block a user