Files
monisuo/flutter_monisuo/PUSH_NOTIFICATION_GUIDE.md
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

7.6 KiB
Raw Blame History

Flutter前端推送通知集成方案

技术选型

推送服务选择

考虑到国内环境,推荐使用 极光推送 (JPush)个推 (Getui)

优势:

  • 国内推送到达率高
  • 支持iOS和Android
  • 提供完善的Flutter SDK
  • 免费版本足够使用

集成步骤

1. 添加依赖

dependencies:
  # 极光推送
  jpush_flutter: ^2.5.0
  
  # 本地通知
  flutter_local_notifications: ^16.0.0
  
  # 权限管理
  permission_handler: ^11.0.0

2. Android配置

android/app/build.gradle:

android {
    defaultConfig {
        manifestPlaceholders = [
            JPUSH_PKGNAME: applicationId,
            JPUSH_APPKEY: "你的APPKEY",
            JPUSH_CHANNEL: "developer-default"
        ]
    }
}

android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.RECEIVE_USER_PRESENT"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

3. iOS配置

ios/Runner/Info.plist:

<key>UIBackgroundModes</key>
<array>
    <string>remote-notification</string>
</array>

4. 初始化推送

lib/services/push_service.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();

  Future<void> init() async {
    // 初始化极光推送
    jpush.addEventHandler(
      onReceiveNotification: (Map<String, dynamic> message) async {
        print('收到推送: $message');
        _handleNotification(message);
      },
      onOpenNotification: (Map<String, dynamic> message) async {
        print('点击推送: $message');
        _handleNotificationTap(message);
      },
    );

    jpush.setup(
      appKey: '你的APPKEY',
      channel: 'developer-default',
      production: false,
      debug: true,
    );

    // 初始化本地通知
    await _initLocalNotifications();
  }

  Future<void> _initLocalNotifications() async {
    const AndroidInitializationSettings initializationSettingsAndroid =
        AndroidInitializationSettings('@mipmap/ic_launcher');

    final DarwinInitializationSettings initializationSettingsDarwin =
        DarwinInitializationSettings();

    final InitializationSettings initializationSettings =
        InitializationSettings(
      android: initializationSettingsAndroid,
      iOS: initializationSettingsDarwin,
    );

    await localNotifications.initialize(
      initializationSettings,
      onDidReceiveNotificationResponse: (NotificationResponse response) {
        _handleLocalNotificationTap(response.payload);
      },
    );
  }

  void _handleNotification(Map<String, dynamic> message) {
    // 处理推送消息
    final title = message['title'] ?? '新消息';
    final body = message['alert'] ?? '';
    
    _showLocalNotification(title, body, message);
  }

  void _handleNotificationTap(Map<String, dynamic> message) {
    // 处理推送点击
    final type = message['extras']['type'];
    
    switch (type) {
      case 'order':
        // 跳转到订单详情
        break;
      case 'asset':
        // 跳转到资产页面
        break;
      default:
        // 跳转到首页
        break;
    }
  }

  Future<void> _showLocalNotification(
    String title,
    String body,
    Map<String, dynamic> data,
  ) async {
    const AndroidNotificationDetails androidPlatformChannelSpecifics =
        AndroidNotificationDetails(
      'monisuo_channel',
      '模拟所通知',
      channelDescription: '模拟所应用通知',
      importance: Importance.max,
      priority: Priority.high,
    );

    const NotificationDetails platformChannelSpecifics =
        NotificationDetails(android: androidPlatformChannelSpecifics);

    await localNotifications.show(
      DateTime.now().millisecondsSinceEpoch ~/ 1000,
      title,
      body,
      platformChannelSpecifics,
      payload: jsonEncode(data),
    );
  }

  void _handleLocalNotificationTap(String? payload) {
    if (payload != null) {
      final data = jsonDecode(payload);
      _handleNotificationTap(data);
    }
  }

  // 设置别名通常是用户ID
  Future<void> setAlias(String userId) async {
    jpush.setAlias(userId);
  }

  // 删除别名
  Future<void> deleteAlias() async {
    jpush.deleteAlias();
  }

  // 设置标签
  Future<void> setTags(List<String> tags) async {
    jpush.setTags(tags);
  }
}

5. 在main.dart中初始化

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 初始化推送服务
  await PushService().init();
  
  runApp(const MyApp());
}

6. 登录后设置别名

// 用户登录成功后
await PushService().setAlias(userId);

// 用户退出登录后
await PushService().deleteAlias();

后端推送接口

后端需要提供推送接口:

@PostMapping("/api/push/send")
public Result<Void> sendPush(
    @RequestParam String userId,
    @RequestParam String title,
    @RequestParam String body,
    @RequestParam(required = false) Map<String, Object> extras
) {
    // 调用极光推送API
    JPushClient jpushClient = new JPushClient(appKey, masterSecret);
    
    PushPayload payload = PushPayload.newBuilder()
        .setPlatform(Platform.all())
        .setAudience(Audience.alias(userId))
        .setNotification(Notification.alert(body))
        .setMessage(Message.content(body))
        .setOptions(Options.newBuilder().setApnsProduction(false).build())
        .build();
    
    jpushClient.sendPush(payload);
    
    return Result.success("推送成功");
}

推送场景

1. 充值审批通知

{
  "title": "充值审批通过",
  "body": "您的充值订单已审批通过,金额: 100 USDT",
  "extras": {
    "type": "order",
    "orderNo": "F20260324001"
  }
}

2. 提现审批通知

{
  "title": "提现审批完成",
  "body": "您的提现申请已处理,金额: 100 USDT",
  "extras": {
    "type": "order",
    "orderNo": "F20260324002"
  }
}

3. 资产变动通知

{
  "title": "资产变动",
  "body": "您的账户余额发生变动",
  "extras": {
    "type": "asset",
    "userId": "123"
  }
}

测试

1. 测试推送

// 在开发环境中测试
await PushService()._showLocalNotification(
  '测试标题',
  '测试内容',
  {'type': 'test'},
);

2. 测试极光推送

  • 在极光推送控制台发送测试推送
  • 检查应用是否收到推送

注意事项

  1. 权限申请: Android需要申请通知权限
  2. iOS证书: 需要配置APNs证书
  3. 测试环境: 开发时使用开发环境
  4. 生产环境: 上线前切换到生产环境
  5. 用户隐私: 首次启动时请求推送权限

成本估算

  • 极光推送免费版每月100万条推送
  • 对于中小规模应用完全够用
  • 如需更多,可以升级付费版本

集成时间: 预计2-3小时 难度: 中等 维护成本: 低