mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
refactor: 移除 Python 后端源码,全面迁移至 Rust gateway 架构
- 删除全部 Python 源码 (src/) 及 Alembic 迁移脚本,归档至 _deprecated_py_src/ - 重构 Rust gateway ai_pipeline: 拆分 planner/finalize 模块,新增 contracts/adaptation 层 - 重组 handlers 模块为 admin/public/proxy/internal/shared 子模块结构 - 新增 executor 模块,引入 Rust 原生数据库迁移 (aether-data/migrations) - 简化 CI/Docker 构建流程,移除 base image 二级构建,统一为单一 app image - 移除 Python 相关基础设施文件 (entrypoint.sh, gunicorn_conf.py, Dockerfile.base)
This commit is contained in:
48
_deprecated_py_src/api/wallet/__init__.py
Normal file
48
_deprecated_py_src/api/wallet/__init__.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""Wallet API routes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter
|
||||
from starlette.routing import BaseRoute
|
||||
|
||||
from .routes import router as wallet_router
|
||||
|
||||
_RUST_OWNED_WALLET_ROUTE_SIGNATURES = frozenset(
|
||||
{
|
||||
("GET", "/api/wallet/balance"),
|
||||
("GET", "/api/wallet/transactions"),
|
||||
("GET", "/api/wallet/flow"),
|
||||
("GET", "/api/wallet/today-cost"),
|
||||
("GET", "/api/wallet/recharge"),
|
||||
("POST", "/api/wallet/recharge"),
|
||||
("GET", "/api/wallet/recharge/{order_id}"),
|
||||
("GET", "/api/wallet/refunds"),
|
||||
("POST", "/api/wallet/refunds"),
|
||||
("GET", "/api/wallet/refunds/{refund_id}"),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _route_is_rust_owned(route: BaseRoute) -> bool:
|
||||
path = getattr(route, "path", None)
|
||||
methods = getattr(route, "methods", None)
|
||||
if not isinstance(path, str) or not methods:
|
||||
return False
|
||||
return any(
|
||||
(method, path) in _RUST_OWNED_WALLET_ROUTE_SIGNATURES
|
||||
for method in methods
|
||||
if method not in {"HEAD", "OPTIONS"}
|
||||
)
|
||||
|
||||
|
||||
def _build_python_wallet_router() -> APIRouter:
|
||||
router = APIRouter()
|
||||
router.include_router(wallet_router)
|
||||
router.routes = [route for route in router.routes if not _route_is_rust_owned(route)]
|
||||
return router
|
||||
|
||||
|
||||
python_wallet_router = _build_python_wallet_router()
|
||||
router = python_wallet_router
|
||||
|
||||
__all__ = ["python_wallet_router", "router"]
|
||||
576
_deprecated_py_src/api/wallet/routes.py
Normal file
576
_deprecated_py_src/api/wallet/routes.py
Normal file
@@ -0,0 +1,576 @@
|
||||
"""用户钱包与退款接口。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import date, datetime, timezone
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from fastapi import APIRouter, Depends, Query, Request
|
||||
from fastapi.concurrency import run_in_threadpool
|
||||
from pydantic import BaseModel, Field, ValidationError
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.api.base.authenticated_adapter import AuthenticatedApiAdapter
|
||||
from src.api.base.context import ApiRequestContext
|
||||
from src.api.base.pipeline import get_pipeline
|
||||
from src.api.serializers import (
|
||||
safe_gateway_response,
|
||||
serialize_payment_order,
|
||||
serialize_wallet_daily_usage,
|
||||
serialize_wallet_payload,
|
||||
serialize_wallet_refund,
|
||||
serialize_wallet_transaction,
|
||||
)
|
||||
from src.core.exceptions import InvalidRequestException, NotFoundException, translate_pydantic_error
|
||||
from src.database import get_db, get_db_context
|
||||
from src.models.database import (
|
||||
PaymentOrder,
|
||||
RefundRequest,
|
||||
User,
|
||||
Wallet,
|
||||
WalletDailyUsageLedger,
|
||||
WalletTransaction,
|
||||
)
|
||||
from src.services.payment import PaymentService
|
||||
from src.services.wallet import WalletDailyUsageLedgerService, WalletService
|
||||
|
||||
router = APIRouter(prefix="/api/wallet", tags=["Wallet"])
|
||||
pipeline = get_pipeline()
|
||||
|
||||
|
||||
def _create_recharge_order_sync(user_id: str, req: CreateRechargePayload) -> dict[str, Any]:
|
||||
with get_db_context() as db:
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
try:
|
||||
order = PaymentService.create_recharge_order(
|
||||
db,
|
||||
user=user,
|
||||
amount_usd=req.amount_usd,
|
||||
payment_method=req.payment_method,
|
||||
pay_amount=req.pay_amount,
|
||||
pay_currency=req.pay_currency,
|
||||
exchange_rate=req.exchange_rate,
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise InvalidRequestException(str(exc)) from exc
|
||||
|
||||
db.commit()
|
||||
db.refresh(order)
|
||||
return {
|
||||
"order": serialize_payment_order(order, sanitize_gateway_response=True),
|
||||
"payment_instructions": safe_gateway_response(order.gateway_response),
|
||||
}
|
||||
|
||||
|
||||
def _list_recharge_orders_sync(user_id: str, limit: int, offset: int) -> dict[str, Any]:
|
||||
with get_db_context() as db:
|
||||
items, total, _changed = PaymentService.list_user_orders(
|
||||
db,
|
||||
user_id=user_id,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
return {
|
||||
"items": [
|
||||
serialize_payment_order(item, sanitize_gateway_response=True) for item in items
|
||||
],
|
||||
"total": total,
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
}
|
||||
|
||||
|
||||
def _get_recharge_order_sync(user_id: str, order_id: str) -> dict[str, Any]:
|
||||
with get_db_context() as db:
|
||||
order = PaymentService.get_user_order(db, user_id=user_id, order_id=order_id)
|
||||
if order is None:
|
||||
raise NotFoundException("Payment order not found")
|
||||
PaymentService.refresh_order_status(order)
|
||||
return {"order": serialize_payment_order(order, sanitize_gateway_response=True)}
|
||||
|
||||
|
||||
def _list_refunds_sync(user_id: str, limit: int, offset: int) -> dict[str, Any]:
|
||||
with get_db_context() as db:
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
existing_wallet = WalletService.get_wallet(db, user_id=user.id)
|
||||
wallet = existing_wallet or WalletService.get_or_create_wallet(db, user=user)
|
||||
if wallet is None:
|
||||
return {"items": [], "total": 0, "limit": limit, "offset": offset}
|
||||
|
||||
if existing_wallet is None:
|
||||
db.commit()
|
||||
db.refresh(wallet)
|
||||
|
||||
base_query = db.query(RefundRequest).filter(RefundRequest.wallet_id == wallet.id)
|
||||
total = base_query.count()
|
||||
items = (
|
||||
base_query.order_by(RefundRequest.created_at.desc()).offset(offset).limit(limit).all()
|
||||
)
|
||||
return {
|
||||
"items": [serialize_wallet_refund(item) for item in items],
|
||||
"total": total,
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
}
|
||||
|
||||
|
||||
def _create_refund_sync(user_id: str, req: CreateRefundPayload) -> dict[str, Any]:
|
||||
with get_db_context() as db:
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
wallet = WalletService.get_or_create_wallet(db, user=user)
|
||||
if wallet is None:
|
||||
raise InvalidRequestException("当前账户尚未开通钱包,无法申请退款")
|
||||
|
||||
payment_order = None
|
||||
source_type = req.source_type or "wallet_balance"
|
||||
source_id = req.source_id
|
||||
refund_mode = req.refund_mode or "offline_payout"
|
||||
|
||||
if req.payment_order_id:
|
||||
payment_order = (
|
||||
db.query(PaymentOrder)
|
||||
.filter(
|
||||
PaymentOrder.id == req.payment_order_id, PaymentOrder.wallet_id == wallet.id
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if payment_order is None:
|
||||
raise NotFoundException("Payment order not found")
|
||||
source_type = "payment_order"
|
||||
source_id = payment_order.id
|
||||
refund_mode = req.refund_mode or _default_refund_mode_for_order(payment_order)
|
||||
|
||||
try:
|
||||
refund = WalletService.create_refund_request(
|
||||
db,
|
||||
wallet=wallet,
|
||||
user_id=user.id,
|
||||
amount_usd=req.amount_usd,
|
||||
refund_no=_build_refund_no(),
|
||||
source_type=source_type,
|
||||
source_id=source_id,
|
||||
refund_mode=refund_mode,
|
||||
payment_order=payment_order,
|
||||
reason=req.reason,
|
||||
requested_by=user.id,
|
||||
idempotency_key=req.idempotency_key,
|
||||
)
|
||||
db.commit()
|
||||
db.refresh(refund)
|
||||
return serialize_wallet_refund(refund)
|
||||
except ValueError as exc:
|
||||
db.rollback()
|
||||
raise InvalidRequestException(str(exc)) from exc
|
||||
except IntegrityError:
|
||||
db.rollback()
|
||||
if req.idempotency_key:
|
||||
existing = (
|
||||
db.query(RefundRequest)
|
||||
.filter(
|
||||
RefundRequest.idempotency_key == req.idempotency_key,
|
||||
RefundRequest.user_id == user.id,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if existing is not None:
|
||||
return serialize_wallet_refund(existing)
|
||||
raise InvalidRequestException("退款申请重复,请勿重复提交")
|
||||
|
||||
|
||||
class CreateRefundPayload(BaseModel):
|
||||
amount_usd: float = Field(..., gt=0, allow_inf_nan=False)
|
||||
payment_order_id: str | None = None
|
||||
source_type: str | None = Field(default=None, max_length=30)
|
||||
source_id: str | None = Field(default=None, max_length=100)
|
||||
refund_mode: str | None = Field(default=None, max_length=30)
|
||||
reason: str | None = Field(default=None, max_length=500)
|
||||
idempotency_key: str | None = Field(default=None, max_length=128)
|
||||
|
||||
|
||||
class CreateRechargePayload(BaseModel):
|
||||
amount_usd: float = Field(..., gt=0, allow_inf_nan=False)
|
||||
payment_method: str = Field(..., min_length=1, max_length=30)
|
||||
pay_amount: float | None = Field(default=None, gt=0, allow_inf_nan=False)
|
||||
pay_currency: str | None = Field(default=None, min_length=3, max_length=3)
|
||||
exchange_rate: float | None = Field(default=None, gt=0, allow_inf_nan=False)
|
||||
|
||||
|
||||
def _default_refund_mode_for_order(order: PaymentOrder) -> str:
|
||||
if order.payment_method in {"admin_manual", "card_recharge", "card_code", "gift_code"}:
|
||||
return "offline_payout"
|
||||
return "original_channel"
|
||||
|
||||
|
||||
def _build_refund_no() -> str:
|
||||
ts = datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S%f")
|
||||
return f"rf_{ts}_{uuid4().hex[:8]}"
|
||||
|
||||
|
||||
def _resolve_user_wallet(db: Session, user: Any) -> Wallet | None:
|
||||
existing_wallet = WalletService.get_wallet(db, user_id=user.id)
|
||||
wallet = existing_wallet or WalletService.get_or_create_wallet(db, user=user)
|
||||
if wallet is not None and existing_wallet is None:
|
||||
db.commit()
|
||||
db.refresh(wallet)
|
||||
return wallet
|
||||
|
||||
|
||||
def _flow_sort_key(item: dict[str, Any], billing_tz: ZoneInfo) -> tuple[date, int, datetime]:
|
||||
item_type = item.get("type")
|
||||
data = item.get("data") or {}
|
||||
if item_type == "daily_usage":
|
||||
raw_date = data.get("date")
|
||||
billing_date = (
|
||||
date.fromisoformat(raw_date) if isinstance(raw_date, str) and raw_date else date.min
|
||||
)
|
||||
sort_dt = (
|
||||
data.get("last_finalized_at")
|
||||
or data.get("aggregated_at")
|
||||
or datetime.min.replace(tzinfo=timezone.utc)
|
||||
)
|
||||
if isinstance(sort_dt, str):
|
||||
sort_dt = datetime.fromisoformat(sort_dt.replace("Z", "+00:00"))
|
||||
return billing_date, 1, sort_dt
|
||||
|
||||
created_at = data.get("created_at")
|
||||
if isinstance(created_at, str):
|
||||
created_at = datetime.fromisoformat(created_at.replace("Z", "+00:00"))
|
||||
if not isinstance(created_at, datetime):
|
||||
created_at = datetime.min.replace(tzinfo=timezone.utc)
|
||||
elif created_at.tzinfo is None:
|
||||
created_at = created_at.replace(tzinfo=timezone.utc)
|
||||
local_date = created_at.astimezone(billing_tz).date()
|
||||
return local_date, 0, created_at
|
||||
|
||||
|
||||
@router.get("/balance")
|
||||
async def get_wallet_balance(request: Request, db: Session = Depends(get_db)) -> Any:
|
||||
adapter = WalletBalanceAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.get("/transactions")
|
||||
async def list_wallet_transactions(
|
||||
request: Request,
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
offset: int = Query(0, ge=0, le=5000),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Any:
|
||||
adapter = WalletTransactionsAdapter(limit=limit, offset=offset)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.get("/flow")
|
||||
async def get_wallet_flow(
|
||||
request: Request,
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
offset: int = Query(0, ge=0, le=5000),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Any:
|
||||
adapter = WalletFlowAdapter(limit=limit, offset=offset)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.get("/today-cost")
|
||||
async def get_wallet_today_cost(request: Request, db: Session = Depends(get_db)) -> Any:
|
||||
adapter = WalletTodayCostAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.post("/recharge")
|
||||
async def create_recharge_order(request: Request, db: Session = Depends(get_db)) -> Any:
|
||||
adapter = WalletRechargeCreateAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.get("/recharge")
|
||||
async def list_recharge_orders(
|
||||
request: Request,
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
offset: int = Query(0, ge=0, le=5000),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Any:
|
||||
adapter = WalletRechargeListAdapter(limit=limit, offset=offset)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.get("/recharge/{order_id}")
|
||||
async def get_recharge_order(order_id: str, request: Request, db: Session = Depends(get_db)) -> Any:
|
||||
adapter = WalletRechargeDetailAdapter(order_id=order_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.get("/refunds")
|
||||
async def list_refunds(
|
||||
request: Request,
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
offset: int = Query(0, ge=0, le=5000),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Any:
|
||||
adapter = WalletRefundListAdapter(limit=limit, offset=offset)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.get("/refunds/{refund_id}")
|
||||
async def get_refund_detail(refund_id: str, request: Request, db: Session = Depends(get_db)) -> Any:
|
||||
adapter = WalletRefundDetailAdapter(refund_id=refund_id)
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@router.post("/refunds")
|
||||
async def create_refund(request: Request, db: Session = Depends(get_db)) -> Any:
|
||||
adapter = WalletRefundCreateAdapter()
|
||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||
|
||||
|
||||
@dataclass
|
||||
class WalletTransactionsAdapter(AuthenticatedApiAdapter):
|
||||
limit: int
|
||||
offset: int
|
||||
|
||||
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
|
||||
db = context.db
|
||||
user = context.user
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
wallet = _resolve_user_wallet(db, user)
|
||||
if wallet is None:
|
||||
return {
|
||||
"items": [],
|
||||
"total": 0,
|
||||
"limit": self.limit,
|
||||
"offset": self.offset,
|
||||
**serialize_wallet_payload(None),
|
||||
}
|
||||
|
||||
base_query = db.query(WalletTransaction).filter(WalletTransaction.wallet_id == wallet.id)
|
||||
total = base_query.count()
|
||||
items = (
|
||||
base_query.order_by(WalletTransaction.created_at.desc())
|
||||
.offset(self.offset)
|
||||
.limit(self.limit)
|
||||
.all()
|
||||
)
|
||||
|
||||
return {
|
||||
"items": [serialize_wallet_transaction(item) for item in items],
|
||||
"total": total,
|
||||
"limit": self.limit,
|
||||
"offset": self.offset,
|
||||
**serialize_wallet_payload(wallet),
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class WalletFlowAdapter(AuthenticatedApiAdapter):
|
||||
limit: int
|
||||
offset: int
|
||||
|
||||
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
|
||||
db = context.db
|
||||
user = context.user
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
wallet = _resolve_user_wallet(db, user)
|
||||
if wallet is None:
|
||||
return {
|
||||
"today_entry": None,
|
||||
"items": [],
|
||||
"total": 0,
|
||||
"limit": self.limit,
|
||||
"offset": self.offset,
|
||||
**serialize_wallet_payload(None),
|
||||
}
|
||||
|
||||
today_entry = WalletDailyUsageLedgerService.get_today_snapshot(db, wallet.id)
|
||||
billing_tz = WalletDailyUsageLedgerService.get_timezone(today_entry.billing_timezone)
|
||||
fetch_size = min(self.offset + self.limit, 5200)
|
||||
|
||||
tx_query = db.query(WalletTransaction).filter(WalletTransaction.wallet_id == wallet.id)
|
||||
tx_total = tx_query.count()
|
||||
tx_items = tx_query.order_by(WalletTransaction.created_at.desc()).limit(fetch_size).all()
|
||||
|
||||
daily_query = db.query(WalletDailyUsageLedger).filter(
|
||||
WalletDailyUsageLedger.wallet_id == wallet.id,
|
||||
WalletDailyUsageLedger.billing_timezone == today_entry.billing_timezone,
|
||||
WalletDailyUsageLedger.billing_date < today_entry.billing_date,
|
||||
)
|
||||
daily_total = daily_query.count()
|
||||
daily_items = (
|
||||
daily_query.order_by(WalletDailyUsageLedger.billing_date.desc()).limit(fetch_size).all()
|
||||
)
|
||||
|
||||
merged = [
|
||||
{"type": "transaction", "data": serialize_wallet_transaction(item)} for item in tx_items
|
||||
] + [
|
||||
{"type": "daily_usage", "data": serialize_wallet_daily_usage(item)}
|
||||
for item in daily_items
|
||||
]
|
||||
merged.sort(key=lambda item: _flow_sort_key(item, billing_tz), reverse=True)
|
||||
paged = merged[self.offset : self.offset + self.limit]
|
||||
|
||||
return {
|
||||
"today_entry": serialize_wallet_daily_usage(today_entry),
|
||||
"items": paged,
|
||||
"total": tx_total + daily_total,
|
||||
"limit": self.limit,
|
||||
"offset": self.offset,
|
||||
**serialize_wallet_payload(wallet),
|
||||
}
|
||||
|
||||
|
||||
class WalletTodayCostAdapter(AuthenticatedApiAdapter):
|
||||
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
|
||||
db = context.db
|
||||
user = context.user
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
wallet = _resolve_user_wallet(db, user)
|
||||
snapshot = WalletDailyUsageLedgerService.get_today_snapshot(
|
||||
db,
|
||||
wallet.id if wallet is not None else None,
|
||||
)
|
||||
return serialize_wallet_daily_usage(snapshot)
|
||||
|
||||
|
||||
class WalletBalanceAdapter(AuthenticatedApiAdapter):
|
||||
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
|
||||
db = context.db
|
||||
user = context.user
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
wallet = _resolve_user_wallet(db, user)
|
||||
if wallet is None:
|
||||
return serialize_wallet_payload(None)
|
||||
|
||||
pending_refunds = (
|
||||
db.query(RefundRequest)
|
||||
.filter(
|
||||
RefundRequest.wallet_id == wallet.id,
|
||||
RefundRequest.status.in_(["pending_approval", "approved", "processing"]),
|
||||
)
|
||||
.count()
|
||||
)
|
||||
|
||||
payload = serialize_wallet_payload(wallet)
|
||||
payload["pending_refund_count"] = pending_refunds
|
||||
return payload
|
||||
|
||||
|
||||
class WalletRechargeCreateAdapter(AuthenticatedApiAdapter):
|
||||
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
|
||||
db = context.db
|
||||
user = context.user
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
payload = context.ensure_json_body()
|
||||
try:
|
||||
req = CreateRechargePayload.model_validate(payload)
|
||||
except ValidationError as exc:
|
||||
errors = exc.errors()
|
||||
if errors:
|
||||
raise InvalidRequestException(translate_pydantic_error(errors[0]))
|
||||
raise InvalidRequestException("请求数据验证失败")
|
||||
|
||||
return await run_in_threadpool(_create_recharge_order_sync, user.id, req)
|
||||
|
||||
|
||||
@dataclass
|
||||
class WalletRechargeListAdapter(AuthenticatedApiAdapter):
|
||||
limit: int
|
||||
offset: int
|
||||
|
||||
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
|
||||
user = context.user
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
return await run_in_threadpool(
|
||||
_list_recharge_orders_sync,
|
||||
user.id,
|
||||
self.limit,
|
||||
self.offset,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class WalletRechargeDetailAdapter(AuthenticatedApiAdapter):
|
||||
order_id: str
|
||||
|
||||
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
|
||||
user = context.user
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
return await run_in_threadpool(_get_recharge_order_sync, user.id, self.order_id)
|
||||
|
||||
|
||||
@dataclass
|
||||
class WalletRefundListAdapter(AuthenticatedApiAdapter):
|
||||
limit: int
|
||||
offset: int
|
||||
|
||||
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
|
||||
db = context.db
|
||||
user = context.user
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
return await run_in_threadpool(_list_refunds_sync, user.id, self.limit, self.offset)
|
||||
|
||||
|
||||
@dataclass
|
||||
class WalletRefundDetailAdapter(AuthenticatedApiAdapter):
|
||||
refund_id: str
|
||||
|
||||
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
|
||||
db = context.db
|
||||
user = context.user
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
refund = (
|
||||
db.query(RefundRequest)
|
||||
.filter(RefundRequest.id == self.refund_id, RefundRequest.user_id == user.id)
|
||||
.first()
|
||||
)
|
||||
if refund is None:
|
||||
raise NotFoundException("Refund request not found")
|
||||
return serialize_wallet_refund(refund)
|
||||
|
||||
|
||||
class WalletRefundCreateAdapter(AuthenticatedApiAdapter):
|
||||
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
|
||||
db = context.db
|
||||
user = context.user
|
||||
if user is None:
|
||||
raise InvalidRequestException("未登录")
|
||||
|
||||
payload = context.ensure_json_body()
|
||||
try:
|
||||
req = CreateRefundPayload.model_validate(payload)
|
||||
except ValidationError as exc:
|
||||
errors = exc.errors()
|
||||
if errors:
|
||||
raise InvalidRequestException(translate_pydantic_error(errors[0]))
|
||||
raise InvalidRequestException("请求数据验证失败")
|
||||
|
||||
return await run_in_threadpool(_create_refund_sync, user.id, req)
|
||||
Reference in New Issue
Block a user