/** * QR Code 產生器 - Server API 呼叫模組 */ /** * 呼叫 Server API 產生 QR Code * @param {Object} studentData - 學員資料 * @param {Object} actions - 執行選項 * @returns {Object} API 回應 */ function callServerAPI(studentData, actions) { const payload = { class_prefix: studentData.classPrefix, student_no: studentData.studentNo, name: studentData.name, email: studentData.email, phone: studentData.phone, actions: { generate_qrcode: true, save_to_db: actions.saveToDb || false, upload_to_server: actions.uploadToServer || false } }; const options = { method: 'post', contentType: 'application/json', payload: JSON.stringify(payload), muteHttpExceptions: true, timeout: 30 }; try { const response = UrlFetchApp.fetch(CONFIG.API_ENDPOINT, options); const responseCode = response.getResponseCode(); const responseText = response.getContentText(); if (responseCode !== 200) { return { success: false, error: { stage: 'api_call', code: 'HTTP_' + responseCode, message: 'Server 回應錯誤: ' + responseCode } }; } const result = JSON.parse(responseText); if (!result.success) { return { success: false, error: { stage: result.error?.stage || 'api_call', code: result.error?.code || 'API_ERROR', message: result.error?.message || '未知錯誤' } }; } return { success: true, data: result.data }; } catch (error) { // 判斷錯誤類型 let errorCode = 'UNKNOWN_ERROR'; let errorMessage = error.message; if (error.message.includes('timeout') || error.message.includes('Timeout')) { errorCode = 'TIMEOUT'; errorMessage = 'API 連線逾時'; } else if (error.message.includes('connect') || error.message.includes('network')) { errorCode = 'NETWORK_ERROR'; errorMessage = '網路連線錯誤'; } else if (error.message.includes('JSON')) { errorCode = 'PARSE_ERROR'; errorMessage = 'API 回應格式錯誤'; } return { success: false, error: { stage: 'api_call', code: errorCode, message: errorMessage } }; } } /** * 測試 API 連線 * @returns {Object} 測試結果 */ function testAPIConnection() { const testPayload = { class_prefix: 'TEST', student_no: 'TEST001', name: '測試', email: 'test@test.com', phone: '0912345678', actions: { generate_qrcode: true, save_to_db: false, upload_to_server: false }, test_mode: true // 告訴 Server 這是測試請求 }; const options = { method: 'post', contentType: 'application/json', payload: JSON.stringify(testPayload), muteHttpExceptions: true, timeout: 10 }; try { const response = UrlFetchApp.fetch(CONFIG.API_ENDPOINT, options); const responseCode = response.getResponseCode(); if (responseCode === 200) { const result = JSON.parse(response.getContentText()); return { success: true, message: 'API 連線正常', serverVersion: result.version || 'unknown' }; } else { return { success: false, message: 'API 回應錯誤: ' + responseCode }; } } catch (error) { return { success: false, message: 'API 連線失敗: ' + error.message }; } } /** * 批次呼叫 API(用於需要更好效能的情況) * @param {Array} studentsData - 學員資料陣列 * @param {Object} actions - 執行選項 * @returns {Array} API 回應陣列 */ function callServerAPIBatch(studentsData, actions) { const payload = { batch: true, students: studentsData.map(s => ({ class_prefix: s.classPrefix, student_no: s.studentNo, name: s.name, email: s.email, phone: s.phone })), actions: { generate_qrcode: true, save_to_db: actions.saveToDb || false, upload_to_server: actions.uploadToServer || false } }; const options = { method: 'post', contentType: 'application/json', payload: JSON.stringify(payload), muteHttpExceptions: true, timeout: 120 // 批次處理需要較長時間 }; try { const response = UrlFetchApp.fetch(CONFIG.API_ENDPOINT, options); const responseCode = response.getResponseCode(); if (responseCode !== 200) { throw new Error('Server 回應錯誤: ' + responseCode); } const result = JSON.parse(response.getContentText()); return result.results || []; } catch (error) { // 批次失敗,回傳所有失敗 return studentsData.map(s => ({ success: false, student_no: s.studentNo, error: { stage: 'batch_api_call', code: 'BATCH_ERROR', message: error.message } })); } }