/** * 硬件外设接口封装 (对接安卓原生插件) * 注意:需要在 manifest.json 的 App原生插件配置 中引入对应的本地插件 */ // 假设你们安卓开发的插件名称为 'business-module',请根据实际 package.json 里的 id 替换 // H5 环境没有 uni.requireNativePlugin,这里要先做兼容判断,避免模块加载阶段直接报错 let HardwarePlugin = null; if (typeof uni !== 'undefined' && typeof uni.requireNativePlugin === 'function') { HardwarePlugin = uni.requireNativePlugin('business-module'); } let initPromise = null; let initResult = null; const PLUGIN_DEBUG_ENABLED = true; let debugModalQueue = Promise.resolve(); const getPluginMethod = (methodNames = []) => { if (!HardwarePlugin) { return null; } return methodNames.find((methodName) => typeof HardwarePlugin[methodName] === 'function') || null; }; // 将插件返回内容转成适合弹窗展示的字符串,避免对象直接显示成 [object Object] const formatPluginResult = (payload) => { if (payload instanceof Error) { return payload.message; } if (typeof payload === 'string') { return payload; } try { const formattedText = JSON.stringify(payload, null, 2); return formattedText.length > 1600 ? `${formattedText.slice(0, 1600)}\n...内容过长,已截断` : formattedText; } catch (error) { return String(payload); } }; // 正式包里没有控制台时,统一用弹窗展示插件回调结果,便于现场联调 const showPluginDebugModal = (title, payload) => { if (!PLUGIN_DEBUG_ENABLED) { return; } const content = formatPluginResult(payload); debugModalQueue = debugModalQueue.finally(() => { return new Promise((resolve) => { uni.showModal({ title, content, showCancel: false, success: () => resolve(), fail: () => resolve(), }); }); }); }; export const HardwareAPI = { /** * 初始化设备 * 全局只执行一次,后续重复调用直接复用首次初始化结果 * @param {Object} options 传给安卓端的配置参数 */ init(options = {}) { if (initResult) { return Promise.resolve(initResult); } if (initPromise) { return initPromise; } initPromise = new Promise((resolve, reject) => { // #ifdef APP-PLUS if (HardwarePlugin) { let isSettled = false; let fallbackTimer = null; const finishInit = (result, shouldReject = false) => { if (isSettled) { return; } isSettled = true; if (fallbackTimer) { clearTimeout(fallbackTimer); fallbackTimer = null; } if (shouldReject) { initPromise = null; reject(result); return; } initResult = result; resolve(result); }; try { HardwarePlugin.init(options, (res) => { // showPluginDebugModal('插件初始化回调', res); if (res) { finishInit(res); } }); // 有些设备上的 init 是“只调用不回调”,这里给一个短超时兜底, // 避免把 App 启动链路一直卡住。 fallbackTimer = setTimeout(() => { finishInit({ code: 200, msg: 'init_called_without_callback', }); }, 300); } catch (error) { initPromise = null; reject(error); } } else { console.warn('未找到硬件原生插件 business-module'); // showPluginDebugModal('插件初始化失败', '未找到硬件原生插件 business-module'); initPromise = null; reject(new Error('Plugin not found')); } // #endif // #ifndef APP-PLUS console.log('[Mock] 硬件初始化成功', options); initResult = { code: 200, msg: 'success' }; // showPluginDebugModal('插件初始化回调(Mock)', initResult); resolve(initResult); // #endif }); return initPromise; }, /** * 开始读卡 (身份证) * 由于可能是多次回调(放上、拿走等),建议安卓端使用 invokeAndKeepAlive * @param {Function} callback 读卡成功的回调 */ startReadCard(callback) { // showPluginDebugModal('开始读卡调用', { // method: 'startReadCard', // message: '前端已发起开始读卡请求', // }); // #ifdef APP-PLUS if (HardwarePlugin) { HardwarePlugin.startReadCard({}, (res) => { // showPluginDebugModal('读卡回调', res); // 如果插件回调中包含 licId (身份证号),说明读卡成功 const resData = JSON.parse(res || '{}'); if (resData && resData.licId) { const cardData = resData; callback(cardData); } else { console.error('读卡失败或状态更新:', resData); } }); } // #endif // #ifndef APP-PLUS console.log('[Mock] 开始读卡,等待回调...'); // Mock 环境下不自动触发,留给界面的 "跳过录入" 按钮手动触发 // #endif }, /** * 停止读卡 */ stopReadCard() { return new Promise((resolve) => { // #ifdef APP-PLUS if (HardwarePlugin) { HardwarePlugin.stopReadCard({}); resolve(); } else { resolve(); } // #endif // #ifndef APP-PLUS console.log('[Mock] 停止读卡'); resolve(); // #endif }); }, /** * 开始扫描 (二维码) * 建议安卓端使用 invokeAndKeepAlive * @param {Function} callback 扫码成功的回调 */ startScan(callback) { // showPluginDebugModal('开始扫码调用', { // method: 'startScan', // message: '前端已发起开始扫码请求', // }); // #ifdef APP-PLUS if (HardwarePlugin) { HardwarePlugin.startScan({ duration: 2000 }, (res) => { // showPluginDebugModal('扫码回调', res); if (res) { callback(res); } else { console.error('扫码失败或状态更新:', res); } }); } // #endif // #ifndef APP-PLUS console.log('[Mock] 开始扫码,等待回调...'); // Mock 环境下不自动触发,留给界面的 "模拟扫码成功" 按钮手动触发 // #endif }, /** * 停止扫描 */ stopScan() { return new Promise((resolve) => { // #ifdef APP-PLUS if (HardwarePlugin) { HardwarePlugin.stopScan({}); resolve(); } else { resolve(); } // #endif // #ifndef APP-PLUS console.log('[Mock] 停止扫码'); resolve(); // #endif }); }, /** * 开始人证照片比对 * @param {Object} options 比对参数,包含身份证照片和现场抓拍照片 * @param {Function} callback 比对成功回调 */ startFaceVerify(options = {}, callback, failCallback) { const face = options?.face || ''; const photo = options?.photo || ''; // showPluginDebugModal('开始人证比对调用', { // method: 'startRecognition', // options: { // face, // photo, // }, // }); // #ifdef APP-PLUS const methodName = getPluginMethod(['startRecognition']); if (methodName) { HardwarePlugin[methodName]({ face, photo }, (res) => { // showPluginDebugModal('人证比对回调', res); if (res && res.code === 0) { callback?.(res); return; } if (failCallback) { failCallback({ ...res, message: res?.response || res?.msg || res?.message || '人脸识别失败', }); return; } console.error('人证比对失败或状态更新:', res); }); return; } if (failCallback) { failCallback({ message: '当前插件未提供人脸识别能力', }); } // #endif // #ifndef APP-PLUS console.log('[Mock] 开始人证比对,等待回调...', options); setTimeout(() => { if (!photo) { failCallback?.({ message: '未获取到身份证照片,请重新读取身份证', }); return; } if (!face) { failCallback?.({ message: '未获取到现场抓拍照片,请重新采集人脸', }); return; } callback?.({ matched: true, face, photo, }); }, 1200); // #endif }, /** * 开始判断图片中是否有人脸 * @param {Object} options * @param {Function} callback 判断到人脸成功回调 * @param {Function} failCallback 判断失败回调 */ startJudgeFace(options = {}, callback, failCallback) { const face = options?.face || ''; // #ifdef APP-PLUS const methodName = getPluginMethod(['startJudgeFace']); if (methodName) { HardwarePlugin[methodName]({ face }, (res) => { if (res && Number(res.code) === 0) { callback?.(res); return; } failCallback?.({ ...res, message: res?.response || res?.msg || res?.message || '未检测到人脸', }); }); return; } failCallback?.({ message: '当前插件未提供人脸检测能力', }); // #endif // #ifndef APP-PLUS console.log('[Mock] 开始判断图片中是否有人脸...', options); setTimeout(() => { if (!face) { failCallback?.({ message: '未获取到待检测图片', }); return; } callback?.({ code: 0, response: 'mock_has_face', }); }, 300); // #endif }, /** * 停止判断图片中是否有人脸 */ stopJudgeFace() { return new Promise((resolve) => { // #ifdef APP-PLUS const methodName = getPluginMethod(['stopJudgeFace']); if (methodName) { HardwarePlugin[methodName]({}); resolve(); return; } resolve(); // #endif // #ifndef APP-PLUS console.log('[Mock] 停止判断图片中是否有人脸'); resolve(); // #endif }); }, /** * 停止人证照片比对 */ stopFaceVerify() { return new Promise((resolve) => { // #ifdef APP-PLUS const methodName = getPluginMethod(['stopRecognition']); if (methodName) { HardwarePlugin[methodName]({}); resolve(); return; } resolve(); // #endif // #ifndef APP-PLUS console.log('[Mock] 停止人证比对'); resolve(); // #endif }); }, /** * 离线激活设备 * @param {Object} options 激活参数,包含 16 位序列号 code */ offlineActivateDevice(options = {}) { // showPluginDebugModal('离线激活设备调用', { // method: 'offlineActivateDevice', // options, // }); return new Promise((resolve, reject) => { // #ifdef APP-PLUS const methodName = getPluginMethod(['offlineActivateDevice']); if (!methodName) { reject(new Error('当前插件未提供离线激活能力')); return; } HardwarePlugin[methodName](options, (res) => { // showPluginDebugModal('离线激活设备回调', res); if (res && res.code === 0) { resolve(res); return; } reject(new Error(res?.response || res?.msg || res?.message || '离线激活失败')); }); // #endif // #ifndef APP-PLUS console.log('[Mock] 离线激活设备成功', options); resolve({ code: 0, response: 'success' }); // #endif }); }, /** * 在线激活设备 * @param {Object} options 激活参数,包含 16 位序列号 code */ onlineActivateDevice(options = {}) { // showPluginDebugModal('在线激活设备调用', { // method: 'onlineActivateDevice', // options, // }); return new Promise((resolve, reject) => { // #ifdef APP-PLUS const methodName = getPluginMethod(['onlineActivateDevice']); if (!methodName) { reject(new Error('当前插件未提供在线激活能力')); return; } HardwarePlugin[methodName](options, (res) => { // showPluginDebugModal('在线激活设备回调', res); if (res && res.code === 0) { resolve(res.data || res); return; } reject(new Error(res?.response || res?.msg || res?.message || '在线激活失败')); }); // #endif // #ifndef APP-PLUS console.log('[Mock] 在线激活设备成功', options); resolve({ code: 0, response: 'success' }); // #endif }); }, /** * 获取设备指纹 * @param {Object} options 传给原生端的扩展参数 */ getDeviceFingerprint(options = {}) { // showPluginDebugModal('获取设备指纹调用', { // method: 'getDeviceFingerprint', // options, // }); return new Promise((resolve, reject) => { // #ifdef APP-PLUS const methodName = getPluginMethod(['getDeviceFingerprint']); if (!methodName) { reject(new Error('百度SDK指纹获取失败')); return; } HardwarePlugin[methodName](options, (res) => { // showPluginDebugModal('获取设备指纹回调', res); if (res) { const fingerprint = res || ''; if (fingerprint) { resolve(String(fingerprint)); return; } } reject(new Error('百度SDK指纹获取失败')); }); // #endif // #ifndef APP-PLUS console.log('[Mock] 获取设备指纹成功'); resolve(`MOCK-FINGERPRINT-${Date.now()}`); // #endif }); }, /** * 打印凭证 * @param {Array} printList 打印信息列表 */ startPrint(printList) { return new Promise((resolve, reject) => { // #ifdef APP-PLUS const methodName = getPluginMethod(['startPrint']); // showPluginDebugModal('打印凭证调用', { // method: 'startPrint', // args: printList, // }); if (methodName) { HardwarePlugin[methodName]({ options: printList }, (res) => { // showPluginDebugModal('打印凭证回调', res); if (res && res.code === 0) { resolve(res); } else { reject(res); } }); return; } // showPluginDebugModal('打印凭证错误', '未找到打印方法 startPrint'); reject(new Error('未找到打印方法 startPrint')); // #endif // #ifndef APP-PLUS console.log('[Mock] 开始打印', printList); setTimeout(() => { resolve({ code: 0, message: 'Mock print success' }); }, 1000); // #endif }); } };