f329f647c7
Co-authored-by: Cursor <cursoragent@cursor.com>
118 lines
3.7 KiB
Python
118 lines
3.7 KiB
Python
"""节点授权订单。付款凭证先入库,管理员通过后才开通节点。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import secrets
|
|
|
|
from sqlalchemy import case, select
|
|
from sqlalchemy.exc import IntegrityError
|
|
from sqlalchemy.orm import Session, selectinload
|
|
|
|
from app.clock import utcnow
|
|
from app.config import settings
|
|
from app.errors import AppError
|
|
from app.models import LicenseOrder, OrderStatus
|
|
from app.money import money
|
|
from app.services.registry import register_node
|
|
|
|
|
|
def place_order(session: Session, user_id: int, tx_id: str) -> LicenseOrder:
|
|
digest = tx_id.strip()
|
|
if session.scalar(select(LicenseOrder.id).where(LicenseOrder.tx_id == digest)) is not None:
|
|
raise AppError("这笔交易已经提交过", 409)
|
|
order = LicenseOrder(
|
|
user_id=user_id,
|
|
amount=money(settings.license_price),
|
|
asset_symbol=settings.license_asset.strip().upper() or "USDT",
|
|
status=OrderStatus.PENDING,
|
|
tx_id=digest,
|
|
created_at=utcnow(),
|
|
)
|
|
session.add(order)
|
|
try:
|
|
session.flush()
|
|
except IntegrityError as exc:
|
|
session.rollback()
|
|
raise AppError("这笔交易已经提交过", 409) from exc
|
|
return order
|
|
|
|
|
|
def approve_order(session: Session, order_id: int) -> LicenseOrder:
|
|
order = _pending_order(session, order_id)
|
|
token = secrets.token_urlsafe(24)
|
|
node = register_node(
|
|
session,
|
|
order.user.wallet_address,
|
|
f"HN{order.id:06d}",
|
|
order.amount,
|
|
node_token=token,
|
|
)
|
|
order.status = OrderStatus.APPROVED
|
|
order.node = node
|
|
order.reviewed_at = utcnow()
|
|
session.flush()
|
|
return order
|
|
|
|
|
|
def reject_order(session: Session, order_id: int, note: str | None = None) -> LicenseOrder:
|
|
order = _pending_order(session, order_id)
|
|
order.status = OrderStatus.REJECTED
|
|
order.note = note
|
|
order.reviewed_at = utcnow()
|
|
session.flush()
|
|
return order
|
|
|
|
|
|
def list_orders(session: Session, user_id: int | None = None) -> list[LicenseOrder]:
|
|
pending_first = case((LicenseOrder.status == OrderStatus.PENDING, 0), else_=1)
|
|
query = select(LicenseOrder).options(selectinload(LicenseOrder.user), selectinload(LicenseOrder.node))
|
|
if user_id is not None:
|
|
query = query.where(LicenseOrder.user_id == user_id)
|
|
return list(session.scalars(query.order_by(pending_first, LicenseOrder.id.desc())).all())
|
|
|
|
|
|
def render_client(sn: str, token: str, base_url: str) -> str:
|
|
config = json.dumps({"sn": sn, "token": token, "base": base_url.rstrip("/")}, ensure_ascii=True)
|
|
return f"""# HyperNode 挂机客户端。这个文件里有节点 Token,不要发给别人。
|
|
import json
|
|
import time
|
|
import urllib.error
|
|
import urllib.request
|
|
|
|
CONFIG = json.loads({config!r})
|
|
|
|
|
|
def beat():
|
|
body = json.dumps({{"sn": CONFIG["sn"], "token": CONFIG["token"]}}).encode()
|
|
request = urllib.request.Request(
|
|
CONFIG["base"] + "/api/v1/node/heartbeat",
|
|
data=body,
|
|
headers={{"Content-Type": "application/json"}},
|
|
)
|
|
with urllib.request.urlopen(request, timeout=20) as response:
|
|
print(response.read().decode())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
while True:
|
|
try:
|
|
beat()
|
|
except (urllib.error.URLError, TimeoutError, OSError) as exc:
|
|
print(exc)
|
|
time.sleep(60)
|
|
"""
|
|
|
|
|
|
def _pending_order(session: Session, order_id: int) -> LicenseOrder:
|
|
order = session.scalar(
|
|
select(LicenseOrder)
|
|
.options(selectinload(LicenseOrder.user), selectinload(LicenseOrder.node))
|
|
.where(LicenseOrder.id == order_id)
|
|
)
|
|
if order is None:
|
|
raise AppError("订单不存在", 404)
|
|
if order.status != OrderStatus.PENDING:
|
|
raise AppError("只有待审核的订单可以处理", 409)
|
|
return order
|