Files
monisuo/PUSH_IMPLEMENTATION_GUIDE.md

9.9 KiB
Raw Blame History

Monisuo 推送功能实现指南

概述

本文档详细说明如何在Monisuo虚拟货币交易平台中实现推送通知功能。

架构设计

1. 推送服务选择

推荐方案: 极光推送 (JPush)

理由:

  • 国内推送到达率高(>95%
  • 支持iOS和Android双平台
  • 提供完整的Flutter SDK
  • 免费版支持100万条/月
  • 提供REST API和SDK两种方式

备选方案: 个推 (Getui)

2. 系统架构

[管理员审批] → [后端服务] → [JPush服务器] → [用户手机]
     ↓              ↓              ↓              ↓
  创建推送任务   调用JPush API   推送消息      显示通知

3. 实现步骤

步骤1: 注册极光推送

  1. 访问 https://www.jiguang.cn/
  2. 注册账号并登录
  3. 创建应用
  4. 获取AppKey和Master Secret

步骤2: 后端集成

2.1 添加依赖

<dependency>
    <groupId>cn.jpush</groupId>
    <artifactId>jpush-client</artifactId>
    <version>3.5.8</version>
</dependency>

2.2 配置文件

# application.yml
jpush:
  app-key: your-app-key
  master-secret: your-master-secret
  production: false  # 开发环境使用false

2.3 推送服务类

package com.it.rattan.monisuo.service;

import cn.jiguang.common.resp.DefaultResult;
import cn.jpush.api.JPushClient;
import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.Message;
import cn.jpush.api.push.model.Notification;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class JPushService {
    
    @Value("${jpush.app-key}")
    private String appKey;
    
    @Value("${jpush.master-secret}")
    private String masterSecret;
    
    @Value("${jpush.production}")
    private boolean production;
    
    private JPushClient jPushClient;
    
    public JPushService() {
        jPushClient = new JPushClient(masterSecret, appKey);
    }
    
    /**
     * 向指定用户发送推送
     */
    public void sendToUser(String userId, String title, String content, Map<String, String> extras) {
        try {
            PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.all())
                .setAudience(Audience.alias(userId))
                .setNotification(Notification.alert(content))
                .setMessage(Message.content(content).setExtras(extras))
                .setOptions(cn.jpush.api.push.model.Options.newBuilder()
                    .setApnsProduction(production)
                    .build())
                .build();
            
            PushResult result = jPushClient.sendPush(payload);
            
            if (result.statusCode == 200) {
                System.out.println("推送成功: " + result.msgId);
            } else {
                System.err.println("推送失败: " + result.statusCode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 向所有用户发送推送
     */
    public void sendToAll(String title, String content) {
        try {
            PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.all())
                .setAudience(Audience.all())
                .setNotification(Notification.alert(content))
                .build();
            
            jPushClient.sendPush(payload);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2.4 推送控制器

package com.it.rattan.monisuo.controller;

import com.it.rattan.monisuo.common.Result;
import com.it.rattan.monisuo.service.JPushService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/push")
public class PushController {
    
    @Autowired
    private JPushService jPushService;
    
    /**
     * 发送充值审批通知
     */
    @PostMapping("/deposit/approval")
    public Result<Void> sendDepositApproval(
            @RequestParam String userId,
            @RequestParam String orderNo,
            @RequestParam String amount,
            @RequestParam boolean approved) {
        
        String title = approved ? "充值审批通过" : "充值审批驳回";
        String content = approved 
            ? "您的充值订单已审批通过,金额: " + amount + " USDT"
            : "您的充值订单已被驳回,金额: " + amount + " USDT";
        
        Map<String, String> extras = new HashMap<>();
        extras.put("type", "order");
        extras.put("orderNo", orderNo);
        extras.put("approved", String.valueOf(approved));
        
        jPushService.sendToUser(userId, title, content, extras);
        
        return Result.success("推送成功");
    }
    
    /**
     * 发送提现审批通知
     */
    @PostMapping("/withdraw/approval")
    public Result<Void> sendWithdrawApproval(
            @RequestParam String userId,
            @RequestParam String orderNo,
            @RequestParam String amount,
            @RequestParam boolean approved) {
        
        String title = approved ? "提现审批通过" : "提现审批驳回";
        String content = approved 
            ? "您的提现申请已处理,金额: " + amount + " USDT"
            : "您的提现申请已被驳回,金额: " + amount + " USDT";
        
        Map<String, String> extras = new HashMap<>();
        extras.put("type", "order");
        extras.put("orderNo", orderNo);
        extras.put("approved", String.valueOf(approved));
        
        jPushService.sendToUser(userId, title, content, extras);
        
        return Result.success("推送成功");
    }
}

步骤3: 在审批流程中集成推送

修改 FundService.approve() 方法:

@Transactional
public void approve(...) {
    // ... 原有审批逻辑 ...
    
    // 审批成功后发送推送
    if (order.getType() == 1) {
        // 充值审批
        jPushService.sendToUser(
            order.getUserId().toString(),
            status == 2 ? "充值审批通过" : "充值审批驳回",
            "订单号: " + orderNo + ", 金额: " + order.getAmount() + " USDT",
            Map.of("type", "order", "orderNo", orderNo)
        );
    } else {
        // 提现审批
        jPushService.sendToUser(
            order.getUserId().toString(),
            status == 2 ? "提现审批通过" : "提现审批驳回",
            "订单号: " + orderNo + ", 金额: " + order.getAmount() + " USDT",
            Map.of("type", "order", "orderNo", orderNo)
        );
    }
}

4. 推送场景设计

场景1: 充值审批通过

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

触发时机: 管理员点击"审批通过"按钮后

场景2: 提现审批驳回

{
  "userId": "5",
  "title": "提现审批驳回",
  "content": "您的提现申请已被驳回,金额: 50 USDT",
  "extras": {
    "type": "order",
    "orderNo": "F20260324002",
    "approved": "false",
    "reason": "余额不足"
  }
}

触发时机: 管理员点击"审批驳回"按钮后

场景3: 系统公告

{
  "title": "系统维护通知",
  "content": "系统将于今晚22:00-23:00进行维护届时暂停服务",
  "extras": {
    "type": "announcement"
  }
}

触发时机: 后台管理页面发布系统公告

5. 测试方法

5.1 本地测试

  1. 启动后端服务
cd ~/Desktop/projects/monisuo
mvn spring-boot:run
  1. 发送测试推送
curl -X POST http://localhost:5010/api/push/deposit/approval \
  -H "Content-Type: application/json" \
  -d "userId=5&orderNo=F20260324001&amount=100&approved=true"
  1. 检查推送日志
tail -f logs/app.log | grep "推送"

5.2 极光推送控制台测试

  1. 登录极光推送控制台
  2. 选择应用
  3. 点击"推送" -> "发送通知"
  4. 选择"别名"推送
  5. 输入用户别名userId
  6. 填写标题和内容
  7. 点击"立即发送"

6. 监控与统计

6.1 推送统计

在极光推送控制台可以查看:

  • 推送到达率
  • 推送打开率
  • 推送失败原因
  • 用户活跃度

6.2 日志记录

application.yml 中添加:

logging:
  level:
    com.it.rattan.monisuo.service.JPushService: DEBUG

7. 费用估算

7.1 免费版

  • 100万条推送/月
  • 适用于中小规模应用
  • 基本统计功能

7.2 付费版

  • 无限制推送
  • 高级统计功能
  • 优先技术支持

推荐: 初期使用免费版,用户量增长后再考虑升级

8. 安全性考虑

8.1 API密钥保护

  • AppKey和Master Secret不要提交到Git
  • 使用环境变量或配置中心管理

8.2 推送内容审核

  • 敏感词过滤
  • 内容长度限制

8.3 推送频率限制

  • 避免频繁推送打扰用户
  • 设置推送间隔如1分钟内不重复推送

9. 最佳实践

9.1 推送时机

  • 充值/提现审批:立即推送
  • 系统公告:定时推送
  • 资产变动:延迟推送(汇总后推送)

9.2 推送内容

  • 标题简洁明了(<20字
  • 内容包含关键信息(订单号、金额等)
  • 避免过度营销

9.3 错误处理

  • 推送失败时记录日志
  • 重大通知支持重试
  • 提供站内信作为备选

10. 后续优化

10.1 站内信系统

  • 推送失败时存入数据库
  • 用户可以在应用内查看历史消息

10.2 推送模板

  • 使用模板管理推送内容
  • 支持多语言推送

10.3 推送策略

  • 根据用户活跃度调整推送频率
  • 支持用户自定义推送设置

文档版本: V1.0 最后更新: 2026-03-24 作者: Monisuo开发团队