import { resolveCurrentAppUserId } from './app-user.js' const PUSH_ALIAS_CACHE_KEY = 'jpushBoundAlias' let lastHandledPushMessageId = '' /** * 读取当前设备上的极光插件实例。 */ function getJPushModule() { // #ifdef APP-PLUS try { return uni.requireNativePlugin('JG-JPush') } catch (error) { return null } // #endif // #ifndef APP-PLUS return null // #endif } /** * 将当前登录用户 ID 归一化为极光 alias。 */ function resolveCurrentPushAlias() { const userId = resolveCurrentAppUserId() if (!userId) { return '' } return String(userId) } /** * 绑定当前登录用户到极光 alias,便于服务端按用户 ID 精准推送。 */ export function syncJPushAliasWithCurrentUser() { const alias = resolveCurrentPushAlias() if (!alias) { return false } const jpushModule = getJPushModule() if (!jpushModule || typeof jpushModule.setAlias !== 'function') { return false } const sequence = Date.now() const invoked = tryInvoke(() => jpushModule.setAlias({ sequence, alias })) || tryInvoke(() => jpushModule.setAlias({ sequence: String(sequence), alias })) || tryInvoke(() => jpushModule.setAlias(alias)) || tryInvoke(() => jpushModule.setAlias(alias, sequence)) if (invoked) { uni.setStorageSync(PUSH_ALIAS_CACHE_KEY, alias) } return invoked } /** * 退出登录后清理本地 alias 缓存,并尽量解除极光别名。 */ export function clearJPushAliasBinding() { uni.removeStorageSync(PUSH_ALIAS_CACHE_KEY) const jpushModule = getJPushModule() if (!jpushModule) { return false } const sequence = Date.now() return tryInvoke(() => { if (typeof jpushModule.deleteAlias === 'function') { jpushModule.deleteAlias({ sequence }) return } if (typeof jpushModule.setAlias === 'function') { jpushModule.setAlias({ sequence, alias: '' }) } }) } /** * 初始化极光推送,并在连接成功后补一次 alias 同步。 */ export function initJPushLifecycle() { const jpushModule = getJPushModule() if (!jpushModule) { return null } if (typeof jpushModule.setLoggerEnable === 'function') { jpushModule.setLoggerEnable(false) } if (typeof jpushModule.initJPushService === 'function') { jpushModule.initJPushService() } if (typeof jpushModule.addConnectEventListener === 'function') { jpushModule.addConnectEventListener((result) => { const connectEnable = !!(result && result.connectEnable) if (!connectEnable) { return } syncJPushAliasWithCurrentUser() }) } if (typeof jpushModule.addNotificationListener === 'function') { jpushModule.addNotificationListener((result) => { handleJPushNotification(result) }) } return jpushModule } /** * 安全执行插件调用,避免因参数签名差异直接中断业务。 */ function tryInvoke(executor) { try { executor() return true } catch (error) { return false } } /** * 根据极光通知内容决定是否执行页面跳转。 */ function handleJPushNotification(result) { const notificationEventType = normalizeOptionalText(result?.notificationEventType) const messageId = normalizeOptionalText(result?.messageID || result?.messageId || result?.android?.msgId) const extras = resolveJPushExtras(result) const targetUrl = resolvePushTargetUrl(extras) if (!targetUrl) { return } if (!shouldHandlePushNavigation(notificationEventType, messageId)) { return } navigateByPush(targetUrl) } /** * 从极光回调中提取扩展参数,优先读取已解析 extras,缺失时兜底解析 Android 原始 JSON。 */ function resolveJPushExtras(result) { const extras = result?.extras if (extras && typeof extras === 'object') { return extras } const notificationExtras = normalizeOptionalText(result?.android?.notificationExtras) if (!notificationExtras) { return {} } try { const parsedExtras = JSON.parse(notificationExtras) return parsedExtras && typeof parsedExtras === 'object' ? parsedExtras : {} } catch (error) { return {} } } /** * 将推送类型映射到应用内页面。 */ function resolvePushTargetUrl(extras) { const noticeTypeCode = normalizeOptionalText(extras?.noticeTypeCode) switch (noticeTypeCode) { case 'MEMBER_EXPIRE_REMIND': return '/pages/vip/vip' case 'LIVE_REMIND': return resolveLiveRemindTargetUrl(extras) case 'ACTIVITY_NOTICE': return resolveActivityNoticeTargetUrl(extras) case 'ORDER_STATUS_UPDATE': return resolveOrderStatusUpdateTargetUrl(extras) case 'HEALTH_COURSE_VIDEO': return resolveHealthCourseVideoTargetUrl(extras) default: return '' } } /** * 解析直播提醒跳转地址,直播详情页当前读取的是 id 参数。 */ function resolveLiveRemindTargetUrl(extras) { const courseId = normalizeOptionalText(extras?.courseId) if (!courseId) { return '' } return `/pages/liveDetail/liveDetail?id=${encodeURIComponent(courseId)}` } /** * 解析活动通知跳转地址,活动详情页当前读取的是 id 参数。 */ function resolveActivityNoticeTargetUrl(extras) { const activityNoticeId = normalizeOptionalText(extras?.activityNoticeId) if (!activityNoticeId) { return '' } return `/pages/activityNotice/activityNoticeDetail?id=${encodeURIComponent(activityNoticeId)}` } /** * 解析兑换订单状态变更跳转地址,明细页当前读取的是 orderId 参数。 */ function resolveOrderStatusUpdateTargetUrl(extras) { const orderId = normalizeOptionalText(extras?.orderId) if (!orderId) { return '' } return `/pages/myPatientExchangeItems/myPatientExchangeItems?orderId=${encodeURIComponent(orderId)}` } /** * 解析养生课程视频更新跳转地址,课程详情页当前读取的是 id 参数。 */ function resolveHealthCourseVideoTargetUrl(extras) { const courseId = normalizeOptionalText(extras?.courseId) if (!courseId) { return '' } return `/pages/courseDetail/courseDetail?id=${encodeURIComponent(courseId)}` } /** * 仅在点击通知或首次收到未处理通知时执行跳转,避免重复打开相同页面。 */ function shouldHandlePushNavigation(notificationEventType, messageId) { const normalizedEventType = normalizeOptionalText(notificationEventType) const canNavigate = normalizedEventType === 'notificationOpened' || normalizedEventType === 'notificationClicked' || normalizedEventType === 'notificationArrived' if (!canNavigate) { return false } if (messageId && messageId === lastHandledPushMessageId) { return false } lastHandledPushMessageId = messageId return true } /** * 根据页面栈状态执行推送跳转,避免在当前页重复打开。 */ function navigateByPush(targetUrl) { const currentPages = getCurrentPages() const currentRoute = currentPages && currentPages.length > 0 ? currentPages[currentPages.length - 1].route || '' : '' const normalizedTargetRoute = targetUrl.replace(/^\//, '').split('?')[0] if (currentRoute === normalizedTargetRoute) { return } uni.navigateTo({ url: targetUrl, fail: () => { uni.reLaunch({ url: targetUrl }) } }) } /** * 将未知值归一化为可安全使用的文本。 */ function normalizeOptionalText(value) { if (typeof value !== 'string') { return '' } return value.trim() }