/** * 前端统一通信组件 (Frontend Unified Communication Component) * * 核心设计原则: * 1. 零硬编码 - 不使用任何IP地址或完整域名 * 2. 相对路径 - 所有请求使用相对路径,由浏览器和Nginx处理 * 3. 自动场景识别 - 根据当前域名自动确定业务场景 * 4. HTTPS兼容 - 完全支持HTTPS协议 * * 架构说明: * 前端页面 (console.duoweiying.cn) * ↓ 发起请求到 /smart-processor/command (相对路径) * Nginx反向代理 (api-gateway.duoweiying.cn) * ↓ 转发到后端 * 后端智能处理器 (127.0.0.1:8000/smart-processor/command) */ // ==================== 配置区域 ==================== /** * 域名到场景的映射表 * 注意: 这里只配置场景名称,不配置任何API地址 * API地址由后端的JSON配置文件管理 */ const SCENE_MAP = { 'console.duoweiying.cn': 'user_dashboard', // 用户首页 'admin.duoweiying.cn': 'admin_console', // 后台管理 'h5.duoweiying.cn': 'ai_army', // AI工具集 'code.duoweiying.cn': 'code_protection', // 代码保护系统 'ai-h5.duoweiying.cn': 'ai_army', // 多维鹰H5小程序 // 开发环境 fallback 'localhost': 'user_dashboard', '127.0.0.1': 'user_dashboard' }; /** * 智能处理器端点 (绝对URL) * 前端页面和API网关在不同域名,必须使用完整URL * 例如: https://api-gateway.duoweiying.cn/smart-processor/command */ const PROCESSOR_ENDPOINT = 'https://api-gateway.duoweiying.cn/smart-processor/command'; // ==================== 核心函数 ==================== /** * 获取当前业务场景 * @returns {string} 场景名称 */ function getCurrentScene() { const hostname = window.location.hostname; const scene = SCENE_MAP[hostname]; if (!scene) { console.warn(`⚠️ 未找到域名 "${hostname}" 的场景映射,使用默认场景`); return 'default'; } return scene; } /** * 获取上下文信息 * @returns {Object} 包含token、userId等认证信息 */ function getContext() { return { token: localStorage.getItem('auth_token') || sessionStorage.getItem('auth_token'), userId: localStorage.getItem('user_id') || sessionStorage.getItem('user_id'), username: localStorage.getItem('username') || sessionStorage.getItem('username'), // 可以添加更多上下文信息 platform: 'web', version: '1.0.0' }; } /** * 生成唯一请求ID * @returns {string} 请求ID */ function generateRequestId() { return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * 统一的API调用函数 * * @param {string} action - 动作名称,如: list_devices, reboot_device * @param {Object} params - 请求参数 * @param {Object} options - 可选配置 * @param {string} options.scene - 显式指定场景(可选,默认自动识别) * @param {number} options.timeout - 超时时间(毫秒),默认30000 * @param {boolean} options.retry - 是否重试,默认false * @param {number} options.maxRetries - 最大重试次数,默认3 * * @returns {Promise} 返回API响应数据 * * @example * // 基本用法 * const devices = await callBackend('list_devices', { page: 1, size: 20 }); * * @example * // 指定场景 * const users = await callBackend('list_users', {}, { scene: 'admin_console' }); * * @example * // 带重试 * const result = await callBackend('complex_task', params, { retry: true, maxRetries: 3 }); */ async function callBackend(action, params = {}, options = {}) { const { scene = getCurrentScene(), timeout = 30000, retry = false, maxRetries = 3 } = options; const requestId = generateRequestId(); // 参数校验 - 根据不同的action类型验证参数 const validatedParams = validateParams(action, params); // 构建请求体 const payload = { scene: scene, action: action, params: validatedParams, context: getContext(), requestId: requestId }; console.log(`📤 [${requestId}] 发送请求:`, { scene, action, paramKeys: Object.keys(validatedParams) }); // 执行请求(支持重试) let lastError; for (let attempt = 1; attempt <= (retry ? maxRetries : 1); attempt++) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); const response = await fetch(PROCESSOR_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json', // 如果需要,可以添加额外的请求头 // 'X-Request-ID': requestId }, body: JSON.stringify(payload), signal: controller.signal }); clearTimeout(timeoutId); // 检查HTTP状态 if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.detail || `HTTP ${response.status}: ${response.statusText}`); } // 解析响应 const result = await response.json(); // 检查业务逻辑成功与否 if (!result.success) { throw new Error(result.error || '请求失败'); } console.log(`✅ [${requestId}] 请求成功:`, { action, executionTime: result.execution_time, dataKeys: result.data ? Object.keys(result.data) : [] }); return result.data; } catch (error) { lastError = error; if (error.name === 'AbortError') { console.error(`⏰ [${requestId}] 请求超时 (${timeout}ms)`); throw new Error(`请求超时: ${action}`); } console.error(`❌ [${requestId}] 请求失败 (尝试 ${attempt}/${maxRetries}):`, error.message); // 如果不是最后一次尝试,等待后重试 if (attempt < maxRetries) { const waitTime = Math.min(1000 * Math.pow(2, attempt - 1), 5000); // 指数退避 console.log(`⏳ [${requestId}] ${waitTime}ms 后重试...`); await new Promise(resolve => setTimeout(resolve, waitTime)); } } } // 所有重试都失败 throw lastError; } /** * 参数校验函数 - 根据action类型验证参数完整性 * @param {string} action - 动作名称 * @param {Object} params - 待校验参数 * @returns {Object} 校验后的参数 */ function validateParams(action, params = {}) { // 对于需要task_type和user_input的操作,确保参数完整性 if (action === 'process_task' || action === 'ai_chat' || action === 'generate_content') { if (!params.task_type) { console.warn(`⚠️ [参数校验] 缺少task_type参数,使用默认值 'general'`); params.task_type = 'general'; } if (!params.user_input && !params.prompt) { console.warn(`⚠️ [参数校验] 缺少user_input或prompt参数,使用默认值 ''`); params.user_input = params.prompt || ''; } } // 特定action的参数校验 switch (action) { case 'query_task_status': if (!params.task_id) { throw new Error('query_task_status操作缺少必需的task_id参数'); } break; case 'publish_video': if (!params.video_id) { console.warn('⚠️ publish_video操作缺少video_id参数'); } if (!params.platforms || !Array.isArray(params.platforms)) { console.warn('⚠️ publish_video操作缺少platforms参数或格式不正确'); } break; case 'verify_qr_scan': if (!params.scan_result) { console.warn('⚠️ verify_qr_scan操作缺少scan_result参数'); } break; default: // 对于其他操作,不做特殊校验 break; } return params; } /** * 批量并行调用多个API * * @param {Array} requests - 请求数组 * @param {string} requests[].action - 动作名称 * @param {Object} requests[].params - 请求参数 * @param {Object} requests[].options - 可选配置 * * @returns {Promise} 按顺序返回结果数组 * * @example * const [devices, users] = await callBackendBatch([ * { action: 'list_devices', params: { page: 1 } }, * { action: 'list_users', params: { page: 1 } } * ]); */ async function callBackendBatch(requests) { const promises = requests.map(req => callBackend(req.action, req.params || {}, req.options || {}) .then(data => ({ success: true, data })) .catch(error => ({ success: false, error: error.message })) ); return Promise.all(promises); } /** * 串行调用多个API(前一个的结果可以作为后一个的参数) * * @param {Array} requests - 请求数组 * @returns {Promise} 按顺序返回结果数组 */ async function callBackendSequential(requests) { const results = []; let previousResult = null; for (const req of requests) { // 可以将前一个结果合并到当前参数中 const params = previousResult && req.mergePrevious ? { ...req.params, ...previousResult } : req.params; const result = await callBackend(req.action, params, req.options); results.push(result); previousResult = result; } return results; } // ==================== 工具函数 ==================== /** * 检查智能处理器是否可用 * @returns {Promise} */ async function checkProcessorHealth() { try { const response = await fetch('/smart-processor/health'); return response.ok; } catch (error) { console.error('健康检查失败:', error); return false; } } /** * 获取当前场景的可用动作列表 * @param {string} scene - 场景名称(可选,默认当前场景) * @returns {Promise>} */ async function getAvailableActions(scene = null) { const targetScene = scene || getCurrentScene(); try { const response = await fetch(`/smart-processor/actions/${targetScene}`); if (!response.ok) { throw new Error('获取动作列表失败'); } const data = await response.json(); return data.actions || []; } catch (error) { console.error('获取动作列表失败:', error); return []; } } // ==================== 导出 ==================== // ✅ 修改为传统的全局变量导出方式(兼容普通