From 6c2dcecf8dbbb4f78f4e37f5df57de003cd008ba Mon Sep 17 00:00:00 2001
From: hejianhao <15708179461@qq.com>
Date: 星期五, 22 五月 2026 17:16:31 +0800
Subject: [PATCH] feat(whitelist): 新增全局白名单校验及拦截页面

---
 src/app.config.ts                          |    1 
 src/pages/not-on-whitelist/index.config.ts |    4 +
 src/pages/not-on-whitelist/index.tsx       |   29 +++++++
 src/pages/not-on-whitelist/index.less      |   12 +++
 src/app.tsx                                |   19 ++++
 src/utils/whitelist.ts                     |  134 +++++++++++++++++++++++++++++++++
 6 files changed, 198 insertions(+), 1 deletions(-)

diff --git a/src/app.config.ts b/src/app.config.ts
index a8dd114..40e9dd6 100644
--- a/src/app.config.ts
+++ b/src/app.config.ts
@@ -7,6 +7,7 @@
 
 if (process.env.TARO_ENV === 'weapp') {
   pages = [
+    'pages/not-on-whitelist/index',
     // 'pages/home/index',
     'pages/robotaxi/home/index',
     'pages/xpeng/index',
diff --git a/src/app.tsx b/src/app.tsx
index 6105237..0c3b1bd 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -23,6 +23,7 @@
 import { privicyShowManager } from './components/privacy-popup/utils';
 import { initPrivacyPopupState } from './reducers/privacyPopup';
 import { preloadLoginBgImageOnWx } from './utils/login-bg-utils';
+import { checkWhitelistAccess, redirectToWhitelistBlockPage } from './utils/whitelist';
 
 const store = configStore();
 const ENV = process.env.TARO_ENV;
@@ -93,7 +94,7 @@
     this.xpReportSend({ pageId: 'P10107', buttonId: 'B001' });
   }
 
-  componentDidShow() {
+  async componentDidShow() {
     wsManager.onAppShow();
     if (ENV === 'weapp' && Taro.canIUse('onBeforePageUnload') && Taro.canIUse('offBeforePageUnload')) {
       // @ts-ignore
@@ -106,6 +107,11 @@
       this.scene = router.scene;
     }
     if (router.scene === 1154) {
+      return;
+    }
+
+    const whitelistAccessResult = await this.handleWhitelistGuard();
+    if (whitelistAccessResult === 'deny') {
       return;
     }
 
@@ -127,6 +133,17 @@
 
   }
 
+  /**
+   * 执行全局白名单守卫。
+   */
+  handleWhitelistGuard = async () => {
+    const whitelistAccessResult = await checkWhitelistAccess();
+    if (whitelistAccessResult === 'deny') {
+      await redirectToWhitelistBlockPage();
+    }
+    return whitelistAccessResult;
+  }
+
   componentDidHide() {
     wsManager.onAppHide();
     if (ENV === 'weapp' && Taro.canIUse('offBeforePageUnload')) {
diff --git a/src/pages/not-on-whitelist/index.config.ts b/src/pages/not-on-whitelist/index.config.ts
new file mode 100644
index 0000000..73327bb
--- /dev/null
+++ b/src/pages/not-on-whitelist/index.config.ts
@@ -0,0 +1,4 @@
+export default {
+  navigationStyle: 'custom',
+  disableScroll: true,
+};
diff --git a/src/pages/not-on-whitelist/index.less b/src/pages/not-on-whitelist/index.less
new file mode 100644
index 0000000..d798164
--- /dev/null
+++ b/src/pages/not-on-whitelist/index.less
@@ -0,0 +1,12 @@
+.not-on-whitelist-page {
+  width: 100vw;
+  height: 100vh;
+  overflow: hidden;
+  background: #ffffff;
+}
+
+.not-on-whitelist-image {
+  width: 100%;
+  height: 100%;
+  display: block;
+}
diff --git a/src/pages/not-on-whitelist/index.tsx b/src/pages/not-on-whitelist/index.tsx
new file mode 100644
index 0000000..5a10592
--- /dev/null
+++ b/src/pages/not-on-whitelist/index.tsx
@@ -0,0 +1,29 @@
+import Taro, { useDidShow } from '@tarojs/taro';
+import { Image, View } from '@tarojs/components';
+import { syncWhitelistBlockPageAccess } from '../../utils/whitelist';
+import './index.less';
+
+/** 白名单拦截图 CDN 地址。 */
+const NOT_ON_WHITELIST_IMAGE_URL = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/not-on-whitelist.png';
+
+/**
+ * 非白名单拦截页。
+ */
+export default function NotOnWhitelist() {
+  useDidShow(() => {
+    if (typeof Taro.hideHomeButton === 'function') {
+      Taro.hideHomeButton();
+    }
+    void syncWhitelistBlockPageAccess();
+  });
+
+  return (
+    <View className="not-on-whitelist-page">
+      <Image
+        className="not-on-whitelist-image"
+        src={NOT_ON_WHITELIST_IMAGE_URL}
+        mode="aspectFill"
+      />
+    </View>
+  );
+}
diff --git a/src/utils/whitelist.ts b/src/utils/whitelist.ts
new file mode 100644
index 0000000..a337fef
--- /dev/null
+++ b/src/utils/whitelist.ts
@@ -0,0 +1,134 @@
+import Taro from '@tarojs/taro';
+import { isWhiteList } from '../robotaxi/api/home';
+import { checkOtp } from './tools';
+
+/** 白名单拦截页路由。 */
+export const WHITELIST_BLOCK_PAGE_URL = '/pages/not-on-whitelist/index';
+
+/** Robotaxi 首页路由。 */
+export const ROBOTAXI_HOME_PAGE_URL = '/pages/robotaxi/home/index';
+
+/** 白名单守卫结果。 */
+export type WhitelistAccessResult = 'pass' | 'guest' | 'deny' | 'skip';
+
+/** 白名单守卫豁免路由。 */
+const WHITELIST_EXEMPT_ROUTES = [
+  'pages/not-on-whitelist/index',
+  'pages/user/login',
+];
+
+/** 白名单请求并发锁。 */
+let whitelistCheckTask: Promise<WhitelistAccessResult> | null = null;
+
+/** 白名单拦截跳转锁。 */
+let isRedirectingToWhitelistBlockPage = false;
+
+/**
+ * 判断当前页面是否跳过白名单守卫。
+ */
+export function isWhitelistExemptPage(route: string) {
+  return WHITELIST_EXEMPT_ROUTES.includes(route);
+}
+
+/**
+ * 获取白名单拦截页地址。
+ */
+export function getWhitelistBlockPageUrl() {
+  return WHITELIST_BLOCK_PAGE_URL;
+}
+
+/**
+ * 获取 Robotaxi 首页地址。
+ */
+export function getRobotaxiHomePageUrl() {
+  return ROBOTAXI_HOME_PAGE_URL;
+}
+
+/**
+ * 获取当前页面路由。
+ */
+export function getCurrentRoute() {
+  const pages = Taro.getCurrentPages();
+  const currentPage = pages[pages.length - 1];
+  return currentPage?.route || '';
+}
+
+/**
+ * 校验当前访问是否允许进入 Robotaxi 业务页。
+ */
+export async function checkWhitelistAccess(options?: { ignoreRouteExempt?: boolean }): Promise<WhitelistAccessResult> {
+  const currentRoute = getCurrentRoute();
+  if (!options?.ignoreRouteExempt && isWhitelistExemptPage(currentRoute)) {
+    return 'skip';
+  }
+
+  if (whitelistCheckTask) {
+    return whitelistCheckTask;
+  }
+
+  whitelistCheckTask = (async () => {
+    const otp = await checkOtp(false);
+    if (!otp) {
+      return 'guest';
+    }
+
+    try {
+      const whitelistResult = await isWhiteList();
+      return whitelistResult ? 'pass' : 'deny';
+    } catch (error) {
+      console.error('白名单校验失败', error);
+      return 'pass';
+    }
+  })();
+
+  return whitelistCheckTask.finally(() => {
+    whitelistCheckTask = null;
+  });
+}
+
+/**
+ * 跳转到白名单拦截页。
+ */
+export async function redirectToWhitelistBlockPage() {
+  const currentRoute = getCurrentRoute();
+  if (isRedirectingToWhitelistBlockPage || currentRoute === WHITELIST_BLOCK_PAGE_URL.replace(/^\//, '')) {
+    return;
+  }
+
+  isRedirectingToWhitelistBlockPage = true;
+  try {
+    await Taro.reLaunch({
+      url: WHITELIST_BLOCK_PAGE_URL,
+    });
+  } finally {
+    setTimeout(() => {
+      isRedirectingToWhitelistBlockPage = false;
+    }, 300);
+  }
+}
+
+/**
+ * 跳回 Robotaxi 首页。
+ */
+export async function redirectToRobotaxiHomePage() {
+  const currentRoute = getCurrentRoute();
+  if (currentRoute === ROBOTAXI_HOME_PAGE_URL.replace(/^\//, '')) {
+    return;
+  }
+
+  await Taro.reLaunch({
+    url: ROBOTAXI_HOME_PAGE_URL,
+  });
+}
+
+/**
+ * 白名单拦截页自恢复:白名单命中后自动返回首页。
+ */
+export async function syncWhitelistBlockPageAccess() {
+  const whitelistAccessResult = await checkWhitelistAccess({ ignoreRouteExempt: true });
+  // 白名单页不允许未登录用户停留,游客态或白名单恢复后都回首页。
+  if (whitelistAccessResult === 'guest' || whitelistAccessResult === 'pass') {
+    await redirectToRobotaxiHomePage();
+  }
+  return whitelistAccessResult;
+}

--
Gitblit v1.7.1