本文深入解析uni-app通知功能的完整实现流程,涵盖权限管理、推送集成、最佳实践等核心技术要点,帮助开发者快速构建稳定可靠的消息推送系统。
通知功能核心概念
通知类型与架构
uni-app通知系统主要包含三种类型:
- 本地通知:应用本地触发的即时通知
- 推送通知:服务端推送的远程消息
- 系统通知:系统级别的状态栏通知
graph TD
A[uni-app通知系统] --> B[本地通知]
A --> C[推送通知]
A --> D[系统通知]
B --> B1[定时提醒]
B --> B2[事件触发]
C --> C1[UniPush]
C --> C2[个推]
C --> C3[极光推送]
D --> D1[状态栏消息]
D --> D2[横幅通知]
权限体系解析
不同平台的通知权限管理存在差异:
| 平台 | 权限申请时机 | 用户可控性 | 系统限制 |
|---|---|---|---|
| iOS | 首次启动时 | 高 | 严格 |
| Android | 运行时申请 | 中 | 版本差异 |
| 微信小程序 | 订阅机制 | 低 | 平台限制 |
权限设置实现详解
1. 基础权限配置
在manifest.json中配置推送权限:
{
"app-plus": {
"distribute": {
"android": {
"permissions": [
"android.permission.RECEIVE_BOOT_COMPLETED",
"android.permission.VIBRATE",
"android.permission.WAKE_LOCK",
"android.permission.ACCESS_NETWORK_STATE",
"android.permission.INTERNET"
]
}
}
}
}2. 运行时权限申请
// 权限管理工具类
class NotificationPermission {
static async requestPermission() {
// #ifdef APP-PLUS
const platform = uni.getSystemInfoSync().platform;
if (platform === 'android') {
return await this.requestAndroidPermission();
} else if (platform === 'ios') {
return await this.requestIOSPermission();
}
// #endif
// #ifdef MP-WEIXIN
return await this.requestWechatPermission();
// #endif
}
static async requestAndroidPermission() {
const permissions = [
'android.permission.POST_NOTIFICATIONS',
'android.permission.ACCESS_NETWORK_STATE'
];
for (const permission of permissions) {
const result = await new Promise((resolve) => {
plus.android.requestPermissions([permission], (e) => {
resolve(e.granted);
});
});
if (!result) {
console.warn(`权限申请失败: ${permission}`);
return false;
}
}
return true;
}
static async requestIOSPermission() {
return new Promise((resolve) => {
plus.ios.import('UIUserNotificationSettings');
const settings = plus.ios.newObject('UIUserNotificationSettings');
plus.push.getClientInfo((info) => {
resolve(info && info.token);
});
});
}
}3. 权限状态检测
// 权限状态监控
async function checkNotificationStatus() {
// #ifdef APP-PLUS
const platform = uni.getSystemInfoSync().platform;
if (platform === 'android') {
const main = plus.android.runtimeMainActivity();
const Context = plus.android.importClass('android.content.Context');
const NotificationManager = plus.android.importClass('android.app.NotificationManager');
const nm = main.getSystemService(Context.NOTIFICATION_SERVICE);
const areEnabled = nm.areNotificationsEnabled();
return areEnabled;
}
// #endif
return true; // 默认允许
}推送集成深度实践
1. UniPush集成方案
步骤一:服务端配置
// 服务端推送接口实现
const uniPush = require('unipush-nodejs');
class PushService {
constructor() {
this.client = new uniPush({
appId: process.env.UNI_APP_ID,
appKey: process.env.UNI_APP_KEY,
appSecret: process.env.UNI_APP_SECRET,
masterSecret: process.env.UNI_MASTER_SECRET
});
}
async sendNotification(options) {
const payload = {
platform: 'all',
audience: {
registration_id: [options.clientId]
},
notification: {
title: options.title,
body: options.content,
android: {
extras: { ...options.data },
channel_id: 'default_channel'
},
ios: {
extras: { ...options.data },
sound: 'default'
}
},
options: {
time_to_live: 86400, // 24小时
apns_production: process.env.NODE_ENV === 'production'
}
};
try {
const result = await this.client.push(payload);
return { success: true, messageId: result.msg_id };
} catch (error) {
console.error('推送失败:', error);
return { success: false, error: error.message };
}
}
}步骤二:客户端集成
// 推送服务管理器
class PushNotificationManager {
constructor() {
this.clientId = null;
this.isInitialized = false;
}
async initialize() {
if (this.isInitialized) return;
// #ifdef APP-PLUS
await this.setupAppPush();
// #endif
// #ifdef MP-WEIXIN
await this.setupWechatPush();
// #endif
this.isInitialized = true;
}
async setupAppPush() {
// 获取客户端推送标识
const clientInfo = await new Promise((resolve) => {
plus.push.getClientInfo((info) => {
resolve(info);
});
});
this.clientId = clientInfo ? clientInfo.clientid : null;
// 监听推送消息
plus.push.addEventListener('receive', (msg) => {
this.handlePushMessage(msg);
});
// 监听点击事件
plus.push.addEventListener('click', (msg) => {
this.handlePushClick(msg);
});
console.log('推送服务初始化完成,客户端ID:', this.clientId);
}
handlePushMessage(message) {
const payload = this.parseMessage(message);
// 本地通知展示
uni.createPushMessage({
title: payload.title,
content: payload.content,
payload: payload.data,
success: () => {
console.log('本地通知创建成功');
},
fail: (error) => {
console.error('本地通知创建失败:', error);
}
});
}
parseMessage(message) {
let payload;
try {
// Android平台
if (message.payload) {
payload = JSON.parse(message.payload);
} else {
// iOS平台
payload = message.aps ? message.aps.alert : message;
}
} catch (e) {
payload = {
title: '新消息',
content: message.content || '您有一条新消息'
};
}
return payload;
}
handlePushClick(message) {
const payload = this.parseMessage(message);
// 路由跳转逻辑
if (payload.data && payload.data.page) {
uni.navigateTo({
url: payload.data.page,
success: () => {
console.log('页面跳转成功:', payload.data.page);
}
});
}
}
}
// 使用示例
const pushManager = new PushNotificationManager();
export default pushManager;2. 第三方推送集成
个推集成示例:
// 个推推送配置
class GeTuiPushService {
constructor() {
this.config = {
appId: process.env.GETUI_APP_ID,
appKey: process.env.GETUI_APP_KEY,
masterSecret: process.env.GETUI_MASTER_SECRET,
host: 'https://restapi.getui.com'
};
}
async pushToSingle(clientId, notification) {
const authToken = await this.getAuthToken();
const payload = {
request_id: this.generateRequestId(),
audience: {
cid: [clientId]
},
push_message: {
notification: {
title: notification.title,
body: notification.content,
click_type: 'intent',
intent: 'intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10000000;component=com.example.app/.MainActivity;end',
notify_id: Date.now()
}
}
};
const response = await fetch(`${this.config.host}/v2/${this.config.appId}/push_single`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'token': authToken
},
body: JSON.stringify(payload)
});
return await response.json();
}
async getAuthToken() {
const payload = {
sign: this.generateSign(),
timestamp: Date.now().toString(),
appkey: this.config.appKey
};
const response = await fetch(`${this.config.host}/v1/${this.config.appId}/auth`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const result = await response.json();
return result.data.token;
}
generateSign() {
const timestamp = Date.now().toString();
return crypto.createHash('sha256')
.update(this.config.appKey + timestamp + this.config.masterSecret)
.digest('hex');
}
}高级功能实现
1. 通知分类与渠道管理
// Android通知渠道管理
class NotificationChannelManager {
static createChannel(channelId, channelName, importance = 'high') {
// #ifdef APP-PLUS && ANDROID
const main = plus.android.runtimeMainActivity();
const Context = plus.android.importClass('android.content.Context');
const NotificationManager = plus.android.importClass('android.app.NotificationManager');
const NotificationChannel = plus.android.importClass('android.app.NotificationChannel');
const nm = main.getSystemService(Context.NOTIFICATION_SERVICE);
const channel = new NotificationChannel(
channelId,
channelName,
NotificationManager.IMPORTANCE_HIGH
);
channel.setDescription(`${channelName}通知渠道`);
channel.enableLights(true);
channel.setLightColor(0xFF0000);
channel.enableVibration(true);
nm.createNotificationChannel(channel);
// #endif
}
static createDefaultChannels() {
const channels = [
{ id: 'chat', name: '聊天消息', importance: 'high' },
{ id: 'system', name: '系统通知', importance: 'default' },
{ id: 'marketing', name: '营销消息', importance: 'low' }
];
channels.forEach(channel => {
this.createChannel(channel.id, channel.name, channel.importance);
});
}
}2. 通知统计与分析
// 通知数据统计
class NotificationAnalytics {
constructor() {
this.db = uni.getStorageSync('notification_stats') || {};
}
recordEvent(eventType, notificationId, extra = {}) {
const timestamp = Date.now();
const date = new Date(timestamp).toISOString().split('T')[0];
if (!this.db[date]) {
this.db[date] = {
sent: 0,
received: 0,
clicked: 0,
dismissed: 0
};
}
this.db[date][eventType]++;
// 记录详细事件
if (!this.db.events) this.db.events = [];
this.db.events.push({
type: eventType,
notificationId,
timestamp,
...extra
});
// 清理过期数据
this.cleanupOldData();
// 持久化存储
uni.setStorageSync('notification_stats', this.db);
}
getStats(days = 7) {
const stats = [];
const today = new Date();
for (let i = 0; i < days; i++) {
const date = new Date(today);
date.setDate(date.getDate() - i);
const dateStr = date.toISOString().split('T')[0];
const dayStats = this.db[dateStr] || {
sent: 0,
received: 0,
clicked: 0,
dismissed: 0
};
stats.push({
date: dateStr,
...dayStats,
clickRate: dayStats.sent > 0 ?
(dayStats.clicked / dayStats.sent * 100).toFixed(2) : 0
});
}
return stats.reverse();
}
cleanupOldData() {
const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;
if (this.db.events) {
this.db.events = this.db.events.filter(
event => event.timestamp > thirtyDaysAgo
);
}
// 清理旧日期数据
Object.keys(this.db).forEach(date => {
if (date !== 'events') {
const dateTimestamp = new Date(date).getTime();
if (dateTimestamp < thirtyDaysAgo) {
delete this.db[date];
}
}
});
}
}最佳实践与性能优化
1. 错误处理与重试机制
// 推送重试策略
class PushRetryStrategy {
constructor(maxRetries = 3, baseDelay = 1000) {
this.maxRetries = maxRetries;
this.baseDelay = baseDelay;
}
async executeWithRetry(operation, context) {
let lastError;
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
const result = await operation();
// 记录成功指标
this.recordSuccess(context, attempt);
return result;
} catch (error) {
lastError = error;
// 判断是否需要重试
if (!this.shouldRetry(error) || attempt === this.maxRetries) {
throw error;
}
// 计算延迟时间(指数退避)
const delay = this.baseDelay * Math.pow(2, attempt - 1);
console.warn(`推送失败,${delay}ms后重试 (第${attempt}次):`, error.message);
await this.sleep(delay);
}
}
throw lastError;
}
shouldRetry(error) {
// 网络错误、超时等可重试
const retryableErrors = [
'NETWORK_ERROR',
'TIMEOUT',
'SERVER_ERROR',
'RATE_LIMIT'
];
return retryableErrors.some(type => error.message.includes(type));
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
recordSuccess(context, attempts) {
console.log(`推送成功: ${context}, 尝试次数: ${attempts}`);
}
}2. 电量与性能优化
// 智能推送策略
class SmartPushStrategy {
constructor() {
this.batteryLevel = 100;
this.networkType = 'wifi';
this.lastPushTime = 0;
}
async initialize() {
// 监听电量变化
// #ifdef APP-PLUS
plus.device.getInfo({
success: (info) => {
this.batteryLevel = info.battery || 100;
}
});
// #endif
// 监听网络状态
uni.onNetworkStatusChange((res) => {
this.networkType = res.networkType;
});
}
shouldSendPush(notification) {
// 低电量时减少 推送频率
if (this.batteryLevel < 20 && !notification.priority) {
return false;
}
// 移动网络下限制大内容推送
if (this.networkType === 'cellular' && this.isLargeContent(notification)) {
return false;
}
// 防打扰时段检查
if (this.isQuietHours()) {
return notification.priority === 'high';
}
return true;
}
isLargeContent(notification) {
const contentSize = JSON.stringify(notification).length;
return contentSize > 1024; // 1KB
}
isQuietHours() {
const hour = new Date().getHours();
return hour >= 23 || hour <= 7;
}
getOptimalPushTime() {
const now = Date.now();
const timeSinceLastPush = now - this.lastPushTime;
// 最小推送间隔
const minInterval = this.batteryLevel < 30 ? 300000 : 60000; // 5分钟或1分钟
if (timeSinceLastPush < minInterval) {
return this.lastPushTime + minInterval;
}
return now;
}
}TRAE IDE 开发效率提升
在使用 TRAE IDE 开发uni-app通知功能时,可以显著提升开发效率:
智能代码补全
TRAE IDE 的智能代码补全功能可以快速生成推送相关的模板代码:
// 输入 "pushnotification" 触发智能补全
// TRAE IDE 会自动生成完整的推送处理模板
class PushNotificationHandler {
constructor() {
this.initializePushService();
this.setupEventListeners();
}
// AI 智能生成的初始化方法
async initializePushService() {
// 权限检查
const hasPermission = await this.checkPermission();
if (!hasPermission) {
await this.requestPermission();
}
// 服务注册
await this.registerPushService();
}
}实时错误检测
TRAE IDE 的实时错误检测可以在编码阶段发现潜在的推送配置问题:
// 错误示例:TRAE IDE 会立即提示
plus.push.getClientInfo((info) => {
// TRAE IDE 提示:未处理 info 为 null 的情况
console.log(info.token); // ⚠️ 潜在的空指针异常
});
// 修正后的代码
plus.push.getClientInfo((info) => {
if (info && info.token) {
console.log('客户端token:', info.token);
} else {
console.warn('获取客户端信息失败');
}
});调试工具集成
TRAE IDE 内置的调试工具可以直接查看推送消息的详细内容:
// 在TRAE IDE中,可以直接在调试控制台查看推送数据
handlePushMessage(message) {
// 使用 TRAE IDE 的调试工具查看 message 结构
console.log('推送消息详情:', JSON.stringify(message, null, 2));
// TRAE IDE 支持断点调试,可以逐步分析消息处理流程
debugger;
const payload = this.parseMessage(message);
this.displayNotification(payload);
}完整项目集成示例
1. 项目结构规划
src/
├── services/
│ ├── push/
│ │ ├── index.js # 推送管理器
│ │ ├── permission.js # 权限管理
│ │ ├── channels.js # 通知渠道
│ │ └── analytics.js # 统计分析
│ └── api/
│ └── push.js # 推送API
├── utils/
│ ├── platform.js # 平台工具
│ └── constants.js # 常量定义
└── pages/
└── settings/
└── notification.vue # 通知设置页面2. 主入口集成
// main.js
import PushManager from '@/services/push/index.js';
// 应用启动时初始化推送服务
async function initializeApp() {
// 初始化推送管理器
const pushManager = new PushManager();
try {
await pushManager.initialize();
console.log('推送服务初始化成功');
} catch (error) {
console.error('推送服务初始化失败:', error);
}
// 其他初始化逻辑...
}
// 使用 TRAE IDE 的智能提示快速完成初始化
initializeApp();3. 用户设置界面
<!-- pages/settings/notification.vue -->
<template>
<view class="notification-settings">
<view class="section">
<text class="title">通知设置</text>
<view class="setting-item">
<text>接收推送通知</text>
<switch
:checked="settings.enabled"
@change="toggleNotification"
/>
</view>
<view class="setting-item" v-if="settings.enabled">
<text>声音提醒</text>
<switch
:checked="settings.sound"
@change="updateSetting('sound', $event.detail.value)"
/>
</view>
<view class="setting-item" v-if="settings.enabled">
<text>振动提醒</text>
<switch
:checked="settings.vibration"
@change="updateSetting('vibration', $event.detail.value)"
/>
</view>
</view>
<view class="section" v-if="channelSupported">
<text class="title">通知类别</text>
<view
class="channel-item"
v-for="channel in channels"
:key="channel.id"
>
<view class="channel-info">
<text class="channel-name">{{ channel.name }}</text>
<text class="channel-desc">{{ channel.description }}</text>
</view>
<switch
:checked="channel.enabled"
@change="toggleChannel(channel.id, $event.detail.value)"
/>
</view>
</view>
</view>
</template>
<script>
import { mapState, mapActions } from 'vuex';
import NotificationService from '@/services/push/permission.js';
export default {
data() {
return {
settings: {
enabled: true,
sound: true,
vibration: true
},
channels: [],
channelSupported: false
};
},
async onLoad() {
await this.loadSettings();
// 检查是否支持通知渠道(Android 8.0+)
this.channelSupported = await this.checkChannelSupport();
if (this.channelSupported) {
await this.loadChannels();
}
},
methods: {
async loadSettings() {
try {
const settings = await NotificationService.getSettings();
this.settings = { ...this.settings, ...settings };
} catch (error) {
console.error('加载设置失败:', error);
}
},
async toggleNotification(e) {
const enabled = e.detail.value;
try {
if (enabled) {
// 申请权限
const granted = await NotificationService.requestPermission();
if (!granted) {
uni.showToast({
title: '需要通知权限才能开启',
icon: 'none'
});
this.settings.enabled = false;
return;
}
}
await NotificationService.updateSettings({ enabled });
this.settings.enabled = enabled;
uni.showToast({
title: enabled ? '通知已开启' : '通知已关闭',
icon: 'success'
});
} catch (error) {
console.error('更新通知设置失败:', error);
uni.showToast({
title: '设置失败,请重试',
icon: 'none'
});
}
},
async updateSetting(key, value) {
try {
await NotificationService.updateSettings({ [key]: value });
this.settings[key] = value;
} catch (error) {
console.error('更新设置失败:', error);
}
}
}
};
</script>测试与验证
1. 单元测试
// tests/push/permission.test.js
import { describe, it, expect, jest } from '@jest/globals';
import NotificationPermission from '@/services/push/permission.js';
describe('NotificationPermission', () => {
it('应该正确申请Android权限', async () => {
// 模拟plus.android API
global.plus = {
android: {
requestPermissions: jest.fn((permissions, callback) => {
callback({ granted: true });
})
}
};
const result = await NotificationPermission.requestAndroidPermission();
expect(result).toBe(true);
});
it('应该处理权限被拒绝的情况', async () => {
global.plus = {
android: {
requestPermissions: jest.fn((permissions, callback) => {
callback({ granted: false });
})
}
};
const result = await NotificationPermission.requestAndroidPermission();
expect(result).toBe(false);
});
});2. 集成测试
// tests/push/integration.test.js
describe('推送集成测试', () => {
it('应该完成完整的推送流程', async () => {
const pushManager = new PushManager();
// 初始化
await pushManager.initialize();
// 模拟推送消息
const mockMessage = {
title: '测试消息',
content: '这是一条测试推送',
payload: { test: true }
};
// 处理推送
const result = await pushManager.handlePushMessage(mockMessage);
expect(result.success).toBe(true);
});
});常见问题与解决方案
1. 权 限申请失败
问题:Android 13+ 设备上通知权限申请失败
解决方案:
// Android 13+ 特殊处理
async function handleAndroid13Permission() {
const sdkVersion = plus.android.getSystemInfo().sdkVersion;
if (sdkVersion >= 33) {
// Android 13+ 需要特殊处理
const result = await new Promise((resolve) => {
plus.android.requestPermissions([
'android.permission.POST_NOTIFICATIONS'
], (e) => {
if (e.deniedAlways) {
// 用户选择了"不再询问"
uni.showModal({
title: '需要通知权限',
content: '请在设置中手动开启通知权限',
success: (res) => {
if (res.confirm) {
// 跳转到应用设置页面
plus.android.runtimeMainActivity().startActivity(
plus.android.newObject('android.content.Intent',
plus.android.importClass('android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS'),
plus.android.newObject('android.net.Uri', `package:${plus.runtime.appid}`)
)
);
}
}
});
}
resolve(e.granted);
});
});
return result;
}
return true;
}2. 推送到达率低
优化策略:
- 厂商通道集成:集成华为、小米、OPPO等厂商推送
- 智能心跳机制:保持长连接活跃
- 消息去重:避免重复推送相同内容
- 分群推送:精准定位目标用户
// 厂商通道适配器
class VendorPushAdapter {
static getInstance() {
const brand = uni.getSystemInfoSync().brand;
switch (brand.toLowerCase()) {
case 'huawei':
return new HuaweiPushAdapter();
case 'xiaomi':
return new XiaomiPushAdapter();
case 'oppo':
return new OppoPushAdapter();
default:
return new DefaultPushAdapter();
}
}
}3. 通知点击无响应
问题排查:
// 调试通知点击事件
plus.push.addEventListener('click', (msg) => {
console.log('通知点击事件触发:', JSON.stringify(msg));
try {
// 检查payload格式
const payload = JSON.parse(msg.payload);
console.log('解析后的payload:', payload);
// 验证页面路由
if (payload.page) {
console.log('目标页面:', payload.page);
// 尝试跳转
uni.navigateTo({
url: payload.page,
success: () => {
console.log('页面跳转成功');
},
fail: (error) => {
console.error('页面跳转失败:', error);
// 降级处理
uni.switchTab({
url: '/pages/index/index'
});
}
});
}
} catch (error) {
console.error('通知处理失败:', error);
}
});06|实战案例:电商应用推送系统
6.1 业务场景分析
假设我们正在开发一个电商应用,需要实现以下推送场景:
- 订单状态更新通知(高优先级)
- 营销活动推送(中优先级,需用户订阅)
- 物流信息提醒(高优先级)
- 优惠券到期提醒(中优先级)
- 系统公告(低优先级)
6.2 完整实现
// ecommercePushService.js - 电商推送服务完整实现
class EcommercePushService {
constructor() {
// 用户偏好设置 - 支持细粒度的推送控制
this.userPreferences = this.loadUserPreferences();
// 推送配置
this.config = {
baseUrl: process.env.BASE_URL || 'https://api.example.com',
timeout: 10000,
retryAttempts: 3
};
// 初始化推送服务
this.initialize();
}
/**
* 初始化推送服务
*/
async initialize() {
try {
// 初始化基础推送服务
await pushService.initialize();
// 检查并申请通知权限
const hasPermission = await PermissionStrategy.smartRequestPermission('order');
if (!hasPermission) {
console.warn('⚠️ 用户未授予通知权限,部分功能可能受限');
}
console.log('✅ 电商推送服务初始化完成');
} catch (error) {
console.error('❌ 电商推送服务初始化失败:', error);
}
}
/**
* 订单状态推送 - 高优先级业务通知
*/
async sendOrderUpdate(userId, orderInfo) {
// 检查用户偏好
if (!this.userPreferences.order) {
console.log('用户关闭了订单推送');
return { success: false, reason: 'user_disabled' };
}
// 构建订单状态消息
const message = {
type: 'order',
priority: 'high',
title: '订单状态更新',
content: `您的订单${orderInfo.orderNo}已${this.getOrderStatusText(orderInfo.status)}`,
payload: {
action: 'order_detail',
orderId: orderInfo.orderId,
orderNo: orderInfo.orderNo,
status: orderInfo.status,
statusText: this.getOrderStatusText(orderInfo.status),
timestamp: Date.now()
},
timestamp: Date.now(),
badge: 1 // 角标数量
};
// 根据不同状态设置不同的声音和震动
if (['shipped', 'delivered'].includes(orderInfo.status)) {
message.sound = 'order_update.wav';
message.vibrate = true;
}
return this.sendPush(userId, message);
}
/**
* 营销活动推送 - 需用户订阅的营销通知
*/
async sendMarketingCampaign(userId, campaign) {
// 检查用户偏好和订阅状态
if (!this.userPreferences.marketing) {
console.log('用户关闭了营销推送');
return { success: false, reason: 'marketing_disabled' };
}
// 检查活动有效性
if (!this.isCampaignValid(campaign)) {
console.log('营销活动无效或已过期');
return { success: false, reason: 'invalid_campaign' };
}
const message = {
type: 'marketing',
priority: 'medium',
title: campaign.title,
content: campaign.content,
payload: {
action: 'campaign_detail',
campaignId: campaign.id,
campaignType: campaign.type,
url: campaign.url,
image: campaign.image,
startTime: campaign.startTime,
endTime: campaign.endTime,
timestamp: Date.now()
},
timestamp: Date.now(),
// 营销消息使用较温和的提醒方式
sound: 'marketing.wav',
vibrate: false
};
return this.sendPush(userId, message);
}
/**
* 物流信息提醒 - 高优先级物流通知
*/
async sendLogisticsUpdate(userId, logisticsInfo) {
if (!this.userPreferences.logistics) {
console.log('用户关闭了物流推送');
return { success: false, reason: 'logistics_disabled' };
}
const message = {
type: 'logistics',
priority: 'high',
title: '物流信息更新',
content: `您的包裹${logisticsInfo.trackingNo}已${logisticsInfo.status}`,
payload: {
action: 'logistics_detail',
orderId: logisticsInfo.orderId,
trackingNo: logisticsInfo.trackingNo,
company: logisticsInfo.company,
status: logisticsInfo.status,
location: logisticsInfo.location,
timestamp: Date.now()
},
timestamp: Date.now(),
// 物流更新使用明显的提醒
sound: 'logistics_update.wav',
vibrate: true,
badge: 1
};
return this.sendPush(userId, message);
}
/**
* 优惠券到期提醒 - 提前提醒用户
*/
async sendCouponExpiryReminder(userId, couponInfo) {
if (!this.userPreferences.coupon) {
console.log('用户关闭了优惠券推送');
return { success: false, reason: 'coupon_disabled' };
}
// 检查是否需要提醒(提前3天)
const daysUntilExpiry = this.getDaysUntilExpiry(couponInfo.expiryDate);
if (daysUntilExpiry > 3) {
console.log('优惠券到期时间还早,暂不提醒');
return { success: false, reason: 'too_early' };
}
const message = {
type: 'coupon',
priority: 'medium',
title: '优惠券即将到期',
content: `您的${couponInfo.title}优惠券将在${daysUntilExpiry}天后过期,快来使用吧!`,
payload: {
action: 'coupon_list',
couponId: couponInfo.id,
title: couponInfo.title,
discount: couponInfo.discount,
minAmount: couponInfo.minAmount,
expiryDate: couponInfo.expiryDate,
daysUntilExpiry: daysUntilExpiry,
timestamp: Date.now()
},
timestamp: Date.now(),
sound: 'coupon_reminder.wav',
vibrate: false
};
return this.sendPush(userId, message);
}
/**
* 系统公告推送 - 低优先级系统通知
*/
async sendSystemAnnouncement(userId, announcement) {
const message = {
type: 'system',
priority: 'low',
title: announcement.title,
content: announcement.content,
payload: {
action: 'announcement_detail',
announcementId: announcement.id,
type: announcement.type,
url: announcement.url,
timestamp: Date.now()
},
timestamp: Date.now(),
// 系统公告使用温和的提醒
sound: 'system.wav',
vibrate: false
};
return this.sendPush(userId, message);
}
/**
* 统一推送发送方法 - 处理通用逻辑
*/
async sendPush(userId, message) {
try {
// 检查推送服务状态
const pushStatus = pushService.getPushStatus();
if (!pushStatus.initialized) {
throw new Error('推送服务未初始化');
}
// 构建推送请求数据
const pushData = {
userId,
message: {
...message,
clientId: pushStatus.clientId,
platform: uni.getSystemInfoSync().platform
}
};
// 调用后端API发送推送
const result = await uni.request({
url: `${this.config.baseUrl}/api/push/send`,
method: 'POST',
timeout: this.config.timeout,
data: pushData,
header: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${uni.getStorageSync('token')}`
}
});
if (result.statusCode === 200 && result.data.success) {
console.log(`✅ 推送发送成功 [${message.type}]: ${userId}`, message.title);
// 记录推送历史
this.recordPushHistory(userId, message, true);
return { success: true, data: result.data };
} else {
throw new Error(result.data.message || '推送发送失败');
}
} catch (error) {
console.error(`❌ 推送发送失败 [${message.type}]: ${userId}`, error);
// 记录推送历史
this.recordPushHistory(userId, message, false, error.message);
// 如果是网络错误,加入重试队列
if (this.isNetworkError(error)) {
await pushNetworkOptimizer.addToRetryQueue(message, error);
}
return { success: false, error: error.message };
}
}
/**
* 获取订单状态文本映射
*/
getOrderStatusText(status) {
const statusMap = {
'pending': '待付款',
'paid': '已付款',
'confirmed': '已确认',
'preparing': '备货中',
'shipped': '已发货',
'in_transit': '运输中',
'delivered': '已送达',
'received': '已签收',
'completed': '已完成',
'cancelled': '已取消',
'refunding': '退款中',
'refunded': '已退款'
};
return statusMap[status] || '状态更新';
}
/**
* 检查营销活动有效性
*/
isCampaignValid(campaign) {
const now = Date.now();
// 检查开始时间
if (campaign.startTime && now < new Date(campaign.startTime).getTime()) {
return false;
}
// 检查结束时间
if (campaign.endTime && now > new Date(campaign.endTime).getTime()) {
return false;
}
// 检查活动状态
if (campaign.status !== 'active') {
return false;
}
return true;
}
/**
* 计算距离到期天数
*/
getDaysUntilExpiry(expiryDate) {
const now = new Date();
const expiry = new Date(expiryDate);
const diffTime = expiry - now;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return Math.max(0, diffDays);
}
/**
* 检查是否为网络错误
*/
isNetworkError(error) {
const networkErrorMessages = [
'network',
'timeout',
'offline',
'connection',
'ENOTFOUND',
'ECONNREFUSED'
];
const errorMessage = error.message || error.toString();
return networkErrorMessages.some(keyword =>
errorMessage.toLowerCase().includes(keyword.toLowerCase())
);
}
/**
* 记录推送历史 - 用于统计和分析
*/
recordPushHistory(userId, message, success, error = null) {
try {
const history = uni.getStorageSync('push_history') || [];
const record = {
userId,
messageId: message.payload.messageId || this.generateMessageId(message),
type: message.type,
title: message.title,
priority: message.priority,
success,
timestamp: Date.now(),
...(error && { error })
};
history.push(record);
// 只保留最近1000条记录
if (history.length > 1000) {
history.splice(0, history.length - 1000);
}
uni.setStorageSync('push_history', history);
} catch (error) {
console.error('记录推送历史失败:', error);
}
}
/**
* 生成消息ID
*/
generateMessageId(message) {
const content = JSON.stringify({
type: message.type,
title: message.title,
content: message.content,
timestamp: Math.floor(message.timestamp / 1000)
});
return this.simpleHash(content);
}
/**
* 简单的哈希函数
*/
simpleHash(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return hash.toString();
}
/**
* 加载用户偏好设置
*/
loadUserPreferences() {
const defaults = {
marketing: true, // 营销推送
order: true, // 订单推送
logistics: true, // 物流推送
coupon: true, // 优惠券推送
social: true, // 社交推送
system: true // 系统推送
};
const saved = uni.getStorageSync('push_preferences') || {};
return {
...defaults,
...saved
};
}
/**
* 更新用户偏好设置
*/
updateUserPreferences(preferences) {
this.userPreferences = {
...this.userPreferences,
...preferences
};
uni.setStorageSync('push_preferences', this.userPreferences);
console.log('✅ 用户偏好设置已更新:', this.userPreferences);
}
/**
* 获取推送统计信息
*/
getPushStats() {
try {
const history = uni.getStorageSync('push_history') || [];
const stats = {
total: history.length,
success: history.filter(h => h.success).length,
failed: history.filter(h => !h.success).length,
byType: {},
lastPush: history.length > 0 ? history[history.length - 1].timestamp : null
};
// 按类型统计
history.forEach(record => {
if (!stats.byType[record.type]) {
stats.byType[record.type] = { total: 0, success: 0, failed: 0 };
}
stats.byType[record.type].total++;
if (record.success) {
stats.byType[record.type].success++;
} else {
stats.byType[record.type].failed++;
}
});
return stats;
} catch (error) {
console.error('获取推送统计失败:', error);
return { total: 0, success: 0, failed: 0, byType: {} };
}
}
}
// 创建并导出单例实例
const ecommercePushService = new EcommercePushService();
export default ecommercePushService;6.3 使用示例
// 在业务逻辑中使用电商推送服务
// 1. 订单状态更新
await ecommercePushService.sendOrderUpdate(userId, {
orderId: '123456',
orderNo: 'ORD202312010001',
status: 'shipped'
});
// 2. 营销活动推送
await ecommercePushService.sendMarketingCampaign(userId, {
id: 'campaign_001',
title: '双12大促',
content: '全场5折起,限时抢购!',
type: 'discount',
startTime: '2023-12-01 00:00:00',
endTime: '2023-12-12 23:59:59',
url: '/pages/campaign/detail?id=campaign_001'
});
// 3. 物流信息更新
await ecommercePushService.sendLogisticsUpdate(userId, {
orderId: '123456',
trackingNo: 'SF1234567890',
company: '顺丰速运',
status: '派送中',
location: '北京市朝阳区'
});
// 4. 优惠券到期提醒
await ecommercePushService.sendCouponExpiryReminder(userId, {
id: 'coupon_001',
title: '满100减20',
discount: '20元',
minAmount: '100元',
expiryDate: '2023-12-15 23:59:59'
});
// 5. 获取推送统计
const stats = ecommercePushService.getPushStats();
console.log('推送统计:', stats);总结与展望
本文深入探讨了uni-app通知功能的完整实现方案,从权限管理到推送集成,从基础功能到高级优化,为开发者提供了全面的技术指导。通过合理的架构设计和最佳实践,可以构建出稳定可靠的通知系统。
关键要点回顾:
- 权限管理是基础:正确处理各平台的权限差异,使用智能申请策略提升用户授权率
- 推送集成需全面:支持多种推送服务,通过多通道策略确保消息可达
- 用户体验要优化:智能推送策略,避免过度打扰用户,提供个性化推送体验
- 性能监控不能少:实时监控推送效果,通过内存优化和网络优化提升系统稳定性
- 错误处理要完善:完善的错误处理和重试机制,确保消息不丢失
TRAE IDE在推送开发中的价值:
通过本文的实践可以看出,TRAE IDE在推送功能开发中发挥了重要作用:
- 智能代码补全:快速生成平台相关的条件编译代码
- 实时错误检测:及时发现潜在的权限和平 台兼容性问题
- 调试工具集成:简化推送测试流程,提升开发效率
- 性能分析:帮助识别和优化性能瓶颈
未来发展方向:
- AI智能推送:基于用户行为的个性化推送,利用机器学习优化推送时机和内容
- 富媒体通知:支持图片、视频、音频等丰富内容格式,提升用户参与度
- 跨平台统一:更完善的跨平台推送解决方案,统一各平台的推送接口
- 隐私保护加强:符合GDPR、CCPA等隐私法规要求,提供更透明的隐私控制
- 边缘计算集成:利用边缘计算降低推送延迟,提升实时性
- 5G网络优化:针对5G网络特性优化推送策略,提供更丰富的推送体验
借助 TRAE IDE 的强大功能,开发者可以更高效地实现这些高级特性,提升开发效率和应用质量。TRAE IDE的智能代码补全、实时错误检测和调试工具,让整个开发过程更加顺畅,帮助开发者专注于业务逻辑的实现。
(此内容由 AI 辅助生成,仅供参考)