expo module 接入 第三方 sdk

698 阅读8分钟

🏢 Expo 项目接入企业微信 SDK 完整指南

在 Expo 项目中集成企业微信 SDK,实现免密登录、应用内跳转等功能的完整实践教程。

📖 前言

企业微信作为企业内部主要的沟通和办公工具,与移动应用的集成能够大大提升用户体验和工作效率。本文将详细介绍如何在 Expo 项目中接入企业微信 SDK,实现以下核心功能:

  • 🔐 免密登录 - 通过企微授权快速登录应用
  • 🔗 深度链接 - 支持企微内打开应用页面
  • 📱 应用分享 - 分享内容到企微会话
  • 🚀 快速跳转 - 从应用跳转到企微指定页面

🎯 技术方案

架构设计

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   企业微信客户端  │───│   原生 SDK 层    │───│   Expo 应用层    │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                        │                        │
    OAuth 授权              JNI/OC 接口              JS Bridge
         │                        │                        │
    ┌─────▼─────┐          ┌─────▼─────┐          ┌─────▼─────┐
    │  授权码   │          │ 自定义模块 │          │ React 组件 │
    └───────────┘          └───────────┘          └───────────┘

技术栈

  • Expo SDK 53+ - 支持自定义原生模块
  • 企业微信 Android SDK - 提供原生能力
  • 企业微信 iOS SDK - iOS 平台支持
  • Expo Modules API - 桥接原生和 JS 层
  • Deep Linking - 处理应用间跳转

🛠️ 环境准备

1. 企业微信开发环境

# 1. 注册企业微信开发者账号
# 访问:https://developer.work.weixin.qq.com/

# 2. 创建企业内部应用
# 获取以下信息:
# - CorpID(企业ID)
# - AgentID(应用ID)  
# - Secret(应用密钥)

# 3. 配置应用回调域名
# 在应用配置中设置:
# - 可信域名
# - OAuth2.0 网页授权回调域名

2. 下载企业微信 SDK

# Android SDK
# 下载地址:https://developer.work.weixin.qq.com/resource/1000005
# 解压后得到 wxwork.aar 文件

# iOS SDK  
# 下载地址:https://developer.work.weixin.qq.com/resource/1000005
# 解压后得到 WWKApi.framework 文件

3. 项目要求

# 确保项目支持自定义原生模块
npx expo install expo-dev-client

# 安装必要依赖
yarn add expo-linking expo-web-browser
npx expo install expo-constants

📦 SDK 集成步骤

第一步:创建自定义 Expo 模块

# 在项目根目录创建模块目录
mkdir -p modules/wework-module

# 创建模块配置文件
touch modules/wework-module/expo-module.config.json

创建 modules/wework-module/expo-module.config.json:

{
  "platforms": ["ios", "android"],
  "ios": {
    "modules": ["WeworkModule"]
  },
  "android": {
    "modules": ["expo.modules.weworkmodule.WeworkModule"]
  }
}

第二步:Android 平台集成

1. 配置 Android 模块

创建 modules/wework-module/android/build.gradle:

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

group = 'expo.modules.weworkmodule'
version = '0.1.0'

buildscript {
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
  if (expoModulesCorePlugin.exists()) {
    apply from: expoModulesCorePlugin
    applyKotlinExpoModulesCorePlugin()
  }

  ext {
    kotlin_version = '1.8.10'
  }
  dependencies {
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  }
}

dependencies {
  implementation project(':expo-modules-core')
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
  
  // 企业微信 SDK
  implementation files('libs/wxwork.aar')
}

android {
  compileSdkVersion safeExtGet("compileSdkVersion", 34)

  compileOptions {
    sourceCompatibility JavaVersion.VERSION_11
    targetCompatibility JavaVersion.VERSION_11
  }

  kotlinOptions {
    jvmTarget = JavaVersion.VERSION_11.majorVersion
  }

  namespace "expo.modules.weworkmodule"
  defaultConfig {
    minSdkVersion safeExtGet("minSdkVersion", 21)
    targetSdkVersion safeExtGet("targetSdkVersion", 34)
    versionCode 1
    versionName "1.0.0"
  }
}

