Files
monisuo/flutter_monisuo/lib/services/push_service.dart
sion 8534306583 feat: 集成推送通知服务
主要功能:
1. 添加推送依赖
   - jpush_flutter: ^2.5.0 (极光推送)
   - flutter_local_notifications: ^16.0.0 (本地通知)
   - permission_handler: ^11.0.0 (权限管理)

2. 创建PushService服务类
   - 初始化极光推送SDK
   - 处理推送消息接收
   - 处理推送点击事件
   - 显示本地通知
   - 设置/删除别名(用户ID绑定)

3. 在main.dart中初始化推送服务
   - 应用启动时自动初始化
   - 登录后可设置用户别名
   - 退出登录时删除别名

4. 推送场景支持
   - 充值审批通知
   - 提现审批通知
   - 资产变动通知
   - 自定义消息推送

5. 文档
   - PUSH_NOTIFICATION_GUIDE.md: 完整的集成指南
   - 包含Android/iOS配置说明
   - 后端接口示例
   - 测试方法

技术栈:
- 极光推送 (JPush) - 国内推送到达率高
- 本地通知 - 支持前台和后台推送
- 别名机制 - 按用户ID精准推送

待完成:
- [ ] 配置极光推送APPKEY
- [ ] Android权限配置
- [ ] iOS证书配置
- [ ] 后端推送接口开发
2026-03-24 18:11:13 +08:00

