// app.js const { api } = require('./utils/request'); App({ globalData: { authState: 'loading', authMessage: '', userId: null, token: null, isConnected: true, networkType: 'wifi' }, onLaunch: function() { this.removeLegacyDataCaches(); this._authListeners = []; require('./utils/subscribe').resetLoginRenewal(); // 冷启动始终换取新 token,所有业务请求共享这一次登录。 if (wx.getStorageSync('accountDeleted')) this.setAuthState('error','账号已注销。再次进入将创建新账号,原数据不会恢复。'); else this.silentLogin().catch(function() {}); this.checkNetwork(); wx.onNetworkStatusChange((res) => { this.globalData.isConnected = res.isConnected; this.globalData.networkType = res.networkType; }); }, finishAccountDeletion: function() { require('./utils/data-cache').clear(); require('./utils/announcement-session').clear(); require('./utils/subscribe').resetLoginRenewal(); this._authGeneration=(this._authGeneration||0)+1;this._authPromise=null; wx.clearStorageSync(); wx.setStorageSync('accountDeleted',true); this.globalData.token=null;this.globalData.userId=null; this.setAuthState('error','账号已注销。再次进入将创建新账号,原数据不会恢复。'); }, onRelationChanged: function() { if (this._relationRedirect) return; wx.removeStorageSync('petHasPet'); this._relationRedirect = true; setTimeout(() => { this._relationRedirect=false; wx.reLaunch({url:'/pages/index/index'}); }, 0); }, onShow: function() { const sync = async () => { if (this._relationSync || this.globalData.authState !== 'ready') return; this._relationSync = true; const before = String(wx.getStorageSync('coupleId') || ''); try { const auth = await api.checkAuth({cache:false, silent:true}); const changed = require('./utils/data-cache').applyRemoteRevision(auth.data_revision); if (before !== String(wx.getStorageSync('coupleId') || '')) { this.onRelationChanged(); } else if (changed && typeof getCurrentPages === 'function') { const pages = getCurrentPages(); const page = pages[pages.length - 1]; if (page && page.onDataChanged) page.onDataChanged(); } } catch(e) {} finally { this._relationSync = false; } }; sync(); if (this.globalData.authState === 'ready') require('./utils/subscribe').prepareLoginRenewal(); if (!this._relationTimer) this._relationTimer = setInterval(sync, 30000); }, onHide: function() { if(this._relationTimer) clearInterval(this._relationTimer); this._relationTimer=null; }, setAuthState: function(state, message) { this.globalData.authState = state; this.globalData.authMessage = message || ''; (this._authListeners || []).slice().forEach(function(listener) { listener(state, message || ''); }); }, observeAuth: function(listener) { if (!this._authListeners) this._authListeners = []; this._authListeners.push(listener); listener(this.globalData.authState, this.globalData.authMessage); return () => { this._authListeners = this._authListeners.filter(function(item) { return item !== listener; }); }; }, ensureAuth: function() { if (this._authPromise) return this._authPromise; if (this.globalData.authState === 'error') return Promise.reject({ error: this.globalData.authMessage, authFailed: true }); if (this.globalData.authState === 'ready' && wx.getStorageSync('token')) return Promise.resolve(); return this.silentLogin(); }, // 多个旧 token 请求同时返回 401 时,只刷新一次;较晚返回的旧请求复用新 token。 refreshAuth: function(failedToken) { if(wx.getStorageSync('accountDeleted')) return Promise.reject({error:'账号已注销',accountDeleted:true}); if (this._authPromise) return this._authPromise; if (this.globalData.authState === 'ready' && wx.getStorageSync('token') !== failedToken) return Promise.resolve(); return this.silentLogin(); }, removeLegacyDataCaches: function() { try { var keys = wx.getStorageInfoSync().keys || []; var exact = { indexCache: true, profileCache: true, discoverStatsCache: true, songsCache: true, albumsCache: true, diaryCache: true, timelineCache: true, timelineBgCache: true, checkinsCache: true, coupleInfoCache: true, petCache: true, petAchievementCache: true, period_cache_data: true, bg_cache: true, subscribe_status_cache: true, _albums_inited: true, myLocation: true }; keys.forEach(function(key) { if (exact[key] || /^(offline_|albumCache_|diaryDetailCache_|timelineDetailCache_|checkinDetailCache_|historyCache_|period_cache_data:|timelineBg_)/.test(key)) { wx.removeStorageSync(key); } }); } catch (e) { console.warn('清理旧数据缓存失败', e); } }, // ★ 获取用户位置(使用 wx.getFuzzyLocation 模糊定位) getLocationRegion: function() { return new Promise(function(resolve) { wx.getFuzzyLocation({ type: 'wgs84', success: function(loc) { // 直接存储经纬度字符串,不依赖地图 API var region = loc.latitude.toFixed(4) + ',' + loc.longitude.toFixed(4); resolve(region); }, fail: function(err) { console.log('获取模糊位置失败:', err); resolve(''); } }); }); }, silentLogin: function() { if (this._authPromise) return this._authPromise; const generation=this._authGeneration||0; this.setAuthState('loading'); this._authPromise = this.performLogin().then((res) => { this.setAuthState('ready'); require('./utils/subscribe').prepareLoginRenewal(); return res; }).catch((error) => { if(generation!==(this._authGeneration||0))throw error; wx.removeStorageSync('token'); this.globalData.token = null; var failure = { error: (error && error.error) || '登录失败,请检查网络后重试', authFailed: true }; this.setAuthState('error', failure.error); throw failure; }).finally(() => { if(generation===(this._authGeneration||0))this._authPromise = null; }); return this._authPromise; }, performLogin: async function() { const generation=this._authGeneration||0; var loginRes = await new Promise(function(resolve, reject) { var done = false; var timer = setTimeout(function() { if (!done) { done = true; reject({ error: '登录超时,请重试' }); } }, 10000); wx.login({ success: function(res) { if (!done) { done = true; clearTimeout(timer); resolve(res); } }, fail: function(err) { if (!done) { done = true; clearTimeout(timer); reject(err); } } }); }); if (!loginRes.code) throw { error: '未能获取微信登录凭证,请重试' }; // 定位授权不阻塞登录,城市信息由页面后续的定位接口更新。 var res = await api.wechatLogin(loginRes.code, ''); if (generation!==(this._authGeneration||0)) throw {error:'账号状态已变更,请重新进入'}; if (!res || !res.token || !res.user_id) throw { error: '登录响应不完整,请重试' }; wx.removeStorageSync('accountDeleted'); wx.setStorageSync('token', res.token); wx.setStorageSync('userId', res.user_id); wx.setStorageSync('coupleId', res.couple_id || ''); wx.setStorageSync('displayId', String(res.display_id || '')); wx.setStorageSync('nickname', res.nickname || ''); wx.setStorageSync('region', res.region || ''); wx.setStorageSync('hasLoggedIn', true); require('./utils/data-cache').applyRemoteRevision(res.data_revision); this.globalData.token = res.token; this.globalData.userId = res.user_id; return res; }, // 检查当前网络状态 checkNetwork: function() { wx.getNetworkType({ success: (res) => { this.globalData.isConnected = res.networkType !== 'none'; this.globalData.networkType = res.networkType; } }); } });