网易考拉商品详情页前端性能优化实战
网易考拉作为跨境电商平台,商品详情页具有全球供应链、多语言多币种、海外直邮/保税仓、正品保障、海淘用户特征明显等特点。本文结合考拉海购的业务特性,分享跨境场景下的性能优化方案。
一、考拉海购详情页业务特点分析
1.1 页面结构特征
┌─────────────────────────────────────────────────────────────────┐ │ 网易考拉商品详情页 - 跨境电商C2C/B2C模式 │ ├─────────────────────────────────────────────────────────────────┤ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 全球化头部(多语言切换+多币种切换+关税计算器+购物车) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 商品展示区(高清图册+短视频+直播+AR试妆+买家秀) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 跨境核心信息区 │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ │ │ 商品标题+跨境标识+正品保障+溯源码 │ │ │ │ │ │ 原价(CNY)+促销价(CNY)+税费预估+运费预估+包税标识 │ │ │ │ │ │ 发货方式(保税仓/海外直邮)+时效预估+清关进度 │ │ │ │ │ │ 库存状态+限购数量+预售标识+缺货预警 │ │ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 跨境购物工具区 │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ │ │ 关税计算器+运费估算器+尺码助手+成分解析器 │ │ │ │ │ │ 多语言描述切换+多地区价格对比+海淘攻略 │ │ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 商品详情区(多语言详情+规格参数+品牌故事+物流说明) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 跨境推荐区(相似海淘商品+同品牌推荐+保税仓热卖+达人推荐)│ │ │ └─────────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 底部行动栏(加入购物车+立即购买+预约购买+收藏监控) │ │ │ └─────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘
1.2 跨境电商场景性能痛点
| 痛点类别 | 具体表现 | 业务影响 |
|---|---|---|
| 全球供应链复杂性 | 多仓库库存同步延迟、跨境物流时效多变 | 库存显示不准确,用户体验差 |
| 多语言多币种 | 12种语言、15种币种实时换算 | 计算复杂度高,渲染延迟 |
| 关税计算复杂 | 动态关税政策、个人物品免税额度、品类税率差异 | 计算耗时,影响下单转化 |
| 高清媒体资源 | 4K商品图片、短视频、360°展示 | 资源体积大,加载缓慢 |
| 跨境网络延迟 | API调用境外服务商、DNS解析慢 | 接口响应慢,超时率高 |
| 合规性要求 | 正品溯源、海关申报、税务合规展示 | 额外数据加载,页面复杂度高 |
| 移动端海淘用户 | 中老年用户居多、网络环境复杂 | 性能要求更高,容错率低 |
1.3 跨境场景性能基线数据
优化前性能报告(基于考拉海购真实监控): ┌─────────────────────────────────────────────────────────────────┐ │ 指标名称 │ 平均值 │ P90值 │ P99值 │ 业务阈值 │ ├─────────────────────────────────────────────────────────────────┤ │ FCP (首次内容绘制) │ 3.5s │ 5.8s │ 9.2s │ <2.0s │ │ LCP (最大内容绘制) │ 5.2s │ 8.5s │ 13.8s │ <3.0s │ │ TTI (可交互时间) │ 7.8s │ 12.5s │ 19.2s │ <4.0s │ │ TBT (总阻塞时间) │ 1450ms │ 2380ms │ 3850ms │ <500ms │ │ CLS (累积布局偏移) │ 0.28 │ 0.45 │ 0.72 │ <0.1 │ │ JS Bundle Size │ 920KB │ 1180KB │ 1520KB │ <400KB │ │ 首屏接口响应时间 │ 1250ms │ 2100ms │ 3400ms │ <300ms │ │ 关税计算响应时间 │ 890ms │ 1560ms │ 2580ms │ <200ms │ │ 多语言切换响应时间 │ 650ms │ 1100ms │ 1850ms │ <150ms │ │ 高清图片加载成功率 │ 83.5% │ 74.2% │ 62.8% │ >95% │ │ 跨境支付成功率 │ 87.3% │ 79.6% │ 71.2% │ >93% │ └─────────────────────────────────────────────────────────────────┘
二、跨境场景首屏优化
2.1 全球CDN+边缘计算架构
// edge/functions/kaola-detail-render.js // 考拉海购边缘计算:全球CDN+边缘节点预渲染 export async function onRequest(context) { const { request, env } = context; const url = new URL(request.url); const productId = url.pathname.split('/').pop(); const country = request.headers.get('CF-IPCountry') || 'CN'; const region = request.cf?.region || 'unknown'; // 构建全球缓存键 const cacheKey = `kaola_detail_${productId}_${country}_${region}`; let cachedResponse = await caches.default.match(cacheKey); if (cachedResponse) { return this.addGlobalHeaders(cachedResponse, 'GLOBAL_HIT'); } // 获取用户位置信息 const locationInfo = this.getLocationInfo(country, region); // 并行获取跨境首屏数据 const [productInfo, inventoryInfo, priceInfo, shippingInfo, customsInfo, globalConfig] = await Promise.all([ this.fetchProductInfo(productId, locationInfo), this.fetchInventoryInfo(productId, locationInfo.country), this.fetchPriceInfo(productId, locationInfo.currency), this.fetchShippingInfo(productId, locationInfo.country), this.fetchCustomsInfo(productId, locationInfo.country), this.fetchGlobalConfig(locationInfo.country) ]); // 组装跨境首屏HTML const html = this.generateGlobalHTML({ productInfo, inventoryInfo, priceInfo, shippingInfo, customsInfo, globalConfig, locationInfo, timestamp: Date.now() }); // 全球边缘缓存(根据地区设置不同TTL) const ttl = this.getGlobalCacheTTL(locationInfo.country, productInfo.hotScore); const response = new Response(html, { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': `public, max-age=${ttl}, s-maxage=${ttl}`, 'X-Global-Cache': 'HIT', 'X-User-Region': locationInfo.region, 'X-User-Country': locationInfo.country, 'Vary': 'Accept-Language, CF-IPCountry' } }); // 异步写入全球缓存 caches.default.put(cacheKey, response.clone()); return this.addGlobalHeaders(response, 'GLOBAL_MISS'); } // 获取地理位置信息 getLocationInfo(country, region) { const countryConfigs = { 'CN': { currency: 'CNY', language: 'zh-CN', taxFreeLimit: 5000, vatRate: 0.13 }, 'US': { currency: 'USD', language: 'en-US', taxFreeLimit: 800, vatRate: 0.08 }, 'JP': { currency: 'JPY', language: 'ja-JP', taxFreeLimit: 50000, vatRate: 0.10 }, 'KR': { currency: 'KRW', language: 'ko-KR', taxFreeLimit: 600000, vatRate: 0.10 }, 'DE': { currency: 'EUR', language: 'de-DE', taxFreeLimit: 430, vatRate: 0.19 }, 'UK': { currency: 'GBP', language: 'en-GB', taxFreeLimit: 390, vatRate: 0.20 }, 'AU': { currency: 'AUD', language: 'en-AU', taxFreeLimit: 900, vatRate: 0.10 }, 'SG': { currency: 'SGD', language: 'en-SG', taxFreeLimit: 400, vatRate: 0.07 } }; const defaultConfig = countryConfigs['CN']; const config = countryConfigs[country] || defaultConfig; return { country, region, currency: config.currency, language: config.language, taxFreeLimit: config.taxFreeLimit, vatRate: config.vatRate, timezone: this.getTimezoneForRegion(region), shippingZone: this.getShippingZone(country) }; } // 生成全球化HTML generateGlobalHTML({ productInfo, inventoryInfo, priceInfo, shippingInfo, customsInfo, globalConfig, locationInfo }) { const { currency, language, taxFreeLimit, vatRate } = locationInfo; const { originalPrice, salePrice, tax, shippingCost, totalPrice } = priceInfo; // 计算关税和税费 const dutyAmount = this.calculateDuty(productInfo, customsInfo, taxFreeLimit, vatRate); const finalTotalPrice = totalPrice + dutyAmount; return `<!DOCTYPE html> <html lang="${language}" data-currency="${currency}" data-country="${locationInfo.country}"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>${this.generateGlobalTitle(productInfo, locationInfo)}</title> <link rel="preconnect" href="https://img.kaola.com"> <link rel="preconnect" href="https://api.kaola.com"> <link rel="dns-prefetch" href="https://global-cdn.kaola.com"> <style> /* 内联关键CSS - 跨境优化版 */ * { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; } .skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%); background-size: 200% 100%; animation: skeleton-loading 1.5s infinite; } @keyframes skeleton-loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } /* 跨境头部 */ .kaola-global-header { position: sticky; top: 0; z-index: 1000; padding: 10px 16px; background: linear-gradient(135deg, #e74c3c, #c0392b); color: white; } .global-language-selector { display: flex; align-items: center; gap: 8px; } .global-currency-selector { display: flex; align-items: center; gap: 4px; } /* 跨境价格区 */ .crossborder-price-section { padding: 16px; background: linear-gradient(180deg, #fff8f0 0%, #ffffff 100%); } .original-price { font-size: 14px; color: #999; text-decoration: line-through; } .sale-price { font-size: 32px; font-weight: 700; color: #e74c3c; } .sale-price::before { content: '${this.getCurrencySymbol(currency)}'; font-size: 18px; } .tax-estimate { display: inline-block; padding: 4px 8px; background: #fff3cd; color: #856404; border-radius: 4px; font-size: 12px; margin-top: 8px; } .shipping-info { display: flex; align-items: center; gap: 8px; margin-top: 8px; font-size: 13px; color: #666; } .free-shipping-badge { display: inline-flex; align-items: center; padding: 4px 8px; background: #d4edda; color: #155724; border-radius: 4px; font-size: 11px; } .customs-clearance { display: flex; align-items: center; gap: 4px; margin-top: 8px; font-size: 12px; color: #17a2b8; } /* 跨境标识 */ .crossborder-badges { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; } .authenticity-badge { display: inline-flex; align-items: center; gap: 4px; padding: 6px 10px; background: linear-gradient(135deg, #667eea, #764ba2); color: white; border-radius: 6px; font-size: 12px; } .traceability-badge { display: inline-flex; align-items: center; gap: 4px; padding: 6px 10px; background: linear-gradient(135deg, #11998e, #38ef7d); color: white; border-radius: 6px; font-size: 12px; } .warehouse-badge { display: inline-flex; align-items: center; gap: 4px; padding: 6px 10px; background: linear-gradient(135deg, #f093fb, #f5576c); color: white; border-radius: 6px; font-size: 12px; } /* 库存与发货 */ .inventory-shipping { padding: 16px; background: #fff; margin-top: 12px; border-radius: 12px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); } .stock-status { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; } .stock-indicator { width: 8px; height: 8px; border-radius: 50%; background: #28a745; } .stock-indicator.low { background: #ffc107; } .stock-indicator.out { background: #dc3545; } .shipping-method { display: flex; align-items: center; justify-content: space-between; padding: 12px; background: #f8f9fa; border-radius: 8px; margin-bottom: 8px; } .delivery-time { font-size: 14px; color: #333; } .delivery-zone { font-size: ; color: #666; } /* 跨境工具 */ .crossborder-tools { padding: 16px; background: #fff; margin-top: 12px; border-radius: 12px; } .tool-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; } .tool-item { display: flex; flex-direction: column; align-items: center; padding: 12px 8px; background: #f8f9fa; border-radius: 8px; cursor: pointer; transition: all 0.2s; } .tool-item:hover { background: #e9ecef; transform: translateY(-2px); } .tool-icon { font-size: 24px; margin-bottom: 6px; } .tool-label { font-size: 11px; color: #666; text-align: center; } /* 骨架屏 */ .skeleton-header { height: 56px; } .skeleton-gallery { height: 320px; margin: 12px; border-radius: 12px; } .skeleton-price { height: 48px; margin: 16px; border-radius: 8px; } .skeleton-info { height: 120px; margin: 12px; border-radius: 12px; } </style> </head> <body> <div id="app"> <!-- 骨架屏占位 --> <div class="skeleton skeleton-header"></div> <div class="skeleton skeleton-gallery"></div> <div class="skeleton skeleton-price"></div> <div class="skeleton skeleton-info"></div> </div> <!-- 预加载全局配置到全局变量 --> <script> window.__KAOLA_GLOBAL_CONFIG__ = { productInfo: ${JSON.stringify(productInfo)}, inventoryInfo: ${JSON.stringify(inventoryInfo)}, priceInfo: ${JSON.stringify(priceInfo)}, shippingInfo: ${JSON.stringify(shippingInfo)}, customsInfo: ${JSON.stringify(customsInfo)}, globalConfig: ${JSON.stringify(globalConfig)}, locationInfo: ${JSON.stringify(locationInfo)}, dutyCalculation: { dutyAmount: ${dutyAmount}, finalTotalPrice: ${finalTotalPrice}, taxFreeLimit: ${taxFreeLimit}, vatRate: ${vatRate} }, serverTime: ${Date.now()}, cdnDomains: ${JSON.stringify(globalConfig.cdnDomains)} }; </script> <!-- 加载主应用 --> <script src="https://global-cdn.kaola.com/app/v2.1.0/kaola-main.js" async crossorigin></script> </body> </html>`; } // 获取全球缓存TTL getGlobalCacheTTL(country, hotScore) { // 热门商品缓存时间短,冷门商品缓存时间长 // 不同地区根据网络状况调整 const regionMultipliers = { 'CN': 1, // 国内最快 'HK': 1.2, // 香港 'JP': 1.5, // 日本 'US': 2, // 美国 'EU': 2.5, // 欧洲 'OTHER': 3 // 其他地区 }; let baseTTL = hotScore > 80 ? 45 : (hotScore > 50 ? 90 : 180); return Math.round(baseTTL * (regionMultipliers[country] || regionMultipliers['OTHER'])); } // 添加全球缓存头 addGlobalHeaders(response, cacheStatus) { const newResponse = new Response(response.body, response); newResponse.headers.set('X-Global-Cache-Status', cacheStatus); newResponse.headers.set('X-CDN-Region', response.headers.get('cf-ray') || 'unknown'); return newResponse; }2.2 跨境SSR服务架构
// server/kaola-ssr.js // 网易考拉SSR服务 - 跨境场景优化版 import { renderToString } from 'react-dom/server'; import { createServerComponent } from './server-components'; import { GlobalDataPrefetcher } from './services/globalDataPrefetcher'; class KaolaSSRService { constructor() { this.prefetcher = new GlobalDataPrefetcher(); this.templateCache = new Map(); this.globalCacheRegions = ['CN', 'US', 'EU', 'APAC']; } // 主渲染方法 async renderPage(ctx) { const { productId, lang, currency, country } = ctx.query; // 构建全球化缓存键 const cacheKey = this.buildGlobalCacheKey(productId, lang, currency, country); // 尝试从缓存获取 const cached = await this.getFromGlobalCache(cacheKey); if (cached) { return this.addGlobalCacheHeaders(cached, 'HIT'); } // 获取用户全球化配置 const globalConfig = this.getGlobalConfig(ctx); // 数据预取 - 跨境场景特殊优化 const preloadContext = await this.prefetcher.prefetch({ productId, globalConfig, userId: ctx.state.userId, deviceInfo: ctx.state.deviceInfo, sessionInfo: ctx.state.sessionInfo }); // 服务端渲染 const html = await this.renderHTML(preloadContext, globalConfig); // 写入全球缓存 const ttl = this.getGlobalCacheTTL(productId, preloadContext.productInfo?.hotScore, country); await this.setGlobalCache(cacheKey, html, ttl); return this.addGlobalCacheHeaders(html, 'MISS'); } // 获取全球化配置 getGlobalConfig(ctx) { const acceptLanguage = ctx.request.headers['accept-language'] || 'zh-CN'; const cfIpCountry = ctx.request.headers['cf-ipcountry'] || 'CN'; const cfRegion = ctx.request.cf?.region || 'unknown'; // 解析语言和货币偏好 const language = this.parseLanguagePreference(acceptLanguage); const currency = this.parseCurrencyPreference(cfIpCountry, ctx.query.currency); const country = cfIpCountry; return { language, currency, country, region: cfRegion, timezone: this.getRegionalTimezone(cfRegion), shippingZone: this.getShippingZone(country), taxConfig: this.getTaxConfiguration(country), complianceConfig: this.getComplianceConfig(country), featureFlags: this.getFeatureFlags(country, language) }; } // 跨境数据预取器 async prefetch({ productId, globalConfig, userId, deviceInfo, sessionInfo }) { const { language, currency, country, taxConfig, complianceConfig } = globalConfig; // 并行获取各类数据,按优先级排序 const [ productInfo, // 商品基本信息 - 最高优先级 inventoryData, // 库存信息 - 高优先级 pricingData, // 价格信息 - 高优先级 shippingOptions, // 配送选项 - 高优先级 customsData, // 海关数据 - 高优先级 authenticityData, // 正品数据 - 高优先级 globalReviews, // 全球评价 - 中优先级 relatedProducts, // 相关商品 - 中优先级 brandStory, // 品牌故事 - 低优先级 complianceDocs // 合规文档 - 低优先级 ] = await Promise.all([ this.fetchProductInfo(productId, language), this.fetchInventoryInfo(productId, country), this.fetchPricingInfo(productId, currency, country, taxConfig), this.fetchShippingOptions(productId, country), this.fetchCustomsInfo(productId, country, complianceConfig), this.fetchAuthenticityInfo(productId), this.fetchGlobalReviews(productId, language), this.fetchRelatedProducts(productId, country), this.fetchBrandStory(productId, language), this.fetchComplianceDocs(productId, country, complianceConfig) ]); // 计算跨境核心数据 const crossborderData = this.calculateCrossborderData({ productInfo, inventoryData, pricingData, shippingOptions, customsData, taxConfig, country }); return { productInfo, inventoryData, pricingData, shippingOptions, customsData, authenticityData, globalReviews, relatedProducts, brandStory, complianceDocs, crossborderData, globalConfig, userId, deviceInfo, sessionInfo, renderTime: Date.now() }; } // 计算跨境核心数据 calculateCrossborderData({ productInfo, inventoryData, pricingData, shippingOptions, customsData, taxConfig, country }) { const { originalPrice, salePrice, baseCurrency } = pricingData; const { dutyRate, vatRate, taxFreeLimit, personalAllowance } = taxConfig; // 汇率转换 const exchangeRate = this.getExchangeRate(baseCurrency, pricingData.displayCurrency); const convertedOriginalPrice = originalPrice * exchangeRate; const convertedSalePrice = salePrice * exchangeRate; // 税费计算 const subtotal = convertedSalePrice * pricingData.quantity; const dutyCalculation = this.calculateDutyAmount(subtotal, customsData.category, dutyRate, personalAllowance); const vatCalculation = this.calculateVAT(subtotal + dutyCalculation.amount, vatRate); const totalTaxAmount = dutyCalculation.amount + vatCalculation.amount; const finalTotalPrice = subtotal + totalTaxAmount + pricingData.shippingCost; // 配送时效计算 const deliveryEstimate = this.calculateDeliveryEstimate(shippingOptions, country); // 库存状态 const stockStatus = this.determineStockStatus(inventoryData, country); // 正品保障级别 const authenticityLevel = this.determineAuthenticityLevel(productInfo, authenticityData); return { pricing: { originalPrice: convertedOriginalPrice, salePrice: convertedSalePrice, exchangeRate, currency: pricingData.displayCurrency, quantity: pricingData.quantity, subtotal, shippingCost: pricingData.shippingCost, dutyAmount: dutyCalculation.amount, vatAmount: vatCalculation.amount, totalTaxAmount, finalTotalPrice, taxBreakdown: { duty: dutyCalculation, vat: vatCalculation } }, shipping: { options: shippingOptions, estimate: deliveryEstimate, fastestOption: shippingOptions.reduce((fastest, option) => option.estimatedDays < fastest.estimatedDays ? option : fastest ), cheapestOption: shippingOptions.reduce((cheapest, option) => option.cost < cheapest.cost ? option : cheapest ) }, inventory: { status: stockStatus, availableQuantity: inventoryData.availableQuantity, reservedQuantity: inventoryData.reservedQuantity, restockDate: inventoryData.restockDate, warehouseLocation: inventoryData.warehouseLocation, isPreorder: inventoryData.isPreorder, preorderEndDate: inventoryData.preorderEndDate }, authenticity: { level: authenticityLevel, certifications: authenticityData.certifications, traceabilityCode: authenticityData.traceabilityCode, verificationUrl: authenticityData.verificationUrl, brandAuthorized: authenticityData.brandAuthorized }, compliance: { isRestricted: customsData.isRestricted, requiresPrescription: customsData.requiresPrescription, ageRestriction: customsData.ageRestriction, importLicense: customsData.importLicense, prohibitedInRegion: customsData.prohibitedInRegion } }; } // 税费计算 calculateDutyAmount(subtotal, productCategory, dutyRate, personalAllowance) { // 检查是否超过免税额 if (subtotal <= personalAllowance) { return { amount: 0, rate: 0, isDutyFree: true, allowance: personalAllowance }; } // 计算应税金额 const taxableAmount = subtotal - personalAllowance; // 获取品类税率 const categoryRate = dutyRate[productCategory] || dutyRate['default'] || 0.1; // 计算关税 const dutyAmount = taxableAmount * categoryRate; return { amount: parseFloat(dutyAmount.toFixed(2)), rate: categoryRate, taxableAmount, isDutyFree: false, allowance: personalAllowance, category: productCategory }; } // VAT计算 calculateVAT(amount, vatRate) { const vatAmount = amount * vatRate; return { amount: parseFloat(vatAmount.toFixed(2)), rate: vatRate, taxableAmount: amount }; } // 配送时效计算 calculateDeliveryEstimate(shippingOptions, destinationCountry) { const estimates = shippingOptions.map(option => ({ method: option.method, estimatedDays: option.estimatedDays, cost: option.cost, reliability: option.reliability, trackingAvailable: option.trackingAvailable })); // 计算加权平均时效 const weightedAverage = estimates.reduce((sum, option) => { return sum + (option.estimatedDays * option.reliability); }, 0) / estimates.reduce((sum, option) => sum + option.reliability, 0); return { options: estimates, averageDays: Math.round(weightedAverage), fastestDays: Math.min(...estimates.map(e => e.estimatedDays)), mostReliable: estimates.reduce((best, option) => option.reliability > best.reliability ? option : best ), destinationCountry }; } // 库存状态判断 determineStockStatus(inventoryData, country) { const { availableQuantity, reservedQuantity, isPreorder, restockDate } = inventoryData; const totalAvailable = availableQuantity - reservedQuantity; if (totalAvailable <= 0) { if (isPreorder) { return { status: 'preorder', label: '预售中', message: `预计${restockDate}开始发货`, canOrder: true }; } return { status: 'out_of_stock', label: '暂时缺货', message: `预计${restockDate}补货`, canOrder: false }; } if (totalAvailable <= 10) { return { status: 'low_stock', label: '仅剩少量', message: `仅剩${totalAvailable}件,欲购从速`, canOrder: true, urgencyLevel: 'high' }; } if (totalAvailable <= 50) { return { status: 'limited_stock', label: '库存紧张', message: `剩余${totalAvailable}件`, canOrder: true, urgencyLevel: 'medium' }; } return { status: 'in_stock', label: '现货充足', message: '下单后24小时内发货', canOrder: true, urgencyLevel: 'low' }; } // 正品保障级别判断 determineAuthenticityLevel(productInfo, authenticityData) { let score = 0; const factors = []; // 官方授权加分 if (authenticityData.brandAuthorized) { score += 40; factors.push('品牌官方授权'); } // 溯源码加分 if (authenticityData.traceabilityCode) { score += 30; factors.push('正品溯源码'); } // 认证证书加分 if (authenticityData.certifications?.length > 0) { score += Math.min(authenticityData.certifications.length * 10, 20); factors.push(`${authenticityData.certifications.length}项权威认证`); } // 平台质检加分 if (productInfo.qualityChecked) { score += 10; factors.push('考拉质检'); } // 确定级别 let level, badge, description; if (score >= 80) { level = 'premium'; badge = '🏆 考拉严选·正品保证'; description = '多重正品保障,假一赔十'; } else if (score >= 60) { level = 'verified'; badge = '✅ 正品保障'; description = '官方认证,品质保证'; } else if (score >= 40) { level = 'standard'; badge = '🔍 正品承诺'; description = '平台正品承诺'; } else { level = 'basic'; badge = '📦 正品行货'; description = '正规渠道采购'; } return { level, score, badge, description, factors }; } // 渲染HTML模板 async renderHTML(context, globalConfig) { const { productInfo, crossborderData, globalConfig: gConfig } = context; const { language, currency, country } = gConfig; // 生成结构化数据(SEO优化) const structuredData = this.generateStructuredData(context, globalConfig); // 生成页面标题(多语言优化) const pageTitle = this.generateGlobalTitle(productInfo, crossborderData, globalConfig); // 生成Meta标签 const metaTags = this.generateMetaTags(context, globalConfig); // 渲染React组件 const appHtml = renderToString( <KaolaDetailApp context={context} globalConfig={globalConfig} /> ); // 组装完整HTML return `<!DOCTYPE html> <html lang="${language}" data-currency="${currency}" data-country="${country}"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>${pageTitle}</title> ${metaTags} ${structuredData} <!-- 预加载关键资源 --> <link rel="preload" href="https://global-cdn.kaola.com/app/main.css" as="style"> <link rel="preload" href="https://global-cdn.kaola.com/app/main.js" as="script"> <link rel="preconnect" href="https://img.kaola.com"> <link rel="preconnect" href="https://api.kaola.com"> <link rel="dns-prefetch" href="https://eu-cdn.kaola.com"> <link rel="dns-prefetch" href="https://us-cdn.kaola.com"> <link rel="dns-prefetch" href="https://jp-cdn.kaola.com"> <style> /* 内联关键CSS - 跨境优化版 */ ${this.getCriticalCSS(globalConfig)} </style> </head> <body> <div id="root">${appHtml}</div> <!-- 客户端水合脚本 --> <script> window.__KAOLA_INITIAL_STATE__ = ${JSON.stringify(context)}; window.__KAOLA_GLOBAL_CONFIG__ = ${JSON.stringify(globalConfig)}; window.__KAOLA_RENDER_TIME__ = ${Date.now()}; window.__KAOLA_SERVER_REGION__ = '${globalConfig.region}'; </script> <script src="https://global-cdn.kaola.com/app/main.js" defer crossorigin></script> </body> </html>`; } // 获取全球化缓存键 buildGlobalCacheKey(productId, lang, currency, country) { const normalizedLang = lang || 'zh-CN'; const normalizedCurrency = currency || 'CNY'; const normalizedCountry = country || 'CN'; return `kaola_detail_${productId}_${normalizedLang}_${normalizedCurrency}_${normalizedCountry}`; } // 获取全球缓存TTL getGlobalCacheTTL(productId, hotScore, country) { // 根据国家/地区设置不同的缓存策略 const regionalMultipliers = { 'CN': 1, // 国内最快更新 'HK': 1.5, // 香港 'TW': 1.5, // 台湾 'JP': 2, // 日本 'KR': 2, // 韩国 'US': 2.5, // 美国 'EU': 3, // 欧洲 'AU': 2.5, // 澳洲 'OTHER': 4 // 其他地区 }; let baseTTL; if (hotScore > 90) { baseTTL = 30; // 超热商品:30秒 } else if (hotScore > 70) { baseTTL = 60; // 热销商品:1分钟 } else if (hotScore > 50) { baseTTL = 120; // 一般商品:2分钟 } else { baseTTL = 300; // 冷门商品:5分钟 } const multiplier = regionalMultipliers[country] || regionalMultipliers['OTHER']; return Math.round(baseTTL * multiplier); } } export const kaolaSSR = new KaolaSSRService();2.3 跨境关键CSS优化
// server/utils/criticalCSS.js // 网易考拉关键CSS生成 - 跨境优化版 class KaolaCriticalCSS { constructor() { this.criticalSelectors = [ // 全球化头部 '.kaola-global-header', '.global-language-selector', '.global-currency-selector', '.region-selector', // 跨境价格区 '.crossborder-price-section', '.original-price', '.sale-price', '.tax-estimate', '.shipping-info', '.free-shipping-badge', '.customs-clearance', // 跨境标识 '.crossborder-badges', '.authenticity-badge', '.traceability-badge', '.warehouse-badge', '.official-authorization', // 库存与发货 '.inventory-shipping', '.stock-status', '.stock-indicator', '.shipping-method', '.delivery-time', '.delivery-zone', '.preorder-notice', // 跨境工具 '.crossborder-tools', '.tool-grid', '.tool-item', '.tool-icon', '.tool-label', // 关税计算器 '.duty-calculator', '.tax-breakdown', '.exchange-rate-info', '.personal-allowance', // 正品溯源 '.authenticity-section', '.traceability-code', '.verification-badge', '.certification-list', // 骨架屏 '.skeleton', '.skeleton-animation' ]; } getCriticalCSS(globalConfig = {}) { const { language = 'zh-CN', currency = 'CNY' } = globalConfig; return ` /* ===== 网易考拉关键CSS - 跨境优化版 ===== */ /* CSS重置与基础 */ *,*::before,*::after{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent} html{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale} body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:14px;line-height:1.6;color:#333;background:#f8f9fa} /* 骨架屏动画 */ .skeleton{background:linear-gradient(90deg,#f0f0f0 25%,#e8e8e8 50%,#f0f0f0 75%);background-size:200% 100%;animation:skeleton-loading 1.5s ease-in-out infinite} @keyframes skeleton-loading{0%{background-position:200% 0}100%{background-position:-200% 0}} /* 全球化头部 */ .kaola-global-header{position:sticky;top:0;z-index:1000;padding:10px 16px;background:linear-gradient(135deg,#e74c3c,#c0392b);color:#fff;display:flex;align-items:center;justify-content:space-between} .global-language-selector{display:flex;align-items:center;gap:8px;cursor:pointer} .global-language-selector select{background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 8px;border-radius:4px;font-size:12px} .global-language-selector select option{color:#333} .global-currency-selector{display:flex;align-items:center;gap:4px;cursor:pointer} .global-currency-selector span{font-size:14px;font-weight:600} .region-selector{display:flex;align-items:center;gap:4px;font-size:12px;opacity:0.9} /* 跨境价格区 */ .crossborder-price-section{padding:16px;background:linear-gradient(180deg,#fff8f0 0%,#ffffff 100%)} .original-price{font-size:14px;color:#999;text-decoration:line-through;margin-right:8px} .sale-price{font-size:32px;font-weight:700;color:#e74c3c} .sale-price::before{content:'${this.getCurrencySymbol(currency)}';font-size:18px} .tax-estimate{display:inline-block;padding:4px 8px;background:#fff3cd;color:#856404;border-radius:4px;font-size:12px;margin-top:8px} .shipping-info{display:flex;align-items:center;gap:8px;margin-top:8px;font-size:13px;color:#666} .free-shipping-badge{display:inline-flex;align-items:center;padding:4px 8px;background:#d4edda;color:#155724;border-radius:4px;font-size:11px} .customs-clearance{display:flex;align-items:center;gap:4px;margin-top:8px;font-size:12px;color:#17a2b8} /* 跨境标识 */ .crossborder-badges{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px} .authenticity-badge{display:inline-flex;align-items:center;gap:4px;padding:6px 10px;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border-radius:6px;font-size:12px} .traceability-badge{display:inline-flex;align-items:center;gap:4px;padding:6px 10px;background:linear-gradient(135deg,#11998e,#38ef7d);color:#fff;border-radius:6px;font-size:12px} .warehouse-badge{display:inline-flex;align-items:center;gap:4px;padding:6px 10px;background:linear-gradient(135deg,#f093fb,#f5576c);color:#fff;border-radius:6px;font-size:12px} .official-authorization{display:inline-flex;align-items:center;gap:4px;padding:6px 10px;background:linear-gradient(135deg,#4facfe,#00f2fe);color:#fff;border-radius:6px;font-size:12px} /* 库存与发货 */ .inventory-shipping{padding:16px;background:#fff;margin-top:12px;border-radius:12px;box-shadow:0 2px 12px rgba(0,0,0,0.08)} .stock-status{display:flex;align-items:center;gap:8px;margin-bottom:12px} .stock-indicator{width:8px;height:8px;border-radius:50%;background:#28a745} .stock-indicator.low{background:#ffc107} .stock-indicator.out{background:#dc3545} .stock-text{font-size:14px;color:#333;font-weight:500} .stock-quantity{font-size:12px;color:#666;margin-left:auto} .shipping-method{display:flex;align-items:center;justify-content:space-between;padding:12px;background:#f8f9fa;border-radius:8px;margin-bottom:8px} .shipping-method:last-child{margin-bottom:0} .method-info{display:flex;align-items:center;gap:8px} .method-icon{width:24px;height:24px;background:#e9ecef;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px} .method-name{font-size:14px;color:#333;font-weight:500} .method-desc{font-size:12px;color:#666} .delivery-time{font-size:14px;color:#28a745;font-weight:600} .delivery-zone{font-size:12px;color:#666} .preorder-notice{display:flex;align-items:center;gap:8px;padding:12px;background:#fff3cd;border-radius:8px;margin-top:12px;font-size:13px;color:#856404} /* 跨境工具 */ .crossborder-tools{padding:16px;background:#fff;margin-top:12px;border-radius:12px} .tool-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px} .tool-item{display:flex;flex-direction:column;align-items:center;padding:12px 8px;background:#f8f9fa;border-radius:8px;cursor:pointer;transition:all 0.2s} .tool-item:hover{background:#e9ecef;transform:translateY(-2px)} .tool-item:active{transform:translateY(0)} .tool-icon{font-size:24px;margin-bottom:6px} .tool-label{font-size:11px;color:#666;text-align:center;line-height:1.3} .tool-badge{position:absolute;top:-4px;right:-4px;width:16px;height:16px;background:#e74c3c;color:#fff;border-radius:50%;font-size:10px;display:flex;align-items:center;justify-content:center} /* 关税计算器 */ .duty-calculator{padding:16px;background:linear-gradient(135deg,#f5f7fa 0%,#c3cfe2 100%);margin-top:12px;border-radius:12px} .calculator-title{font-size:16px;font-weight:600;color:#333;margin-bottom:12px;display:flex;align-items:center;gap:8px} .calculator-form{display:grid;grid-template-columns:1fr 1fr;gap:12px} .form-group{display:flex;flex-direction:column;gap:4px} .form-label{font-size:12px;color:#666} .form-input{padding:8px 12px;border:1px solid #ddd;border-radius:6px;font-size:14px;outline:none;transition:border-color 0.2s} .form-input:focus{border-color:#e74c3c} .calculator-result{margin-top:12px;padding:12px;background:#fff;border-radius:8px;display:flex;align-items:center;justify-between} .result-label{font-size:14px;color:#666} .result-value{font-size:20px;font-weight:700;color:#e74c3c} .result-value::before{content:'${this.getCurrencySymbol(currency)}';font-size:14px} /* 正品溯源 */ .authenticity-section{padding:16px;background:#fff;margin-top:12px;border-radius:12px} .section-title{font-size:16px;font-weight:600;color:#333;margin-bottom:12px;display:flex;align-items:center;gap:8px} .traceability-code{display:flex;align-items:center;gap:12px;padding:12px;background:#f8f9fa;border-radius:8px} .code-display{font-family:monospace;font-size:16px;color:#333;letter-spacing:2px} .verify-btn{padding:8px 16px;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border:none;border-radius:6px;font-size:14px;cursor:pointer} .certification-list{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px} .cert-item{display:flex;align-items:center;gap:4px;padding:6px 10px;background:#e8f5e9;border-radius:4px;font-size:11px;color:#2e7d32} /* 响应式适配 */ @media (max-width: 375px){ .sale-price{font-size:26px} .tool-grid{grid-template-columns:repeat(2,1fr)} .calculator-form{grid-template-columns:1fr} .crossborder-badges{flex-direction:column} } `; } getCurrencySymbol(currency) { const symbols = { 'CNY': '¥', 'USD': '$', 'EUR': '€', 'GBP': '£', 'JPY': '¥', 'KRW': '₩', 'AUD': 'A$', 'CAD': 'C$', 'SGD': 'S$', 'HKD': 'HK$' }; return symbols[currency] || currency; } } export const kaolaCriticalCSS = new KaolaCriticalCSS();三、跨境税费计算引擎
3.1 高性能跨境税费计算引擎
// engines/crossborderTaxEngine.js // 网易考拉跨境税费计算引擎 - 高性能版本 class CrossborderTaxEngine { constructor(config) { this.config = config; this.cache = new Map(); this.calculationQueue = []; this.batchSize = 50; this.flushInterval = 100; // 100ms批量处理 this.rateUpdateInterval = 3600000; // 1小时更新汇率 } // 初始化计算引擎 initialize() { // 定时刷新汇率 setInterval(() => { this.refreshExchangeRates(); }, this.rateUpdateInterval); // 定时刷新关税税率 setInterval(() => { this.refreshDutyRates(); }, this.rateUpdateInterval * 2); // 批量处理队列 setInterval(() => { this.processBatch(); }, this.flushInterval); // 清理过期缓存 setInterval(() => { this.cleanExpiredCache(); }, 300000); // 每5分钟清理 } // 计算跨境税费(高性能版本) async calculateTax(params) { const { productId, basePrice, baseCurrency, targetCurrency, quantity = 1, country, productCategory, customerType = 'individual', sessionId = this.generateSessionId() } = params; // 构建缓存键 const cacheKey = this.buildTaxCacheKey(params); // 检查缓存 const cached = this.cache.get(cacheKey); if (cached && !this.isCacheExpired(cached)) { return cached.result; } // 添加到计算队列 return new Promise((resolve, reject) => { this.calculationQueue.push({ params, cacheKey, resolve, reject, timestamp: Date.now(), sessionId }); // 队列过长时触发紧急处理 if (this.calculationQueue.length >= this.batchSize * 2) { this.processBatch(true); } }); } // 批量处理计算任务 async processBatch(urgent = false) { if (this.calculationQueue.length === 0) return; const batch = urgent ? this.calculationQueue.splice(0, this.batchSize) : this.calculationQueue.splice(0, Math.min(this.batchSize, this.calculationQueue.length)); const results = await Promise.allSettled( batch.map(async ({ params, cacheKey, sessionId }) => { try { const result = await this.doCalculateTax(params, sessionId); return { cacheKey, result, success: true }; } catch (error) { return { cacheKey, error, success: false }; } }) ); // 处理结果并更新缓存 results.forEach(({ cacheKey, result, error, success }) => { if (success) { this.cache.set(cacheKey, { result, timestamp: Date.now(), ttl: this.getCacheTTL(params.productId, params.country), sessionId }); // 通知等待的请求 const queueItem = batch.find(item => item.cacheKey === cacheKey); if (queueItem) { queueItem.resolve(result); } } else { const queueItem = batch.find(item => item.cacheKey === cacheKey); if (queueItem) { queueItem.reject(error); } } }); } // 实际税费计算逻辑 async doCalculateTax(params, sessionId) { const { productId, basePrice, baseCurrency, targetCurrency, quantity, country, productCategory, customerType } = params; // 并行获取所需数据 const [ exchangeRate, dutyRate, vatRate, taxFreeAllowance, complianceRules, productClassification ] = await Promise.all([ this.getExchangeRate(baseCurrency, targetCurrency), this.getDutyRate(productCategory, country), this.getVATRate(country), this.getTaxFreeAllowance(country, customerType), this.getComplianceRules(productId, country), this.getClassifyProduct(productId, productCategory) ]); // 汇率转换 const convertedPrice = basePrice * exchangeRate; const subtotal = convertedPrice * quantity; // 税费计算 const dutyCalculation = this.calculateDuty({ subtotal, dutyRate, taxFreeAllowance, productClassification, complianceRules }); const vatCalculation = this.calculateVAT({ subtotal, dutyAmount: dutyCalculation.amount, vatRate, complianceRules }); // 其他费用 const additionalFees = await this.calculateAdditionalFees({ subtotal, country, productClassification, complianceRules }); // 总费用 const totalTaxAmount = dutyCalculation.amount + vatCalculation.amount + additionalFees.total; const finalTotalPrice = subtotal + totalTaxAmount; // 生成税费明细 const taxBreakdown = this.generateTaxBreakdown({ dutyCalculation, vatCalculation, additionalFees, exchangeRate, baseCurrency, targetCurrency }); return { calculationId: sessionId, input: { basePrice, baseCurrency, targetCurrency, quantity, country, productCategory, exchangeRate }, pricing: { subtotal: parseFloat(subtotal.toFixed(2)), dutyAmount: parseFloat(dutyCalculation.amount.toFixed(2)), vatAmount: parseFloat(vatCalculation.amount.toFixed(2)), additionalFees: parseFloat(additionalFees.total.toFixed(2)), totalTaxAmount: parseFloat(totalTaxAmount.toFixed(2)), finalTotalPrice: parseFloat(finalTotalPrice.toFixed(2)) }, taxBreakdown, compliance: { isCompliant: complianceRules.isAllowed, restrictions: complianceRules.restrictions, warnings: complianceRules.warnings, requiredDocuments: complianceRules.requiredDocuments }, metadata: { calculatedAt: Date.now(), currency: targetCurrency, exchangeRateSource: 'live_api', dutyRateSource: 'official_customs', vatRateSource: 'official_tax_authority' } }; } // 税费计算 calculateDuty({ subtotal, dutyRate, taxFreeAllowance, productClassification, complianceRules }) { // 检查是否符合免税条件 const isDutyFree = this.checkDutyFreeEligibility({ subtotal, taxFreeAllowance, productClassification, complianceRules }); if (isDutyFree.eligible) { return { amount: 0, rate: 0, taxableAmount: 0, isDutyFree: true, reason: isDutyFree.reason, allowance: taxFreeAllowance, savings: subtotal }; } // 计算应税金额 let taxableAmount = Math.max(0, subtotal - taxFreeAllowance); // 应用产品特定减免 if (productClassification.dutyReductions) { taxableAmount = this.applyDutyReductions(taxableAmount, productClassification.dutyReductions); } // 计算关税 let dutyAmount = taxableAmount * dutyRate.rate; // 应用最低关税 if (dutyRate.minimumAmount && dutyAmount < dutyRate.minimumAmount) { dutyAmount = dutyRate.minimumAmount; } // 应用最高关税上限 if (dutyRate.maximumAmount && dutyAmount > dutyRate.maximumAmount) { dutyAmount = dutyRate.maximumAmount; } return { amount: parseFloat(dutyAmount.toFixed(2)), rate: dutyRate.rate, taxableAmount: parseFloat(taxableAmount.toFixed(2)), isDutyFree: false, allowance: taxFreeAllowance, category: dutyRate.category, reason: null, savings: 0 }; } // VAT计算 calculateVAT({ subtotal, dutyAmount, vatRate, complianceRules }) { // 检查VAT豁免 if (complianceRules.vatExempt) { return { amount: 0, rate: 0, taxableAmount: 0, isVATExempt: true, reason: complianceRules.vatExemptReason }; } // 计算应税基数(含税基包含关税) const taxableAmount = subtotal + dutyAmount; const vatAmount = taxableAmount * vatRate.rate; return { amount: parseFloat(vatAmount.toFixed(2)), rate: vatRate.rate, taxableAmount: parseFloat(taxableAmount.toFixed(2)), isVATExempt: false, reason: null }; } // 计算附加费用 async calculateAdditionalFees({ subtotal, country, productClassification, complianceRules }) { const fees = { handlingFee: 0, processingFee: 0, inspectionFee: 0, storageFee: 0, documentationFee: 0 }; // 处理费 if (complianceRules.requiresProcessing) { fees.processingFee = this.calculateProcessingFee(subtotal, country); } // 检验费(特定品类) if (productClassification.requiresInspection) { fees.inspectionFee = this.calculateInspectionFee(productClassification.category, country); } // 仓储费(特殊商品) if (productClassification.storageRequired) { fees.storageFee = this.calculateStorageFee(subtotal, country); } // 单证费 if (complianceRules.requiresDocumentation) { fees.documentationFee = this.calculateDocumentationFee(complianceRules.requiredDocuments); } const total = Object.values(fees).reduce((sum, fee) => sum + fee, 0); return { breakdown: fees, total: parseFloat(total.toFixed(2)) }; } // 生成税费明细 generateTaxBreakdown({ dutyCalculation, vatCalculation, additionalFees, exchangeRate, baseCurrency, targetCurrency }) { return { summary: { totalTaxAmount: dutyCalculation.amount + vatCalculation.amount + additionalFees.total, dutyPercentage: dutyCalculation.isDutyFree ? 0 : (dutyCalculation.amount / (dutyCalculation.amount + vatCalculation.amount + additionalFees.total) * 100), vatPercentage: vatCalculation.isVATExempt ? 0 : (vatCalculation.amount / (dutyCalculation.amount + vatCalculation.amount + additionalFees.total) * 100), feesPercentage: additionalFees.total > 0 ? (additionalFees.total / (dutyCalculation.amount + vatCalculation.amount + additionalFees.total) * 100) : 0 }, details: { duty: { description: dutyCalculation.isDutyFree ? '免税商品' : '进口关税', rate: dutyCalculation.rate, taxableAmount: dutyCalculation.taxableAmount, amount: dutyCalculation.amount, currency: targetCurrency, reason: dutyCalculation.reason }, vat: { description: vatCalculation.isVATExempt ? 'VAT豁免' : '增值税', rate: vatCalculation.rate, taxableAmount: vatCalculation.taxableAmount, amount: vatCalculation.amount, currency: targetCurrency, reason: vatCalculation.reason }, additionalFees: { description: '附加费用', breakdown: additionalFees.breakdown, total: additionalFees.total, currency: targetCurrency } }, exchangeRate: { rate: exchangeRate, from: baseCurrency, to: targetCurrency, source: 'live_market_data', updatedAt: Date.now() } }; } // 检查免税资格 checkDutyFreeEligibility({ subtotal, taxFreeAllowance, productClassification, complianceRules }) { // 个人物品免税额度检查 if (subtotal <= taxFreeAllowance.personal) { return { eligible: true, reason: `个人物品免税额度内(${taxFreeAllowance.personal} ${taxFreeAllowance.currency})` }; } // 特定品类免税检查 if (productClassification.dutyFreeCategories) { const isInDutyFreeCategory = productClassification.dutyFreeCategories.some( cat => cat === productClassification.category ); if (isInDutyFreeCategory) { return { eligible: true, reason: '该品类享受免税政策' }; } } // 贸易协定免税检查 if (complianceRules.tradeAgreements) { const hasApplicableAgreement = complianceRules.tradeAgreements.some( agreement => agreement.appliesTo === productClassification.category ); if (hasApplicableAgreement) { return { eligible: true, reason: `适用${complianceRules.tradeAgreements[0].name}贸易协定免税` }; } } return { eligible: false, reason: null }; } // 获取汇率 async getExchangeRate(fromCurrency, toCurrency) { const cacheKey = `exchange_rate_${fromCurrency}_${toCurrency}`; const cached = this.cache.get(cacheKey); if (cached && !this.isCacheExpired(cached, 300)) { // 汇率缓存5分钟 return cached.result; } try { // 调用汇率API const response = await fetch(`https://api.exchangerate-api.com/v4/latest/${fromCurrency}`); const data = await response.json(); const rate = data.rates[toCurrency]; // 缓存汇率 this.cache.set(cacheKey, { result: rate, timestamp: Date.now(), ttl: 300 }); return rate; } catch (error) { console.error('Failed to fetch exchange rate:', error); // 返回缓存的旧汇率或默认值 return cached?.result || 1; } } // 获取关税税率 async getDutyRate(category, country) { const cacheKey = `duty_rate_${category}_${country}`; const cached = this.cache.get(cacheKey); if (cached && !this.isCacheExpired(cached, 3600)) { // 关税税率缓存1小时 return cached.result; } try { // 调用关税税率API const response = await fetch(`https://api.customs.gov/${country}/duty-rates/${category}`); const data = await response.json(); const dutyRate = { rate: data.rate, category: data.category, minimumAmount: data.minimumAmount, maximumAmount: data.maximumAmount, effectiveDate: data.effectiveDate, source: 'official_customs' }; // 缓存关税税率 this.cache.set(cacheKey, { result: dutyRate, timestamp: Date.now(), ttl: 3600 }); return dutyRate; } catch (error) { console.error('Failed to fetch duty rate:', error); // 返回默认税率 return { rate: 0.1, // 默认10% category: category, minimumAmount: 0, maximumAmount: null, effectiveDate: new Date().toISOString(), source: 'default' }; } } // 获取VAT税率 async getVATRate(country) { const cacheKey = `vat_rate_${country}`; const cached = this.cache.get(cacheKey); if (cached && !this.isCacheExpired(cached, 7200)) { // VAT税率缓存2小时 return cached.result; } try { // 调用VAT税率API const response = await fetch(`https://api.taxauthority.gov/${country}/vat-rate`); const data = await response.json(); const vatRate = { rate: data.rate, reducedRate: data.reducedRate, superReducedRate: data.superReducedRate, effectiveDate: data.effectiveDate, source: 'official_tax_authority' }; // 缓存VAT税率 this.cache.set(cacheKey, { result: vatRate, timestamp: Date.now(), ttl: 7200 }); return vatRate; } catch (error) { console.error('Failed to fetch VAT rate:', error); // 返回默认VAT税率 return { rate: 0.2, // 默认20% reducedRate: 0.1, superReducedRate: 0.05, effectiveDate: new Date().toISOString(), source: 'default' }; } } // 获取免税额度 async getTaxFreeAllowance(country, customerType) { const cacheKey = `tax_free_allowance_${country}_${customerType}`; const cached = this.cache.get(cacheKey); if (cached && !this.isCacheExpired(cached, 86400)) { // 免税额度缓存24小时 return cached.result; } try { // 调用免税额度API const response = await fetch(`https://api.customs.gov/${country}/tax-free-allowance/${customerType}`); const data = await response.json(); const allowance = { personal: data.personalAllowance, currency: data.currency, familyAllowance: data.familyAllowance, frequentTravelerAllowance: data.frequentTravelerAllowance, effectiveDate: data.effectiveDate, source: 'official_customs' }; // 缓存免税额度 this.cache.set(cacheKey, { result: allowance, timestamp: Date.now(), ttl: 86400 }); return allowance; } catch (error) { console.error('Failed to fetch tax free allowance:', error); // 返回默认免税额度 return { personal: 5000, // 默认5000 currency: 'CNY', familyAllowance: 10000, frequentTravelerAllowance: 15000, effectiveDate: new Date().toISOString(), source: 'default' }; } } // 获取合规规则 async getComplianceRules(productId, country) { const cacheKey = `compliance_rules_${productId}_${country}`; const cached = this.cache.get(cacheKey); if (cached && !this.isCacheExpired(cached, 3600)) { return cached.result; } try { // 调用合规规则API const response = await fetch(`https://api.compliance.kaola.com/rules/${productId}/${country}`); const data = await response.json(); const rules = { isAllowed: data.isAllowed, restrictions: data.restrictions || [], warnings: data.warnings || [], requiredDocuments: data.requiredDocuments || [], vatExempt: data.vatExempt || false, vatExemptReason: data.vatExemptReason, requiresProcessing: data.requiresProcessing || false, requiresInspection: data.requiresInspection || false, requiresDocumentation: data.requiresDocumentation || false, tradeAgreements: data.tradeAgreements || [], prohibitions: data.prohibitions || [] }; // 缓存合规规则 this.cache.set(cacheKey, { result: rules, timestamp: Date.now(), ttl: 3600 }); return rules; } catch (error) { console.error('Failed to fetch compliance rules:', error); // 返回默认合规规则 return { isAllowed: true, restrictions: [], warnings: [], requiredDocuments: [], vatExempt: false, vatExemptReason: null, requiresProcessing: false, requiresInspection: false, requiresDocumentation: false, tradeAgreements: [], prohibitions: [] }; } } // 产品分类 async getClassifyProduct(productId, providedCategory) { const cacheKey = `product_classification_${productId}`; const cached = this.cache.get(cacheKey); if (cached && !this.isCacheExpired(cached, 86400)) { return cached.result; } try { // 调用产品分类API const response = await fetch(`https://api.classification.kaola.com/classify/${productId}`); const data = await response.json(); const classification = { category: data.category || providedCategory, hsCode: data.hsCode, dutyReductions: data.dutyReductions || [], requiresInspection: data.requiresInspection || false, storageRequired: data.storageRequired || false, restrictedCountries: data.restrictedCountries || [], ageRestriction: data.ageRestriction, prescriptionRequired: data.prescriptionRequired || false }; // 缓存产品分类 this.cache.set(cacheKey, { result: classification, timestamp: Date.now(), ttl: 86400 }); return classification; } catch (error) { console.error('Failed to classify product:', error); // 返回默认分类 return { category: providedCategory || 'general', hsCode: null, dutyReductions: [], requiresInspection: false, storageRequired: false, restrictedCountries: [], ageRestriction: null, prescriptionRequired: false }; } } // 辅助计算方法 calculateProcessingFee(subtotal, country) { const feeStructure = { 'CN': subtotal * 0.02, // 中国:2% 'US': Math.max(5, subtotal * 0.015), // 美国:最低5美元 'EU': Math.max(3, subtotal * 0.01), // 欧盟:最低3欧元 'JP': Math.max(500, subtotal * 0.01), // 日本:最低500日元 'KR': Math.max(3000, subtotal * 0.015), // 韩国:最低3000韩元 'DEFAULT': subtotal * 0.02 }; return feeStructure[country] || feeStructure['DEFAULT']; } calculateInspectionFee(category, country) { const inspectionRates = { 'cosmetics': { 'CN': 50, 'US': 35, 'EU': 40, 'JP': 5000, 'KR': 35000 }, 'food': { 'CN': 80, 'US': 55, 'EU': 60, 'JP': 8000, 'KR': 55000 }, 'electronics': { 'CN': 30, 'US': 25, 'EU': 30, 'JP': 3000, 'KR': 25000 }, 'clothing': { 'CN': 20, 'US': 15, 'EU': 20, 'JP': 2000, 'KR': 15000 }, 'DEFAULT': { 'CN': 40, 'US': 30, 'EU': 35, 'JP': 4000, 'KR': 30000 } }; const categoryRates = inspectionRates[category] || inspectionRates['DEFAULT']; return categoryRates[country] || categoryRates['CN']; } calculateStorageFee(subtotal, country) { const storageRates = { 'CN': subtotal * 0.005, // 中国:0.5%/天 'US': subtotal * 0.008, // 美国:0.8%/天 'EU': subtotal * 0.006, // 欧盟:0.6%/天 'JP': subtotal * 0.004, // 日本:0.4%/天 'KR': subtotal * 0.007, // 韩国:0.7%/天 'DEFAULT': subtotal * 0.006 }; return storageRates[country] || storageRates['DEFAULT']; } calculateDocumentationFee(documents) { const baseFee = 10; // 基础单证费 const perDocFee = 5; // 每份额外单证费 return baseFee + (documents.length * perDocFee); } applyDutyReductions(taxableAmount, reductions) { let reducedAmount = taxableAmount; reductions.forEach(reduction => { if (reduction.type === 'percentage') { reducedAmount *= (1 - reduction.value); } else if (reduction.type === 'fixed') { reducedAmount -= reduction.value; } }); return Math.max(0, reducedAmount); } // 缓存管理 buildTaxCacheKey(params) { const { productId, basePrice, baseCurrency, targetCurrency, quantity, country, productCategory } = params; const roundedPrice = Math.floor(basePrice * 100) / 100; return `tax_${productId}_${roundedPrice}_${baseCurrency}_${targetCurrency}_${quantity}_${country}_${productCategory}`; } getCacheTTL(productId, country) { // 热门商品缓存时间短,冷门商品缓存时间长 // 不同国家根据政策更新频率调整 const countryMultipliers = { 'CN': 1, // 中国政策相对稳定 'US': 1.5, // 美国 'EU': 2, // 欧盟政策变化较频繁 'JP': 1.5, // 日本 'KR': 1.5, // 韩国 'OTHER': 3 // 其他地区 }; const hotProducts = this.config.hotProducts || new Set(); const baseTTL = hotProducts.has(productId) ? 60 : 300; // 1分钟或5分钟 return Math.round(baseTTL * (countryMultipliers[country] || countryMultipliers['OTHER'])); } isCacheExpired(cached, customTTL = null) { const ttl = customTTL || cached.ttl; return Date.now() - cached.timestamp > ttl * 1000; } cleanExpiredCache() { const now = Date.now(); for (const [key, value] of this.cache.entries()) { if (now - value.timestamp > value.ttl * 1000) { this.cache.delete(key); } } } generateSessionId() { return `tax_session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } // 批量税费预计算(用于热门商品) async precomputeTaxes(productList, globalConfig) { const precomputed = new Map(); await Promise.all( productList.map(async (product) => { try { const tax = await this.calculateTax({ productId: product.id, basePrice: product.basePrice, baseCurrency: product.currency, targetCurrency: globalConfig.currency, quantity: 1, country: globalConfig.country, productCategory: product.category }); precomputed.set(product.id, tax); } catch (error) { console.error(`Failed to precompute tax for product ${product.id}:`, error); } }) ); return precomputed; } // 刷新汇率 async refreshExchangeRates() { // 清除所有汇率缓存 for (const [key, value] of this.cache.entries()) { if (key.startsWith('exchange_rate_')) { this.cache.delete(key); } } console.log('Exchange rates cache cleared for refresh'); } // 刷新关税税率 async refreshDutyRates() { // 清除所有关税税率缓存 for (const [key, value] of this.cache.entries()) { if (key.startsWith('duty_rate_')) { this.cache.delete(key); } } console.log('Duty rates cache cleared for refresh'); } } // 创建单例 let taxEngineInstance = null; export function getCrossborderTaxEngine(config) { if (!taxEngineInstance) { taxEngineInstance = new CrossborderTaxEngine(config); taxEngineInstance.initialize(); } return taxEngineInstance; }3.2 实时汇率与政策更新
// services/policyUpdateService.js // 网易考拉政策更新服务 - 实时同步 class PolicyUpdateService { constructor() { this.subscriptions = new Map(); this.policyCache = new Map(); this.updateChannels = new Map(); this.websocketConnections = new Map(); } // 初始化服务 initialize() { // 建立政策更新WebSocket连接 this.initPolicyWebSocket(); // 定时检查政策更新 this.startPolicyCheck(); // 初始化各国家/地区的政策订阅 this.initPolicySubscriptions(); } // 初始化政策WebSocket initPolicyWebSocket() { // 连接政策更新服务 const ws = new WebSocket('wss://policy-updates.kaola.com/stream'); ws.onopen = () => { console.log('Policy update WebSocket connected'); this.subscribeToAllPolicies(ws); }; ws.onmessage = (event) => { this.handlePolicyUpdate(JSON.parse(event.data)); }; ws.onerror = (error) => { console.error('Policy WebSocket error:', error); // 降级到轮询 this.startPolicyPolling(); }; ws.onclose = () => { console.log('Policy WebSocket closed, reconnecting...'); setTimeout(() => this.initPolicyWebSocket(), 5000); }; this.websocketConnections.set('policy', ws); } // 订阅所有政策更新 subscribeToAllPolicies(ws) { const countries = ['CN', 'US', 'EU', 'JP', 'KR', 'AU', 'SG', 'MY', 'TH', 'VN']; const policyTypes = ['duty_rates', 'vat_rates', 'tax_free_allowances', 'compliance_rules', 'trade_agreements']; const subscription = { type: 'subscribe', countries, policyTypes, clientId: this.generateClientId() }; ws.send(JSON.stringify(subscription)); } // 处理政策更新 handlePolicyUpdate(update) { const { type, country, policyType, data, effectiveDate, source } = update; // 更新缓存 const cacheKey = `policy_${country}_${policyType}`; this.policyCache.set(cacheKey, { data, effectiveDate, source, updatedAt: Date.now() }); // 通知订阅者 this.notifyPolicySubscribers(country, policyType, data); // 触发相关服务更新 this.triggerServiceUpdates(country, policyType, data); // 记录政策变更日志 this.logPolicyChange(country, policyType, data, effectiveDate); } // 通知政策订阅者 notifyPolicySubscribers(country, policyType, data) { const subscribers = this.subscriptions.get(`${country}_${policyType}`) || []; const globalSubscribers = this.subscriptions.get(`global_${policyType}`) || []; [...subscribers, ...globalSubscribers].forEach(subscriber => { try { subscriber.callback({ country, policyType, data, timestamp: Date.now() }); } catch (error) { console.error('Error notifying policy subscriber:', error); } }); } // 触发服务更新 triggerServiceUpdates(country, policyType, data) { // 通知税费计算引擎 if (policyType === 'duty_rates' || policyType === 'vat_rates' || policyType === 'tax_free_allowances') { this.notifyTaxEngine(country, policyType, data); } // 通知合规检查服务 if (policyType === 'compliance_rules' || policyType === 'trade_agreements') { this.notifyComplianceService(country, policyType, data); } // 通知产品分类服务 if (policyType === 'restricted_products' || policyType === 'prohibited_items') { this.notifyClassificationService(country, policyType, data); } } // 通知税费计算引擎 notifyTaxEngine(country, policyType, data) { // 发送内部消息或调用API if (window.taxEngine) { window.taxEngine.clearCacheForCountry(country); } } // 通知合规检查服务 notifyComplianceService(country, policyType, data) { // 发送内部消息或调用API if (window.complianceService) { window.complianceService.clearCacheForCountry(country); } } // 通知产品分类服务 notifyClassificationService(country, policyType, data) { // 发送内部消息或调用API if (window.classificationService) { window.classificationService.clearCacheForCountry(country); } } // 记录政策变更日志 logPolicyChange(country, policyType, data, effectiveDate) { const logEntry = { country, policyType, data, effectiveDate, changedAt: new Date().toISOString(), source: 'policy_update_service' }; // 发送到日志系统 if (navigator.sendBeacon) { navigator.sendBeacon('/api/policy-changes', JSON.stringify(logEntry)); } else { fetch('/api/policy-changes', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(logEntry) }).catch(() => {}); } } // 订阅政策更新 subscribe(country, policyType, callback) { const subscriptionKey = `${country}_${policyType}`; if (!this.subscriptions.has(subscriptionKey)) { this.subscriptions.set(subscriptionKey, []); } const subscriptionId = `${subscriptionKey}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; this.subscriptions.get(subscriptionKey).push({ id: subscriptionId, callback, subscribedAt: Date.now() }); // 立即返回当前政策数据 const currentData = this.policyCache.get(`policy_${country}_${policyType}`); if (currentData) { callback({ country, policyType, data: currentData.data, timestamp: currentData.updatedAt }); } return subscriptionId; } // 取消订阅 unsubscribe(subscriptionId) { for (const [key, subscribers] of this.subscriptions.entries()) { const index = subscribers.findIndex(s => s.id === subscriptionId); if (index !== -1) { subscribers.splice(index, 1); if (subscribers.length === 0) { this.subscriptions.delete(key); } return true; } } return false; } // 获取当前政策 getCurrentPolicy(country, policyType) { return this.policyCache.get(`policy_${country}_${policyType}`); } // 启动政策检查(轮询备用方案) startPolicyCheck() { setInterval(() => { this.checkForPolicyUpdates(); }, 300000); // 每5分钟检查一次 } // 检查政策更新 async checkForPolicyUpdates() { try { const response = await fetch('/api/policy-updates/check'); const updates = await response.json(); updates.forEach(update => { this.handlePolicyUpdate(update); }); } catch (error) { console.error('Failed to check for policy updates:', error); } } // 启动政策轮询(WebSocket失败时的备用方案) startPolicyPolling() { if (this.pollingTimer) { clearInterval(this.pollingTimer); } this.pollingTimer = setInterval(() => { this.fetchPolicyUpdates(); }, 60000); // 每1分钟轮询 } // 获取政策更新 async fetchPolicyUpdates() { try { const response = await fetch('/api/policy-updates/since/' + this.lastUpdateTimestamp); const updates = await response.json(); if (updates.length > 0) { this.lastUpdateTimestamp = Math.max(...updates.map(u => u.updatedAt)); updates.forEach(update => { this.handlePolicyUpdate(update); }); } } catch (error) { console.error('Failed to fetch policy updates:', error); } } // 初始化政策订阅 initPolicySubscriptions() { // 为各国家/地区初始化政策订阅 const countries = ['CN', 'US', 'EU', 'JP', 'KR', 'AU', 'SG']; const policyTypes = ['duty_rates', 'vat_rates', 'tax_free_allowances']; countries.forEach(country => { policyTypes.forEach(policyType => { this.subscribe(country, policyType, (update) => { console.log(`Policy updated for ${country} - ${policyType}:`, update.data); }); }); }); } // 生成客户端ID generateClientId() { return `client_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } // 清除指定国家的缓存 clearCacheForCountry(country) { const keysToDelete = []; for (const [key, value] of this.policyCache.entries()) { if (key.includes(`_${country}_`)) { keysToDelete.push(key); } } keysToDelete.forEach(key => this.policyCache.delete(key)); } } export const policyUpdateService = new PolicyUpdateService();四、多语言多币种优化
4.1 智能国际化引擎
// i18n/intelligentI18n.js // 网易考拉智能国际化引擎 class IntelligentI18n { constructor() { this.currentLocale = 'zh-CN'; this.currentCurrency = 'CNY'; this.translations = new Map(); this.currencyFormats = new Map(); this.rtlLanguages = new Set(['ar', 'he', 'fa', 'ur']); this.loadedNamespaces = new Set(); this.fallbackChain = ['zh-CN', 'en-US', 'ja-JP']; this.init(); } // 初始化 async init() { // 检测用户语言和货币偏好 await this.detectUserPreferences(); // 加载初始翻译 await this.loadTranslations(this.currentLocale, ['common', 'product', 'checkout']); // 初始化货币格式化 this.initCurrencyFormats(); // 监听语言切换事件 this.setupLanguageListeners(); } // 检测用户偏好 async detectUserPreferences() { // 从URL参数获取 const urlParams = new URLSearchParams(window.location.search); const langParam = urlParams.get('lang'); const currencyParam = urlParams.get('currency'); // 从Cookie获取 const cookieLang = this.getCookie('kaola_lang'); const cookieCurrency = this.getCookie('kaola_currency'); // 从浏览器设置获取 const browserLang = navigator.language || navigator.userLanguage; const browserCurrency = this.detectBrowserCurrency(); // 从地理位置获取 const geoCurrency = await this.detectGeoCurrency(); // 优先级:URL参数 > Cookie > 地理位置 > 浏览器设置 > 默认值 this.currentLocale = langParam || cookieLang || this.normalizeLocale(browserLang) || 'zh-CN'; this.currentCurrency = currencyParam || cookieCurrency || geoCurrency || 'CNY'; // 保存到Cookie this.setCookie('kaola_lang', this.currentLocale, 365); this.setCookie('kaola_currency', this.currentCurrency, 365); } // 标准化语言代码 normalizeLocale(locale) { const localeMap = { 'zh': 'zh-CN', 'zh-TW': 'zh-TW', 'zh-HK': 'zh-TW', 'en': 'en-US', 'ja': 'ja-JP', 'ko': 'ko-KR', 'de': 'de-DE', 'fr': 'fr-FR', 'es': 'es-ES', 'it': 'it-IT', 'pt': 'pt-BR', 'ru': 'ru-RU', 'ar': 'ar-SA', 'th': 'th-TH', 'vi': 'vi-VN', 'id': 'id-ID' }; const normalized = locale.toLowerCase().replace('_', '-'); // 精确匹配 if (localeMap[normalized]) { return localeMap[normalized]; } // 前缀匹配 for (const [key, value] of Object.entries(localeMap)) { if (normalized.startsWith(key)) { return value; } } return null; } // 检测浏览器货币偏好 detectBrowserCurrency() { // 基于语言推断货币 const languageCurrencyMap = { 'zh': 'CNY', 'en-US': 'USD', 'en-GB': 'GBP', 'ja': 'JPY', 'ko': 'KRW', 'de': 'EUR', 'fr': 'EUR', 'es': 'EUR', 'it': 'EUR', 'pt': 'BRL', 'ru': 'RUB', 'ar': 'SAR', 'th': 'THB', 'vi': 'VND', 'id': 'IDR' }; const browserLang = (navigator.language || 'en-US').toLowerCase(); for (const [lang, currency] of Object.entries(languageCurrencyMap)) { if (browserLang.startsWith(lang.toLowerCase())) { return currency; } } return 'USD'; // 默认美元 } // 检测地理位置货币 async detectGeoCurrency() { try { // 使用免费IP地理定位API const response = await fetch('https://ipapi.co/json/'); const data = await response.json(); const countryCurrencyMap = { 'CN': 'CNY', 'US': 'USD', 'JP': 'JPY', 'KR': 'KRW', 'DE': 'EUR', 'FR': 'EUR', 'GB': 'GBP', 'AU': 'AUD', 'CA': 'CAD', 'SG': 'SGD', 'HK': 'HKD', 'TW': 'TWD', 'TH': 'THB', 'VN': 'VND', 'ID': 'IDR', 'MY': 'MYR', 'PH': 'PHP', 'IN': 'INR', 'BR': 'BRL', 'MX': 'MXN', 'RU': 'RUB', 'AE': 'AED', 'SA': 'SAR' }; return countryCurrencyMap[data.country_code] || 'USD'; } catch (error) { console.error('Failed to detect geo currency:', error); return null; } } // 加载翻译文件 async loadTranslations(locale, namespaces = ['common']) { const loadPromises = namespaces.map(namespace => { if (this.loadedNamespaces.has(`${locale}_${namespace}`)) { return Promise.resolve(); } return this.fetchTranslation(locale, namespace).then(translations => { if (!this.translations.has(locale)) { this.translations.set(locale, {}); } this.translations.get(locale)[namespace] = translations; this.loadedNamespaces.add(`${locale}_${namespace}`); }); }); await Promise.all(loadPromises); } // 获取翻译文件 async fetchTranslation(locale, namespace) { try { // 尝试从CDN加载 const response = await fetch(`https://global-cdn.kaola.com/i18n/${locale}/${namespace}.json`); if (response.ok) { return await response.json(); } throw new Error(`Translation file not found: ${locale}/${namespace}`); } catch (error) { console.warn(`Failed to load translation for ${locale}/${namespace}:`, error); // 尝试加载回退语言的翻译 if (this.fallbackChain.includes(locale)) { const fallbackIndex = this.fallbackChain.indexOf(locale); if (fallbackIndex < this.fallbackChain.length - 1) { const fallbackLocale = this.fallbackChain[fallbackIndex + 1]; console.log(`Trying fallback locale: ${fallbackLocale}`); return this.fetchTranslation(fallbackLocale, namespace); } } return {}; // 返回空对象 } } // 初始化货币格式化 initCurrencyFormats() { const currencyFormatConfigs = { 'CNY': { style: 'currency', currency: 'CNY', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'USD': { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'EUR': { style: 'currency', currency: 'EUR', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'GBP': { style: 'currency', currency: 'GBP', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'JPY': { style: 'currency', currency: 'JPY', minimumFractionDigits: 0, maximumFractionDigits: 0 }, 'KRW': { style: 'currency', currency: 'KRW', minimumFractionDigits: 0, maximumFractionDigits: 0 }, 'AUD': { style: 'currency', currency: 'AUD', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'CAD': { style: 'currency', currency: 'CAD', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'CHF': { style: 'currency', currency: 'CHF', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'HKD': { style: 'currency', currency: 'HKD', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'SGD': { style: 'currency', currency: 'SGD', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'SEK': { style: 'currency', currency: 'SEK', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'NOK': { style: 'currency', currency: 'NOK', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'DKK': { style: 'currency', currency: 'DKK', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'NZD': { style: 'currency', currency: 'NZD', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'MXN': { style: 'currency', currency: 'MXN', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'BRL': { style: 'currency', currency: 'BRL', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'RUB': { style: 'currency', currency: 'RUB', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'INR': { style: 'currency', currency: 'INR', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'THB': { style: 'currency', currency: 'THB', minimumFractionDigits: 2, maximumFractionDigits: 2 }, 'PHP': { style: 'currency', currency: 'PHP', minimumFractionDigits: 2, maximumFractionDigits: 2 } }; for (const [currency, config] of Object.entries(currencyFormatConfigs)) { this.currencyFormats.set(currency, new Intl.NumberFormat(this.getLocaleForCurrency(currency), config)); } } // 获取货币的默认语言环境 getLocaleForCurrency(currency) { const currencyLocaleMap = { 'CNY': 'zh-CN', 'USD': 'en-US', 'EUR': 'de-DE', 'GBP': 'en-GB', 'JPY': 'ja-JP', 'KRW': 'ko-KR', 'AUD': 'en-AU', 'CAD': 'en-CA', 'CHF': 'de-CH', 'HKD': 'zh-HK', 'SGD': 'en-SG', 'SEK': 'sv-SE', 'NOK': 'nb-NO', 'DKK': 'da-DK', 'NZD': 'en-NZ', 'MXN': 'es-MX', 'BRL': 'pt-BR', 'RUB': 'ru-RU', 'INR': 'en-IN', 'THB': 'th-TH', 'PHP': 'en-PH' }; return currencyLocaleMap[currency] || 'en-US'; } // 翻译函数 t(key, params = {}, locale = this.currentLocale) { // 支持嵌套键,如 'product.title' const keys = key.split('.'); let translation = this.translations.get(locale) || {}; for (const k of keys) { if (translation && typeof translation === 'object' && k in translation) { translation = translation[k]; } else { // 尝试回退链 translation = this.findFallbackTranslation(keys, locale); break; } } // 如果不是字符串,返回键名 if (typeof translation !== 'string') { return key; } // 替换参数 return this.interpolate(translation, params); } // 查找回退翻译 findFallbackTranslation(keys, locale) { const fallbackIndex = this.fallbackChain.indexOf(locale); for (let i = fallbackIndex + 1; i < this.fallbackChain.length; i++) { const fallbackLocale = this.fallbackChain[i]; let translation = this.translations.get(fallbackLocale) || {}; for (const k of keys) { if (translation && typeof translation === 'object' && k in translation) { translation = translation[k]; } else { translation = null; break; } } if (typeof translation === 'string') { return translation; } } return null; } // 插值替换 interpolate(template, params) { return template.replace(/\{(\w+)\}/g, (match, key) => { return params[key] !== undefined ? String(params[key]) : match; }); } // 格式化货币 formatCurrency(amount, currency = this.currentCurrency, locale = this.currentLocale) { const formatter = this.currencyFormats.get(currency); if (formatter) { return formatter.format(amount); } // 回退到简单格式化 return new Intl.NumberFormat(locale, { style: 'currency', currency: currency }).format(amount); } // 格式化数字 formatNumber(number, options = {}, locale = this.currentLocale) { return new Intl.NumberFormat(locale, options).format(number); } // 格式化日期 formatDate(date, options = {}, locale = this.currentLocale) { const defaultOptions = { year: 'numeric', month: 'long', day: 'numeric' }; return new Intl.DateTimeFormat(locale, { ...defaultOptions, ...options }).format(new Date(date)); } // 格式化时间 formatTime(date, options = {}, locale = this.currentLocale) { const defaultOptions = { hour: '2-digit', minute: '2-digit', second: '2-digit' }; return new Intl.DateTimeFormat(locale, { ...defaultOptions, ...options }).format(new Date(date)); } // 格式化相对时间 formatRelativeTime(date, locale = this.currentLocale) { const now = new Date(); const diff = now - new Date(date); const seconds = Math.floor(diff / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); const months = Math.floor(days / 30); const years = Math.floor(months / 12); const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }); if (years > 0) return rtf.format(-years, 'year'); if (months > 0) return rtf.format(-months, 'month'); if (days > 0) return rtf.format(-days, 'day'); if (hours > 0) return rtf.format(-hours, 'hour'); if (minutes > 0) return rtf.format(-minutes, 'minute'); return rtf.format(-seconds, 'second'); } // 切换语言 async switchLanguage(locale) { const normalizedLocale = this.normalizeLocale(locale) || 'zh-CN'; if (normalizedLocale === this.currentLocale) { return; } // 显示加载状态 this.showLoadingIndicator(); try { // 加载新语言的翻译 await this.loadTranslations(normalizedLocale, ['common', 'product', 'checkout', 'shipping']); // 更新当前语言 this.currentLocale = normalizedLocale; // 保存到Cookie this.setCookie('kaola_lang', normalizedLocale, 365); // 更新HTML lang属性 document.documentElement.lang = normalizedLocale; // 更新页面方向 this.updateDocumentDirection(); // 触发语言切换事件 this.dispatchLanguageChangeEvent(normalizedLocale); } finally { this.hideLoadingIndicator(); } } // 切换货币 async switchCurrency(currency) { if (this.currencyFormats.has(currency)) { this.currentCurrency = currency; this.setCookie('kaola_currency', currency, 365); this.dispatchCurrencyChangeEvent(currency); } } // 更新文档方向 updateDocumentDirection() { const isRTL = this.rtlLanguages.has(this.currentLocale.split('-')[0]); document.documentElement.dir = isRTL ? 'rtl' : 'ltr'; } // 设置语言监听器 setupLanguageListeners() { // 监听语言切换事件 document.addEventListener('languagechange', (e) => { this.switchLanguage(e.detail.locale); }); // 监听货币切换事件 document.addEventListener('currencychange', (e) => { this.switchCurrency(e.detail.currency); }); // 监听网络状态变化,离线时切换到缓存的语言 window.addEventListener('online', () => { this.syncTranslations(); }); } // 同步翻译(在线时) async syncTranslations() { try { await this.loadTranslations(this.currentLocale, ['common', 'product']); } catch (error) { console.warn('Failed to sync translations:', error); } } // 显示加载指示器 showLoadingIndicator() { const indicator = document.createElement('div'); indicator.id = 'i18n-loading-indicator'; indicator.innerHTML = '<div></div><span>Loading...</span>'; indicator.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(255,255,255,0.8); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 9999; `; document.body.appendChild(indicator); } // 隐藏加载指示器 hideLoadingIndicator() { const indicator = document.getElementById('i18n-loading-indicator'); if (indicator) { indicator.remove(); } } // 派发语言切换事件 dispatchLanguageChangeEvent(locale) { const event = new CustomEvent('i18n:languageChanged', { detail: { locale, previousLocale: this.currentLocale } }); document.dispatchEvent(event); } // 派发货币切换事件 dispatchCurrencyChangeEvent(currency) { const event = new CustomEvent('i18n:currencyChanged', { detail: { currency, previousCurrency: this.currentCurrency } }); document.dispatchEvent(event); } // Cookie操作 getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); } setCookie(name, value, days) { const date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); document.cookie = `${name}=${value};expires=${date.toUTCString()};path=/;SameSite=Lax`; } // 预加载语言 async preloadLanguages(locales) { const preloadPromises = locales.map(locale => this.loadTranslations(locale, ['common', 'product']) ); await Promise.all(preloadPromises); } // 获取支持的语言列表 getSupportedLanguages() { return [ { code: 'zh-CN', name: '简体中文', nativeName: '简体中文' }, { code: 'zh-TW', name: '繁体中文', nativeName: '繁體中文' }, { code: 'en-US', name: 'English', nativeName: 'English' }, { code: 'ja-JP', name: 'Japanese', nativeName: '日本語' }, { code: 'ko-KR', name: 'Korean', nativeName: '한국어' }, { code: 'de-DE', name: 'German', nativeName: 'Deutsch' }, { code: 'fr-FR', name: 'French', nativeName: 'Français' }, { code: 'es-ES', name: 'Spanish', nativeName: 'Español' }, { code: 'it-IT', name: 'Italian', nativeName: 'Italiano' }, { code: 'pt-BR', name: 'Portuguese', nativeName: 'Português' }, { code: 'ru-RU', name: 'Russian', nativeName: 'Русский' }, { code: 'ar-SA', name: 'Arabic', nativeName: 'العربية' }, { code: 'th-TH', name: 'Thai', nativeName: 'ไทย' }, { code: 'vi-VN', name: 'Vietnamese', nativeName: 'Tiếng Việt' }, { code: 'id-ID', name: 'Indonesian', nativeName: 'Bahasa Indonesia' } ]; } // 获取支持的货币列表 getSupportedCurrencies() { return [ { code: 'CNY', name: 'Chinese Yuan', symbol: '¥', flag: '🇨🇳' }, { code: 'USD', name: 'US Dollar', symbol: '$', flag: '🇺🇸' }, { code: 'EUR', name: 'Euro', symbol: '€', flag: '🇪🇺' }, { code: 'GBP', name: 'British Pound', symbol: '£', flag: '🇬🇧' }, { code: 'JPY', name: 'Japanese Yen', symbol: '¥', flag: '🇯🇵' }, { code: 'KRW', name: 'South Korean Won', symbol: '₩', flag: '🇰🇷' }, { code: 'AUD', name: 'Australian Dollar', symbol: 'A$', flag: '🇦🇺' }, { code: 'CAD', name: 'Canadian Dollar', symbol: 'C$', flag: '🇨🇦' }, { code: 'CHF', name: 'Swiss Franc', symbol: 'Fr', flag: '🇨🇭' }, { code: 'HKD', name: 'Hong Kong Dollar', symbol: 'HK$', flag: '🇭🇰' }, { code: 'SGD', name: 'Singapore Dollar', symbol: 'S$', flag: '🇸🇬' }, { code: 'SEK', name: 'Swedish Krona', symbol: 'kr', flag: '🇸🇪' }, { code: 'NOK', name: 'Norwegian Krone', symbol: 'kr', flag: '🇳🇴' }, { code: 'DKK', name: 'Danish Krone', symbol: 'kr', flag: '🇩🇰' }, { code: 'NZD', name: 'New Zealand Dollar', symbol: 'NZ$', flag: '🇳🇿' }, { code: 'MXN', name: 'Mexican Peso', symbol: '$', flag: '🇲🇽' }, { code: 'BRL', name: 'Brazilian Real', symbol: 'R$', flag: '🇧🇷' }, { code: 'RUB', name: 'Russian Ruble', symbol: '₽', flag: '🇷🇺' }, { code: 'INR', name: 'Indian Rupee', symbol: '₹', flag: '🇮🇳' }, { code: 'THB', name: 'Thai Baht', symbol: '฿', flag: '🇹🇭' }, { code: 'PHP', name: 'Philippine Peso', symbol: '₱', flag: '🇵🇭' } ]; } } export const intelligentI18n = new IntelligentI18n();4.2 智能语言切换优化
// components/SmartLanguageSwitcher.js // 网易考拉智能语言切换组件 import { intelligentI18n } from '../i18n/intelligentI18n'; class SmartLanguageSwitcher extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this.currentLocale = intelligentI18n.currentLocale; this.supportedLanguages = intelligentI18n.getSupportedLanguages(); } connectedCallback() { this.render(); this.setupEventListeners(); this.preloadLanguageFlags(); } render() { const currentLang = this.supportedLanguages.find(l => l.code === this.currentLocale); this.shadowRoot.innerHTML = ` <style> :host { display: block; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } .language-switcher { position: relative; display: inline-block; } .switcher-button { display: flex; align-items: center; gap: 8px; padding: 10px 14px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.3s ease; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4); } .switcher-button:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.5); } .switcher-button:active { transform: translateY(0); } .current-flag { font-size: 20px; } .current-lang { text-transform: uppercase; } .dropdown-arrow { font-size: 10px; transition: transform 0.3s ease; } .language-switcher.open .dropdown-arrow { transform: rotate(180deg); } .dropdown-menu { position: absolute; top: calc(100% + 8px); right: 0; min-width: 200px; background: white; border-radius: 12px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); overflow: hidden; opacity: 0; visibility: hidden; transform: translateY(-10px); transition: all 0.3s ease; z-index: 1000; } .language-switcher.open .dropdown-menu { opacity: 1; visibility: visible; transform: translateY(0); } .search-box { padding: 12px; border-bottom: 1px solid #eee; } .search-input { width: 100%; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; outline: none; transition: border-color 0.2s; } .search-input:focus { border-color: #667eea; } .language-list { max-height: 300px; overflow-y: auto; } .language-item { display: flex; align-items: center; gap: 12px; padding: 12px 16px; cursor: pointer; transition: background 0.2s; } .language-item:hover { background: #f8f9fa; } .language-item.active { background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%); } .language-item.active .language-name { color: #667eea; font-weight: 600; } .flag { font-size: 24px; width: 32px; text-align: center; } .language-info { flex: 1; } .language-name { font-size: 14px; color: #333; margin-bottom: 2px; } .native-name { font-size: 12px; color: #888; } .check-mark { color: #667eea; font-size: 16px; opacity: 0; transition: opacity 0.2s; } .language-item.active .check-mark { opacity: 1; } .loading-spinner { display: flex; align-items: center; justify-content: center; padding: 20px; color: #888; } .spinner { width: 20px; height: 20px; border: 2px solid #eee; border-top-color: #667eea; border-radius: 50%; animation: spin 0.8s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } .popular-badge { font-size: 10px; padding: 2px 6px; background: #ff6b6b; color: white; border-radius: 4px; margin-left: auto; } /* 移动端适配 */ @media (max-width: 480px) { .dropdown-menu { min-width: 280px; right: -50px; } } </style> <div id="switcher"> <button id="switcherBtn" aria-haspopup="listbox" aria-expanded="false"> <span>${currentLang?.flag || '🌐'}</span> <span>${currentLang?.code.split('-')[0] || 'EN'}</span> <span>▼</span> </button> <div id="dropdown" role="listbox"> <div> <input type="text" placeholder="Search language..." id="searchInput" autocomplete="off" > </div> <div id="languageList"> <!-- 语言列表将通过JS动态生成 --> </div> </div> </div> `; } setupEventListeners() { const switcher = this.shadowRoot.getElementById('switcher'); const switcherBtn = this.shadowRoot.getElementById('switcherBtn'); const dropdown = this.shadowRoot.getElementById('dropdown'); const searchInput = this.shadowRoot.getElementById('searchInput'); const languageList = this.shadowRoot.getElementById('languageList'); // 切换下拉菜单 switcherBtn.addEventListener('click', (e) => { e.stopPropagation(); this.toggleDropdown(); }); // 点击外部关闭 document.addEventListener('click', (e) => { if (!switcher.contains(e.target)) { this.closeDropdown(); } }); // 搜索功能 searchInput.addEventListener('input', (e) => { this.filterLanguages(e.target.value); }); // 键盘导航 searchInput.addEventListener('keydown', (e) => { this.handleKeyboardNavigation(e); }); // 语言选择 languageList.addEventListener('click', (e) => { const languageItem = e.target.closest('.language-item'); if (languageItem) { const langCode = languageItem.dataset.langCode; this.switchLanguage(langCode); } }); } toggleDropdown() { const switcher = this.shadowRoot.getElementById('switcher'); const isOpen = switcher.classList.contains('open'); if (isOpen) { this.closeDropdown(); } else { this.openDropdown(); } } openDropdown() { const switcher = this.shadowRoot.getElementById('switcher'); const switcherBtn = this.shadowRoot.getElementById('switcherBtn'); const dropdown = this.shadowRoot.getElementById('dropdown'); switcher.classList.add('open'); switcherBtn.setAttribute('aria-expanded', 'true'); // 生成语言列表 this.renderLanguageList(this.supportedLanguages); // 聚焦搜索框 setTimeout(() => { this.shadowRoot.getElementById('searchInput')?.focus(); }, 100); } closeDropdown() { const switcher = this.shadowRoot.getElementById('switcher'); const switcherBtn = this.shadowRoot.getElementById('switcherBtn'); switcher.classList.remove('open'); switcherBtn.setAttribute('aria-expanded', 'false'); } renderLanguageList(languages) { const languageList = this.shadowRoot.getElementById('languageList'); if (languages.length === 0) { languageList.innerHTML = ` <div> <div></div> </div> `; return; } const popularLanguages = ['zh-CN', 'en-US', 'ja-JP', 'ko-KR']; languageList.innerHTML = languages.map(lang => { const isActive = lang.code === this.currentLocale; const isPopular = popularLanguages.includes(lang.code); return ` <div class="language-item ${isActive ? 'active' : ''}" data-lang-code="${lang.code}" role="option" aria-selected="${isActive}" > <span>${lang.flag}</span> <div> <div>${lang.name}</div> <div>${lang.nativeName}</div> </div> ${isPopular ? '<span>Popular</span>' : ''} <span>✓</span> </div> `; }).join(''); } filterLanguages(query) { const filtered = this.supportedLanguages.filter(lang => lang.name.toLowerCase().includes(query.toLowerCase()) || lang.nativeName.toLowerCase().includes(query.toLowerCase()) || lang.code.toLowerCase().includes(query.toLowerCase()) ); this.renderLanguageList(filtered); } handleKeyboardNavigation(e) { const items = this.shadowRoot.querySelectorAll('.language-item'); const activeElement = document.activeElement; const currentIndex = Array.from(items).indexOf(activeElement.closest('.language-item')); switch (e.key) { case 'ArrowDown': e.preventDefault(); const nextIndex = (currentIndex + 1) % items.length; items[nextIndex]?.focus(); break; case 'ArrowUp': e.preventDefault(); const prevIndex = currentIndex <= 0 ? items.length - 1 : currentIndex - 1; items[prevIndex]?.focus(); break; case 'Enter': if (activeElement.classList.contains('language-item')) { const langCode = activeElement.dataset.langCode; this.switchLanguage(langCode); } break; case 'Escape': this.closeDropdown(); break; } } async switchLanguage(langCode) { if (langCode === this.currentLocale) { this.closeDropdown(); return; } // 显示加载状态 const switcherBtn = this.shadowRoot.getElementById('switcherBtn'); const originalContent = switcherBtn.innerHTML; switcherBtn.innerHTML = ` <div style="width:16px;height:16px;border-width:2px;"></div> <span>Switching...</span> `; switcherBtn.disabled = true; try { // 切换语言 await intelligentI18n.switchLanguage(langCode); this.currentLocale = langCode; // 更新按钮显示 const newLang = this.supportedLanguages.find(l => l.code === langCode); switcherBtn.innerHTML = ` <span>${newLang?.flag || '🌐'}</span> <span>${newLang?.code.split('-')[0] || 'EN'}</span> <span>▼</span> `; // 触发自定义事件 this.dispatchEvent(new CustomEvent('language-switched', { detail: { locale: langCode, previousLocale: this.currentLocale } })); } catch (error) { console.error('Failed to switch language:', error); // 恢复原始状态 switcherBtn.innerHTML = originalContent; } finally { switcherBtn.disabled = false; this.closeDropdown(); } } preloadLanguageFlags() { // 预加载国旗emoji的SVG版本(可选优化) // 这里可以预加载高分辨率的国家旗帜图标 } } // 注册自定义元素 customElements.define('smart-language-switcher', SmartLanguageSwitcher); export { SmartLanguageSwitcher };五、全球CDN与资源优化
5.1 全球CDN智能调度
// cdn/globalCDNOptimizer.js // 网易考拉全球CDN智能调度器 class GlobalCDNOptimizer { constructor() { this.cdnProviders = { primary: { name: 'kaola-global-cdn', regions: { 'CN': 'https://cn-cdn.kaola.com', 'APAC': 'https://apac-cdn.kaola.com', 'EU': 'https://eu-cdn.kaola.com', 'US': 'https://us-cdn.kaola.com', 'SA': 'https://sa-cdn.kaola.com', 'AF': 'https://af-cdn.kaola.com' } }, secondary: { name: 'cloudflare-kaola', baseUrl: 'https://kaola.cdn.cloudflare.net' }, tertiary: { name: 'aws-kaola', baseUrl: 'https://kaola-cdn.s3.amazonaws.com' } }; this.resourceTypes = { images: { priority: 1, compression: 'aggressive', formats: ['webp', 'avif', 'jpg'] }, videos: { priority: 2, compression: 'moderate', formats: ['mp4', 'webm'] }, scripts: { priority: 1, compression: 'none', formats: ['js'] }, styles: { priority: 1, compression: 'aggressive', formats: ['css'] }, fonts: { priority: 3, compression: 'moderate', formats: ['woff2', 'woff'] }, documents: { priority: 2, compression: 'aggressive', formats: ['pdf', 'txt'] } }; this.performanceMetrics = new Map(); this.healthChecks = new Map(); this.smartRoutingEnabled = true; } // 获取最优CDN URL getOptimalCDNUrl(resourcePath, resourceType, userLocation = null) { const typeConfig = this.resourceTypes[resourceType] || this.resourceTypes.images; // 确定用户位置 const location = userLocation || this.detectUserLocation(); // 选择CDN提供商 const provider = this.selectBestProvider(location, resourceType); // 生成优化的资源URL const optimizedPath = this.optimizeResourcePath(resourcePath, typeConfig); // 构建完整URL const baseUrl = this.getBaseUrl(provider, location, resourceType); return { url: `${baseUrl}${optimizedPath}`, provider: provider.name, region: location, format: this.getBestFormat(typeConfig, userLocation), compression: typeConfig.compression, cacheTTL: this.getCacheTTL(resourceType, location) }; } // 检测用户位置 detectUserLocation() { // 从Cloudflare头信息获取 const cfCountry = this.getHeader('CF-IPCountry'); const cfRegion = this.getHeader('CF-Region'); if (cfCountry) { return this.mapCountryToRegion(cfCountry, cfRegion); } // 从其他CDN头信息获取 const country = this.getHeader('X-Country') || this.getHeader('X-Geo-Country'); if (country) { return this.mapCountryToRegion(country); } // 从IP地址推断 return this.inferLocationFromIP(); } // 映射国家到区域 mapCountryToRegion(country, region = null) { const regionMapping = { 'CN': { region: 'CN', subregion: 'East Asia' }, 'HK': { region: 'APAC', subregion: 'East Asia' }, 'TW': { region: 'APAC', subregion: 'East Asia' }, 'MO': { region: 'APAC', subregion: 'East Asia' }, 'JP': { region: 'APAC', subregion: 'East Asia' }, 'KR': { region: 'APAC', subregion: 'East Asia' }, 'SG': { region: 'APAC', subregion: 'Southeast Asia' }, 'MY': { region: 'APAC', subregion: 'Southeast Asia' }, 'TH': { region: 'APAC', subregion: 'Southeast Asia' }, 'VN': { region: 'APAC', subregion: 'Southeast Asia' }, 'ID': { region: 'APAC', subregion: 'Southeast Asia' }, 'PH': { region: 'APAC', subregion: 'Southeast Asia' }, 'IN': { region: 'APAC', subregion: 'South Asia' }, 'US': { region: 'US', subregion: 'North America' }, 'CA': { region: 'US', subregion: 'North America' }, 'MX': { region: 'US', subregion: 'North America' }, 'GB': { region: 'EU', subregion: 'Western Europe' }, 'DE': { region: 'EU', subregion: 'Western Europe' }, 'FR': { region: 'EU', subregion: 'Western Europe' }, 'IT': { region: 'EU', subregion: 'Southern Europe' }, 'ES': { region: 'EU', subregion: 'Southern Europe' }, 'NL': { region: 'EU', subregion: 'Western Europe' }, 'BE': { region: 'EU', subregion: 'Western Europe' }, 'SE': { region: 'EU', subregion: 'Northern Europe' }, 'NO': { region: 'EU', subregion: 'Northern Europe' }, 'DK': { region: 'EU', subregion: 'Northern Europe' }, 'FI': { region: 'EU', subregion: 'Northern Europe' }, 'PL': { region: 'EU', subregion: 'Eastern Europe' }, 'CZ': { region: 'EU', subregion: 'Eastern Europe' }, 'AU': { region: 'APAC', subregion: 'Oceania' }, 'NZ': { region: 'APAC', subregion: 'Oceania' }, 'AE': { region: 'SA', subregion: 'Middle East' }, 'SA': { region: 'SA', subregion: 'Middle East' }, 'IL': { region: 'SA', subregion: 'Middle East' }, 'ZA': { region: 'AF', subregion: 'Southern Africa' }, 'NG': { region: 'AF', subregion: 'West Africa' }, 'EG': { region: 'AF', subregion: 'North Africa' } }; const mapping = regionMapping[country] || { region: 'US', subregion: 'Unknown' }; return mapping.region; } // 推断IP位置 inferLocationFromIP() { // 使用免费的IP地理位置API const ip = this.getHeader('CF-Connecting-IP') || this.getHeader('X-Forwarded-For'); if (!ip || ip === '127.0.0.1') { return 'US'; // 默认美国 } // 简单的IP范围映射(实际应用中应使用专业的IP地理定位服务) const ipRanges = { 'CN': [[1, 126], [175, 191]], // A类和部分B类 'APAC': [[203, 223]], // APNIC分配范围 'EU': [[77, 95], [146, 173]], // RIPE分配范围 'US': [[3, 63], [96, 126]], // ARIN分配范围 'SA': [[189, 190]], // AfriNIC分配范围 'AF': [[41, 43]] // AfriNIC分配范围 }; // 简化处理:返回默认区域 return 'US'; } // 选择最佳CDN提供商 selectBestProvider(location, resourceType) { // 获取各提供商的性能分数 const providerScores = this.calculateProviderScores(location, resourceType); // 选择分数最高的提供商 let bestProvider = this.cdnProviders.primary; let bestScore = 0; for (const [providerName, score] of Object.entries(providerScores)) { if (score > bestScore) { bestScore = score; bestProvider = this.cdnProviders[providerName]; } } return bestProvider; } // 计算提供商分数 calculateProviderScores(location, resourceType) { const scores = {}; for (const [providerName, provider] of Object.entries(this.cdnProviders)) { let score = 0; const metrics = this.performanceMetrics.get(providerName) || {}; const health = this.healthChecks.get(providerName) || { healthy: true, uptime: 100 }; // 健康检查权重 if (!health.healthy) { score = -1000; continue; } score += health.uptime * 0.3; // 延迟分数 const latency = metrics.latency?.[location] || 100; score += Math.max(0, 100 - latency) * 0.4; // 可用性分数 const availability = metrics.availability?.[location] || 99.9; score += availability * 0.2; // 带宽分数 const bandwidth = metrics.bandwidth?.[location] || 1000; score += Math.min(100, bandwidth / 10) * 0.1; // 资源类型优化 if (resourceType === 'videos' && providerName === 'cloudflare') { score += 10; // Cloudflare对视频优化更好 } if (resourceType === 'images' && providerName === 'primary') { score += 5; // 自有CDN对图片优化更好 } scores[providerName] = score; } return scores; } // 优化资源路径 optimizeResourcePath(resourcePath, typeConfig) { // 解析路径 const url = new URL(resourcePath, 'http://example.com'); const pathname = url.pathname; // 添加优化参数 const optimizations = []; // 图片优化 if (typeConfig === this.resourceTypes.images) { // 添加质量参数 if (!url.searchParams.has('q')) { optimizations.push('q=85'); } // 添加尺寸参数(如果可能) if (this.canOptimizeDimensions(pathname)) { const dimensions = this.getOptimalDimensions(pathname); if (dimensions) { optimizations.push(`w=${dimensions.width}`); optimizations.push(`h=${dimensions.height}`); } } } // 视频优化 if (typeConfig === this.resourceTypes.videos) { if (!url.searchParams.has('quality')) { optimizations.push('quality=auto'); } } // 添加版本号(用于缓存控制) if (!url.searchParams.has('v')) { optimizations.push(`v=${this.getResourceVersion(pathname)}`); } // 构建优化后的路径 if (optimizations.length > 0) { url.searchParams.set('opt', optimizations.join(',')); } return url.pathname + url.search; } // 获取最佳格式 getBestFormat(typeConfig, userLocation) { const formats = typeConfig.formats; // 根据用户浏览器支持选择最佳格式 const supportedFormats = this.detectSupportedFormats(); for (const format of formats) { if (supportedFormats.includes(format)) { return format; } } return formats[0]; // 返回默认格式 } // 检测支持的格式 detectSupportedFormats() { const formats = []; const canvas = document.createElement('canvas'); // 检测WebP支持 if (canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0) { formats.push('webp'); } // 检测AVIF支持 const avifTest = new Image(); avifTest.onload = () => formats.push('avif'); avifTest.onerror = () => {}; avifTest.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGyJDb2xvci3/2VtZGF0EgAKBzgABpAQIYGy