04e467e433
后端: - 微信回调 db.commit 失败仍返回 SUCCESS,避免无限重试 - recharge() 加 order_id 幂等保护,防重复充值 - time_expire 使用北京时间(UTC+8),修复时区 bug - 充值档位后端配置化(points-config.yaml + /recharge-options API) - 代码审查 20 项修复(认证加固、扣费顺序、错误响应、状态同步等) 前端: - 充值弹窗:自动轮询 + 【我已支付】手动兜底 - 二维码倒计时显示,过期后遮罩 + 刷新按钮 - 充值档位从后端动态加载 - 去掉 select/qrcode 弹窗标题,金额红色突出显示 - 全项目命名统一(视频生成/压制成片/配音合成/声音复刻等) - Modal 关闭按钮独立于 title 显示
133 lines
3.2 KiB
Python
133 lines
3.2 KiB
Python
"""
|
|
Adapter 基础定义
|
|
===============
|
|
|
|
所有第三方平台 Adapter 的统一契约。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from dataclasses import dataclass
|
|
from typing import Any, Protocol, runtime_checkable
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AdapterResponse:
|
|
"""Adapter 统一响应格式"""
|
|
|
|
success: bool
|
|
data: dict[str, Any] | None = None
|
|
error_code: str | None = None
|
|
error_message: str | None = None
|
|
retryable: bool = False
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TaskStatus:
|
|
"""异步任务状态统一格式"""
|
|
|
|
state: str # "pending" | "processing" | "completed" | "failed"
|
|
result: dict[str, Any] | None = None
|
|
error_message: str | None = None
|
|
|
|
|
|
@runtime_checkable
|
|
class PlatformAdapter(Protocol):
|
|
"""所有 Adapter 的准入门槛
|
|
|
|
每个新平台必须实现 platform_id + health() + close()。
|
|
"""
|
|
|
|
platform_id: str
|
|
|
|
async def health(self) -> AdapterResponse:
|
|
"""健康检查,返回是否可用"""
|
|
...
|
|
|
|
async def close(self) -> None:
|
|
"""清理资源(关闭 HTTP Client、释放连接池)"""
|
|
...
|
|
|
|
|
|
@runtime_checkable
|
|
class SyncCapable(Protocol):
|
|
"""同步调用能力(TTS、Chat 等)"""
|
|
|
|
async def call(self, method: str, payload: dict[str, Any]) -> AdapterResponse:
|
|
"""同步调用统一入口
|
|
|
|
Args:
|
|
method: 方法标识,如 "tts", "chat"
|
|
payload: 请求体字典
|
|
|
|
Returns:
|
|
AdapterResponse: 统一响应格式
|
|
|
|
各方法返回结构(docstring 约定):
|
|
- "tts": data={"audio_url": str}
|
|
- "chat": data={"content": str, "usage": dict, "model": str}
|
|
"""
|
|
|
|
...
|
|
|
|
|
|
@runtime_checkable
|
|
class TaskCapable(Protocol):
|
|
"""异步任务能力(视频生成、字幕、TTS 等)"""
|
|
|
|
async def submit(
|
|
self,
|
|
task_type: str,
|
|
payload: dict[str, Any],
|
|
callback_url: str | None = None,
|
|
) -> AdapterResponse:
|
|
"""提交任务,返回 platform_task_id"""
|
|
...
|
|
|
|
async def query(self, platform_task_id: str) -> TaskStatus:
|
|
"""查询任务状态"""
|
|
...
|
|
|
|
|
|
@runtime_checkable
|
|
class CallbackCapable(Protocol):
|
|
"""回调验签能力(可选,只有需要验签的平台才实现)"""
|
|
|
|
async def verify_signature(
|
|
self,
|
|
headers: dict[str, str],
|
|
body: bytes,
|
|
secret: str,
|
|
callback_url: str | None = None,
|
|
) -> bool:
|
|
"""验证回调签名
|
|
|
|
Args:
|
|
callback_url: 回调请求完整 URL(用于构建 signingString,部分平台需要)
|
|
"""
|
|
...
|
|
|
|
async def verify_nonce(
|
|
self,
|
|
headers: dict[str, str],
|
|
redis: Any,
|
|
) -> bool:
|
|
"""验证回调 nonce 防重放(可选实现)
|
|
|
|
Args:
|
|
headers: 回调请求头
|
|
redis: Redis 客户端(用于检查 nonce 是否已使用)
|
|
|
|
Returns:
|
|
True: nonce 有效(未使用)
|
|
False: nonce 已使用(可能为重放攻击)
|
|
"""
|
|
...
|
|
|
|
async def parse_callback(self, body: bytes) -> TaskStatus:
|
|
"""解析回调体为统一任务状态"""
|
|
...
|