加上登录和角色权限,并用 Vue 控制台区分用户与管理员。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""控制台要看的实时快照。今日积分按产出公式预览,不写入流水。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import Session, selectinload
|
||||
|
||||
from app.clock import utcnow
|
||||
from app.models import ComputeNode, NodeStatus, NodeYield, User, WithdrawalLog, WithdrawalStatus, YieldStatus
|
||||
from app.money import money, to_decimal
|
||||
from app.services.withdrawals import available_balance
|
||||
from app.services.yield_engine import compute_node_yield
|
||||
|
||||
|
||||
def settled_points(session: Session, user_id: int | None = None) -> Decimal:
|
||||
query = select(NodeYield.points_delta).where(NodeYield.status == YieldStatus.SETTLED)
|
||||
if user_id is not None:
|
||||
query = query.where(NodeYield.user_id == user_id)
|
||||
rows = session.scalars(query).all()
|
||||
return money(sum((to_decimal(item) for item in rows), Decimal("0")))
|
||||
|
||||
|
||||
def pending_withdrawal_amount(session: Session) -> Decimal:
|
||||
rows = session.scalars(
|
||||
select(WithdrawalLog.amount).where(WithdrawalLog.status == WithdrawalStatus.PENDING)
|
||||
).all()
|
||||
return money(sum((to_decimal(item) for item in rows), Decimal("0")))
|
||||
|
||||
|
||||
def authorized_amount(session: Session) -> Decimal:
|
||||
rows = session.scalars(select(ComputeNode.purchase_amount)).all()
|
||||
return money(sum((to_decimal(item) for item in rows), Decimal("0")))
|
||||
|
||||
|
||||
def node_counts(session: Session) -> tuple[int, int]:
|
||||
nodes = session.scalar(select(func.count()).select_from(ComputeNode)) or 0
|
||||
online = session.scalar(
|
||||
select(func.count()).select_from(ComputeNode).where(ComputeNode.status == NodeStatus.ONLINE)
|
||||
) or 0
|
||||
return nodes, online
|
||||
|
||||
|
||||
def live_nodes(session: Session, user_id: int | None = None) -> list[dict]:
|
||||
query = select(ComputeNode).options(selectinload(ComputeNode.user)).order_by(ComputeNode.id.desc())
|
||||
if user_id is not None:
|
||||
query = query.where(ComputeNode.user_id == user_id)
|
||||
today = utcnow().date()
|
||||
snapshots = []
|
||||
for node in session.scalars(query).all():
|
||||
computed = compute_node_yield(session, node, today)
|
||||
snapshots.append(
|
||||
{
|
||||
"id": node.id,
|
||||
"sn": node.sn,
|
||||
"user_id": node.user_id,
|
||||
"wallet_address": node.user.wallet_address,
|
||||
"purchase_amount": node.purchase_amount,
|
||||
"license_months": node.license_months,
|
||||
"licensed_at": node.licensed_at,
|
||||
"license_expires_at": node.license_expires_at,
|
||||
"status": node.status,
|
||||
"last_heartbeat_at": node.last_heartbeat_at,
|
||||
"callback_url": node.callback_url,
|
||||
"online_rate": computed.online_rate,
|
||||
"today_points": computed.points_delta if computed.eligible else Decimal("0"),
|
||||
}
|
||||
)
|
||||
return snapshots
|
||||
|
||||
|
||||
def user_rows(session: Session) -> list[dict]:
|
||||
counts = dict(
|
||||
session.execute(select(ComputeNode.user_id, func.count()).group_by(ComputeNode.user_id)).all()
|
||||
)
|
||||
rows = []
|
||||
for user in session.scalars(select(User).order_by(User.id.desc())).all():
|
||||
rows.append(
|
||||
{
|
||||
"id": user.id,
|
||||
"wallet_address": user.wallet_address,
|
||||
"username": user.username,
|
||||
"role": user.role,
|
||||
"created_at": user.created_at,
|
||||
"node_count": int(counts.get(user.id, 0)),
|
||||
"available_points": available_balance(session, user.id),
|
||||
}
|
||||
)
|
||||
return rows
|
||||
Reference in New Issue
Block a user