repositories {
  mavenCentral()
}
2. 复制 SDK 文件
# 创建 libs 目录并复制 SDK
mkdir -p modules/wework-module/android/libs
cp /path/to/wxwork.aar modules/wework-module/android/libs/
3. 创建 Android 原生模块

创建 modules/wework-module/android/src/main/java/expo/modules/weworkmodule/WeworkModule.kt:

package expo.modules.weworkmodule

import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
import expo.modules.kotlin.Promise
import expo.modules.kotlin.exception.Exceptions

import android.content.Context
import android.content.Intent
import com.tencent.wework.api.IWWAPI
import com.tencent.wework.api.WWAPIFactory
import com.tencent.wework.api.model.*

class WeworkModule : Module() {
  private var wwapi: IWWAPI? = null
  private val context: Context
    get() = appContext.reactContext ?: throw Exceptions.ReactContextLost()

  override fun definition() = ModuleDefinition {
    Name("WeworkModule")

    OnCreate {
      // 初始化企业微信 API
      wwapi = WWAPIFactory.createWWAPI(context)
    }

    // 检查是否安装企业微信
    AsyncFunction("isWeworkInstalled") { promise: Promise ->
      try {
        val isInstalled = wwapi?.isWWAppInstalled ?: false
        promise.resolve(isInstalled)
      } catch (e: Exception) {
        promise.reject("CHECK_INSTALLATION_ERROR", e.message, e)
      }
    }

    // 企业微信授权登录
    AsyncFunction("weworkAuth") { corpId: String, agentId: String, state: String, promise: Promise ->
      try {
        if (wwapi?.isWWAppInstalled != true) {
          promise.reject("WEWORK_NOT_INSTALLED", "企业微信未安装", null)
          return@AsyncFunction
        }

        val req = WWAuthMessage.Req().apply {
          sch = "your-app-scheme" // 应用回调 scheme
          appId = corpId
          agentId = agentId
          state = state
        }

        val result = wwapi?.sendMessage(req) ?: false
        if (result) {
          promise.resolve(true)
        } else {
          promise.reject("AUTH_FAILED", "授权请求发送失败", null)
        }
      } catch (e: Exception) {
        promise.reject("AUTH_ERROR", e.message, e)
      }
    }

    // 打开企业微信会话
    AsyncFunction("openWeworkChat") { chatId: String, promise: Promise ->
      try {
        val req = WWOpenChatMessage.Req().apply {
          chatId = chatId
        }

        val result = wwapi?.sendMessage(req) ?: false
        promise.resolve(result)
      } catch (e: Exception) {
        promise.reject("OPEN_CHAT_ERROR", e.message, e)
      }
    }
  }
}
4. 创建回调处理 Activity

创建 modules/wework-module/android/src/main/java/expo/modules/weworkmodule/WXEntryActivity.kt:

package expo.modules.weworkmodule

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import com.tencent.wework.api.IWWAPI
import com.tencent.wework.api.IWWAPIEventHandler
import com.tencent.wework.api.WWAPIFactory
import com.tencent.wework.api.model.BaseMessage

class WXEntryActivity : Activity(), IWWAPIEventHandler {
    private var wwapi: IWWAPI? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        wwapi = WWAPIFactory.createWWAPI(this)
        wwapi?.handleIntent(intent, this)
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        setIntent(intent)
        wwapi?.handleIntent(intent, this)
    }

    override fun onMessage(message: BaseMessage?) {
        // 处理企业微信回调
        when (message) {
            is com.tencent.wework.api.model.WWAuthMessage.Resp -> {
                handleAuthResponse(message)
            }
        }
        finish()
    }

    private fun handleAuthResponse(resp: com.tencent.wework.api.model.WWAuthMessage.Resp) {
        when (resp.errCode) {
            0 -> {
                // 授权成功,发送结果到 React Native
                val intent = Intent("WEWORK_AUTH_SUCCESS").apply {
                    putExtra("code", resp.code)
                    putExtra("state", resp.state)
                }
                sendBroadcast(intent)
            }
            -2 -> {
                // 用户取消
                sendBroadcast(Intent("WEWORK_AUTH_CANCELLED"))
            }
            else -> {
                // 授权失败
                val intent = Intent("WEWORK_AUTH_FAILED").apply {
                    putExtra("errCode", resp.errCode)
                    putExtra("errStr", resp.errStr)
                }
                sendBroadcast(intent)
            }
        }
    }
}

