"""控制台要看的实时快照。今日积分按产出公式预览,不写入流水。""" 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