From 5a7688cb80d0a838bf48783f945a1b616d774076 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 16 Sep 2025 21:51:43 +0100 Subject: [PATCH] emblemscanner: various improvements --- scanner/pages/emblemscanner/emblemscanner.js | 174 ++++++++++-------- .../pages/emblemscanner/emblemscanner.wxml | 43 ++--- scanner/pages/emblemscanner/upload.js | 32 ++++ 3 files changed, 147 insertions(+), 102 deletions(-) diff --git a/scanner/pages/emblemscanner/emblemscanner.js b/scanner/pages/emblemscanner/emblemscanner.js index 220626f..815867d 100644 --- a/scanner/pages/emblemscanner/emblemscanner.js +++ b/scanner/pages/emblemscanner/emblemscanner.js @@ -37,6 +37,7 @@ const { // Import upload functionality for verification (self-contained) const { upload_image_data_urls, + upload_image_data_urls_with_metadata, check_auto_torch } = require('./upload.js'); @@ -74,11 +75,12 @@ Page({ server_url: 'https://themblem.com', // Default server URL real_ip: '', // User's real IP address tenant_id: '', // Tenant identifier - debug_msgs: [], + userinfo: null, // User information from API debug_image_data_url: '', debug_last_result: null, debug_current_frame_url: '', // Current frame being processed - logs: [], // Backend logging messages (like camera.js) + logs: [], // Unified logging messages for both backend reporting and debug overlay + debug_msgs: [], // Recent logs formatted for debug overlay (computed from logs) qrtool_ready: false, // Frame processing statistics frames_processed: 0, @@ -129,13 +131,29 @@ Page({ this.setData({ tenant_id }); }, + fetchUserInfo() { + // Use WeChat official API to get user info + wx.getUserInfo({ + success: (res) => { + const userInfo = res.userInfo; + this.setData({ userinfo: userInfo }); + this.log('User info fetched via API:', userInfo.nickName || 'unknown'); + }, + fail: (err) => { + this.log('Failed to fetch user info:', err.errMsg); + // Fallback to empty userinfo if API fails + this.setData({ userinfo: {} }); + } + }); + }, + initializeQRTool() { load_qrtool(); const checkReady = () => { if (is_qrtool_ready()) { this.setData({ qrtool_ready: true }); - this.addDebugMessage('QRTool WASM loaded and ready'); + this.log('QRTool WASM loaded and ready'); // Step 4: QRTool loaded, now initialize camera this.startCameraInit(); @@ -188,7 +206,7 @@ Page({ // Add WASM response to debug messages if (this.data.enable_debug) { - this.addDebugMessage(`WASM response: ${JSON.stringify(result)}`); + this.log(`WASM response: ${JSON.stringify(result)}`); } // Update statistics @@ -206,7 +224,7 @@ Page({ if (result) { // For worker, we need to trigger image collection when we find a good QR if (result.qrcode && is_emblem_qr_pattern(result.qrcode)) { - this.addDebugMessage(`Worker QR detected: ${result.qrcode}`); + this.log(`Worker QR detected: ${result.qrcode}`); // Check auto-torch on first valid QR detection (only when in QR detecting state) if (this.data.app_state === 'qr_detecting') { @@ -246,19 +264,19 @@ Page({ }); if (this.image_data_urls.length >= 3) { - this.addDebugMessage('3 good images collected via worker, starting verification'); + this.log('3 good images collected via worker, starting verification'); this.startVerifying(); this.submitImageForVerification(this.image_data_urls, result.qrcode); this.image_data_urls = []; // Reset for next scan this.setData({ ok_frames: 0 }); // Reset counter } else { - this.addDebugMessage(`Collected ${this.image_data_urls.length}/3 worker images`); + this.log(`Collected ${this.image_data_urls.length}/3 worker images`); } } } }); - this.addDebugMessage('Worker set up for iPhone processing'); + this.log('Worker set up for iPhone processing'); return worker; }, @@ -271,9 +289,9 @@ Page({ if (should_use_webview) { emblem_camera_url = "https://themblem.com/camera-5.1/?" + make_query(rule.zoom, this.data.return_page, this.data.real_ip, this.data.tenant_id); - this.addDebugMessage(`Using web-view camera: ${emblem_camera_url}`); + this.log(`Using web-view camera: ${emblem_camera_url}`); } else { - this.addDebugMessage('Using native camera with local WASM processing'); + this.log('Using native camera with local WASM processing'); } this.setData({ @@ -285,14 +303,14 @@ Page({ emblem_camera_url: emblem_camera_url }); - this.addDebugMessage(`Camera rule: zoom=${rule.zoom}, web_view=${rule.web_view}${this.data.no_web_view ? ' (NO_WEB_VIEW)' : ''}`); + this.log(`Camera rule: zoom=${rule.zoom}, web_view=${rule.web_view}${this.data.no_web_view ? ' (NO_WEB_VIEW)' : ''}`); if (should_use_webview) { // Step 3a: Go directly to webview scanning (no QRTool needed) this.startWebviewScanning(); } else { // Step 3b: Load QRTool for camera mode - this.addDebugMessage('Starting QRTool initialization'); + this.log('Starting QRTool initialization'); this.transitionToState('loading_qrtool'); this.initializeQRTool(); } @@ -309,16 +327,17 @@ Page({ this.setData({ max_zoom: max_zoom }); - this.addDebugMessage(`Camera max zoom: ${max_zoom}`); + this.log(`Camera max zoom: ${max_zoom}`); } + this.log("onCameraReady: create camera context"); this.camera_context = wx.createCameraContext(); - this.addDebugMessage('Camera context created'); + this.log('Camera context created'); // Step 5: Set up initial zoom and start scanning if (this.data.camera_rule) { this.setupCameraZoom(this.data.camera_rule); - this.addDebugMessage('Initial zoom set up'); + this.log('Initial zoom set up'); } // Step 6: Transition to scanning (which also starts frame processing) @@ -327,7 +346,7 @@ Page({ onCameraError(e) { console.error('Camera error', e); - this.addDebugMessage(`Camera error: ${JSON.stringify(e.detail)}`); + this.log(`Camera error: ${JSON.stringify(e.detail)}`); // Redirect with failure instead of showing modal this.goToResult(null, null, false); }, @@ -386,7 +405,7 @@ Page({ */ setupCameraZoom(rule) { if (!this.camera_context) { - this.addDebugMessage('Cannot setup zoom: camera context not ready'); + this.log('Cannot setup zoom: camera context not ready'); return; } @@ -399,12 +418,12 @@ Page({ rule_zoom: zoom }); - this.addDebugMessage(`Camera set initial zoom to ${initial_zoom}x, will zoom in to ${zoom}x when QR is found`); + this.log(`Camera set initial zoom to ${initial_zoom}x, will zoom in to ${zoom}x when QR is found`); this.camera_context.setZoom({ zoom: initial_zoom }); // Set up zoom-in behavior when QR is found this.on_first_qr_found = () => { - this.addDebugMessage(`First QR found, zoom to ${zoom}x`); + this.log(`First QR found, zoom to ${zoom}x`); this.camera_context.setZoom({ zoom: zoom }); this.setData({ zoom: zoom, @@ -419,7 +438,7 @@ Page({ */ transitionToState(newState, mode = null) { const oldState = this.data.app_state; - this.addDebugMessage(`State: ${oldState} -> ${newState}${mode ? ` (${mode})` : ''}`); + this.log(`State: ${oldState} -> ${newState}${mode ? ` (${mode})` : ''}`); const stateData = { app_state: newState }; if (mode) stateData.scan_mode = mode; @@ -450,7 +469,7 @@ Page({ }); // Start frame processing - all conditions are implied to be ready at this point - this.addDebugMessage('Starting camera frame listener'); + this.log('Starting camera frame listener'); this.lastFrameTime = 0; // Set up camera frame listener @@ -563,7 +582,7 @@ Page({ this.handleQRResult(result, frame, uca); } } catch (error) { - this.addDebugMessage(`Frame processing error: ${error.message}`); + this.log(`Frame processing error: ${error.message}`); // Still count failed processing attempts const processEnd = Date.now(); const processingTime = processEnd - processStart; @@ -609,7 +628,7 @@ Page({ // don't require ok as we only care about the view has a valid qrcode in it // zooming in so that it's more likely to be clear enough for upload if (result.qrcode && is_emblem_qr_pattern(result.qrcode)) { - this.addDebugMessage(`QR detected: ${result.qrcode} ok: ${result.ok}: err ${result.err}`); + this.log(`QR detected: ${result.qrcode} ok: ${result.ok}: err ${result.err}`); // Check auto-torch on first valid QR detection (only when in QR detecting state) if (this.data.app_state === 'qr_detecting') { @@ -631,7 +650,7 @@ Page({ this.setData({ hint_text: hint }); if (result.qrcode) { - this.addDebugMessage(`QR found but not valid: ${result.qrcode} (${result.err})`); + this.log(`QR found but not valid: ${result.qrcode} (${result.err})`); } } }, @@ -652,16 +671,16 @@ Page({ ok_frames: this.image_data_urls.length }); - this.addDebugMessage(`Collected ${this.image_data_urls.length}/3 good images (direct) - using copied data`); + this.log(`Collected ${this.image_data_urls.length}/3 good images (direct) - using copied data`); // Add debug info about the submitted image if (this.data.enable_debug) { - this.addDebugMessage(`Submitted image preview: ${dataUrl.substring(0, 50)}...`); + this.log(`Submitted image preview: ${dataUrl.substring(0, 50)}...`); } // Need 3 "ok" frames before verification (like camera.js) if (this.image_data_urls.length >= 3) { - this.addDebugMessage('3 good images collected, starting verification'); + this.log('3 good images collected, starting verification'); this.startVerifying(); this.submitImageForVerification(this.image_data_urls, qrCode); this.image_data_urls = []; // Reset for next scan @@ -676,11 +695,11 @@ Page({ * Submit images for verification like camera.js */ submitImageForVerification(dataUrls, qrCode) { - this.addDebugMessage('Submitting images for verification'); + this.log('Submitting images for verification'); const begin = Date.now(); - + const success = (res) => { - this.addDebugMessage(`Upload success, code: ${res.statusCode}`); + this.log(`Upload success, code: ${res.statusCode}`); if (res.statusCode === 200) { let resp; if (typeof res.data === "string") { @@ -688,10 +707,10 @@ Page({ } else { resp = res.data; } - + // Store verification response getApp().globalData.verify_resp = resp; - + if (resp.serial_code) { // Let verification animation run for a bit, then redirect with success const delay = 3000 - (Date.now() - begin); @@ -710,19 +729,27 @@ Page({ this.goToResult(qrCode, null, false); } }; - + const fail = (e) => { - this.addDebugMessage(`Upload failed: ${JSON.stringify(e)}`); + this.log(`Upload failed: ${JSON.stringify(e)}`); // Redirect with failure instead of showing modal this.goToResult(qrCode, null, false); }; - + // Store global data like camera.js const gd = getApp().globalData; gd.image_data_urls = dataUrls; gd.qr_code = qrCode; - - upload_image_data_urls(dataUrls, success, fail, this.data.logs.join("\n")); + + // Use enhanced upload function with local metadata instead of global data + const metadata = { + real_ip: this.data.real_ip, + phone_model: this.data.phone_model, + server_url: this.data.server_url, + userinfo: this.data.userinfo + }; + + upload_image_data_urls_with_metadata(dataUrls, qrCode, metadata, success, fail, this.data.logs.join("\n")); }, @@ -747,13 +774,13 @@ Page({ }); check_auto_torch(qrcode, (auto_torch, camera_sensitivity) => { - this.addDebugMessage(`Auto-torch check: ${auto_torch}, sensitivity: ${camera_sensitivity}`); + this.log(`Auto-torch check: ${auto_torch}, sensitivity: ${camera_sensitivity}`); if (auto_torch) { this.setData({ camera_flash: 'torch' }); - this.addDebugMessage('Auto-torch enabled'); + this.log('Auto-torch enabled'); } // Update camera sensitivity and continue scanning @@ -811,10 +838,10 @@ Page({ wx.redirectTo({ url: url, success: () => { - this.addDebugMessage(`Navigated to: ${this.data.return_page}`); + this.log(`Navigated to: ${this.data.return_page}`); }, fail: (err) => { - this.addDebugMessage(`Navigation failed: ${err.errMsg}`); + this.log(`Navigation failed: ${err.errMsg}`); this.restart_camera(); } }); @@ -852,30 +879,31 @@ Page({ }, /** - * Log function for backend reporting (like camera.js) + * Unified logging function for both backend reporting and debug overlay */ log(...what) { + const message = what.join(" "); + + // Always log to console console.log(...what); - this.setData({ - logs: this.data.logs.concat([new Date() + ": " + what.join(" ")]) - }); - }, - /** - * Add debug message to debug overlay and backend logs - */ - addDebugMessage(message) { // Always add to backend logs for reporting - this.log(message); + const newLogs = this.data.logs.concat([new Date() + ": " + message]); - // Only show in debug overlay if debug is enabled - if (!this.data.enable_debug) return; - - const timestamp = new Date().toLocaleTimeString(); - const debugMsg = `${timestamp}: ${message}`; + // Update debug overlay with recent logs if debug is enabled + let debugMsgs = []; + if (this.data.enable_debug) { + // Show only the 5 most recent logs, formatted with short timestamp + debugMsgs = newLogs.slice(-5).reverse().map(log => { + const timestamp = new Date().toLocaleTimeString(); + const logMessage = log.substring(log.indexOf(": ") + 2); // Remove full date, keep message + return `${timestamp}: ${logMessage}`; + }); + } this.setData({ - debug_msgs: [debugMsg, ...this.data.debug_msgs].slice(0, 5) // Keep first 5 messages (newest on top) + logs: newLogs, + debug_msgs: debugMsgs }); }, @@ -891,20 +919,12 @@ Page({ }); }, - /** - * Log function for debugging - */ - log(...args) { - console.log(...args); - this.addDebugMessage(args.join(' ')); - }, - /** * Handle messages from web-view camera */ on_webview_message(e) { console.log('Web-view message received:', e); - this.addDebugMessage(`Web-view message: ${JSON.stringify(e.detail)}`); + this.log(`Web-view message: ${JSON.stringify(e.detail)}`); // Handle QR code results from web-view if (e.detail && e.detail.data && e.detail.data.length > 0) { @@ -913,7 +933,7 @@ Page({ // Web-view results go directly to result (no verification step) this.goToResult(messageData.qr_code, messageData.serial_code, true); } else if (messageData.error) { - this.addDebugMessage(`Web-view error: ${messageData.error}`); + this.log(`Web-view error: ${messageData.error}`); // Redirect with failure instead of showing hint this.goToResult(null, null, false); } @@ -951,7 +971,12 @@ Page({ ok_frames: 0, total_processing_time: 0, avg_processing_time_ms: 0, - last_frame_time_ms: 0 + last_frame_time_ms: 0, + // Clear logs and debug messages + logs: [], + debug_msgs: [], + // Clear userinfo + userinfo: null }); // Initialize image collection for verification @@ -963,6 +988,7 @@ Page({ this.initializeSystem(enable_debug); this.fetchRealIP(); this.fetchTenantID(); + this.fetchUserInfo(); // Step 2: Load camera rules based on phone model this.loadCameraRules(); @@ -987,7 +1013,10 @@ Page({ this.image_data_urls = []; // Clear logs to prevent memory buildup - this.setData({ logs: [] }); + this.setData({ + logs: [], + debug_msgs: [] + }); // Clear zoom function reference this.on_first_qr_found = null; @@ -1005,15 +1034,16 @@ Page({ */ cleanupListener() { if (this.listener) { + this.log("cleanupListener: stop camera frame listener"); this.listener.stop(); this.listener = null; - this.addDebugMessage('Camera frame listener stopped'); + this.log('Camera frame listener stopped'); } // Clear camera context to prevent multiple camera elements if (this.camera_context) { this.camera_context = null; - this.addDebugMessage('Camera context cleared'); + this.log('Camera context cleared'); } // Clean up worker state @@ -1041,7 +1071,7 @@ Page({ // Clear stored worker frame reference this.lastWorkerFrame = null; - this.addDebugMessage('Worker state cleaned up'); + this.log('Worker state cleaned up'); } } diff --git a/scanner/pages/emblemscanner/emblemscanner.wxml b/scanner/pages/emblemscanner/emblemscanner.wxml index d32e936..8ad441c 100644 --- a/scanner/pages/emblemscanner/emblemscanner.wxml +++ b/scanner/pages/emblemscanner/emblemscanner.wxml @@ -11,51 +11,34 @@ 初始化QR工具... - - - - - - {{ hint_text }} - - + + - - - - - - - - - + + - - - + + + - - + + {{ hint_text }} - - + diff --git a/scanner/pages/emblemscanner/upload.js b/scanner/pages/emblemscanner/upload.js index 58fe480..1b2d0bc 100644 --- a/scanner/pages/emblemscanner/upload.js +++ b/scanner/pages/emblemscanner/upload.js @@ -61,7 +61,39 @@ function upload_image_data_urls(image_data_urls, success, fail, log) { }); } +function upload_image_data_urls_with_metadata(image_data_urls, qrcode, metadata, success, fail, log) { + var gd = getApp().globalData; + var fd = { + emblem_id: metadata.userinfo.emblem_id, + nick_name: metadata.userinfo.nickName, + realip: metadata.real_ip, + qrcode: qrcode, + angle: 0, + phonemodel: metadata.phone_model, + image_data_urls, + use_roi_verify: 1, + log, + }; + var ci = gd.caller_info; + if (ci && ci.token) { + fd.token = ci.token; + } + var url = metadata.server_url + '/api/v1/qr-verify/'; + console.log("wx.request", url, fd.qrcode, fd.angle, fd.phonemodel, fd.realip); + wx.request({ + url, + method: "POST", + header: { + "Content-Type": "application/json", + }, + data: JSON.stringify(fd), + success, + fail, + }); +} + module.exports = { upload_image_data_urls, + upload_image_data_urls_with_metadata, check_auto_torch, }; \ No newline at end of file