From 9a5afd74932ec9fd36ad42591cd07edf1a12b55b Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 26 Mar 2026 11:31:57 -0500 Subject: [PATCH] feat: clean commit without .next --- app/main.py | 8 +++++++ app/routers/nomadvip.py | 53 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/app/main.py b/app/main.py index a197b5b..effbac1 100755 --- a/app/main.py +++ b/app/main.py @@ -115,6 +115,14 @@ def _check_base_url(): @app.on_event("startup") async def startup_event(): _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") diff --git a/app/routers/nomadvip.py b/app/routers/nomadvip.py index 66e7de8..233e764 100644 --- a/app/routers/nomadvip.py +++ b/app/routers/nomadvip.py @@ -33,6 +33,27 @@ _http_session.mount("https://", HTTPAdapter(pool_connections=32, pool_maxsize=64 _admin_token_cache_lock = threading.Lock() _admin_token_cache_value: str | None = None _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): @@ -302,6 +323,10 @@ async def nomadvip_check_vip(user_id: str = ""): logging.info("[payjsapi] check_vip user_id=%s", user_id or "(empty)") if not 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: from ..services.pb import get_pb_client @@ -309,7 +334,9 @@ async def nomadvip_check_vip(user_id: str = ""): pb = get_pb_client() if not _uid_has_vip(pb, 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) if not email: @@ -326,10 +353,14 @@ async def nomadvip_check_vip(user_id: str = ""): user_id, email or "(none)", ) - return {"vip": True, "email": email} + result = {"vip": True, "email": email} + _cache_set(cache_key, result) + return result except Exception as 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: """获取 PocketBase 管理员 token。""" @@ -671,6 +702,10 @@ async def nomadvip_check_vip_by_email(item: dict): if not email or not 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("/") login_res = _http_session.post( @@ -768,10 +803,12 @@ async def nomadvip_check_vip_by_email(item: dict): if not matched_user_id: logging.info("[payjsapi] check_vip_by_email no vip match for email=%s", email) - return { + result = { "vip": False, "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: _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, recovered_anonymous_user_id or "(none)", ) - return { + result = { "vip": True, "token": token, "record": record, "effectiveUserId": pb_user_id, "anonymousUserId": recovered_anonymous_user_id, } + _cache_set(cache_key, result) + return result except Exception as 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") async def nomadvip_order_status(order_id: str = "", out_trade_no: str = ""):