2025-11-11 21:39:12 +08:00
from flask import request , jsonify , current_app
2025-11-15 23:57:05 +08:00
from datetime import datetime , timedelta , time
from sqlalchemy import func , and_ , not_ , text , case
2025-11-11 21:39:12 +08:00
from app import db
from app . models import Product , License , Device , Version , Ticket
from . import api_bp
2025-11-15 23:57:05 +08:00
from . decorators import require_login , require_admin
2025-11-11 21:39:12 +08:00
@api_bp.route ( ' /statistics/overview ' , methods = [ ' GET ' ] )
2025-11-15 23:57:05 +08:00
@require_login
2025-11-11 21:39:12 +08:00
def get_overview_stats ( ) :
""" 获取总览统计 """
try :
2025-11-15 23:57:05 +08:00
# 优化:使用单个查询获取所有产品统计
product_stats = db . session . query (
func . count ( Product . product_id ) . label ( ' total ' ) ,
func . sum ( case ( ( Product . status == 1 , 1 ) , else_ = 0 ) ) . label ( ' active ' )
) . first ( )
total_products = product_stats . total or 0
active_products = product_stats . active or 0
# 优化:使用单个查询获取所有卡密统计
license_stats = db . session . query (
func . count ( License . license_id ) . label ( ' total ' ) ,
func . sum ( case ( ( License . status == 1 , 1 ) , else_ = 0 ) ) . label ( ' active ' ) ,
func . sum ( case ( ( License . type == 0 , 1 ) , else_ = 0 ) ) . label ( ' trial ' )
) . first ( )
total_licenses = license_stats . total or 0
active_licenses = license_stats . active or 0
trial_licenses = license_stats . trial or 0
# 优化:使用单个查询获取设备统计
device_stats = db . session . query (
func . count ( Device . device_id ) . label ( ' total ' )
) . first ( )
total_devices = device_stats . total or 0
# 在线设备统计( 7天内验证过)
start_date = datetime . utcnow ( ) - timedelta ( days = 7 )
2025-11-11 21:39:12 +08:00
online_devices = db . session . execute (
text ( " SELECT COUNT(*) FROM device WHERE last_verify_time IS NOT NULL AND last_verify_time >= :start_date " ) ,
2025-11-15 23:57:05 +08:00
{ " start_date " : start_date }
) . scalar ( ) or 0
# 优化:使用单个查询获取激活统计
today = datetime . utcnow ( ) . date ( )
today_start = datetime . combine ( today , time . min )
today_end = datetime . combine ( today , time . max )
# 今日激活数( 使用日期范围查询, 兼容SQLite)
today_activations = db . session . query ( func . count ( License . license_id ) ) . filter (
License . activate_time . isnot ( None ) ,
License . activate_time > = today_start ,
License . activate_time < = today_end
) . scalar ( ) or 0
# 本周激活数
week_activations = db . session . query ( func . count ( License . license_id ) ) . filter (
License . activate_time . isnot ( None ) ,
License . activate_time > = start_date
) . scalar ( ) or 0
2025-11-11 21:39:12 +08:00
# 工单统计
2025-11-15 23:57:05 +08:00
total_tickets = db . session . query ( func . count ( Ticket . ticket_id ) ) . scalar ( ) or 0
2025-11-11 21:39:12 +08:00
return jsonify ( {
' success ' : True ,
' data ' : {
' products ' : {
' total ' : total_products ,
' active ' : active_products
} ,
' licenses ' : {
' total ' : total_licenses ,
' active ' : active_licenses ,
' trial ' : trial_licenses ,
' formal ' : total_licenses - trial_licenses
} ,
' devices ' : {
' total ' : total_devices ,
' online ' : online_devices ,
' offline ' : total_devices - online_devices
} ,
' tickets ' : {
' total ' : total_tickets
} ,
' activations ' : {
' today ' : today_activations ,
' week ' : week_activations
}
}
} )
except Exception as e :
current_app . logger . error ( f " 获取总览统计失败: { str ( e ) } " )
return jsonify ( { ' success ' : False , ' message ' : ' 服务器内部错误 ' } ) , 500
@api_bp.route ( ' /statistics/activations ' , methods = [ ' GET ' ] )
2025-11-15 23:57:05 +08:00
@require_login
2025-11-11 21:39:12 +08:00
def get_activation_trend ( ) :
""" 获取激活趋势 """
try :
days = request . args . get ( ' days ' , 30 , type = int )
days = min ( days , 365 ) # 最多查询一年
start_date = datetime . utcnow ( ) - timedelta ( days = days )
# 按日期分组统计激活数
result = db . session . execute (
text ( " SELECT DATE(activate_time) as date, COUNT(license_id) as count FROM license WHERE activate_time IS NOT NULL AND activate_time >= :start_date GROUP BY DATE(activate_time) ORDER BY date " ) ,
{ " start_date " : start_date }
) . fetchall ( )
# 填充缺失的日期
activation_data = [ ]
current_date = start_date . date ( )
end_date = datetime . utcnow ( ) . date ( )
result_dict = { str ( r . date ) : r . count for r in result }
while current_date < = end_date :
activation_data . append ( {
' date ' : str ( current_date ) ,
' count ' : result_dict . get ( str ( current_date ) , 0 )
} )
current_date + = timedelta ( days = 1 )
return jsonify ( {
' success ' : True ,
' data ' : {
' activations ' : activation_data ,
' period ' : f ' { days } 天 '
}
} )
except Exception as e :
current_app . logger . error ( f " 获取激活趋势失败: { str ( e ) } " )
return jsonify ( { ' success ' : False , ' message ' : ' 服务器内部错误 ' } ) , 500
@api_bp.route ( ' /statistics/products ' , methods = [ ' GET ' ] )
2025-11-15 23:57:05 +08:00
@require_login
2025-11-11 21:39:12 +08:00
def get_product_stats ( ) :
""" 获取产品统计信息 """
try :
# 获取产品分布数据
product_stats = db . session . query (
Product . product_name ,
func . count ( License . license_id ) . label ( ' license_count ' ) ,
func . count ( Device . device_id ) . label ( ' device_count ' )
) . outerjoin ( License , Product . product_id == License . product_id ) \
. outerjoin ( Device , Product . product_id == Device . product_id ) \
. group_by ( Product . product_id , Product . product_name ) \
. order_by ( func . count ( License . license_id ) . desc ( ) ) \
. all ( )
# 转换为字典列表
products_data = [ ]
for product in product_stats :
products_data . append ( {
' product_name ' : product . product_name ,
' license_count ' : product . license_count or 0 ,
' device_count ' : product . device_count or 0
} )
return jsonify ( {
' success ' : True ,
' data ' : {
' products ' : products_data
}
} )
except Exception as e :
current_app . logger . error ( f " 获取产品统计失败: { str ( e ) } " )
2025-11-15 23:57:05 +08:00
return jsonify ( { ' success ' : False , ' message ' : ' 服务器内部错误 ' } ) , 500