dashboard.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. """
  2. 仪表盘统计接口
  3. """
  4. from fastapi import APIRouter, Depends
  5. from sqlalchemy.orm import Session
  6. from sqlalchemy import func, and_
  7. from app.database import get_db
  8. from app.api.deps import get_current_admin
  9. from app.models.user import User
  10. from app.models.software import Software
  11. from app.models.card import Card
  12. from app.models.api_log import APILog
  13. from app.models.session import Session as UserSession
  14. from app.background_tasks import get_task_manager
  15. from datetime import datetime, timedelta
  16. from app.utils.timezone import beijing_now
  17. router = APIRouter()
  18. @router.get("/stats")
  19. async def get_dashboard_stats(
  20. db: Session = Depends(get_db),
  21. current_admin = Depends(get_current_admin)
  22. ):
  23. """获取仪表盘统计数据"""
  24. # 用户总数
  25. total_users = db.query(func.count(User.id)).scalar() or 0
  26. # 今日新增用户
  27. today_start = beijing_now().replace(hour=0, minute=0, second=0, microsecond=0)
  28. today_new_users = db.query(func.count(User.id)).filter(
  29. User.created_at >= today_start
  30. ).scalar() or 0
  31. # 软件数量
  32. total_software = db.query(func.count(Software.id)).scalar() or 0
  33. # 卡密总数
  34. total_cards = db.query(func.count(Card.id)).scalar() or 0
  35. # 已激活卡密数
  36. activated_cards = db.query(func.count(Card.id)).filter(
  37. Card.status == 1
  38. ).scalar() or 0
  39. # 未激活卡密数
  40. unactivated_cards = db.query(func.count(Card.id)).filter(
  41. Card.status == 0
  42. ).scalar() or 0
  43. # 日志总数
  44. total_logs = db.query(func.count(APILog.id)).scalar() or 0
  45. # 今日日志数
  46. today_logs = db.query(func.count(APILog.id)).filter(
  47. APILog.created_at >= today_start
  48. ).scalar() or 0
  49. # 有效用户数(未过期)
  50. active_users = db.query(func.count(User.id)).filter(
  51. and_(
  52. User.expire_time.isnot(None),
  53. User.expire_time > beijing_now()
  54. )
  55. ).scalar() or 0
  56. # 过期用户数
  57. expired_users = db.query(func.count(User.id)).filter(
  58. and_(
  59. User.expire_time.isnot(None),
  60. User.expire_time <= beijing_now()
  61. )
  62. ).scalar() or 0
  63. # 最近7天新增用户趋势
  64. seven_days_ago = beijing_now() - timedelta(days=7)
  65. recent_users = db.query(
  66. func.date(User.created_at).label('date'),
  67. func.count(User.id).label('count')
  68. ).filter(
  69. User.created_at >= seven_days_ago
  70. ).group_by(func.date(User.created_at)).all()
  71. user_trend = [
  72. {
  73. "date": str(row.date),
  74. "count": row.count
  75. }
  76. for row in recent_users
  77. ]
  78. # 最近7天日志趋势
  79. recent_logs = db.query(
  80. func.date(APILog.created_at).label('date'),
  81. func.count(APILog.id).label('count')
  82. ).filter(
  83. APILog.created_at >= seven_days_ago
  84. ).group_by(func.date(APILog.created_at)).all()
  85. log_trend = [
  86. {
  87. "date": str(row.date),
  88. "count": row.count
  89. }
  90. for row in recent_logs
  91. ]
  92. # 今日心跳/活跃人数(按会话 last_activity 去重 user_id)
  93. today_start_naive = beijing_now().replace(tzinfo=None, hour=0, minute=0, second=0, microsecond=0)
  94. heartbeat_today_users = db.query(
  95. func.count(func.distinct(UserSession.user_id))
  96. ).filter(
  97. and_(
  98. UserSession.is_active == 1,
  99. UserSession.last_activity >= today_start_naive
  100. )
  101. ).scalar() or 0
  102. heartbeat_today_sessions = db.query(func.count(UserSession.id)).filter(
  103. and_(
  104. UserSession.is_active == 1,
  105. UserSession.last_activity >= today_start_naive
  106. )
  107. ).scalar() or 0
  108. # 获取后台任务状态
  109. task_manager = get_task_manager()
  110. background_tasks_status = {
  111. "running": task_manager.running,
  112. "total_tasks": len(task_manager.tasks),
  113. "active_tasks": sum(1 for task in task_manager.tasks if not task.done()),
  114. "completed_tasks": sum(1 for task in task_manager.tasks if task.done()),
  115. }
  116. return {
  117. "code": 200,
  118. "msg": "成功",
  119. "data": {
  120. "heartbeat": {
  121. "today_users": heartbeat_today_users,
  122. "today_sessions": heartbeat_today_sessions
  123. },
  124. "users": {
  125. "total": total_users,
  126. "today_new": today_new_users,
  127. "active": active_users,
  128. "expired": expired_users,
  129. "trend": user_trend
  130. },
  131. "software": {
  132. "total": total_software
  133. },
  134. "cards": {
  135. "total": total_cards,
  136. "activated": activated_cards,
  137. "unactivated": unactivated_cards
  138. },
  139. "logs": {
  140. "total": total_logs,
  141. "today": today_logs,
  142. "trend": log_trend
  143. },
  144. "background_tasks": background_tasks_status
  145. }
  146. }
  147. @router.get("/background-tasks/status")
  148. async def get_background_tasks_status(
  149. db: Session = Depends(get_db),
  150. current_admin = Depends(get_current_admin)
  151. ):
  152. """获取后台任务状态详情"""
  153. task_manager = get_task_manager()
  154. # 获取各个任务的详细信息
  155. task_details = []
  156. for i, task in enumerate(task_manager.tasks):
  157. task_info = {
  158. "id": i + 1,
  159. "name": f"后台任务-{i + 1}",
  160. "status": "running" if not task.done() else "completed",
  161. "done": task.done(),
  162. "cancelled": task.cancelled(),
  163. }
  164. # 如果任务已完成,获取结果或异常信息
  165. if task.done():
  166. try:
  167. result = task.result()
  168. task_info["result"] = str(result) if result is not None else None
  169. except Exception as e:
  170. task_info["exception"] = str(e)
  171. task_details.append(task_info)
  172. return {
  173. "code": 200,
  174. "msg": "成功",
  175. "data": {
  176. "manager_running": task_manager.running,
  177. "total_tasks": len(task_manager.tasks),
  178. "tasks": task_details
  179. }
  180. }