第三步:iOS 平台集成

1. 创建 iOS 模块配置

创建 modules/wework-module/ios/WeworkModule.podspec:

Pod::Spec.new do |s|
  s.name           = 'WeworkModule'
  s.version        = '1.0.0'
  s.summary        = 'A sample Expo module'
  s.description    = 'A sample Expo module for Wework integration'
  s.author         = ''
  s.homepage       = 'https://docs.expo.dev/modules/'
  s.platforms      = { :ios => '13.0', :tvos => '13.0' }
  s.source         = { git: '' }
  s.static_framework = true

  s.dependency 'ExpoModulesCore'

  # 企业微信 SDK
  s.vendored_frameworks = 'WWKApi.framework'

  # Swift/Objective-C compatibility
  s.pod_target_xcconfig = {
    'DEFINES_MODULE' => 'YES',
    'SWIFT_COMPILATION_MODE' => 'wholemodule'
  }

  s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
end
2. 复制 iOS SDK
# 复制企业微信 iOS SDK
cp -R /path/to/WWKApi.framework modules/wework-module/ios/
3. 创建 iOS 原生模块

创建 modules/wework-module/ios/WeworkModule.swift:

import ExpoModulesCore
import WWKApi

public class WeworkModule: Module {
  public func definition() -> ModuleDefinition {
    Name("WeworkModule")

    OnCreate {
      // 初始化企业微信 SDK
      WWKApi.registerApp("your-corp-id")
    }

    // 检查是否安装企业微信
    AsyncFunction("isWeworkInstalled") { (promise: Promise) in
      let isInstalled = WWKApi.isAppInstalled()
      promise.resolve(isInstalled)
    }

    // 企业微信授权登录
    AsyncFunction("weworkAuth") { (corpId: String, agentId: String, state: String, promise: Promise) in
      guard WWKApi.isAppInstalled() else {
        promise.reject("WEWORK_NOT_INSTALLED", "企业微信未安装")
        return
      }

      let req = WWKSendAuthReq()
      req.sch = "your-app-scheme"
      req.appId = corpId
      req.agentId = agentId
      req.state = state

      let result = WWKApi.sendReq(req)
      if result {
        promise.resolve(true)
      } else {
        promise.reject("AUTH_FAILED", "授权请求发送失败")
      }
    }

    // 打开企业微信会话
    AsyncFunction("openWeworkChat") { (chatId: String, promise: Promise) in
      let req = WWKOpenChatReq()
      req.chatId = chatId

      let result = WWKApi.sendReq(req)
      promise.resolve(result)
    }
  }
}

第四步:JavaScript 层封装

1. 创建模块接口

创建 modules/wework-module/src/WeworkModule.ts:

import { NativeModule, requireNativeModule } from 'expo';

export interface WeworkAuthResult {
  code?: string;
  state?: string;
  errCode?: number;
  errStr?: string;
}

export interface WeworkModuleEvents {
  onAuthResult: (result: WeworkAuthResult) => void;
}

class WeworkModule extends NativeModule<WeworkModuleEvents> {
  /**
   * 检查是否安装企业微信
   */
  async isWeworkInstalled(): Promise<boolean> {
    return await this.nativeModule.isWeworkInstalled();
  }

  /**
   * 企业微信授权登录
   * @param corpId 企业ID
   * @param agentId 应用ID
   * @param state 状态参数
   */
  async weworkAuth(corpId: string, agentId: string, state: string): Promise<boolean> {
    return await this.nativeModule.weworkAuth(corpId, agentId, state);
  }

