From 4f025ce7889466241465e838ca0eb83429199c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Fri, 30 Jan 2026 16:06:25 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=EF=BC=9A=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E7=AE=A1=E7=90=86=E5=92=8C=E6=B6=88=E9=99=A4?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/core/cache-manager.js | 103 +++++++++++++++++++++++++++++++ src/js/core/form-id.js | 39 +++--------- src/js/pages/basic-info.page.js | 33 +--------- src/js/pages/index.page.js | 28 +++++---- src/js/services/area.service.js | 104 ++++++++++++++------------------ src/js/ui/city-picker.js | 38 +----------- 6 files changed, 173 insertions(+), 172 deletions(-) create mode 100644 src/js/core/cache-manager.js diff --git a/src/js/core/cache-manager.js b/src/js/core/cache-manager.js new file mode 100644 index 0000000..6140413 --- /dev/null +++ b/src/js/core/cache-manager.js @@ -0,0 +1,103 @@ +/** + * 通用缓存管理器 + * 提供 localStorage 缓存、请求去重和过期时间管理 + */ + +export class CacheManager { + // 正在进行的请求缓存(静态) + static pendingRequests = new Map(); + + /** + * 获取缓存或执行请求 + * @param {string} cacheKey - 缓存键 + * @param {Function} fetchFn - 获取数据的异步函数 + * @param {number} duration - 缓存有效期(毫秒),默认 10 分钟 + * @returns {Promise} - 缓存的数据或新获取的数据 + */ + static async getCachedOrFetch(cacheKey, fetchFn, duration = 10 * 60 * 1000) { + // 检查 localStorage 缓存 + const cached = this._getFromCache(cacheKey, duration); + if (cached !== null) { + return cached; + } + + // 检查是否有正在进行的请求 + if (this.pendingRequests.has(cacheKey)) { + console.log(`[CacheManager] 等待正在进行的请求: ${cacheKey}`); + return this.pendingRequests.get(cacheKey); + } + + // 创建新请求 + const requestPromise = this._fetchAndCache(cacheKey, fetchFn, duration); + this.pendingRequests.set(cacheKey, requestPromise); + + try { + return await requestPromise; + } finally { + // 请求完成后清除缓存 + this.pendingRequests.delete(cacheKey); + } + } + + /** + * 从 localStorage 获取缓存 + * @private + * @param {string} key - 缓存键 + * @param {number} duration - 有效期(毫秒) + * @returns {any|null} - 缓存的数据,如果不存在或已过期则返回 null + */ + static _getFromCache(key, duration) { + const cached = localStorage.getItem(key); + if (cached) { + try { + const { data, timestamp } = JSON.parse(cached); + if (Date.now() - timestamp < duration) { + console.log(`[CacheManager] 使用缓存: ${key}`); + return data; + } + } catch (e) { + console.warn(`[CacheManager] 缓存解析失败: ${key}`, e); + } + } + return null; + } + + /** + * 获取数据并缓存 + * @private + * @param {string} key - 缓存键 + * @param {Function} fetchFn - 获取数据的异步函数 + * @param {number} duration - 有效期(毫秒) + * @returns {Promise} - 获取的数据 + */ + static async _fetchAndCache(key, fetchFn, duration) { + const data = await fetchFn(); + localStorage.setItem(key, JSON.stringify({ + data, + timestamp: Date.now() + })); + return data; + } + + /** + * 清除指定前缀的所有缓存 + * @param {string} prefix - 缓存键前缀 + */ + static clearCacheByPrefix(prefix) { + Object.keys(localStorage) + .filter(key => key.startsWith(prefix)) + .forEach(key => localStorage.removeItem(key)); + console.log(`[CacheManager] 清除缓存前缀: ${prefix}`); + } + + /** + * 清除单个缓存 + * @param {string} key - 缓存键 + */ + static clearCache(key) { + localStorage.removeItem(key); + console.log(`[CacheManager] 清除缓存: ${key}`); + } +} + +export default CacheManager; diff --git a/src/js/core/form-id.js b/src/js/core/form-id.js index bcd92c3..8102188 100644 --- a/src/js/core/form-id.js +++ b/src/js/core/form-id.js @@ -6,17 +6,13 @@ import { CACHE_CONFIG } from '../config/index.js'; export class FormIdGenerator { - // 防止递归调用检测 - static _gettingFormId = false; - static _lastFormId = ''; - /** * 生成随机9位数字的表单唯一标识符 * @returns {number} - 9位随机数字 * @private */ static _generateRandomId() { - return Math.floor(Math.random() * 900000000) + 100000000; // 生成100000000-999999999之间的9位数字 + return Math.floor(Math.random() * 900000000) + 100000000; } /** @@ -24,32 +20,15 @@ export class FormIdGenerator { * @returns {string} - 表单ID字符串 */ static getOrCreate() { - // 防止递归调用检测 - if (this._gettingFormId) { - console.error('[FormIdGenerator] 检测到递归调用 getOrCreateFormId', new Error().stack); - return this._lastFormId || ''; + let formId = localStorage.getItem(CACHE_CONFIG.KEYS.FORM_ID); + + if (!formId) { + formId = this._generateRandomId().toString(); + localStorage.setItem(CACHE_CONFIG.KEYS.FORM_ID, formId); + console.log('[FormIdGenerator] 生成新的表单ID:', formId); } - this._gettingFormId = true; - try { - console.log('[FormIdGenerator] getOrCreate 被调用'); - let formId = localStorage.getItem(CACHE_CONFIG.KEYS.FORM_ID); - console.log('[FormIdGenerator] 从 localStorage 获取的 formId:', formId); - - if (!formId) { - formId = this._generateRandomId().toString(); - localStorage.setItem(CACHE_CONFIG.KEYS.FORM_ID, formId); - console.log('[FormIdGenerator] 生成新的表单ID:', formId); - } - - this._lastFormId = formId; - return formId; - } catch (error) { - console.error('[FormIdGenerator] getOrCreate 出错:', error); - return ''; - } finally { - this._gettingFormId = false; - } + return formId; } /** @@ -57,7 +36,6 @@ export class FormIdGenerator { */ static clear() { localStorage.removeItem(CACHE_CONFIG.KEYS.FORM_ID); - this._lastFormId = ''; console.log('[FormIdGenerator] 已清除表单ID'); } @@ -79,7 +57,6 @@ export class FormIdGenerator { return; } localStorage.setItem(CACHE_CONFIG.KEYS.FORM_ID, formId); - this._lastFormId = formId; console.log('[FormIdGenerator] 已设置表单ID:', formId); } } diff --git a/src/js/pages/basic-info.page.js b/src/js/pages/basic-info.page.js index 9ec0153..d09a600 100644 --- a/src/js/pages/basic-info.page.js +++ b/src/js/pages/basic-info.page.js @@ -910,7 +910,7 @@ export class BasicInfoPage { console.log('[BasicInfoPage] IP定位成功:', province, city); // 查找城市代码 - const cityCode = await this.findCityCode(province, city); + const cityCode = await AreaService.findCityCode(province, city); if (cityCode) { // 自动填充城市 @@ -935,37 +935,6 @@ export class BasicInfoPage { } } - /** - * 查找城市代码 - */ - async findCityCode(provinceName, cityName) { - try { - // 获取省份数据 - const provinces = await AreaService.getProvinces(); - const province = provinces.find(p => p.name === provinceName); - - if (!province) { - return null; - } - - // 获取城市数据 - const cities = await AreaService.getCities(province.code); - const city = cities.find(c => c.name === cityName && c.code.length === 4); - - if (city) { - return { - provinceCode: province.code, - cityCode: city.code - }; - } - - return null; - } catch (error) { - console.error('[BasicInfoPage] 查找城市代码失败:', error); - return null; - } - } - /** * 销毁页面 */ diff --git a/src/js/pages/index.page.js b/src/js/pages/index.page.js index 05c255c..2d8d01c 100644 --- a/src/js/pages/index.page.js +++ b/src/js/pages/index.page.js @@ -6,6 +6,7 @@ import { Picker, Modal, OneClickLoginButton } from '../ui/index.js'; import { Validator, Formatter } from '../utils/index.js'; import { SMSService, AuthService, LoanService } from '../services/index.js'; +import { AuthFlowService } from '../services/auth-flow.service.js'; import { LOAN_CONFIG, PURPOSE_PICKER_CONFIG, TERM_PICKER_CONFIG, ANIMATION_CONFIG } from '../config/index.js'; import { UserCache } from '../core/user-cache.js'; import { showToast } from '../ui/toast.js'; @@ -30,8 +31,8 @@ export class IndexPage { // 注意:使用一键登录需要在极光控制台配置域名白名单,否则会出现跨域错误 this.jVerifyAppId = '80570da3ef331d9de547b4f1'; - // 倒计时定时器 - this.countdownTimer = null; + // 倒计时取消函数 + this.countdownCancel = null; this.init(); } @@ -314,24 +315,27 @@ export class IndexPage { * @param {HTMLElement} countdownEl - 倒计时元素 */ startCountdown(countdownEl) { - let time = 59; - countdownEl.textContent = `${time}s`; countdownEl.style.color = '#999'; countdownEl.style.cursor = 'default'; countdownEl.onclick = null; - this.countdownTimer = setInterval(() => { - time--; - if (time > 0) { + // 清除之前的倒计时 + if (this.countdownCancel) { + this.countdownCancel(); + } + + this.countdownCancel = AuthFlowService.startCountdown( + 59, + (time) => { countdownEl.textContent = `${time}s`; - } else { + }, + () => { countdownEl.textContent = '重新发送'; countdownEl.style.color = '#3474fe'; countdownEl.style.cursor = 'pointer'; countdownEl.onclick = () => this.resendSMS(countdownEl); - clearInterval(this.countdownTimer); } - }, 1000); + ); } /** @@ -534,8 +538,8 @@ export class IndexPage { * 销毁页面 */ destroy() { - if (this.countdownTimer) { - clearInterval(this.countdownTimer); + if (this.countdownCancel) { + this.countdownCancel(); } if (this.purposePicker) { diff --git a/src/js/services/area.service.js b/src/js/services/area.service.js index ecc6ff8..e309c36 100644 --- a/src/js/services/area.service.js +++ b/src/js/services/area.service.js @@ -5,6 +5,7 @@ import { API_CONFIG } from '../config/api.config.js'; import { ApiClient } from '../core/api.js'; +import { CacheManager } from '../core/cache-manager.js'; const CACHE_KEY_PREFIX = 'area_cache_'; const CACHE_DURATION = 10 * 60 * 1000; // 10分钟 @@ -13,9 +14,6 @@ const CACHE_DURATION = 10 * 60 * 1000; // 10分钟 * 区域服务 */ export class AreaService { - // 正在进行的请求缓存 - static pendingRequests = new Map(); - /** * 获取区域列表 * @param {string} provincecode - 省份代码(可选) @@ -24,68 +22,18 @@ export class AreaService { static async getAreaList(provincecode) { const cacheKey = `${CACHE_KEY_PREFIX}${provincecode || 'all'}`; - // 检查 localStorage 缓存 - const cached = localStorage.getItem(cacheKey); - if (cached) { - const { data, timestamp } = JSON.parse(cached); - if (Date.now() - timestamp < CACHE_DURATION) { - console.log(`[AreaService] 使用缓存 ${provincecode || 'all'}`); - return data; - } - } - - // 检查是否有正在进行的请求 - if (this.pendingRequests.has(cacheKey)) { - console.log(`[AreaService] 等待正在进行的请求 ${provincecode || 'all'}`); - return this.pendingRequests.get(cacheKey); - } - - // 创建新请求 - const requestPromise = this.doGetAreaList(provincecode, cacheKey); - this.pendingRequests.set(cacheKey, requestPromise); - - try { - return await requestPromise; - } finally { - // 请求完成后清除缓存 - this.pendingRequests.delete(cacheKey); - } - } - - /** - * 执行实际的区域列表请求 - * @private - */ - static async doGetAreaList(provincecode, cacheKey) { - try { - // 构建请求参数 - const params = {}; - if (provincecode) { - params.provincecode = provincecode; - } - + return CacheManager.getCachedOrFetch(cacheKey, async () => { + const params = provincecode ? { provincecode } : {}; console.log(`[AreaService] 请求区域数据 ${provincecode || 'all'}`); - // 使用 ApiClient 发送请求 const result = await ApiClient.get(API_CONFIG.ENDPOINTS.AREA_LIST, params); if (result.retcode !== 0) { throw new Error(result.retmsg || '获取区域数据失败'); } - const areaList = result.result || []; - - // 保存到 localStorage 缓存 - localStorage.setItem(cacheKey, JSON.stringify({ - data: areaList, - timestamp: Date.now() - })); - - return areaList; - } catch (error) { - console.error('[AreaService] 获取区域数据失败:', error); - throw error; - } + return result.result || []; + }, CACHE_DURATION); } /** @@ -108,13 +56,49 @@ export class AreaService { return this.getAreaList(provincecode); } + /** + * 根据省市名称查找代码 + * @param {string} provinceName - 省份名称 + * @param {string} cityName - 城市名称 + * @returns {Promise} - {provinceCode, cityCode},如果未找到则返回 null + */ + static async findCityCode(provinceName, cityName) { + if (!provinceName || !cityName) { + return null; + } + + try { + const provinces = await this.getProvinces(); + const province = provinces.find(p => p.name === provinceName); + + if (!province) { + console.warn(`[AreaService] 未找到省份: ${provinceName}`); + return null; + } + + const areas = await this.getCities(province.code); + const city = areas.find(c => c.name === cityName && c.code.length === 4); + + if (!city) { + console.warn(`[AreaService] 未找到城市: ${provinceName}-${cityName}`); + return null; + } + + return { + provinceCode: province.code, + cityCode: city.code + }; + } catch (error) { + console.error('[AreaService] 查找城市代码失败:', error); + return null; + } + } + /** * 清除缓存 */ static clearCache() { - Object.keys(localStorage) - .filter(key => key.startsWith(CACHE_KEY_PREFIX)) - .forEach(key => localStorage.removeItem(key)); + CacheManager.clearCacheByPrefix(CACHE_KEY_PREFIX); } } diff --git a/src/js/ui/city-picker.js b/src/js/ui/city-picker.js index 865897c..4b61e21 100644 --- a/src/js/ui/city-picker.js +++ b/src/js/ui/city-picker.js @@ -671,7 +671,7 @@ export class CityPicker { console.log('[CityPicker] IP定位成功:', province, city); // 查找城市代码 - const cityCode = await this.findCityCode(province, city); + const cityCode = await AreaService.findCityCode(province, city); if (cityCode) { this.currentLocationCity = { @@ -703,40 +703,4 @@ export class CityPicker { } } } - - /** - * 查找城市代码 - * @param {string} provinceName - 省份名称 - * @param {string} cityName - 城市名称 - * @returns {Promise} - 包含省代码和市代码的对象 - */ - async findCityCode(provinceName, cityName) { - try { - // 先在省份数据中查找省份代码 - if (this.provinces.length === 0) { - await this.loadProvinces(); - } - - const province = this.provinces.find(p => p.name === provinceName); - if (!province) { - return null; - } - - // 加载该省的城市数据 - const cities = await AreaService.getCities(province.code); - const city = cities.find(c => c.name === cityName && c.code.length === 4); - - if (city) { - return { - provinceCode: province.code, - cityCode: city.code - }; - } - - return null; - } catch (error) { - console.error('[CityPicker] 查找城市代码失败:', error); - return null; - } - } }