This commit is contained in:
Daniel
2026-04-28 19:16:27 +08:00
parent 6de7e782fc
commit c234fe64d6
13 changed files with 229 additions and 31 deletions

View File

@@ -4,6 +4,7 @@ import logging
import math
import re
import secrets
import socket
import time
import uuid
from pathlib import Path
@@ -175,18 +176,31 @@ def _platform_model_cfg() -> dict:
def _select_model_cfg(user_id: int, prefer_vip: bool = True) -> tuple[dict | None, str]:
vip = users.get_vip_status(user_id)
now = int(time.time())
active_user_model = users.get_active_ai_model(user_id)
has_user_model = bool(
active_user_model
and (active_user_model.get("api_key") or "").strip()
and (active_user_model.get("model") or "").strip()
)
purchased_cycle_active = bool(int(vip.get("cycle_started_at") or 0) > 0 and int(vip.get("cycle_expires_at") or 0) > now)
if prefer_vip and vip.get("vip_enabled"):
if int(vip.get("total_available_credits") or 0) <= 0:
total_available = int(vip.get("total_available_credits") or 0)
if total_available < _min_platform_credits_required():
return None, "vip_empty"
cfg = _platform_model_cfg()
if cfg.get("api_key"):
return cfg, "vip"
cfg = users.get_active_ai_model(user_id)
# 已购买订阅:优先平台模型
# 未购买订阅:若未配置自有模型,则默认走平台模型(可使用赠送额度)
if purchased_cycle_active or not has_user_model:
cfg = _platform_model_cfg()
if cfg.get("api_key"):
return cfg, "vip"
cfg = active_user_model
return cfg, "user"
def _quota_detail() -> str:
return "Credits 额度已用完(席位额度+共享加油包),请充值或等待下个计费周期"
min_required = _min_platform_credits_required()
return f"Credits 额度不足,平台模型至少需要 {min_required} Credits1张图片或10000Token门槛请订阅或充值"
def _credits_from_cny(amount_cny: float) -> int:
@@ -222,6 +236,13 @@ def _estimate_image_cost(image_count: int) -> int:
return _credits_from_cny((cnt / pkg_images) * pkg_cny)
def _min_platform_credits_required() -> int:
one_image_credits = _estimate_image_cost(1)
token_10k_cny = (10_000.0 / 1_000_000.0) * max(0.0, float(settings.credits_token_price_per_million_cny))
token_10k_credits = _credits_from_cny(token_10k_cny)
return max(1, one_image_credits, token_10k_credits)
def _new_order_no() -> str:
return f"RC{time.strftime('%Y%m%d%H%M%S')}{uuid.uuid4().hex[:10].upper()}"
@@ -743,6 +764,31 @@ async def pay_address_suggest(request: Request):
return {"ok": True, "detail": "IP地址解析失败已填入IP信息可手动修改", "ip": ip, "address": f"IP:{ip}"}
@app.get("/api/tools/server-ip")
async def tools_server_ip(request: Request):
user = _require_user(request)
if not user:
return {"ok": False, "detail": "请先登录"}
public_ip = ""
try:
async with httpx.AsyncClient(timeout=5) as client:
resp = await client.get("https://api64.ipify.org?format=json")
body = resp.json() if resp.content else {}
if resp.status_code < 400 and isinstance(body, dict):
public_ip = str(body.get("ip") or "").strip()
except Exception:
public_ip = ""
private_ip = ""
try:
private_ip = socket.gethostbyname(socket.gethostname())
except Exception:
private_ip = ""
ip = public_ip or private_ip
if not ip:
return {"ok": False, "detail": "无法识别服务器IP请稍后重试"}
return {"ok": True, "ip": ip, "public_ip": public_ip, "private_ip": private_ip}
@app.post("/api/billing/recharge/notify")
async def billing_recharge_notify(req: BillingRechargeNotifyRequest, request: Request):
token = (request.headers.get("X-Shop-Token") or "").strip()
@@ -967,7 +1013,7 @@ async def rewrite(req: RewriteRequest, request: Request):
billed_basis = "usage_tokens" if total_tokens > 0 else "char_estimate"
token_cost = _estimate_rewrite_cost(req, result)
vip_status = users.get_vip_status(user["id"])
should_consume = bool(vip_status.get("vip_enabled"))
should_consume = model_source == "vip"
if should_consume:
ok_cost, balance = users.consume_tokens(
user["id"],