  /**
   * 打开企业微信会话
   * @param chatId 会话ID
   */
  async openWeworkChat(chatId: string): Promise<boolean> {
    return await this.nativeModule.openWeworkChat(chatId);
  }

  /**
   * 注册授权结果回调
   */
  onAuthResult(callback: (result: WeworkAuthResult) => void): void {
    this.addListener('onAuthResult', callback);
  }

  /**
   * 移除授权结果回调
   */
  removeAuthResultListener(): void {
    this.removeAllListeners('onAuthResult');
  }
}

export default requireNativeModule<WeworkModule>('WeworkModule');
2. 创建 React Hook

创建 modules/wework-module/src/useWework.ts:

import { useEffect, useCallback, useState } from 'react';
import { Platform } from 'react-native';
import * as Linking from 'expo-linking';
import WeworkModule, { WeworkAuthResult } from './WeworkModule';

interface UseWeworkConfig {
  corpId: string;
  agentId: string;
  scheme: string;
}

export const useWework = (config: UseWeworkConfig) => {
  const [isInstalled, setIsInstalled] = useState<boolean>(false);
  const [isLoading, setIsLoading] = useState<boolean>(false);

  // 检查安装状态
  useEffect(() => {
    const checkInstallation = async () => {
      try {
        const installed = await WeworkModule.isWeworkInstalled();
        setIsInstalled(installed);
      } catch (error) {
        console.error('检查企业微信安装状态失败:', error);
        setIsInstalled(false);
      }
    };

    checkInstallation();
  }, []);

  // 处理深度链接回调
  useEffect(() => {
    const handleDeepLink = (url: string) => {
      const parsed = Linking.parse(url);
      if (parsed.scheme === config.scheme && parsed.hostname === 'wework') {
        // 处理企业微信回调
        const { code, state, errCode, errStr } = parsed.queryParams || {};
        
        const result: WeworkAuthResult = {
          code: code as string,
          state: state as string,
          errCode: errCode ? parseInt(errCode as string) : undefined,
          errStr: errStr as string,
        };

        // 触发回调事件
        WeworkModule.getEmitter().emit('onAuthResult', result);
      }
    };

    const subscription = Linking.addEventListener('url', ({ url }) => {
      handleDeepLink(url);
    });

    // 检查应用启动时的 URL
    Linking.getInitialURL().then((url) => {
      if (url) {
        handleDeepLink(url);
      }
    });

    return () => {
      subscription?.remove();
    };
  }, [config.scheme]);

  // 企业微信授权登录
  const login = useCallback(async (state?: string): Promise<WeworkAuthResult> => {
    if (!isInstalled) {
      throw new Error('企业微信未安装');
    }

    setIsLoading(true);

    return new Promise((resolve, reject) => {
      const authState = state || `auth_${Date.now()}`;
      
      // 注册一次性回调
      const handleAuthResult = (result: WeworkAuthResult) => {
        WeworkModule.removeAuthResultListener();
        setIsLoading(false);
        
        if (result.errCode === 0 || result.code) {
          resolve(result);
        } else {
          reject(new Error(result.errStr || '授权失败'));
        }
      };

      WeworkModule.onAuthResult(handleAuthResult);

      // 发起授权请求
      WeworkModule.weworkAuth(config.corpId, config.agentId, authState)
        .catch((error) => {
          WeworkModule.removeAuthResultListener();
          setIsLoading(false);
          reject(error);
        });
    });
  }, [isInstalled, config]);

  // 打开企业微信会话
  const openChat = useCallback(async (chatId: string): Promise<boolean> => {
    if (!isInstalled) {
      throw new Error('企业微信未安装');
    }

    return await WeworkModule.openWeworkChat(chatId);
  }, [isInstalled]);

  return {
    isInstalled,
    isLoading,
    login,
    openChat,
  };
};

⚙️ 应用配置

1. 更新 Expo 配置

更新 app.config.ts:

import { ExpoConfig } from "expo/config";

