// app.js import GoEasy from './static/lib/goeasy-2.13.24.esm.min'; const ChatCore = require('./utils/chat-core'); import { ensurePhoneAuth } from './utils/phone-auth'; import { getPrimaryRole, lockPrimaryRole, migrateLegacyCenterRole, PRIMARY_DEFAULT_PAGES } from './utils/primary-role'; import { setClubId, getConfiguredClubId, buildClubHeaders, getClubId } from './utils/club-context'; import { CLUB_ID, WX_APP_ID } from './config/club-config'; import { applyMiniappAssetsFromConfig, warmupPindaoConfig } from './utils/miniapp-icons.js'; import { refreshDashouMembership } from './utils/dashou-profile.js'; const { getDefaultAvatarUrl, resolveAvatarFromStorage } = require('./utils/avatar.js'); // 三端固定首页 const roleDefaultPage = PRIMARY_DEFAULT_PAGES; App({ globalData: { hasShownPopupOnColdStart: false, apiBaseUrl: 'https://www.abas.asia/hqhd', ossImageUrl: '', morentouxiang: '', dashouguize: '', shangpinliebiao: [], shangpinzhuanqu: [], shangpinleixing: [], shangpinlunbo: [], shangpingonggao: '', lunbozhanwei: '/images/lunbozhanwei.jpg', dingdanTiaoshu: { daifuwu: 0, fuwuzhong: 0, yiwancheng: 0, yituikuan: 0 }, dashouqun: '', dashouqunid: '', guanshiqun: '', guanshiqunid: '', appId: WX_APP_ID, clubId: CLUB_ID, shangjiastatus: 0, dashoustatus: 0, guanshistatus: 0, dashouNicheng: '', zhanghaoStatus: null, dashouzhuangtai: null, yongjin: null, zonge: null, yajin: null, chenghao: '', jinfen: null, clumber: [], chengjiaoliang: null, zaixianZhuangtai: null, guanshi: { nicheng: '', uid: '', touxiang: '', gszhstatus: '', yaoqingzongshu: 0, fenyongzonge: '0.00', fenyongtixian: '0.00' }, shangjia: { sjzhzhuangtai: '', sjyue: '', fadanzong: null, tuikuanzong: null, riliushui: '', yueliushui: '' }, liaotian_liebiao: [], xshenfen: 1, goEasyConfig: null, chatEnabled: false, groupInfoMap: {}, kefuConfig: { link: '', enterpriseId: '' }, cosConfig: { bucket: '', region: 'ap-shanghai', uploadPathPrefix: 'order/' }, goEasyConnection: { status: 'disconnected', userId: '', identityType: '', lastConnectTime: 0, autoReconnect: true, reconnectAttempts: 0, maxReconnectAttempts: 5, heartbeatInterval: null, cacheKeys: { savedConnection: 'savedGoEasyConnection', userId: 'goEasyUserId', identityType: 'currentGoEasyIdentity', connectTime: 'goEasyConnectTime' }, config: { heartbeatInterval: 20000, reconnectDelay: 3000, offlineTimeout: 30000, cacheValidityHours: 12 } }, messageManager: { unreadTotal: 0, unreadMap: {}, latestMessages: [], notificationQueue: [], notificationVisible: false, lastNotificationTime: 0, notificationCooldown: 3000, tabBarIndex: 2, showTabBarBadge: true, customTabBar: true, tabBarBadgeText: 0, soundEnabled: true, vibrationEnabled: true, doNotDisturb: false, doNotDisturbStart: '22:00', doNotDisturbEnd: '08:00', notificationStyle: { position: 'top', duration: 3000, backgroundColor: '#00f7ff', textColor: '#ffffff', borderRadius: '16rpx', boxShadow: '0 10rpx 30rpx rgba(0, 247, 255, 0.3)' }, currentNotification: null, cacheKeys: { messageSettings: 'messageSettings', unreadTotal: 'messageUnreadTotal', latestMessages: 'latestMessages', notificationMuted: 'notificationMuted' } }, pageState: { currentPage: '', isInChatPage: false, isInGroupChat: false, currentChatId: '', lastPageUpdate: 0 }, eventListeners: {}, debugMode: false, currentUser: null, currentRole: 'normal', primaryRole: 'normal', /** 客服浮钮:full | mini,onLaunch 重置为 full */ kefuFloatMode: 'full', /** 本次启动内隐藏客服浮钮(不持久化,杀进程后恢复) */ kefuFloatSessionHidden: false, kefuFloatY: null, /** 从「我的」跳抢单页时高亮指定单 */ _acceptOrderHighlight: '', }, // 核心启动流程 async onLaunch() { this.globalData.kefuFloatMode = 'full'; this.globalData.kefuFloatSessionHidden = false; try { wx.removeStorageSync('kefuFloatPermanentHidden'); } catch (e) {} setClubId(getConfiguredClubId(), this); this.globalData.clubId = CLUB_ID; // ① 隐藏返回首页按钮 wx.hideHomeButton(); wx.onAppRoute((res) => { wx.hideHomeButton(); const path = (res && res.path) || ''; if (path === 'pages/manager/manager' || path === 'pages/leader/leader') { migrateLegacyCenterRole(this); lockPrimaryRole('dashou', this); } }); // ② 已登录时先恢复子客服身份,再决定主端 Tab(避免客服被当成未入驻) if (wx.getStorageSync('token')) { try { const { restoreStaffContextAfterAuth } = require('./utils/staff-api.js'); await restoreStaffContextAfterAuth(); } catch (e) {} } migrateLegacyCenterRole(this); const savedRole = getPrimaryRole(this); lockPrimaryRole(savedRole, this); // ②b 冷启动:有 token 时先问后端是否需要手机号认证 if (wx.getStorageSync('token')) { const phoneOk = await ensurePhoneAuth({ redirect: true }); if (!phoneOk) return; } // ②c 打手端冷启动:同步会员/押金等到 globalData(抢单页依赖 clumber) if (wx.getStorageSync('token') && savedRole === 'dashou') { refreshDashouMembership(this).catch(() => {}); } const targetPage = roleDefaultPage[savedRole] || '/pages/index/index'; if (savedRole !== 'normal') { setTimeout(() => { wx.reLaunch({ url: targetPage }); }, 0); } // ③ 应用本地缓存配置 const cachedConfig = this.readConfigFromStorage(); if (cachedConfig) { this.applyDynamicConfig(cachedConfig); } // ④ 初始化 this.initGoEasyWithConfig(); // 只有appkey有效时才真正初始化 ChatCore.initGlobalMessageSystem(this); this.initCurrentUser(); if (wx.getStorageSync('token') && this.emitEvent) { this.emitEvent('staffContextChanged', {}); } // ⑤ 建立连接(文赫式:全局监听,不反复 disconnect) setTimeout(() => { if (this.globalData.chatEnabled && this.ensureConnection) { this.ensureConnection(); } }, 300); // ⑥ 获取远程配置 this.fetchConfigSafely() .then(latestConfig => { this.saveConfigToStorage(latestConfig); this.applyDynamicConfig(latestConfig); warmupPindaoConfig(this); try { const { fetchGonggaoLunbo } = require('./utils/display-config'); fetchGonggaoLunbo(this, 'accept_order').catch(() => {}); } catch (e) {} this.initGoEasyWithConfig(); this.initCurrentUser(); if (this.globalData.chatEnabled && this.ensureConnection) { this.ensureConnection(); } console.log('远程配置更新完成'); }) .catch(err => { console.warn('远程配置获取失败,继续使用本地缓存', err); }); }, /** 从后台切回前台时再次向后端校验,并维持 IM 连接 */ async onShow() { if (!wx.getStorageSync('token')) return; await ensurePhoneAuth({ redirect: true }); if (this.globalData.chatEnabled && typeof this.startImWhenReady === 'function') { this.startImWhenReady(); } else if (this.globalData.chatEnabled && typeof this.ensureConnection === 'function') { this.globalData.goEasyConnection.autoReconnect = true; if (typeof this.restoreTabBarBadge === 'function') { this.restoreTabBarBadge(); } this.ensureConnection(); if (typeof this.syncConnectionStatus === 'function') { setTimeout(() => this.syncConnectionStatus(), 800); } if (typeof this.loadConversations === 'function') { setTimeout(() => this.loadConversations(), 300); } } }, // 连接管理方法 getSavedConnection() { try { const saved = wx.getStorageSync( this.globalData.goEasyConnection.cacheKeys.savedConnection ); return saved || null; } catch (e) { return null; } }, // 配置缓存读写 CONFIG_CACHE_KEY_PREFIX: 'app_dynamic_config_', getConfigCacheKey() { const cid = getClubId(this) || CLUB_ID; return this.CONFIG_CACHE_KEY_PREFIX + cid; }, readConfigFromStorage() { try { const data = wx.getStorageSync(this.getConfigCacheKey()); if (data && typeof data === 'object') { return data; } } catch (e) {} return null; }, saveConfigToStorage(config) { try { if (config && typeof config === 'object') { wx.setStorageSync(this.getConfigCacheKey(), config); } } catch (e) {} }, // 获取远程配置 fetchConfigSafely() { return new Promise((resolve) => { wx.request({ url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq', method: 'POST', header: buildClubHeaders({ 'content-type': 'application/json' }), success: (res) => { if (res.statusCode === 200 && res.data && res.data.code === 0 && res.data.data) { resolve(res.data.data); } else { console.warn('远程配置接口返回异常,使用已有配置'); resolve(this.readConfigFromStorage() || {}); } }, fail: (err) => { console.warn('远程配置网络请求失败,使用已有配置', err); resolve(this.readConfigFromStorage() || {}); } }); }); }, // 获取远程配置 fetchDynamicConfig() { return new Promise((resolve, reject) => { wx.request({ url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq', method: 'POST', header: buildClubHeaders({ 'content-type': 'application/json' }), success: (res) => { if (res.statusCode === 200 && res.data && res.data.code === 0) { resolve(res.data.data); } else { reject(new Error(res.data?.msg || '接口返回错误')); } }, fail: reject }); }); }, applyDynamicConfig(config) { if (!config || typeof config !== 'object') return; if (config.cos) { this.globalData.ossImageUrl = config.cos.ossImageUrl || ''; this.globalData.cosConfig = { bucket: config.cos.bucket || '', region: config.cos.region || 'ap-shanghai', uploadPathPrefix: config.cos.uploadPathPrefix || 'order/' }; } if (config.goEasy) { this.globalData.goEasyConfig = { host: config.goEasy.host || 'hangzhou.goeasy.io', appkey: config.goEasy.appkey || '' }; } if (config.otherConfig) { this.globalData.morentouxiang = config.otherConfig.morentouxiang || ''; this.globalData.dashouguize = config.otherConfig.dashouguize || ''; } if (config.kefu) { this.globalData.kefuConfig = { link: config.kefu.link || '', enterpriseId: config.kefu.enterpriseId || '' }; } try { applyMiniappAssetsFromConfig(this, config); } catch (e) { console.warn('miniapp assets apply failed', e); } if (this.emitEvent) { this.emitEvent('configApplied', { ossImageUrl: this.globalData.ossImageUrl, morentouxiang: this.globalData.morentouxiang, }); } }, // 仅在有效 appkey 时初始化 initGoEasyWithConfig() { const cfg = this.globalData.goEasyConfig; if (!cfg || !cfg.appkey) { console.warn('GoEasy appkey 未配置,聊天功能将在配置就绪后自动恢复'); return; } // 避免重复初始化 if (wx.goEasy && wx.GoEasy) { console.log('GoEasy 已初始化,跳过'); return; } try { wx.goEasy = GoEasy.getInstance({ host: cfg.host || 'hangzhou.goeasy.io', appkey: cfg.appkey, modules: ['im', 'pubsub'] }); wx.GoEasy = GoEasy; this.globalData.chatEnabled = true; console.log('GoEasy 初始化成功'); if (this.ensureConnection && wx.getStorageSync('uid')) { setTimeout(() => this.ensureConnection(), 100); } } catch (error) { console.error('GoEasy 初始化失败:', error); } }, initCurrentUser() { const uid = wx.getStorageSync('uid'); if (uid) { const def = getDefaultAvatarUrl(this); const avatar = resolveAvatarFromStorage(this) || def; this.globalData.currentUser = { id: uid, name: wx.getStorageSync('nicheng') || ('用户' + uid.substring(0, 6)), avatar: avatar || def, }; } }, emitEvent(eventName, data) { if (this.globalData.eventListeners[eventName]) { this.globalData.eventListeners[eventName].forEach(callback => { try { callback(data); } catch (error) { console.error(error); } }); } }, /** 切换角色并通知刷新 */ setCurrentRole(role) { if (!role) return; this.globalData.currentRole = role; wx.setStorageSync('currentRole', role); this.emitEvent('currentRoleChanged', { role }); }, on(eventName, callback) { if (!this.globalData.eventListeners[eventName]) { this.globalData.eventListeners[eventName] = []; } this.globalData.eventListeners[eventName].push(callback); }, off(eventName, callback) { if (this.globalData.eventListeners[eventName]) { const index = this.globalData.eventListeners[eventName].indexOf(callback); if (index > -1) { this.globalData.eventListeners[eventName].splice(index, 1); } } } });