小羊苏西 發表於 2025-12-2 09:40:57

前端使用阿里云图形验证码并且与安卓进行交互实现方法

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>流程如下</li><ul class="second_class_ul"><li>1.引入阿里云前端脚本 &amp; 基础配置 在&lt;head&gt; 里:</li><li>2.初始化阿里云验证码实例</li><li>3.验证码通过后,前端调用你后端接口</li></ul><li>跟安卓原生通讯的逻辑</li><ul class="second_class_ul"><li>与安卓约定的对象和方法</li><li>小结</li><li>完整代码:</li></ul><li>总结&nbsp;</li><ul class="second_class_ul"></ul></ul></div><p><strong>最近安卓同事那边没办法去用原生的图形验证码;要前端写个html内嵌进去,下面是流程如何实现的:</strong></p>
<p class="maodian"></p><h2>流程如下</h2>
<p class="maodian"></p><h3>1.引入阿里云前端脚本 &amp; 基础配置 在&lt;head&gt; 里:</h3>
<div class="jb51code"><pre class="brush:js;">&lt;script&gt;
    window.AliyunCaptchaConfig = {
      region: "cn",
      prefix: "1fnzmt",
    };
&lt;/script&gt;

&lt;script src="https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js"&gt;&lt;/script&gt;
</pre></div>
<blockquote><p>window.AliyunCaptchaConfig:阿里云验证码的全局配置 region: &ldquo;cn&rdquo;:中国大陆区域<br />prefix: &ldquo;1fnzmt&rdquo;:你在阿里云控制台那边分配的前缀<br />AliyunCaptcha.js:阿里云的前端验证码 SDK 脚本,加载后会在window 上挂 initAliyunCaptcha 之类的方法。</p></blockquote>
<p><strong>自己又定义了一个 CONFIG:</strong></p>
<div class="jb51code"><pre class="brush:js;">const CONFIG = {
    region: "cn",
    prefix: "1fnzmt",
    sceneId: "vjsqgp2k",
    mode: "popup",
    language: "cn",
    slideStyle: { width: 360, height: 40 },
    apiBaseURL: "http://39.96.184.160",
    endpoints: {
      captchaVerify: "/dev-api/android/sms/code"
    }
};
</pre></div>
<blockquote><p>sceneId: 阿里云验证码具体场景 ID。<br />apiBaseURL + endpoints.captchaVerify:你自己后端的验证码验证接口地址。</p></blockquote>
<p class="maodian"></p><h3>2.初始化阿里云验证码实例</h3>
<p>在 <code>CaptchaManager.init()</code> 里:</p>
<div class="jb51code"><pre class="brush:js;">window.initAliyunCaptcha({
    SceneId: CONFIG.sceneId,
    mode: CONFIG.mode,
    element: '#captcha-element',
    button: '#verify-btn',
    captchaVerifyCallback: this.captchaVerifyCallback.bind(this),
    onBizResultCallback: this.onBizResultCallback.bind(this),
    getInstance: (instance) =&gt; {
      this.captcha = instance;
      debugLog('验证码初始化成功');
    }
});
</pre></div>
<p>逻辑:</p>
<ul><li>element: &lsquo;#captcha-element&rsquo;:验证码挂载在页面中这个 div 上。</li><li>button: &lsquo;#verify-btn&rsquo;:点击&ldquo;开始验证&rdquo;按钮时,弹出/触发验证码。</li><li>captchaVerifyCallback:用户通过验证码后,阿里云前端 SDK 会给你一个 captchaVerifyParam,然后会调用这个回调。</li><li>onBizResultCallback:你业务侧验证成功/失败后,返回一个业务结果给前端,这个回调会被触发。</li></ul>
<p class="maodian"></p><h3>3.验证码通过后,前端调用你后端接口</h3>
<p>captchaVerifyCallback 的实现:</p>
<div class="jb51code"><pre class="brush:js;">async captchaVerifyCallback(captchaVerifyParam) {
    this.showStatus('验证中...', 'loading');

    try {
      document.getElementById('verify-btn').disabled = true;

      // 1. 先去原生拿手机号(下面第二部分会讲这个接口)
      const phoneNumber = await this.getPhoneNumber();
      debugLog('最终使用的手机号: ' + phoneNumber);

      // 2. 调用你后端的验证接口(目前是 GET 请求)
      const result = await this.apiRequest(CONFIG.endpoints.captchaVerify, {
            phoneNumber: phoneNumber,
            captchaVerifyParam: captchaVerifyParam
      });

      debugLog('后端返回结果: ' + JSON.stringify(result));

      // 3. 按你的后端返回格式判断是否成功
      const success = result &amp;&amp; result.code === 200 &amp;&amp; result.data === true;

      if (success) {
            this.showStatus('验证成功!', 'success');
      } else {
            const msg = (result &amp;&amp; result.msg) ? result.msg : '验证失败';
            this.showStatus(msg, 'error');
      }

      // 4. 返回给阿里云 SDK 一个统一结构
      return {
            captchaResult: success,
            bizResult: success
      };
    } catch (error) {
      debugLog('验证失败: ' + error.message);
      this.showStatus('验证失败: ' + error.message, 'error');
      return {
            captchaResult: false,
            bizResult: false
      };
    } finally {
      document.getElementById('verify-btn').disabled = false;
    }
}
</pre></div>
<p>调用后端的封装在 apiRequest里:</p>
<div class="jb51code"><pre class="brush:js;">async apiRequest(url, data) {
    // 1. 拼接完整 URL
    if (url.startsWith('http')) {
      fullUrl = url;
    } else if (url.startsWith('/')) {
      fullUrl = CONFIG.apiBaseURL + url;
    } else {
      fullUrl = CONFIG.apiBaseURL + '/' + url;
    }

    // 2. 把 data 转成 query 参数 ?phoneNumber=xxx&amp;captchaVerifyParam=yyy
    const queryParams = new URLSearchParams();
    for (const key in data) {
      if (data !== null &amp;&amp; data !== undefined) {
            queryParams.append(key, data);
      }
    }
    ...

    const response = await fetch(fullUrl, {
      method: 'GET',
      headers: {
            'Content-Type': 'application/json',
      }
    });

    const result = await response.json();
    ...
    return result;
}
</pre></div>
<p><strong>总结这一块:</strong></p>
<blockquote><p>前端通过 AliyunCaptcha.js 提供的 initAliyunCaptcha 接入阿里云验证码。<br />用户操作 &rarr; 验证码通过 &rarr; 阿里云前端回调 captchaVerifyCallback。<br />回调里先从安卓拿手机号,再调用你自己后端接口 /dev-api/android/sms/code。<br />后端返回 code=200 &amp;&amp; data=true 视为验证成功,再通过返回值告知阿里云 SDK 验证结果。</p></blockquote>
<p class="maodian"></p><h2>跟安卓原生通讯的逻辑</h2>
<p class="maodian"></p><h3>与安卓约定的对象和方法</h3>
<p>在 JS 这边约定:</p>
<p>原生会在 window 上注入一个对象:window.testInterface。</p>
<p>你用的接口方法:</p>
<p>testInterface.getPhoneNumber(callbackName):安卓会拿到 callbackName,执行完再在 JS 中调用 windowcallbackName 回传结果。<br />testInterface.closeWebView():用于在业务成功时关闭 WebView。</p>
<p>这些是靠安卓那边 WebView addJavascriptInterface 或 evaluateJavascript 实现的。</p>
<p class="maodian"></p><h3>小结</h3>
<blockquote><p>阿里云连接逻辑: 通过 AliyunCaptcha.js + window.AliyunCaptchaConfig +initAliyunCaptcha 接入。 成功后在 captchaVerifyCallback 里拿到captchaVerifyParam。 再带上手机号请求你自己后端http://39.96.184.160/dev-api/android/sms/code 进行业务验证。 验证结果再通过返回值以及<br />onBizResultCallback 和页面交互。 与安卓通讯逻辑: 浏览器端默认认为安卓会注入<br />window.testInterface。 通过 NativeInterfaceManager 轮询和回调机制确定接口是否可用。 通过<br />sendMessageToNative(action, callbackName) 把一个回调函数名给安卓,安卓执行完再回调<br />windowcallbackName。 当前主要用到:<br />testInterface.getPhoneNumber(callbackName) &rarr; 获取手机号<br />testInterface.closeWebView() &rarr; 业务成功后关闭 WebView 如果原生没准备好或调用失败,就用模拟数据<br />13400134000,页面上也会显示&ldquo;模拟数据&rdquo;。</p></blockquote>
<p class="maodian"></p><h3>完整代码:</h3>
<div class="jb51code"><pre class="brush:js;">&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;图形验证码验证&lt;/title&gt;
    &lt;style&gt;
      * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
      }

      body {
            font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
      }

      .captcha-container {
            width: 100%;
            max-width: 450px;
      }

      .captcha-form {
            background: white;
            padding: 30px;
            border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
            text-align: center;
      }

      .captcha-form h2 {
            color: #333;
            margin-bottom: 8px;
            font-weight: 600;
      }

      .description {
            color: #666;
            margin-bottom: 24px;
            font-size: 14px;
            line-height: 1.5;
      }

      .captcha-element {
            margin: 20px 0;
            min-height: 80px;
            display: flex;
            justify-content: center;
            align-items: center;
            border: 2px dashed #e0e0e0;
            border-radius: 8px;
            padding: 15px;
            background: #fafafa;
      }

      .verify-btn {
            width: 100%;
            padding: 14px;
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            border: none;
            border-radius: 6px;
            font-size: 16px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.3s ease;
            margin-bottom: 15px;
      }

      .verify-btn:hover:not(:disabled) {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
      }

      .verify-btn:disabled {
            background: #ccc;
            cursor: not-allowed;
            transform: none;
            box-shadow: none;
      }

      .status-message {
            margin: 15px 0;
            padding: 10px;
            border-radius: 4px;
            font-size: 14px;
            min-height: 20px;
      }

      .status-success {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
      }

      .status-error {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
      }

      .status-loading {
            background: #d1ecf1;
            color: #0c5460;
            border: 1px solid #bee5eb;
      }

      .server-info {
            background: rgba(0, 0, 0, 0.05);
            padding: 10px;
            border-radius: 5px;
            margin-top: 15px;
            font-size: 12px;
            color: #666;
            line-height: 1.5;
      }

      .debug-info {
            background: #f8f9fa;
            border: 1px solid #e9ecef;
            border-radius: 5px;
            padding: 10px;
            margin-top: 15px;
            font-size: 12px;
            text-align: left;
            max-height: 200px;
            overflow-y: auto;
      }

      .debug-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 8px;
      }

      .debug-controls button {
            background: #6c757d;
            color: white;
            border: none;
            padding: 4px 8px;
            border-radius: 3px;
            font-size: 10px;
            cursor: pointer;
            margin-left: 5px;
      }

      .debug-controls button:hover {
            background: #5a6268;
      }

      @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
      }

      .loading {
            display: inline-block;
            width: 16px;
            height: 16px;
            border: 2px solid #f3f3f3;
            border-top: 2px solid #667eea;
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin-right: 8px;
            vertical-align: middle;
      }

      .interface-status {
            padding: 8px;
            border-radius: 4px;
            margin: 10px 0;
            font-weight: bold;
      }

      .interface-ready {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
      }

      .interface-waiting {
            background: #fff3cd;
            color: #856404;
            border: 1px solid #ffeaa7;
      }

      .interface-missing {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
      }

      .phone-info {
            background: #e7f3ff;
            color: #0066cc;
            border: 1px solid #b3d9ff;
            padding: 8px;
            border-radius: 4px;
            margin: 10px 0;
            font-size: 14px;
      }

      .phone-number {
            font-weight: bold;
            color: #004085;
      }
    &lt;/style&gt;

    &lt;!-- 阿里云验证码配置 --&gt;
    &lt;script&gt;
      window.AliyunCaptchaConfig = {
            region: "cn",
            prefix: "1fnzmt",
      };
    &lt;/script&gt;

    &lt;!-- 引入阿里云验证码JS --&gt;
    &lt;script src="https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js"&gt;&lt;/script&gt;
   
    &lt;!-- 引入vConsole用于移动端调试 --&gt;
    &lt;script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"&gt;&lt;/script&gt;
    &lt;script&gt;
      // 初始化vConsole
      var vConsole = new VConsole();
      console.log('✅ vConsole已加载,可以在移动端查看调试信息');
    &lt;/script&gt;
