feat: clean commit without .next

This commit is contained in:
eric
2026-03-26 11:31:57 -05:00
parent e6b9c1dc9b
commit 9a5afd7493
2 changed files with 55 additions and 6 deletions

View File

@@ -115,6 +115,14 @@ def _check_base_url():
@app.on_event("startup") @app.on_event("startup")
async def startup_event(): async def startup_event():
_check_base_url() _check_base_url()
# 预热 PocketBase 连接,减少首个业务请求冷启动延迟
try:
from .services.pb import get_pb_client
get_pb_client()
logging.info("PocketBase client warmup done")
except Exception as error:
logging.warning("PocketBase client warmup failed: %s", error)
@app.get("/health") @app.get("/health")

View File

@@ -33,6 +33,27 @@ _http_session.mount("https://", HTTPAdapter(pool_connections=32, pool_maxsize=64
_admin_token_cache_lock = threading.Lock() _admin_token_cache_lock = threading.Lock()
_admin_token_cache_value: str | None = None _admin_token_cache_value: str | None = None
_admin_token_cache_expire_at = 0.0 _admin_token_cache_expire_at = 0.0
_vip_cache_lock = threading.Lock()
_vip_check_cache: dict[str, tuple[float, dict]] = {}
_VIP_CACHE_TTL_SECONDS = 20
def _cache_get(key: str):
now = time.time()
with _vip_cache_lock:
item = _vip_check_cache.get(key)
if not item:
return None
expire_at, value = item
if expire_at < now:
_vip_check_cache.pop(key, None)
return None
return value
def _cache_set(key: str, value: dict, ttl: int = _VIP_CACHE_TTL_SECONDS):
with _vip_cache_lock:
_vip_check_cache[key] = (time.time() + ttl, value)
def _record_get(obj, key: str, default=None): def _record_get(obj, key: str, default=None):
@@ -302,6 +323,10 @@ async def nomadvip_check_vip(user_id: str = ""):
logging.info("[payjsapi] check_vip user_id=%s", user_id or "(empty)") logging.info("[payjsapi] check_vip user_id=%s", user_id or "(empty)")
if not user_id: if not user_id:
return {"vip": False, "error": "missing user_id"} return {"vip": False, "error": "missing user_id"}
cache_key = f"check_vip:{user_id}"
cached = _cache_get(cache_key)
if cached is not None:
return cached
try: try:
from ..services.pb import get_pb_client from ..services.pb import get_pb_client
@@ -309,7 +334,9 @@ async def nomadvip_check_vip(user_id: str = ""):
pb = get_pb_client() pb = get_pb_client()
if not _uid_has_vip(pb, user_id): if not _uid_has_vip(pb, user_id):
logging.info("[payjsapi] check_vip miss user_id=%s vip=False", user_id) logging.info("[payjsapi] check_vip miss user_id=%s vip=False", user_id)
return {"vip": False} result = {"vip": False}
_cache_set(cache_key, result)
return result
email = _get_linked_email_for_user(pb, user_id) email = _get_linked_email_for_user(pb, user_id)
if not email: if not email:
@@ -326,10 +353,14 @@ async def nomadvip_check_vip(user_id: str = ""):
user_id, user_id,
email or "(none)", email or "(none)",
) )
return {"vip": True, "email": email} result = {"vip": True, "email": email}
_cache_set(cache_key, result)
return result
except Exception as error: except Exception as error:
logging.warning("[payjsapi] check_vip failed: %s", error) logging.warning("[payjsapi] check_vip failed: %s", error)
return {"vip": False, "error": str(error)} result = {"vip": False, "error": str(error)}
_cache_set(cache_key, result, ttl=5)
return result
def _get_pb_admin_token() -> str | None: def _get_pb_admin_token() -> str | None:
"""获取 PocketBase 管理员 token。""" """获取 PocketBase 管理员 token。"""
@@ -671,6 +702,10 @@ async def nomadvip_check_vip_by_email(item: dict):
if not email or not password: if not email or not password:
return {"vip": False, "error": "missing email or password"} return {"vip": False, "error": "missing email or password"}
cache_key = f"check_vip_by_email:{email}:{anonymous_user_id}"
cached = _cache_get(cache_key)
if cached is not None:
return cached
base = (PB_URL or "").rstrip("/") base = (PB_URL or "").rstrip("/")
login_res = _http_session.post( login_res = _http_session.post(
@@ -768,10 +803,12 @@ async def nomadvip_check_vip_by_email(item: dict):
if not matched_user_id: if not matched_user_id:
logging.info("[payjsapi] check_vip_by_email no vip match for email=%s", email) logging.info("[payjsapi] check_vip_by_email no vip match for email=%s", email)
return { result = {
"vip": False, "vip": False,
"error": "no linked vip record found; provide the paid user_id if this is a historical anonymous order", "error": "no linked vip record found; provide the paid user_id if this is a historical anonymous order",
} }
_cache_set(cache_key, result, ttl=5)
return result
if recovered_anonymous_user_id: if recovered_anonymous_user_id:
_pb_ensure_site_vip(admin_token, recovered_anonymous_user_id, pb_user_id) _pb_ensure_site_vip(admin_token, recovered_anonymous_user_id, pb_user_id)
@@ -791,16 +828,20 @@ async def nomadvip_check_vip_by_email(item: dict):
pb_user_id, pb_user_id,
recovered_anonymous_user_id or "(none)", recovered_anonymous_user_id or "(none)",
) )
return { result = {
"vip": True, "vip": True,
"token": token, "token": token,
"record": record, "record": record,
"effectiveUserId": pb_user_id, "effectiveUserId": pb_user_id,
"anonymousUserId": recovered_anonymous_user_id, "anonymousUserId": recovered_anonymous_user_id,
} }
_cache_set(cache_key, result)
return result
except Exception as error: except Exception as error:
logging.warning("[payjsapi] check_vip_by_email failed: %s", error) logging.warning("[payjsapi] check_vip_by_email failed: %s", error)
return {"vip": False, "error": str(error)} result = {"vip": False, "error": str(error)}
_cache_set(cache_key, result, ttl=5)
return result
@router.get("/order_status") @router.get("/order_status")
async def nomadvip_order_status(order_id: str = "", out_trade_no: str = ""): async def nomadvip_order_status(order_id: str = "", out_trade_no: str = ""):