export default ({ config }: { config: ExpoConfig }) => {
  const weworkScheme = process.env.WEWORK_SCHEME || "yourapp";
  const corpId = process.env.WEWORK_CORP_ID || "your-corp-id";
  const agentId = process.env.WEWORK_AGENT_ID || "your-agent-id";

  return {
    ...config,
    scheme: weworkScheme,
    plugins: [
      // 其他插件...
      "./plugins/withWeworkConfig.js"
    ],
    android: {
      ...config.android,
      intentFilters: [
        {
          action: "VIEW",
          category: ["DEFAULT", "BROWSABLE"],
          data: [
            {
              scheme: weworkScheme
            }
          ]
        }
      ]
    },
    ios: {
      ...config.ios,
      infoPlist: {
        CFBundleURLTypes: [
          {
            CFBundleURLName: "wework-auth",
            CFBundleURLSchemes: [weworkScheme]
          }
        ],
        LSApplicationQueriesSchemes: ["wxwork"]
      }
    },
    extra: {
      ...config.extra,
      wework: {
        corpId,
        agentId,
        scheme: weworkScheme
      }
    }
  };
};

2. 创建配置插件

创建 plugins/withWeworkConfig.js:

const { withAndroidManifest, withInfoPlist } = require('@expo/config-plugins');

function withWeworkConfig(config) {
  // Android 配置
  config = withAndroidManifest(config, (config) => {
    const manifest = config.modResults;
    
    // 添加企业微信权限
    if (!manifest.manifest['uses-permission']) {
      manifest.manifest['uses-permission'] = [];
    }
    
    manifest.manifest['uses-permission'].push({
      $: { 'android:name': 'android.permission.INTERNET' }
    });

    // 添加 WXEntryActivity
    const application = manifest.manifest.application[0];
    if (!application.activity) {
      application.activity = [];
    }

    application.activity.push({
      $: {
        'android:name': 'expo.modules.weworkmodule.WXEntryActivity',
        'android:exported': 'true',
        'android:launchMode': 'singleTop'
      },
      'intent-filter': [
        {
          action: [{ $: { 'android:name': 'android.intent.action.VIEW' } }],
          category: [
            { $: { 'android:name': 'android.intent.category.DEFAULT' } },
            { $: { 'android:name': 'android.intent.category.BROWSABLE' } }
          ],
          data: [{ $: { 'android:scheme': config.extra?.wework?.scheme || 'yourapp' } }]
        }
      ]
    });

    return config;
  });

  // iOS 配置
  config = withInfoPlist(config, (config) => {
    const infoPlist = config.modResults;
    
    // 确保 URL Schemes 配置
    if (!infoPlist.CFBundleURLTypes) {
      infoPlist.CFBundleURLTypes = [];
    }

    const weworkScheme = config.extra?.wework?.scheme || 'yourapp';
    infoPlist.CFBundleURLTypes.push({
      CFBundleURLName: 'wework-auth',
      CFBundleURLSchemes: [weworkScheme]
    });

    // 添加查询白名单
    if (!infoPlist.LSApplicationQueriesSchemes) {
      infoPlist.LSApplicationQueriesSchemes = [];
    }
    
    if (!infoPlist.LSApplicationQueriesSchemes.includes('wxwork')) {
      infoPlist.LSApplicationQueriesSchemes.push('wxwork');
    }

    return config;
  });

  return config;
}

module.exports = withWeworkConfig;

🎬 使用示例

1. 在组件中使用

import React, { useEffect } from 'react';
import { View, Text, TouchableOpacity, Alert } from 'react-native';
import Constants from 'expo-constants';
import { useWework } from '@/modules/wework-module';