&lt;/head&gt;

&lt;body&gt;
    &lt;div class="captcha-container"&gt;
      &lt;div class="captcha-form"&gt;
            &lt;h2&gt;安全验证&lt;/h2&gt;
            &lt;p class="description"&gt;请完成验证以继续操作&lt;/p&gt;

            &lt;!-- 接口状态显示 --&gt;
            &lt;div id="interface-status" class="interface-status interface-waiting"&gt;
                正在检测原生接口...
            &lt;/div&gt;

            &lt;!-- 手机号信息显示 --&gt;
            &lt;div id="phone-info" class="phone-info"&gt;
                当前手机号: &lt;span id="current-phone" class="phone-number"&gt;等待获取中...&lt;/span&gt;
            &lt;/div&gt;

            &lt;!-- 验证码容器 --&gt;
            &lt;div id="captcha-element" class="captcha-element"&gt;&lt;/div&gt;

            &lt;!-- 验证状态 --&gt;
            &lt;div id="status-message" class="status-message"&gt;&lt;/div&gt;

            &lt;button id="verify-btn" class="verify-btn"&gt;开始验证&lt;/button&gt;

            &lt;div class="server-info"&gt;
                服务器地址: 192.168.5.59:5500

                当前时间: &lt;span id="current-time"&gt;&lt;/span&gt;
            &lt;/div&gt;

            &lt;!-- 调试信息 --&gt;
            &lt;div class="debug-info"&gt;
                &lt;div class="debug-header"&gt;
                  &lt;h4&gt;调试信息:&lt;/h4&gt;
                  &lt;div class="debug-controls"&gt;
                        &lt;button id="clear-log"&gt;清空日志&lt;/button&gt;
                        &lt;button id="test-interface"&gt;测试接口&lt;/button&gt;
                  &lt;/div&gt;
                &lt;/div&gt;
                &lt;div id="debug-log"&gt;&lt;/div&gt;
            &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;script&gt;
      // 调试日志
      function debugLog(message) {
            const logElement = document.getElementById('debug-log');
            const timestamp = new Date().toLocaleTimeString();
            logElement.innerHTML += `[${timestamp}] ${message}
`;
            console.log(message);
            
            // 自动滚动到底部
            logElement.scrollTop = logElement.scrollHeight;
      }

      // 显示当前时间
      function updateTime() {
            document.getElementById('current-time').textContent = new Date().toLocaleString();
      }
      setInterval(updateTime, 1000);
      updateTime();

      // 配置参数
      const CONFIG = {
            region: "cn",
            prefix: "1fnzmt",
            sceneId: "vjsqgp2k",
            mode: "popup",
            language: "cn",
            slideStyle: {
                width: 360,
                height: 40
            },
            apiBaseURL: "http://39.96.184.160",
            endpoints: {
                captchaVerify: "/dev-api/android/sms/code"
            }
      };

      // 原生接口管理器
      class NativeInterfaceManager {
            constructor() {
                this.isReady = false;
                this.interfaceName = 'testInterface';
                this.readyCallbacks = [];
                this.retryCount = 0;
                this.maxRetries = 100; // 延长到10秒
                this.checkInterval = null;
                this.init();
            }
            
            init() {
                debugLog('初始化原生接口管理器');
                debugLog('当前window对象的属性: ' + Object.keys(window).filter(k =&gt; k.includes('test') || k.includes('Interface')).join(', '));
               
                // 立即检测一次
                if (this.checkInterface()) {
                  return;
                }
               
                // 延迟启动检测,给Android更多注入时间
                setTimeout(() =&gt; {
                  debugLog('开始定期检测原生接口...');
                  this.startPeriodicCheck();
                }, 500);
            }
            
            startPeriodicCheck() {
                this.checkInterval = setInterval(() =&gt; {
                  debugLog(`检测原生接口 (${this.retryCount + 1}/${this.maxRetries})`);
                  
                  if (this.checkInterface() || this.retryCount &gt;= this.maxRetries) {
                        clearInterval(this.checkInterval);
                        if (!this.isReady) {
                            debugLog('❌ 达到最大重试次数,原生接口仍未就绪');
                            this.showInterfaceStatus('missing', '原生接口未就绪,使用模拟模式');
                        }
                  }
                  this.retryCount++;
                }, 100);
            }
            
            checkInterface() {
                if (window) {
                  this.isReady = true;
                  debugLog('✅ 原生接口检测成功');
                  this.showInterfaceStatus('ready', '原生接口已就绪');
                  
                  // 执行所有等待的回调
                  this.readyCallbacks.forEach(callback =&gt; callback(window));
                  this.readyCallbacks = [];
                  
                  return true;
                }
                return false;
            }
            
            onReady(callback) {
                if (this.isReady) {
                  callback(window);
                } else {
                  this.readyCallbacks.push(callback);
                }
            }
            
            async call(method, data) {
                return new Promise((resolve, reject) =&gt; {
                  this.onReady(async (interfaceObj) =&gt; {
                        try {
                            const result = await this.sendMessageToNative(method, data, interfaceObj);
                            resolve(result);
                        } catch (error) {
                            reject(error);
                        }
                  });
                  
                  // 如果接口未就绪,直接使用模拟数据
                  if (!this.isReady) {
                        debugLog('接口未就绪,使用模拟数据');
                        setTimeout(() =&gt; {
                            this.simulateNativeResponse(method).then(resolve);
                        }, 500);
                  }
                });
            }
            
            // async sendMessageToNative(action, data, interfaceObj) {
            //   return new Promise((resolve, reject) =&gt; {
            //         const callbackName = 'cb_' + Math.random().toString(36).substring(2);
                  
            //         debugLog(`发送消息给原生代码,action: ${action}, callback: ${callbackName}`);
                  
            //         window = (response) =&gt; {
            //             debugLog('安卓返回的验证结果: ' + response);
                        
            //             try {
            //               const result = typeof response === 'string' ? JSON.parse(response) : response;
            //               resolve(result);
            //             } catch (error) {
            //               debugLog('解析安卓返回结果失败: ' + error);
            //               reject(error);
            //             }
                        
            //             delete window;
            //         };
                  
            //         const timeoutId = setTimeout(() =&gt; {
            //             debugLog('调用原生方法超时');
            //             delete window;
            //             this.simulateNativeResponse(action).then(resolve);
            //         }, 5000);
                  
            //         if (interfaceObj &amp;&amp; interfaceObj) {
            //             try {
            //               // 直接传递回调函数名给安卓端
            //               interfaceObj(callbackName);
            //               clearTimeout(timeoutId);
            //             } catch (error) {
            //               debugLog('调用原生方法失败: ' + error);
            //               clearTimeout(timeoutId);
            //               this.simulateNativeResponse(action).then(resolve);
            //             }
            //         } else {
            //             debugLog(`接口 ${this.interfaceName}.${action} 不存在,使用模拟数据`);
            //             clearTimeout(timeoutId);
            //             this.simulateNativeResponse(action).then(resolve);
            //         }
            //   });
            // }

            // 修改sendMessageToNative方法
async sendMessageToNative(action, data, interfaceObj) {
    return new Promise((resolve, reject) =&gt; {
      const callbackName = 'cb_' + Math.random().toString(36).substring(2);
      
      debugLog(`发送消息给原生代码,action: ${action}, callback: ${callbackName}`);
      
      window = (response) =&gt; {
            debugLog('安卓返回的验证结果: ' + response);
            
            try {
                const result = typeof response === 'string' ? JSON.parse(response) : response;
                resolve(result);
            } catch (error) {
                debugLog('解析安卓返回结果失败: ' + error);
                reject(error);
            }
            
            delete window;
      };
      
      const timeoutId = setTimeout(() =&gt; {
            debugLog('调用原生方法超时');
            delete window;
            this.simulateNativeResponse(action).then(resolve);
      }, 5000);
      
      if (interfaceObj &amp;&amp; interfaceObj) {
            try {
                // 直接传递回调函数名给安卓端
                interfaceObj(callbackName);
                clearTimeout(timeoutId);
            } catch (error) {
                debugLog('调用原生方法失败: ' + error);
                clearTimeout(timeoutId);
                this.simulateNativeResponse(action).then(resolve);
            }
      } else {
            debugLog(`接口 ${this.interfaceName}.${action} 不存在,使用模拟数据`);
            clearTimeout(timeoutId);
            this.simulateNativeResponse(action).then(resolve);
      }
    });
}
            
            simulateNativeResponse(action) {
                return new Promise((resolve) =&gt; {
                  setTimeout(() =&gt; {
                        let mockData = {};
                        if (action === 'getPhoneNumber') {
                            mockData = {
                              phoneNumber: '13400134000',
                              success: true
                            };
                        }
                        debugLog(`模拟响应: ${JSON.stringify(mockData)}`);
                        resolve(mockData);
                  }, 500);
                });
            }
            
            showInterfaceStatus(status, message) {
                const element = document.getElementById('interface-status');
                element.textContent = message;
                element.className = `interface-status interface-${status}`;
            }
            
            // 测试接口
            async test() {
                debugLog('=== 测试原生接口 ===');
               
                if (this.isReady) {
                  debugLog('✅ testInterface对象存在');
                  debugLog('testInterface方法: ' + Object.keys(window.testInterface).join(', '));
                  
                  if (window.testInterface.getPhoneNumber) {
                        debugLog('✅ getPhoneNumber方法存在');
                        
                        try {
                            const result = await this.call('getPhoneNumber', {});
                            debugLog('原生接口测试成功: ' + JSON.stringify(result));
                        } catch (error) {
                            debugLog('原生接口测试失败: ' + error);
                        }
                  } else {
                        debugLog('❌ getPhoneNumber方法不存在');
                  }
                } else {
                  debugLog('❌ testInterface对象不存在');
                }
            }
      }

      // 验证码管理
      class CaptchaManager {
            constructor() {
                this.captcha = null;
                this.nativeInterface = new NativeInterfaceManager();
                this.currentPhoneNumber = null;
                this.init();
            }

            init() {
                try {
                  debugLog('开始初始化阿里云验证码');
                  
                  window.initAliyunCaptcha({
                        SceneId: CONFIG.sceneId,
                        mode: CONFIG.mode,
                        element: '#captcha-element',
                        button: '#verify-btn',
                        captchaVerifyCallback: this.captchaVerifyCallback.bind(this),
                        onBizResultCallback: this.onBizResultCallback.bind(this),
                        getInstance: (instance) =&gt; {
                            this.captcha = instance;
                            debugLog('验证码初始化成功');
                        }
                  });
                } catch (error) {
                  debugLog('验证码初始化失败: ' + error.message);
                }
            }

            // 获取手机号 - 使用原生接口管理器
            async getPhoneNumber() {
                debugLog('开始获取手机号');
               
                try {
                  const result = await this.nativeInterface.call('getPhoneNumber', {});
                  
                  if (result &amp;&amp; result.phoneNumber) {
                        debugLog('成功获取手机号: ' + result.phoneNumber);
                        this.updatePhoneDisplay(result.phoneNumber, true);
                        return result.phoneNumber;
                  } else {
                        debugLog('获取手机号失败,使用默认值');
                        this.updatePhoneDisplay('13400134000', false);
                        return '13400134000';
                  }
                } catch (error) {
                  debugLog('获取手机号异常: ' + error.message);
                  this.updatePhoneDisplay('13400134000', false);
                  return '13400134000';
                }
            }

            // 更新手机号显示
            updatePhoneDisplay(phoneNumber, isReal) {
                this.currentPhoneNumber = phoneNumber;
                const phoneElement = document.getElementById('current-phone');
               
                if (isReal) {
                  phoneElement.textContent = phoneNumber;
                  phoneElement.style.color = '#155724';
                  document.getElementById('phone-info').style.background = '#d4edda';
                  document.getElementById('phone-info').style.borderColor = '#c3e6cb';
                  document.getElementById('phone-info').style.color = '#155724';
                } else {
                  phoneElement.textContent = phoneNumber + ' (模拟数据)';
                  phoneElement.style.color = '#721c24';
                  document.getElementById('phone-info').style.background = '#f8d7da';
                  document.getElementById('phone-info').style.borderColor = '#f5c6cb';
                  document.getElementById('phone-info').style.color = '#721c24';
                }
               
                debugLog(`当前使用的手机号: ${phoneNumber} ${isReal ? '(真实数据)' : '(模拟数据)'}`);
            }

            // API请求封装 - 修改为GET请求
            async apiRequest(url, data) {
                try {
                  let fullUrl;
                  if (url.startsWith('http')) {
                        fullUrl = url;
                  } else if (url.startsWith('/')) {
                        fullUrl = CONFIG.apiBaseURL + url;
                  } else {
                        fullUrl = CONFIG.apiBaseURL + '/' + url;
                  }

                  // 构建查询字符串
                  const queryParams = new URLSearchParams();
                  for (const key in data) {
                        if (data !== null &amp;&amp; data !== undefined) {
                            queryParams.append(key, data);
                        }
                  }
                  
                  const queryString = queryParams.toString();
                  if (queryString) {
                        fullUrl += (fullUrl.includes('?') ? '&amp;' : '?') + queryString;
                  }

                  debugLog('请求URL: ' + fullUrl);
                  debugLog('请求数据: ' + JSON.stringify(data));

                  const response = await fetch(fullUrl, {
                        method: 'GET',
                        headers: {
                            'Content-Type': 'application/json',
                        }
                  });

                  if (!response.ok) {
                        throw new Error(`HTTP error! status: ${response.status}`);
                  }

                  const result = await response.json();
                  debugLog('响应结果: ' + JSON.stringify(result));
                  return result;

                } catch (error) {
                  debugLog('API请求失败: ' + error.message);
                  throw error;
                }
            }

            // 验证回调
            async captchaVerifyCallback(captchaVerifyParam) {
                this.showStatus('验证中...', 'loading');

                try {
                  // 禁用验证按钮
                  document.getElementById('verify-btn').disabled = true;
                  
                  // 使用原生接口管理器获取手机号
                  const phoneNumber = await this.getPhoneNumber();
                  debugLog('最终使用的手机号: ' + phoneNumber);

                  // 调用后端API - 使用GET请求
                  const result = await this.apiRequest(CONFIG.endpoints.captchaVerify, {
                        phoneNumber: phoneNumber,
                        captchaVerifyParam: captchaVerifyParam
                  });

                  debugLog('后端返回结果: ' + JSON.stringify(result));

                  // 根据后端 code 和 data 判断是否验证成功
                  const success = result &amp;&amp; result.code === 200 &amp;&amp; result.data === true;

                  if (success) {
                        this.showStatus('验证成功!', 'success');
                  } else {
                        const msg = (result &amp;&amp; result.msg) ? result.msg : '验证失败';
                        this.showStatus(msg, 'error');
                  }

                  // 按照阿里云要求返回结构
                  return {
                        captchaResult: success,
                        bizResult: success
                  };
                } catch (error) {
                  debugLog('验证失败: ' + error.message);
                  this.showStatus('验证失败: ' + error.message, 'error');
                  return {
                        captchaResult: false,
                        bizResult: false
                  };
                } finally {
                  document.getElementById('verify-btn').disabled = false;
                }
            }

            // 业务回调
            onBizResultCallback(bizResult) {
                if (bizResult) {
                  this.showStatus('验证成功!', 'success');

                  // 调用安卓关闭 WebView
                  try {
                        if (window.testInterface &amp;&amp; typeof window.testInterface.closeWebView === 'function') {
                            debugLog('调用安卓 closeWebView 方法关闭页面');
                            window.testInterface.closeWebView();
                        } else {
                            debugLog('未找到 testInterface.closeWebView 方法');
                        }
                  } catch (e) {
                        debugLog('调用安卓 closeWebView 出错: ' + e);
                  }

                  setTimeout(() =&gt; {
                        if (window.onCaptchaSuccess) {
                            window.onCaptchaSuccess();
                        }
                  }, 1000);
                } else {
                  this.showStatus('验证失败,请重试', 'error');
                }
            }

            showStatus(message, type) {
                const element = document.getElementById('status-message');
                element.textContent = message;
                element.className = `status-message status-${type}`;

                if (type === 'loading') {
                  element.innerHTML = `&lt;span class="loading"&gt;&lt;/span&gt;${message}`;
                }
            }
      }

      // 页面加载后初始化
      document.addEventListener('DOMContentLoaded', () =&gt; {
            debugLog('页面加载完成,开始初始化验证码');
            window.captchaManager = new CaptchaManager();
            
            // 初始化调试控件
            document.getElementById('clear-log').addEventListener('click', () =&gt; {
                document.getElementById('debug-log').innerHTML = '';
                debugLog('日志已清空');
            });
            
            document.getElementById('test-interface').addEventListener('click', () =&gt; {
                window.captchaManager.nativeInterface.test();
            });
      });

      // 成功回调
      window.onCaptchaSuccess = function () {
            debugLog('验证码验证成功');
            alert('验证成功!');
      };

      // 添加安卓端回调通知 - 增强版
      window.onNativeInterfaceReady = function() {
            debugLog('🎉 收到安卓端通知:原生接口已就绪');
            if (window.captchaManager &amp;&amp; window.captchaManager.nativeInterface) {
                // 强制重新检测
                window.captchaManager.nativeInterface.isReady = false;
                if (window.captchaManager.nativeInterface.checkInterface()) {
                  // 停止定期检测
                  if (window.captchaManager.nativeInterface.checkInterval) {
                        clearInterval(window.captchaManager.nativeInterface.checkInterval);
                  }
                }
            }
      };
      
      // 全局暴露检测方法,供Android主动调用
      window.forceCheckInterface = function() {
            debugLog('🔍 Android主动触发接口检测');
            if (window.captchaManager &amp;&amp; window.captchaManager.nativeInterface) {
                return window.captchaManager.nativeInterface.checkInterface();
            }
            return false;
      };

      // 延迟测试原生接口
      setTimeout(() =&gt; {
            debugLog('开始测试原生接口');
            if (window.captchaManager &amp;&amp; window.captchaManager.nativeInterface) {
                window.captchaManager.nativeInterface.test();
            }
      }, 3000);
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre></div>
<p class="maodian"></p><h2>总结&nbsp;</h2>
<p>到此这篇关于前端使用阿里云图形验证码并且与安卓进行交互实现方法的文章就介绍到这了,更多相关前端使用阿里云图形验证码内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>阿里云PHP SMS短信服务验证码发送方法</li><li>Springboot实现阿里云通信短信服务有关短信验证码的发送功能</li><li>go使用Gin框架利用阿里云实现短信验证码功能</li><li>详细介绍Java阿里云的短信验证码实现</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: 前端使用阿里云图形验证码并且与安卓进行交互实现方法