This commit is contained in:
eric
2026-03-31 14:00:00 -05:00
parent da08f91b4d
commit 3590142297

View File

@@ -333,13 +333,52 @@ async def nomadvip_check_vip(user_id: str = ""):
from ..services.pb import get_pb_client
pb = get_pb_client()
linked_user_id = ""
if not _uid_has_vip(pb, user_id):
logging.info("[payjsapi] check_vip miss user_id=%s vip=False", user_id)
result = {"vip": False}
_cache_set(cache_key, result)
return result
# 关键补链:真实 wechat userid 可通过 vip_email_link.anonymous_user_id 关联到会员 user_id
try:
link_rec = pb.collection("vip_email_link").get_list(
1, 1, {"filter": f'anonymous_user_id = "{user_id}"'}
)
if link_rec.items:
linked_user_id = (
_record_get(link_rec.items[0], "user_id", "") or ""
).strip()
except Exception:
linked_user_id = ""
if not linked_user_id:
try:
admin_token = _get_pb_admin_token()
wx_record = (
_pb_get_wechat_private_user(admin_token, user_id)
if admin_token
else None
)
wx_link = _extract_wechat_vip_link(wx_record)
linked_user_id = (
str(wx_link.get("pb_user_id", "")).strip()
or str(wx_link.get("anonymous_user_id", "")).strip()
)
except Exception:
linked_user_id = ""
if not linked_user_id or not _uid_has_vip(pb, linked_user_id):
logging.info("[payjsapi] check_vip miss user_id=%s vip=False", user_id)
result = {"vip": False}
_cache_set(cache_key, result)
return result
email = _get_linked_email_for_user(pb, user_id)
if not email and linked_user_id:
try:
link_rec = pb.collection("vip_email_link").get_list(
1, 1, {"filter": f'user_id = "{linked_user_id}"'}
)
if link_rec.items:
email = (
_record_get(link_rec.items[0], "email", "") or ""
).strip().lower()
except Exception:
pass
if not email:
admin_token = _get_pb_admin_token()
if admin_token and user_id.startswith("user") and user_id[4:].isdigit():
@@ -355,6 +394,8 @@ async def nomadvip_check_vip(user_id: str = ""):
email or "(none)",
)
result = {"vip": True, "email": email}
if linked_user_id:
result["linked_user_id"] = linked_user_id
_cache_set(cache_key, result)
return result
except Exception as error:
@@ -398,6 +439,76 @@ def _pb_get_wechat_private_user(admin_token: str, wechat_userid: str) -> dict |
return items[0] if items else None
def _extract_wechat_vip_link(record: dict | None) -> dict:
if not isinstance(record, dict):
return {}
raw_contact = record.get("raw_contact")
if isinstance(raw_contact, str):
try:
import json as _json
raw_contact = _json.loads(raw_contact)
except Exception:
raw_contact = {}
if not isinstance(raw_contact, dict):
return {}
link = raw_contact.get("_vip_link")
return link if isinstance(link, dict) else {}
def _pb_bind_wechat_vip_link(
admin_token: str,
wechat_userid: str,
*,
email: str | None = None,
pb_user_id: str | None = None,
anonymous_user_id: str | None = None,
):
"""
在 wechat_private_users.raw_contact 中记录 _vip_link 关系。
注意:不改变历史 user_id 语义anonymous_user_id 仍是历史业务 user_id。
"""
record = _pb_get_wechat_private_user(admin_token, wechat_userid)
if not isinstance(record, dict):
return
record_id = str(record.get("id", "")).strip()
if not record_id:
return
raw_contact = record.get("raw_contact")
if isinstance(raw_contact, str):
try:
import json as _json
raw_contact = _json.loads(raw_contact)
except Exception:
raw_contact = {}
if not isinstance(raw_contact, dict):
raw_contact = {}
current = _extract_wechat_vip_link(record)
raw_contact["_vip_link"] = {
"email": (email or current.get("email") or "").strip().lower() or None,
"pb_user_id": (pb_user_id or current.get("pb_user_id") or "").strip() or None,
"anonymous_user_id": (
anonymous_user_id or current.get("anonymous_user_id") or ""
).strip()
or None,
"updated_at": int(time.time()),
}
base = (PB_URL or "").rstrip("/")
_http_session.patch(
f"{base}/api/collections/wechat_private_users/records/{record_id}",
json={"raw_contact": raw_contact},
headers={
"Authorization": f"Bearer {admin_token}",
"Content-Type": "application/json",
},
timeout=DEFAULT_HTTP_TIMEOUT,
)
def _pb_get_user_by_id(admin_token: str, user_id: str) -> dict | None:
base = (PB_URL or "").rstrip("/")
response = _http_session.get(
@@ -493,12 +604,22 @@ async def nomadvip_wechat_profile(item: dict):
)
if email and effective_member_user_id:
try:
# 保持历史语义vip_email_link.anonymous_user_id 仍仅承载历史业务 user_id
link_anonymous_id = anonymous_user_id or None
_pb_upsert_email_link(
admin_token,
email,
effective_member_user_id,
anonymous_user_id or None,
link_anonymous_id,
)
if wechat_userid:
_pb_bind_wechat_vip_link(
admin_token,
wechat_userid,
email=email,
pb_user_id=effective_member_user_id,
anonymous_user_id=link_anonymous_id,
)
except Exception:
pass
@@ -1124,6 +1245,7 @@ async def nomadvip_check_vip_by_email(item: dict):
anonymous_user_id = (
item.get("user_id") or item.get("anonymousUserId") or ""
).strip()
wechat_userid = (item.get("wechat_userid") or item.get("userid") or "").strip()
logging.info(
"[payjsapi] check_vip_by_email email=%s user_id=%s",
email or "(empty)",
@@ -1132,7 +1254,7 @@ 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}"
cache_key = f"check_vip_by_email:{email}:{anonymous_user_id}:{wechat_userid}"
cached = _cache_get(cache_key)
if cached is not None:
return cached
@@ -1175,11 +1297,8 @@ async def nomadvip_check_vip_by_email(item: dict):
pb = get_pb_client()
matched_user_id = None
vip_check_cache: dict[str, bool] = {}
recovered_anonymous_user_id = (
anonymous_user_id
if anonymous_user_id.startswith("user") and anonymous_user_id[4:].isdigit()
else None
)
# 保持历史语义匿名ID只来自历史 user_id不使用真实 wechat userid 覆盖
recovered_anonymous_user_id = (anonymous_user_id or None)
recovery_source = None
def has_vip_cached(uid: str) -> bool:
@@ -1216,9 +1335,9 @@ async def nomadvip_check_vip_by_email(item: dict):
if not has_vip_cached(linked_uid):
continue
matched_user_id = linked_uid
if linked_anonymous_user_id.startswith("user") and linked_anonymous_user_id[4:].isdigit():
if linked_anonymous_user_id:
recovered_anonymous_user_id = linked_anonymous_user_id
elif linked_uid.startswith("user") and linked_uid[4:].isdigit():
elif linked_uid:
recovered_anonymous_user_id = linked_uid
recovery_source = "vip_email_link"
break
@@ -1250,6 +1369,14 @@ async def nomadvip_check_vip_by_email(item: dict):
)
else:
_pb_upsert_email_link(admin_token, email, pb_user_id)
if wechat_userid:
_pb_bind_wechat_vip_link(
admin_token,
wechat_userid,
email=email,
pb_user_id=pb_user_id,
anonymous_user_id=recovered_anonymous_user_id,
)
logging.info(
"[payjsapi] check_vip_by_email recovered by %s matched_user_id=%s effectiveUserId=%s anonymousUserId=%s",