hejianhao
2026-06-26 efea577abcd272e921ae3716165f92f9bf377d73
feat(bluetooth-access): 优化蓝牙权限检测逻辑,兼容新版App权限接口

提取复用的权限状态检测函数,优先调用新版App级权限接口,旧环境自动回退至原有接口,统一各处权限检查逻辑
1个文件已修改
110 ■■■■■ 已修改文件
src/robotaxi/pages/begin-route/hooks/use-bluetooth-access.ts 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-bluetooth-access.ts
@@ -13,6 +13,7 @@
type BluetoothAccessStatus = 'system_off' | 'unauthorized' | 'authorized';
type BluetoothErrorType = 'system_off' | 'permission_deny' | 'env_unsupported' | 'unknown';
type MiniappBluetoothAuthState = 'authorized' | 'denied' | 'unknown';
type BluetoothOpenResult = {
  opened: boolean;
  errorType: BluetoothErrorType;
@@ -34,6 +35,37 @@
// 车端钥匙下发失败时的描述文案与文字颜色
const UNLOCK_TOKEN_FAILED_TEXT = '蓝牙解锁失败, 请点击右下方按键解锁车门';
const UNLOCK_TOKEN_FAILED_COLOR = '#FF4D4F';
/**
 * 兼容读取小程序蓝牙权限状态。
 * 优先使用新版 App 级权限接口,旧环境再回退到 getSetting。
 */
async function readMiniappBluetoothAuthState() : Promise<MiniappBluetoothAuthState> {
  try {
    const appAuthorizeSetting = await Promise.resolve((Taro as any).getAppAuthorizeSetting?.());
    const bluetoothAuthorized = appAuthorizeSetting?.bluetoothAuthorized;
    if (typeof bluetoothAuthorized === 'string') {
      if (bluetoothAuthorized === 'authorized') {
        return 'authorized';
      }
      if (bluetoothAuthorized === 'denied' || bluetoothAuthorized === 'not determined') {
        return 'denied';
      }
    }
  } catch (error) {
  }
  try {
    const setting = await Taro.getSetting();
    const authSetting = setting?.authSetting || {};
    if (Object.prototype.hasOwnProperty.call(authSetting, BLUETOOTH_SCOPE)) {
      return authSetting[BLUETOOTH_SCOPE] === true ? 'authorized' : 'denied';
    }
  } catch (error) {
  }
  return 'unknown';
}
/**
 * 输出蓝牙权限链路日志,便于排查授权、系统蓝牙和环境问题。
@@ -114,9 +146,30 @@
   */
  const getMiniappBluetoothAuthState = useCallback(async () => {
    try {
      const appAuthorizeSetting = await Promise.resolve((Taro as any).getAppAuthorizeSetting?.());
      const bluetoothAuthorized = appAuthorizeSetting?.bluetoothAuthorized;
      logBluetoothAccess('读取小程序权限设置', {
        bluetoothAuthorized,
        appAuthorizeSetting
      });
      if (typeof bluetoothAuthorized === 'string') {
        if (bluetoothAuthorized === 'authorized') {
          return 'authorized';
        }
        if (bluetoothAuthorized === 'denied' || bluetoothAuthorized === 'not determined') {
          return 'denied';
        }
      }
    } catch (error) {
      logBluetoothAccess('读取 App 级蓝牙权限设置失败', {
        error
      });
    }
    try {
      const setting = await Taro.getSetting();
      const authSetting = setting?.authSetting || {};
      logBluetoothAccess('读取小程序权限设置', {
      logBluetoothAccess('回退读取小程序权限设置', {
        bluetoothScope: authSetting?.[BLUETOOTH_SCOPE],
        authSetting
      });
@@ -124,10 +177,11 @@
        return authSetting[BLUETOOTH_SCOPE] === true ? 'authorized' : 'denied';
      }
    } catch (error) {
      logBluetoothAccess('读取小程序权限设置失败', {
      logBluetoothAccess('回退读取小程序权限设置失败', {
        error
      });
    }
    logBluetoothAccess('小程序蓝牙权限状态未知');
    return 'unknown';
  }, []);
@@ -211,44 +265,64 @@
      isAuthorizeProtectionActive
    });
    try {
      const setting = await Taro.getSetting();
      const appAuthorizeSetting = await Promise.resolve((Taro as any).getAppAuthorizeSetting?.());
      const bluetoothAuthorized = appAuthorizeSetting?.bluetoothAuthorized;
      logBluetoothAccess('刷新蓝牙状态时读取设置', {
        bluetoothScope: setting?.authSetting?.['scope.bluetooth'],
        authSetting: setting?.authSetting || {}
        bluetoothAuthorized,
        appAuthorizeSetting
      });
      if (setting?.authSetting?.['scope.bluetooth']) {
      if (bluetoothAuthorized === 'authorized') {
        hasMiniappAuth = true;
        recentAuthorizedAtRef.current = 0;
      }
    } catch (error) {
      logBluetoothAccess('刷新蓝牙状态时读取设置失败', {
      logBluetoothAccess('刷新蓝牙状态时读取 App 级权限失败', {
        error
      });
    }
    if (!hasMiniappAuth) {
      const initialMiniappAuthState = await readMiniappBluetoothAuthState();
      logBluetoothAccess('刷新蓝牙状态时统一确认权限结果', {
        initialMiniappAuthState
      });
      if (initialMiniappAuthState === 'authorized') {
        hasMiniappAuth = true;
        Taro.setStorageSync(BLUETOOTH_AUTH_KEY, true);
        recentAuthorizedAtRef.current = 0;
      }
    }
    const syncMiniappAuthAfterSystemPrompt = async (scene: string) => {
      const latestMiniappAuthState = await getMiniappBluetoothAuthState();
      logBluetoothAccess(`${scene}后再次确认小程序蓝牙权限`, {
        latestMiniappAuthState
      });
      if (latestMiniappAuthState === 'authorized') {
        hasMiniappAuth = true;
        Taro.setStorageSync(BLUETOOTH_AUTH_KEY, true);
        recentAuthorizedAtRef.current = 0;
        return true;
      }
      return false;
    };
    let systemBluetoothEnabled = false;
    const openResult = await openBluetoothAdapterSafe();
    if (openResult.opened) {
      systemBluetoothEnabled = true;
      if (!hasMiniappAuth) {
        const latestMiniappAuthState = await getMiniappBluetoothAuthState();
        logBluetoothAccess('蓝牙适配器打开后再次确认小程序蓝牙权限', {
          latestMiniappAuthState
        });
        if (latestMiniappAuthState === 'authorized') {
          hasMiniappAuth = true;
          Taro.setStorageSync(BLUETOOTH_AUTH_KEY, true);
          recentAuthorizedAtRef.current = 0;
        }
        await syncMiniappAuthAfterSystemPrompt('蓝牙适配器打开');
      }
    } else if (openResult.errorType === 'system_off') {
      systemBluetoothEnabled = false;
      recentAuthorizedAtRef.current = 0;
    } else if (openResult.errorType === 'permission_deny') {
      systemBluetoothEnabled = true;
      if (!isAuthorizeProtectionActive) {
      const authGrantedAfterPrompt = await syncMiniappAuthAfterSystemPrompt('蓝牙权限弹窗处理');
      if (!authGrantedAfterPrompt && !isAuthorizeProtectionActive) {
        hasMiniappAuth = false;
      } else {
      } else if (!authGrantedAfterPrompt) {
        logBluetoothAccess('授权保护窗口内忽略瞬时 permission_deny', {
          errCode: openResult.error?.errCode,
          errMsg: openResult.error?.errMsg