refactor: 平台改为代码内置 provider 架构,一次性迁移现有数据 - 新增 app/providers:BalanceProvider 基类 + DeepSeek 适配器 + 注册表 - platforms 表去除 method/url/headers/body/balance_path,新增 provider_id - 现有数据库已一次性迁移(备份 data/monitor.db.bak-v1),不保留迁移工具 - 平台 UI 改为选择内置提供方;fetcher/monitor 走 provider 构建请求与解析 - 表达式引擎(运算符/函数)随架构保留,供 provider 内部使用 - 测试更新至 provider 模式,共 92 个

This commit is contained in:
2026-08-05 00:56:22 +08:00
parent 1475821519
commit bdeb353bc2
16 changed files with 704 additions and 169 deletions
+32 -28
View File
@@ -3,7 +3,6 @@
from __future__ import annotations
import base64
import json
import logging
import secrets
import sqlite3
@@ -27,6 +26,7 @@ from app.models import (
SettingsUpdate,
)
from app.monitor import Monitor
from app.providers import get_provider, list_providers as list_providers_svc
logger = logging.getLogger("monitor.api")
@@ -130,36 +130,40 @@ def create_app(cfg: Config | None = None) -> FastAPI:
# ---------- 平台 ----------
@app.get("/api/providers", dependencies=[Depends(require_auth)])
def list_providers():
"""代码内置的平台适配器列表(新增平台 = 代码扩展)。"""
return list_providers_svc()
@app.get("/api/platforms", dependencies=[Depends(require_auth)])
def list_platforms():
with db.get_conn() as conn:
rows = conn.execute(
"""SELECT p.*, (SELECT COUNT(*) FROM accounts a WHERE a.platform_id = p.id) AS account_count
"""SELECT p.id, p.name, p.currency, p.icon, p.provider_id,
p.interval_seconds, p.retry_count, p.timeout_seconds,
p.enabled, p.note, p.created_at, p.updated_at,
(SELECT COUNT(*) FROM accounts a WHERE a.platform_id = p.id) AS account_count
FROM platforms p ORDER BY p.id"""
).fetchall()
result = []
for r in rows:
d = dict(r)
try:
d["headers"] = json.loads(d["headers"]) if d["headers"] else {}
except (ValueError, TypeError):
d["headers"] = {}
result.append(d)
return result
return [dict(r) for r in rows]
@app.post("/api/platforms", dependencies=[Depends(require_auth)])
def create_platform(body: PlatformCreate):
provider = get_provider(body.provider_id)
if provider is None:
raise HTTPException(status_code=400, detail=f"不支持的平台提供方: {body.provider_id}")
currency = body.currency or provider.currency
icon = body.icon or provider.icon
try:
with db.get_conn() as conn:
cur = conn.execute(
"""INSERT INTO platforms (name, currency, icon, method, url, headers, body,
balance_path, interval_seconds, retry_count, timeout_seconds, enabled, note)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
"""INSERT INTO platforms (provider_id, name, currency, icon,
interval_seconds, retry_count, timeout_seconds, enabled, note)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(
body.name, body.currency, body.icon, body.method, body.url,
__import__("json").dumps(body.headers, ensure_ascii=False),
body.body, body.balance_path, body.interval_seconds,
body.retry_count, body.timeout_seconds, int(body.enabled), body.note,
body.provider_id, body.name, currency, icon,
body.interval_seconds, body.retry_count, body.timeout_seconds,
int(body.enabled), body.note,
),
)
pid = cur.lastrowid
@@ -172,22 +176,22 @@ def create_app(cfg: Config | None = None) -> FastAPI:
@app.put("/api/platforms/{pid}", dependencies=[Depends(require_auth)])
def update_platform(pid: int, body: PlatformUpdate):
fields = {}
if body.provider_id is not None:
provider = get_provider(body.provider_id)
if provider is None:
raise HTTPException(status_code=400, detail=f"不支持的平台提供方: {body.provider_id}")
fields["provider_id"] = body.provider_id
# 换 provider 时若未显式给货币/图标,则重置为 provider 默认
if "currency" not in body.model_fields_set:
fields["currency"] = provider.currency
if "icon" not in body.model_fields_set:
fields["icon"] = provider.icon
if body.name is not None:
fields["name"] = body.name
if body.currency is not None:
fields["currency"] = body.currency
if body.icon is not None:
fields["icon"] = body.icon
if body.method is not None:
fields["method"] = body.method
if body.url is not None:
fields["url"] = body.url
if body.headers is not None:
fields["headers"] = __import__("json").dumps(body.headers, ensure_ascii=False)
if body.body is not None:
fields["body"] = body.body
if body.balance_path is not None:
fields["balance_path"] = body.balance_path
if body.interval_seconds is not None:
fields["interval_seconds"] = body.interval_seconds
if body.retry_count is not None: