diff --git a/app/routers/nomadvip.py b/app/routers/nomadvip.py index 799474a..5c2f83b 100644 --- a/app/routers/nomadvip.py +++ b/app/routers/nomadvip.py @@ -361,6 +361,15 @@ async def nomadvip_check_vip(user_id: str = ""): or str(wx_link.get("anonymous_user_id", "")).strip() ) wx_link_email = str(wx_link.get("email", "")).strip().lower() + if admin_token and linked_user_id and _uid_has_vip(pb, linked_user_id): + # 补齐标准关联表,避免后续链路只能依赖 raw_contact + if wx_link_email: + _pb_upsert_email_link( + admin_token, + wx_link_email, + linked_user_id, + user_id, + ) except Exception: linked_user_id = "" wx_link_email = "" @@ -501,6 +510,12 @@ def _pb_bind_wechat_vip_link( or None, "updated_at": int(time.time()), } + if not raw_contact["_vip_link"].get("anonymous_user_id"): + derived = _pb_find_anonymous_user_id_by_member( + admin_token, str(raw_contact["_vip_link"].get("pb_user_id") or "").strip() + ) + if derived: + raw_contact["_vip_link"]["anonymous_user_id"] = derived base = (PB_URL or "").rstrip("/") _http_session.patch( @@ -514,6 +529,58 @@ def _pb_bind_wechat_vip_link( ) +def _pb_find_anonymous_user_id_by_member( + admin_token: str, member_user_id: str +) -> str | None: + """ + 从会员用户 ID 反查对应历史支付匿名 user_id(user[0-9]+)。 + 路径:site_vip(user_id=member) -> order_id -> payments(order_id) -> user_id + """ + member_user_id = (member_user_id or "").strip() + if not member_user_id: + return None + base = (PB_URL or "").rstrip("/") + safe_member = _pb_escape(member_user_id) + site_resp = _http_session.get( + f"{base}/api/collections/site_vip/records", + params={ + "filter": f'user_id = "{safe_member}" && site_id = "vip"', + "perPage": 20, + "sort": "-created", + }, + headers={"Authorization": f"Bearer {admin_token}"}, + timeout=DEFAULT_HTTP_TIMEOUT, + ) + if site_resp.status_code != 200: + return None + site_items = (site_resp.json() or {}).get("items") or [] + seen_orders = set() + for sv in site_items: + order_id = str((sv or {}).get("order_id") or "").strip() + if not order_id or order_id in seen_orders: + continue + seen_orders.add(order_id) + safe_order = _pb_escape(order_id) + pay_resp = _http_session.get( + f"{base}/api/collections/payments/records", + params={ + "filter": f'order_id = "{safe_order}" && type = "vip"', + "perPage": 20, + "sort": "-created", + }, + headers={"Authorization": f"Bearer {admin_token}"}, + timeout=DEFAULT_HTTP_TIMEOUT, + ) + if pay_resp.status_code != 200: + continue + pay_items = (pay_resp.json() or {}).get("items") or [] + for payment in pay_items: + uid = str((payment or {}).get("user_id") or "").strip() + if uid.startswith("user") and uid[4:].isdigit(): + return uid + return None + + def _pb_get_user_by_id(admin_token: str, user_id: str) -> dict | None: base = (PB_URL or "").rstrip("/") response = _http_session.get(