322 lines
8.9 KiB
Dart
Raw 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.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:jpush_flutter/jpush_flutter.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
/// 推送通知服务
class PushService {
static final PushService _instance = PushService._internal();
factory PushService() => _instance;
PushService._internal();
final JPush jpush = JPush();
final FlutterLocalNotificationsPlugin localNotifications =
FlutterLocalNotificationsPlugin();
bool _initialized = false;
/// 初始化推送服务
Future<void> init({String? appKey}) async {
if (_initialized) return;
try {
// 初始化极光推送
jpush.addEventHandler(
onReceiveNotification: (Map<String, dynamic> message) async {
debugPrint('📱 收到推送: $message');
_handleNotification(message);
},
onOpenNotification: (Map<String, dynamic> message) async {
debugPrint('📱 点击推送: $message');
_handleNotificationTap(message);
},
onReceiveMessage: (Map<String, dynamic> message) async {
debugPrint('📱 收到自定义消息: $message');
},
);
// 如果有appKey则设置否则跳过用于开发环境
if (appKey != null && appKey.isNotEmpty) {
jpush.setup(
appKey: appKey,
channel: 'developer-default',
production: false,
debug: true,
);
}
// 初始化本地通知
await _initLocalNotifications();
_initialized = true;
debugPrint('✅ 推送服务初始化成功');
} catch (e) {
debugPrint('❌ 推送服务初始化失败: $e');
}
}
/// 初始化本地通知
Future<void> _initLocalNotifications() async {
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher');
final DarwinInitializationSettings initializationSettingsDarwin =
DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
final InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsDarwin,
);
await localNotifications.initialize(
initializationSettings,
onDidReceiveNotificationResponse: (NotificationResponse response) {
_handleLocalNotificationTap(response.payload);
},
);
// 请求通知权限Android 13+
await localNotifications
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.requestNotificationsPermission();
}
/// 处理推送消息
void _handleNotification(Map<String, dynamic> message) {
try {
final extras = message['extras'] as Map<String, dynamic>?;
final title = extras?['title']?.toString() ??
message['title']?.toString() ??
'新消息';
final body = extras?['body']?.toString() ??
message['alert']?.toString() ??
'';
_showLocalNotification(title, body, message);
} catch (e) {
debugPrint('❌ 处理推送消息失败: $e');
}
}
/// 处理推送点击
void _handleNotificationTap(Map<String, dynamic> message) {
try {
final extras = message['extras'] as Map<String, dynamic>?;
final type = extras?['type']?.toString();
debugPrint('📱 推送类型: $type');
// TODO: 根据type跳转到对应页面
switch (type) {
case 'order':
// 跳转到订单详情
final orderNo = extras?['orderNo']?.toString();
debugPrint('📱 跳转到订单: $orderNo');
break;
case 'asset':
// 跳转到资产页面
debugPrint('📱 跳转到资产页面');
break;
default:
// 默认跳转到首页
debugPrint('📱 默认跳转');
break;
}
} catch (e) {
debugPrint('❌ 处理推送点击失败: $e');
}
}
/// 显示本地通知
Future<void> _showLocalNotification(
String title,
String body,
Map<String, dynamic> data,
) async {
try {
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'monisuo_channel',
'模拟所通知',
channelDescription: '模拟所应用通知',
importance: Importance.max,
priority: Priority.high,
showWhen: true,
enableVibration: true,
enableLights: true,
);
const DarwinNotificationDetails iOSPlatformChannelSpecifics =
DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
);
const NotificationDetails platformChannelSpecifics =
NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: iOSPlatformChannelSpecifics,
);
await localNotifications.show(
DateTime.now().millisecondsSinceEpoch ~/ 1000,
title,
body,
platformChannelSpecifics,
payload: jsonEncode(data),
);
debugPrint('✅ 本地通知显示成功: $title - $body');
} catch (e) {
debugPrint('❌ 显示本地通知失败: $e');
}
}
/// 处理本地通知点击
void _handleLocalNotificationTap(String? payload) {
if (payload != null) {
try {
final data = jsonDecode(payload) as Map<String, dynamic>;
_handleNotificationTap(data);
} catch (e) {
debugPrint('❌ 处理本地通知点击失败: $e');
}
}
}
/// 设置别名通常是用户ID
Future<void> setAlias(String userId) async {
try {
if (!_initialized) {
debugPrint('⚠️ 推送服务未初始化,无法设置别名');
return;
}
jpush.setAlias(userId);
debugPrint('✅ 设置推送别名: $userId');
} catch (e) {
debugPrint('❌ 设置别名失败: $e');
}
}
/// 删除别名
Future<void> deleteAlias() async {
try {
if (!_initialized) {
debugPrint('⚠️ 推送服务未初始化,无法删除别名');
return;
}
jpush.deleteAlias();
debugPrint('✅ 删除推送别名');
} catch (e) {
debugPrint('❌ 删除别名失败: $e');
}
}
/// 设置标签
Future<void> setTags(List<String> tags) async {
try {
if (!_initialized) {
debugPrint('⚠️ 推送服务未初始化,无法设置标签');
return;
}
jpush.setTags(tags);
debugPrint('✅ 设置推送标签: $tags');
} catch (e) {
debugPrint('❌ 设置标签失败: $e');
}
}
/// 获取注册ID
Future<String?> getRegistrationId() async {
try {
if (!_initialized) {
debugPrint('⚠️ 推送服务未初始化');
return null;
}
final rid = await jpush.getRegistrationId();
debugPrint('📱 注册ID: $rid');
return rid;
} catch (e) {
debugPrint('❌ 获取注册ID失败: $e');
return null;
}
}
/// 测试本地通知(用于开发环境)
Future<void> testNotification() async {
await _showLocalNotification(
'测试通知',
'这是一条测试推送消息',
{
'type': 'test',
'title': '测试通知',
'body': '这是一条测试推送消息',
},
);
}
/// 发送充值审批通知
Future<void> sendDepositApprovalNotification({
required String userId,
required String orderNo,
required String amount,
required bool approved,
}) async {
final title = approved ? '充值审批通过' : '充值审批驳回';
final body = approved
? '您的充值订单已审批通过,金额: $amount USDT'
: '您的充值订单已被驳回,金额: $amount USDT';
await _showLocalNotification(title, body, {
'type': 'order',
'orderNo': orderNo,
'title': title,
'body': body,
});
}
/// 发送提现审批通知
Future<void> sendWithdrawApprovalNotification({
required String userId,
required String orderNo,
required String amount,
required bool approved,
}) async {
final title = approved ? '提现审批通过' : '提现审批驳回';
final body = approved
? '您的提现申请已处理,金额: $amount USDT'
: '您的提现申请已被驳回,金额: $amount USDT';
await _showLocalNotification(title, body, {
'type': 'order',
'orderNo': orderNo,
'title': title,
'body': body,
});
}
/// 发送资产变动通知
Future<void> sendAssetChangeNotification({
required String userId,
required String message,
}) async {
await _showLocalNotification('资产变动', message, {
'type': 'asset',
'userId': userId,
'title': '资产变动',
'body': message,
});
}
}