findme-miniprogram-frontend/utils/notification-manager.js
2025-12-27 17:16:03 +08:00

598 lines
15 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 实时通知管理器 - 微信小程序专用
// 处理WebSocket消息、本地通知、订阅消息等
const wsManager = require('./websocket-manager-v2.js');
/**
* 微信小程序实时通知管理器
* 功能:
* 1. WebSocket消息处理和分发
* 2. 本地通知提醒
* 3. 订阅消息管理
* 4. 消息状态同步
* 5. 离线消息处理
*/
class NotificationManager {
constructor() {
this.isInitialized = false;
this.messageHandlers = new Map();
this.notificationQueue = [];
this.unreadCounts = {
messages: 0,
friends: 0,
system: 0
};
// 订阅消息模板ID需要在微信公众平台配置
this.subscribeTemplates = {
newMessage: 'template_id_for_new_message',
friendRequest: 'template_id_for_friend_request',
systemNotice: 'template_id_for_system_notice'
};
// 通知设置
this.notificationSettings = {
sound: true,
vibrate: true,
showBadge: true,
quietHours: {
enabled: false,
start: '22:00',
end: '08:00'
}
};
this.init();
}
// 初始化通知管理器
async init() {
if (this.isInitialized) return;
try {
// 加载通知设置
await this.loadNotificationSettings();
// 注册WebSocket消息处理器
this.registerWebSocketHandlers();
// 注册小程序生命周期事件
this.registerAppLifecycleEvents();
// 初始化未读计数
await this.loadUnreadCounts();
this.isInitialized = true;
} catch (error) {
console.error('❌ 通知管理器初始化失败:', error);
}
}
// 注册WebSocket消息处理器
registerWebSocketHandlers() {
// 新消息通知
wsManager.on('message', (data) => {
this.handleWebSocketMessage(data);
});
// 连接状态变化
wsManager.on('connected', () => {
this.syncOfflineMessages();
});
wsManager.on('disconnected', () => {
});
}
// 处理WebSocket消息
async handleWebSocketMessage(data) {
try {
const message = typeof data === 'string' ? JSON.parse(data) : data;
switch (message.type) {
case 'new_message':
await this.handleNewMessage(message.data);
break;
case 'friend_request':
await this.handleFriendRequest(message.data);
break;
case 'friend_accepted':
await this.handleFriendAccepted(message.data);
break;
case 'system_notice':
await this.handleSystemNotice(message.data);
break;
case 'message_read':
await this.handleMessageRead(message.data);
break;
case 'user_online':
await this.handleUserOnline(message.data);
break;
case 'user_offline':
await this.handleUserOffline(message.data);
break;
default:
}
// 触发自定义事件
this.triggerEvent('message_received', message);
} catch (error) {
console.error('❌ 处理WebSocket消息失败:', error);
}
}
// 处理新消息
async handleNewMessage(messageData) {
// 更新未读计数
this.unreadCounts.messages++;
await this.saveUnreadCounts();
// 显示通知
await this.showNotification({
type: 'new_message',
title: messageData.senderName || '新消息',
content: this.formatMessageContent(messageData),
data: messageData
});
// 触发页面更新
this.triggerEvent('new_message', messageData);
// 更新tabBar徽章
this.updateTabBarBadge();
}
// 处理好友请求
async handleFriendRequest(requestData) {
// 更新未读计数
this.unreadCounts.friends++;
await this.saveUnreadCounts();
// 显示通知
await this.showNotification({
type: 'friend_request',
title: '新的好友请求',
content: `${requestData.senderName} 请求添加您为好友`,
data: requestData
});
// 触发页面更新
this.triggerEvent('friend_request', requestData);
// 更新tabBar徽章
this.updateTabBarBadge();
}
// 处理好友请求被接受
async handleFriendAccepted(acceptData) {
// 显示通知
await this.showNotification({
type: 'friend_accepted',
title: '好友请求已接受',
content: `${acceptData.friendName} 已接受您的好友请求`,
data: acceptData
});
// 触发页面更新
this.triggerEvent('friend_accepted', acceptData);
}
// 处理系统通知
async handleSystemNotice(noticeData) {
// 更新未读计数
this.unreadCounts.system++;
await this.saveUnreadCounts();
// 显示通知
await this.showNotification({
type: 'system_notice',
title: '系统通知',
content: noticeData.content,
data: noticeData
});
// 触发页面更新
this.triggerEvent('system_notice', noticeData);
}
// 处理消息已读
async handleMessageRead(readData) {
// 更新未读计数
if (readData.count && this.unreadCounts.messages >= readData.count) {
this.unreadCounts.messages -= readData.count;
await this.saveUnreadCounts();
this.updateTabBarBadge();
}
// 触发页面更新
this.triggerEvent('message_read', readData);
}
// 显示通知
async showNotification(notification) {
try {
// 检查是否在静默时间
if (this.isInQuietHours()) {
return;
}
// 检查应用状态
const appState = this.getAppState();
if (appState === 'foreground') {
// 前台显示本地通知
await this.showLocalNotification(notification);
} else {
// 后台尝试发送订阅消息
await this.sendSubscribeMessage(notification);
}
// 播放提示音
if (this.notificationSettings.sound) {
this.playNotificationSound();
}
// 震动提醒
if (this.notificationSettings.vibrate) {
this.vibrateDevice();
}
} catch (error) {
console.error('❌ 显示通知失败:', error);
}
}
// 显示本地通知(前台)
async showLocalNotification(notification) {
// 只对已登录用户显示通知
const app = getApp();
const isLoggedIn = app?.globalData?.isLoggedIn || false;
if (!isLoggedIn) return;
// 微信小程序没有原生的本地通知API
// 这里使用自定义的通知组件或Toast
wx.showToast({
title: notification.content,
icon: 'none',
duration: 3000
});
// 如果有自定义通知组件,可以在这里调用
// this.triggerEvent('show_custom_notification', notification);
}
// 发送订阅消息(后台)
async sendSubscribeMessage(notification) {
try {
// 检查是否有订阅权限
const templateId = this.getTemplateId(notification.type);
if (!templateId) {
return;
}
// 这里需要调用后端API发送订阅消息
// 因为订阅消息需要在服务端发送
// TODO: 调用后端API发送订阅消息
} catch (error) {
console.error('❌ 发送订阅消息失败:', error);
}
}
// 格式化消息内容
formatMessageContent(messageData) {
switch (messageData.msgType) {
case 0: // 文本消息
return messageData.content;
case 1: // 图片消息
return '[图片]';
case 2: // 语音消息
return '[语音]';
case 3: // 视频消息
return '[视频]';
case 4: // 文件消息
return '[文件]';
default:
return '[消息]';
}
}
// 获取模板ID
getTemplateId(notificationType) {
return this.subscribeTemplates[notificationType];
}
// 检查是否在静默时间
isInQuietHours() {
if (!this.notificationSettings.quietHours.enabled) {
return false;
}
const now = new Date();
const currentTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
const { start, end } = this.notificationSettings.quietHours;
if (start <= end) {
return currentTime >= start && currentTime <= end;
} else {
return currentTime >= start || currentTime <= end;
}
}
// 获取应用状态
getAppState() {
// 微信小程序中可以通过页面栈判断应用状态
const pages = getCurrentPages();
return pages.length > 0 ? 'foreground' : 'background';
}
// 播放提示音
playNotificationSound() {
try {
// 微信小程序播放系统提示音
wx.createInnerAudioContext().play();
} catch (error) {
console.error('❌ 播放提示音失败:', error);
}
}
// 震动设备
vibrateDevice() {
try {
wx.vibrateShort({
type: 'medium'
});
} catch (error) {
console.error('❌ 震动失败:', error);
}
}
// 更新tabBar徽章
updateTabBarBadge() {
try {
const totalUnread = this.unreadCounts.messages + this.unreadCounts.friends + this.unreadCounts.system;
// 原生tabBar角标兜底部分机型自定义tabBar仍可用该API显示消息数
if (totalUnread > 0) {
wx.setTabBarBadge({ index: 1, text: totalUnread > 99 ? '99+' : totalUnread.toString() });
} else {
wx.removeTabBarBadge({ index: 1 });
}
// 自定义tabBar同步优先显示各自分类的数量
const pages = getCurrentPages();
const cur = pages[pages.length - 1];
if (cur && typeof cur.getTabBar === 'function') {
const tb = cur.getTabBar && cur.getTabBar();
if (tb && typeof tb.setFriendsBadge === 'function' && typeof tb.setMessagesBadge === 'function') {
tb.setFriendsBadge(this.unreadCounts.friends || 0);
tb.setMessagesBadge(this.unreadCounts.messages || 0);
}
}
} catch (error) {
console.error('❌ 更新tabBar徽章失败:', error);
}
}
// 同步离线消息
async syncOfflineMessages() {
try {
// 获取最后同步时间
const lastSyncTime = wx.getStorageSync('lastMessageSyncTime') || 0;
// 调用API获取离线消息
const apiClient = require('./api-client.js');
const response = await apiClient.request({
url: '/api/v1/messages/offline',
method: 'GET',
data: {
since: lastSyncTime
}
});
if (response.success && response.data.messages) {
// 处理离线消息
for (const message of response.data.messages) {
await this.handleNewMessage(message);
}
// 更新同步时间
wx.setStorageSync('lastMessageSyncTime', Date.now());
}
} catch (error) {
console.error('❌ 同步离线消息失败:', error);
}
}
// 注册小程序生命周期事件
registerAppLifecycleEvents() {
// 监听小程序显示
wx.onAppShow(() => {
this.syncOfflineMessages();
});
// 监听小程序隐藏
wx.onAppHide(() => {
this.saveUnreadCounts();
});
}
// 加载通知设置
async loadNotificationSettings() {
try {
const settings = wx.getStorageSync('notificationSettings');
if (settings) {
this.notificationSettings = { ...this.notificationSettings, ...settings };
}
} catch (error) {
console.error('❌ 加载通知设置失败:', error);
}
}
// 保存通知设置
async saveNotificationSettings() {
try {
wx.setStorageSync('notificationSettings', this.notificationSettings);
} catch (error) {
console.error('❌ 保存通知设置失败:', error);
}
}
// 加载未读计数
async loadUnreadCounts() {
try {
const counts = wx.getStorageSync('unreadCounts');
if (counts) {
this.unreadCounts = { ...this.unreadCounts, ...counts };
this.updateTabBarBadge();
}
} catch (error) {
console.error('❌ 加载未读计数失败:', error);
}
}
// 保存未读计数
async saveUnreadCounts() {
try {
wx.setStorageSync('unreadCounts', this.unreadCounts);
} catch (error) {
console.error('❌ 保存未读计数失败:', error);
}
}
// 清除未读计数
async clearUnreadCount(type) {
if (this.unreadCounts[type] !== undefined) {
this.unreadCounts[type] = 0;
await this.saveUnreadCounts();
this.updateTabBarBadge();
}
}
// 显式设置未读计数(用于以服务端真实数量为准时校准)
async setUnreadCount(type, value) {
if (this.unreadCounts[type] === undefined) return;
const v = Math.max(0, Number(value) || 0);
this.unreadCounts[type] = v;
await this.saveUnreadCounts();
this.updateTabBarBadge();
}
async setFriendsUnreadCount(value) { return this.setUnreadCount('friends', value); }
async setMessagesUnreadCount(value) { return this.setUnreadCount('messages', value); }
// 获取未读计数
getUnreadCount(type) {
return this.unreadCounts[type] || 0;
}
// 获取总未读计数
getTotalUnreadCount() {
return Object.values(this.unreadCounts).reduce((total, count) => total + count, 0);
}
// 更新通知设置
updateNotificationSettings(settings) {
this.notificationSettings = { ...this.notificationSettings, ...settings };
this.saveNotificationSettings();
}
// 请求订阅消息权限
async requestSubscribeMessage(templateIds) {
try {
const result = await new Promise((resolve, reject) => {
wx.requestSubscribeMessage({
tmplIds: Array.isArray(templateIds) ? templateIds : [templateIds],
success: resolve,
fail: reject
});
});
return result;
} catch (error) {
console.error('❌ 请求订阅消息权限失败:', error);
return null;
}
}
// 事件处理器
eventHandlers = new Map();
// 注册事件监听器
on(event, handler) {
if (!this.eventHandlers.has(event)) {
this.eventHandlers.set(event, []);
}
this.eventHandlers.get(event).push(handler);
}
// 移除事件监听器
off(event, handler) {
const handlers = this.eventHandlers.get(event);
if (handlers) {
const index = handlers.indexOf(handler);
if (index > -1) {
handlers.splice(index, 1);
}
}
}
// 触发事件
triggerEvent(event, data) {
const handlers = this.eventHandlers.get(event);
if (handlers) {
handlers.forEach(handler => {
try {
handler(data);
} catch (error) {
console.error(`❌ 事件处理器错误 [${event}]:`, error);
}
});
}
}
// 获取通知管理器状态
getStatus() {
return {
isInitialized: this.isInitialized,
unreadCounts: { ...this.unreadCounts },
notificationSettings: { ...this.notificationSettings },
totalUnread: this.getTotalUnreadCount()
};
}
// 重置通知管理器
reset() {
this.unreadCounts = {
messages: 0,
friends: 0,
system: 0
};
this.saveUnreadCounts();
this.updateTabBarBadge();
this.eventHandlers.clear();
}
}
// 创建全局实例
const notificationManager = new NotificationManager();
module.exports = notificationManager;