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:
fawney19
2026-04-03 16:26:16 +08:00
parent 8f26e1a31f
commit 1d9c77522a
868 changed files with 1735 additions and 2433 deletions

View File

@@ -0,0 +1,39 @@
"""Payment API routes."""
from __future__ import annotations
from fastapi import APIRouter
from starlette.routing import BaseRoute
from .routes import router as payment_router
_RUST_OWNED_PAYMENT_ROUTE_SIGNATURES = frozenset(
{
("POST", "/api/payment/callback/{payment_method}"),
}
)
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_PAYMENT_ROUTE_SIGNATURES
for method in methods
if method not in {"HEAD", "OPTIONS"}
)
def _build_python_payment_router() -> APIRouter:
router = APIRouter()
router.include_router(payment_router)
router.routes = [route for route in router.routes if not _route_is_rust_owned(route)]
return router
python_payment_router = _build_python_payment_router()
router = python_payment_router
__all__ = ["python_payment_router", "router"]

View File

@@ -0,0 +1,97 @@
"""支付回调接口。"""
from __future__ import annotations
import secrets
from typing import Any
from fastapi import APIRouter, Depends, HTTPException, Request
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from src.config import config
from src.database import get_db
from src.services.payment import PaymentService
router = APIRouter(prefix="/api/payment", tags=["Payment"])
CALLBACK_TOKEN_HEADER = "x-payment-callback-token"
CALLBACK_SIGNATURE_HEADER = "x-payment-callback-signature"
class PaymentCallbackPayload(BaseModel):
callback_key: str = Field(..., min_length=1, max_length=128)
order_no: str | None = Field(default=None, max_length=64)
gateway_order_id: str | None = Field(default=None, max_length=128)
amount_usd: float = Field(..., gt=0, allow_inf_nan=False)
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)
payload: dict[str, Any] | None = None
def _verify_callback_request_auth(request: Request) -> None:
expected_token = config.payment_callback_secret
if not expected_token:
raise HTTPException(status_code=503, detail="payment callback is disabled")
provided_token = (request.headers.get(CALLBACK_TOKEN_HEADER) or "").strip()
if not provided_token or not secrets.compare_digest(provided_token, expected_token):
raise HTTPException(status_code=401, detail="invalid payment callback token")
async def _process_callback(
*,
payment_method: str,
request: Request,
payload: PaymentCallbackPayload,
db: Session = Depends(get_db),
) -> dict[str, Any]:
if not payment_method:
raise HTTPException(status_code=400, detail="payment_method is required")
_verify_callback_request_auth(request)
callback_signature = (request.headers.get(CALLBACK_SIGNATURE_HEADER) or "").strip()
if not callback_signature:
raise HTTPException(status_code=401, detail="missing payment callback signature")
try:
callback_payload = payload.payload if payload.payload is not None else payload.model_dump()
result = PaymentService.handle_callback(
db,
payment_method=payment_method,
callback_key=payload.callback_key,
payload=callback_payload,
callback_signature=callback_signature,
callback_secret=config.payment_callback_secret,
order_no=payload.order_no,
gateway_order_id=payload.gateway_order_id,
amount_usd=payload.amount_usd,
pay_amount=payload.pay_amount,
pay_currency=payload.pay_currency,
exchange_rate=payload.exchange_rate,
)
db.commit()
return {
**result,
"payment_method": payment_method,
"request_path": request.url.path,
}
except Exception as exc:
db.rollback()
if isinstance(exc, HTTPException):
raise
raise HTTPException(status_code=400, detail=str(exc)) from exc
@router.post("/callback/{payment_method}")
async def handle_payment_callback(
payment_method: str,
request: Request,
payload: PaymentCallbackPayload,
db: Session = Depends(get_db),
) -> dict[str, Any]:
return await _process_callback(
payment_method=payment_method,
request=request,
payload=payload,
db=db,
)