export const LoginScreen = () => {
  const weworkConfig = Constants.expoConfig?.extra?.wework;
  const { isInstalled, isLoading, login, openChat } = useWework(weworkConfig);

  useEffect(() => {
    if (!isInstalled) {
      Alert.alert('提示', '请先安装企业微信客户端');
    }
  }, [isInstalled]);

  const handleWeworkLogin = async () => {
    try {
      const result = await login();
      
      if (result.code) {
        // 使用授权码换取用户信息
        console.log('授权成功,授权码:', result.code);
        // 调用后端接口换取 access_token 和用户信息
        await exchangeCodeForToken(result.code);
      }
    } catch (error) {
      Alert.alert('授权失败', error.message);
    }
  };

  const exchangeCodeForToken = async (code: string) => {
    // 调用后端接口
    const response = await fetch('/api/wework/oauth', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ code })
    });
    
    const data = await response.json();
    // 处理用户信息和 token
  };

  return (
    <View className="flex-1 justify-center items-center p-4">
      <Text className="text-2xl font-bold mb-8">登录</Text>
      
      {isInstalled ? (
        <TouchableOpacity
          onPress={handleWeworkLogin}
          disabled={isLoading}
          className="bg-blue-500 px-6 py-3 rounded-lg"
        >
          <Text className="text-white font-semibold">
            {isLoading ? '授权中...' : '企业微信登录'}
          </Text>
        </TouchableOpacity>
      ) : (
        <Text className="text-red-500">请先安装企业微信</Text>
      )}
    </View>
  );
};

2. 后端 OAuth 处理

