322 lines
8.9 KiB
Dart
322 lines
8.9 KiB
Dart
|
|
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,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|