8b55b9cf55
Co-authored-by: Cursor <cursoragent@cursor.com>
89 lines
2.7 KiB
Python
89 lines
2.7 KiB
Python
"""用户与节点登记。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from decimal import Decimal
|
|
from urllib.parse import urlparse
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.exc import IntegrityError
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.clock import add_months, utcnow
|
|
from app.config import settings
|
|
from app.errors import AppError
|
|
from app.models import ComputeNode, NodeStatus, User, UserRole
|
|
from app.money import money
|
|
from app.services.common import get_user_by_wallet
|
|
|
|
|
|
def register_user(
|
|
session: Session,
|
|
wallet_address: str,
|
|
username: str | None = None,
|
|
password_hash: str | None = None,
|
|
role: UserRole = UserRole.USER,
|
|
) -> User:
|
|
wallet = wallet_address.strip()
|
|
if session.scalar(select(User).where(User.wallet_address == wallet)) is not None:
|
|
raise AppError("钱包地址已注册", 409)
|
|
if username and session.scalar(select(User).where(User.username == username)) is not None:
|
|
raise AppError("用户名已占用", 409)
|
|
user = User(
|
|
wallet_address=wallet,
|
|
username=username,
|
|
password_hash=password_hash,
|
|
role=role,
|
|
created_at=utcnow(),
|
|
)
|
|
session.add(user)
|
|
try:
|
|
session.flush()
|
|
except IntegrityError as exc:
|
|
session.rollback()
|
|
raise AppError("钱包地址或用户名已注册", 409) from exc
|
|
return user
|
|
|
|
|
|
def register_node(
|
|
session: Session,
|
|
wallet_address: str,
|
|
sn: str,
|
|
purchase_amount: Decimal,
|
|
callback_url: str | None = None,
|
|
) -> ComputeNode:
|
|
user = get_user_by_wallet(session, wallet_address.strip())
|
|
serial = sn.strip()
|
|
if session.scalar(select(ComputeNode).where(ComputeNode.sn == serial)) is not None:
|
|
raise AppError("节点序列号已存在", 409)
|
|
callback = _normalize_callback(callback_url)
|
|
started = utcnow()
|
|
node = ComputeNode(
|
|
user_id=user.id,
|
|
sn=serial,
|
|
purchase_amount=money(purchase_amount),
|
|
license_months=settings.license_months,
|
|
licensed_at=started,
|
|
license_expires_at=add_months(started, settings.license_months),
|
|
status=NodeStatus.OFFLINE,
|
|
callback_url=callback,
|
|
created_at=started,
|
|
updated_at=started,
|
|
)
|
|
session.add(node)
|
|
try:
|
|
session.flush()
|
|
except IntegrityError as exc:
|
|
session.rollback()
|
|
raise AppError("节点序列号已存在", 409) from exc
|
|
return node
|
|
|
|
|
|
def _normalize_callback(callback_url: str | None) -> str | None:
|
|
if callback_url is None or callback_url.strip() == "":
|
|
return None
|
|
parsed = urlparse(callback_url.strip())
|
|
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
|
|
raise AppError("callback_url 必须是 http 或 https 地址")
|
|
return callback_url.strip()
|