// 后端接口示例 (Node.js)
app.post('/api/wework/oauth', async (req, res) => {
  const { code } = req.body;
  
  try {
    // 1. 使用 code 换取 access_token
    const tokenResponse = await fetch(
      `https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${CORP_ID}&corpsecret=${SECRET}`
    );
    const { access_token } = await tokenResponse.json();
    
    // 2. 获取用户信息
    const userResponse = await fetch(
      `https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=${access_token}&code=${code}`
    );
    const userInfo = await userResponse.json();
    
    // 3. 生成应用 token
    const appToken = generateJWT(userInfo);
    
    res.json({
      success: true,
      token: appToken,
      user: userInfo
    });
  } catch (error) {
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

🚨 常见问题和坑点

1. Android 相关问题

问题:AAR 文件无法识别
# 解决方案:检查 build.gradle 配置
# 确保正确引用 AAR 文件
implementation files('libs/wxwork.aar')

# 如果还是有问题,尝试手动解压 AAR
unzip wxwork.aar -d wxwork/
# 然后引用 classes.jar
implementation files('wxwork/classes.jar')
问题:混淆导致的问题

android/app/proguard-rules.pro 中添加:

# 企业微信 SDK
-keep class com.tencent.wework.api.** { *; }
-keep class com.tencent.wework.api.model.** { *; }
-dontwarn com.tencent.wework.api.**
问题:WXEntryActivity 无法接收回调
<!-- 确保在 AndroidManifest.xml 中正确配置 -->
<activity
    android:name=".wxapi.WXEntryActivity"
    android:exported="true"
    android:launchMode="singleTop"
    android:theme="@android:style/Theme.Translucent.NoTitleBar">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="你的应用scheme" />
    </intent-filter>
</activity>

2. iOS 相关问题

问题:Framework 找不到
# 解决方案:检查 Podspec 配置
s.vendored_frameworks = 'WWKApi.framework'

# 确保 Framework 路径正确
s.framework_search_paths = '$(PODS_TARGET_SRCROOT)'
问题:编译报错 "OBJC_CLASS$_WWKApi"
# 在 Podfile 中添加
pod 'WeworkModule', :path => '../modules/wework-module'

# 如果还是有问题,尝试手动链接
target.build_configurations.each do |config|
  config.build_settings['OTHER_LDFLAGS'] ||= ['$(inherited)']
  config.build_settings['OTHER_LDFLAGS'] << '-ObjC'
end
问题:URL Scheme 不生效

确保在 Info.plist 中正确配置:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>wework-auth</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>你的应用scheme</string>
        </array>
    </dict>
</array>

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>wxwork</string>
</array>

3. JavaScript 层问题

问题:模块找不到
# 确保正确安装开发客户端
npx expo install expo-dev-client

# 重新构建项目
npx expo run:android --clear
npx expo run:ios --clear
问题:深度链接不工作
// 确保正确处理 Linking
import * as Linking from 'expo-linking';

// 检查 URL 格式
const url = await Linking.getInitialURL();
console.log('Initial URL:', url);

// 确保 scheme 匹配
const parsed = Linking.parse(url);
console.log('Parsed URL:', parsed);

4. 网络和权限问题

问题:网络请求失败
<!-- Android 9+ 需要允许 HTTP 请求 -->
<!-- android/app/src/main/res/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">qyapi.weixin.qq.com</domain>
    </domain-config>
</network-security-config>
问题:企业微信检测不到应用
# 确保应用签名与企业微信后台配置一致
# 获取应用签名
keytool -list -v -keystore your-release-key.keystore

# 配置企业微信后台
# 应用详情 -> 功能设置 -> 网页授权及JS-SDK -> 可信域名

🧪 测试验证

1. 开发环境测试

# 启动开发服务器
yarn start

# 在模拟器中测试
yarn android
yarn ios

# 检查日志
npx react-native log-android
npx react-native log-ios

2. 真机测试

# 构建测试版本
eas build --platform android --profile preview
eas build --platform ios --profile preview

# 安装到真机测试
# 确保设备已安装企业微信

3. 功能测试清单

  • 安装检测 - 正确检测企业微信安装状态
  • 授权流程 - 完整的授权登录流程
  • 回调处理 - 正确处理授权回调
  • 错误处理 - 各种异常情况的处理
  • 深度链接 - 应用间跳转功能
  • 会话跳转 - 跳转到指定企微会话

📚 最佳实践

1. 错误处理

export class WeworkError extends Error {
  constructor(
    message: string,
    public code: string,
    public details?: any
  ) {
    super(message);
    this.name = 'WeworkError';
  }
}

export const handleWeworkError = (error: any): WeworkError => {
  if (error.code === 'WEWORK_NOT_INSTALLED') {
    return new WeworkError('请先安装企业微信', 'NOT_INSTALLED');
  }
  
  if (error.code === 'AUTH_CANCELLED') {
    return new WeworkError('用户取消授权', 'USER_CANCELLED');
  }
  
  return new WeworkError('未知错误', 'UNKNOWN', error);
};

2. 配置管理

interface WeworkConfig {
  corpId: string;
  agentId: string;
  scheme: string;
  secret?: string; // 仅后端使用
}

export const getWeworkConfig = (): WeworkConfig => {
  const config = Constants.expoConfig?.extra?.wework;
  
  if (!config?.corpId || !config?.agentId) {
    throw new Error('企业微信配置缺失');
  }
  
  return config;
};

3. 状态管理

// 使用 Zustand 管理企业微信状态
interface WeworkState {
  isInstalled: boolean;
  isLoggedIn: boolean;
  userInfo: any;
  setInstalled: (installed: boolean) => void;
  setUserInfo: (user: any) => void;
  logout: () => void;
}

export const useWeworkStore = create<WeworkState>((set) => ({
  isInstalled: false,
  isLoggedIn: false,
  userInfo: null,
  setInstalled: (installed) => set({ isInstalled: installed }),
  setUserInfo: (user) => set({ userInfo: user, isLoggedIn: !!user }),
  logout: () => set({ userInfo: null, isLoggedIn: false }),
}));

📝 总结

企业微信 SDK 集成虽然步骤较多,但按照本指南的步骤操作,可以避免大部分常见问题。关键要点:

  1. 环境配置 - 确保企业微信后台配置正确
  2. 原生模块 - 正确集成 Android 和 iOS SDK
  3. 深度链接 - 处理好应用间跳转和回调
  4. 错误处理 - 完善的异常处理机制
  5. 测试验证 - 在真机上完整测试功能

完成集成后,您的应用将具备企业微信免密登录、会话跳转等强大功能,大大提升企业用户的使用体验。


💡 提示: 企业微信 SDK 更新较为频繁,建议定期关注官方文档和更新日志,及时升级 SDK 版本。