guomengjiao před 5 měsíci
rodič
revize
218906a20f

+ 7 - 0
pom.xml

@@ -539,6 +539,13 @@
                 <artifactId>jwks-rsa</artifactId>
                 <version>${jwks-rsa.version}</version>
             </dependency>
+
+            <!-- 个推 -->
+            <dependency>
+                <groupId>com.getui.push</groupId>
+                <artifactId>restful-sdk</artifactId>
+                <version>1.0.0.4</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 

+ 15 - 17
ruoyi-api/src/main/java/com/ruoyi/api/controller/user/ApiUserLoginController.java

@@ -1,28 +1,25 @@
 package com.ruoyi.api.controller.user;
 
 import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
-import cn.hutool.core.util.ObjectUtil;
 import com.ruoyi.api.controller.common.AbstractApiController;
 import com.ruoyi.common.core.domain.R;
 import com.ruoyi.common.core.validate.RegGroup;
+import com.ruoyi.common.utils.push.GeTuiUtils;
+import com.ruoyi.common.utils.redis.RedisUtils;
 import com.ruoyi.framework.web.service.ApiTokenService;
-import com.ruoyi.shop.config.domain.ShopSaleConfig;
-import com.ruoyi.shop.config.service.IShopSaleConfigService;
-import com.ruoyi.shop.coupon.service.ICouponReceiveRecordService;
 import com.ruoyi.user.domain.User;
 import com.ruoyi.user.domain.bo.ThirdLoginBo;
 import com.ruoyi.user.domain.bo.ThirdRegisterBo;
-import com.ruoyi.user.domain.bo.UserIntegralRecordBo;
 import com.ruoyi.user.domain.bo.UserLoginBo;
 import com.ruoyi.user.domain.vo.UserLoginVo;
-import com.ruoyi.user.enums.IntegralSourceType;
-import com.ruoyi.user.service.IUserIntegralRecordService;
 import com.ruoyi.user.service.IUserService;
 import com.ruoyi.weixin.domain.WxUserDto;
 import com.ruoyi.weixin.service.WxUserService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -44,6 +41,7 @@ import java.util.Map;
 @RequiredArgsConstructor
 @RestController
 @RequestMapping("/api/miniapp/user")
+@Slf4j
 public class ApiUserLoginController extends AbstractApiController {
     @Autowired
     private IUserService userService;
@@ -53,11 +51,6 @@ public class ApiUserLoginController extends AbstractApiController {
 
     private final WxUserService wxUserService;
 
-    private final IUserIntegralRecordService userIntegralRecordService;
-
-    private final IShopSaleConfigService shopSaleConfigService;
-
-    private final ICouponReceiveRecordService couponReceiveRecordService;
     /**
      * 获取小程序openId
      */
@@ -105,7 +98,7 @@ public class ApiUserLoginController extends AbstractApiController {
         User user = userService.authorization(wxUserDto);
 
         HashMap<String, Object> result = new HashMap<>();
-        result.put("token", loginSuccess(user, true));
+        result.put("token", loginSuccess(user, true, null));
         return R.ok(result);
     }
 
@@ -116,7 +109,7 @@ public class ApiUserLoginController extends AbstractApiController {
     @PostMapping("/captcha")
     public R<UserLoginVo> loginByCaptcha(@Validated @RequestBody UserLoginBo bo) {
         UserLoginVo userLoginVo = userService.loginByCaptcha(bo);
-        userLoginVo.setToken(loginSuccess(userLoginVo.getUser(), userLoginVo.getIsReg()));
+        userLoginVo.setToken(loginSuccess(userLoginVo.getUser(), userLoginVo.getIsReg(), bo.getCid()));
         return R.ok(userLoginVo);
     }
 
@@ -127,7 +120,7 @@ public class ApiUserLoginController extends AbstractApiController {
     @PostMapping("/third")
     public R<UserLoginVo> loginByThird(@Validated @RequestBody ThirdLoginBo bo) {
         UserLoginVo userLoginVo = userService.loginByThird(bo);
-        userLoginVo.setToken(loginSuccess(userLoginVo.getUser(), false));
+        userLoginVo.setToken(loginSuccess(userLoginVo.getUser(), false, bo.getCid()));
         return R.ok(userLoginVo);
     }
 
@@ -138,11 +131,16 @@ public class ApiUserLoginController extends AbstractApiController {
     @PostMapping("/third/register")
     public R<UserLoginVo> registerByThird(@Validated @RequestBody ThirdRegisterBo bo) {
         UserLoginVo userLoginVo = userService.registerByThird(bo);
-        userLoginVo.setToken(loginSuccess(userLoginVo.getUser(), userLoginVo.getIsReg()));
+        userLoginVo.setToken(loginSuccess(userLoginVo.getUser(), userLoginVo.getIsReg(), bo.getCid()));
         return R.ok(userLoginVo);
     }
 
-    private String loginSuccess(User user, Boolean isReg) {
+    private String loginSuccess(User user, Boolean isReg, String cid) {
+//        //将unipush的客户端ID存入缓存
+        log.info(String.format("个推客户端ID:%s", cid));
+        if (StringUtils.isNotEmpty(cid)) {
+            RedisUtils.setCacheObject(GeTuiUtils.getPushCid(String.valueOf(user.getId())), cid);
+        }
         return apiTokenService.generateToken(user);
     }
 }

+ 6 - 0
ruoyi-common/pom.xml

@@ -261,6 +261,12 @@
             <groupId>com.auth0</groupId>
             <artifactId>jwks-rsa</artifactId>
         </dependency>
+
+        <!-- 个推 -->
+        <dependency>
+            <groupId>com.getui.push</groupId>
+            <artifactId>restful-sdk</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 58 - 0
ruoyi-common/src/main/java/com/ruoyi/common/properties/GeTuiConfig.java

@@ -0,0 +1,58 @@
+package com.ruoyi.common.properties;
+
+import com.getui.push.v2.sdk.ApiHelper;
+import com.getui.push.v2.sdk.GtApiConfiguration;
+import com.getui.push.v2.sdk.api.PushApi;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Component;
+
+/**
+ * 个推配置
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "getui")
+public class GeTuiConfig {
+    private String baseUrl;
+
+    private String appId;
+
+    private String appKey;
+
+    private String appSecret;
+
+    private String masterSecret;
+
+//    @Bean
+    public PushApi pushApi() {
+        GtApiConfiguration apiConfiguration = new GtApiConfiguration();
+        //填写应用配置
+        apiConfiguration.setAppId(appId);
+        apiConfiguration.setAppKey(appKey);
+        apiConfiguration.setMasterSecret(masterSecret);
+        // 接口调用前缀,请查看文档: 接口调用规范 -> 接口前缀, 可不填写appId
+        apiConfiguration.setDomain(baseUrl);
+        // 实例化ApiHelper对象,用于创建接口对象
+        ApiHelper apiHelper = ApiHelper.build(apiConfiguration);
+        // 创建对象,建议复用。目前有PushApi、StatisticApi、UserApi
+        PushApi pushApi = apiHelper.creatApi(PushApi.class);
+        return pushApi;
+    }
+
+    @Bean
+    public ApiHelper apiHelper() {
+        GtApiConfiguration apiConfiguration = new GtApiConfiguration();
+        //填写应用配置
+        apiConfiguration.setAppId(appId);
+        apiConfiguration.setAppKey(appKey);
+        apiConfiguration.setMasterSecret(masterSecret);
+        // 接口调用前缀,请查看文档: 接口调用规范 -> 接口前缀, 可不填写appId
+        apiConfiguration.setDomain(baseUrl);
+        // 实例化ApiHelper对象,用于创建接口对象
+        // 创建对象,建议复用。目前有PushApi、StatisticApi、UserApi
+        return ApiHelper.build(apiConfiguration);
+    }
+
+}

+ 251 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/push/GeTuiUtils.java

@@ -0,0 +1,251 @@
+package com.ruoyi.common.utils.push;
+
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.getui.push.v2.sdk.ApiHelper;
+import com.getui.push.v2.sdk.api.PushApi;
+import com.getui.push.v2.sdk.api.UserApi;
+import com.getui.push.v2.sdk.common.ApiResult;
+import com.getui.push.v2.sdk.dto.req.Audience;
+import com.getui.push.v2.sdk.dto.req.AudienceDTO;
+import com.getui.push.v2.sdk.dto.req.Settings;
+import com.getui.push.v2.sdk.dto.req.Strategy;
+import com.getui.push.v2.sdk.dto.req.message.PushChannel;
+import com.getui.push.v2.sdk.dto.req.message.PushDTO;
+import com.getui.push.v2.sdk.dto.req.message.PushMessage;
+import com.getui.push.v2.sdk.dto.req.message.android.AndroidDTO;
+import com.getui.push.v2.sdk.dto.req.message.android.ThirdNotification;
+import com.getui.push.v2.sdk.dto.req.message.android.Ups;
+import com.getui.push.v2.sdk.dto.req.message.ios.Alert;
+import com.getui.push.v2.sdk.dto.req.message.ios.Aps;
+import com.getui.push.v2.sdk.dto.req.message.ios.IosDTO;
+import com.getui.push.v2.sdk.dto.res.CidStatusDTO;
+import com.getui.push.v2.sdk.dto.res.TaskIdDTO;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.ruoyi.common.utils.ClassConvertUtils;
+import com.ruoyi.common.utils.redis.RedisUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author wuchao
+ */
+@Slf4j
+@Component
+public class GeTuiUtils {
+
+    private PushApi pushApi;
+    private UserApi userApi;
+
+    public GeTuiUtils(ApiHelper apiHelper) {
+        pushApi = apiHelper.creatApi(PushApi.class);
+        userApi = apiHelper.creatApi(UserApi.class);
+    }
+
+    public boolean pushToSingleByCid(Msg msg) {
+        PushDTO<Audience> pushDTO = this.buildPushDTO(msg);
+        // 设置接收人信息
+        Audience audience = new Audience();
+        pushDTO.setAudience(audience);
+        audience.addCid(msg.getCid());// cid
+        // 进行cid单推
+        ApiResult<Map<String, Map<String, String>>> apiResult = pushApi.pushToSingleByCid(pushDTO);
+        //successed_offline: 离线下发(包含厂商通道下发),successed_online: 在线下发,successed_ignore: 最近90天内不活跃用户不下发
+        log.info(String.format("单推[%s],返回结果:%s", apiResult.getMsg(), apiResult.getData()));
+        return apiResult.isSuccess();
+    }
+
+    /**
+     * cid批量推
+     *
+     * @param msg
+     */
+    public boolean pushListByCid(Msg msg) {
+        //批量发送
+        AudienceDTO audienceDTO = new AudienceDTO();
+        PushDTO<Audience> pushDTO = this.buildPushDTO(msg);
+
+        ApiResult<TaskIdDTO> createApiResult = pushApi.createMsg(pushDTO);
+        if (!createApiResult.isSuccess()) {
+            log.error("推送:创建消息失败:" + createApiResult.getMsg());
+            return false;
+        }
+        // 设置接收人信息
+        Audience audience = new Audience();
+        pushDTO.setAudience(audience);
+        audience.setCid(msg.getCidList());
+
+        audienceDTO.setAudience(audience);
+        audienceDTO.setTaskid(createApiResult.getData().getTaskId());
+        audienceDTO.setAsync(true);
+
+        ApiResult<Map<String, Map<String, String>>> apiResult = pushApi.pushListByCid(audienceDTO);
+        log.info(String.format("批量推[%s],返回结果:%s", apiResult.getMsg(), apiResult.getData()));
+        return apiResult.isSuccess();
+    }
+
+    private PushDTO<Audience> buildPushDTO(Msg msg) {
+        PushDTO<Audience> pushDTO = new PushDTO<>();
+        //前端接收的消息
+        Map<String, Object> map = new HashMap<>();
+        map.put("title", msg.getTitle());
+        map.put("content", msg.getContent());
+        map.put("payload", msg.getMsg());
+        String payload = ClassConvertUtils.transformJSONToSnake(map);
+
+        // 设置推送参数
+        //requestid需要每次变化唯一
+        pushDTO.setRequestId(System.currentTimeMillis() + "");
+        pushDTO.setGroupName("wxb-group");
+
+        //配置推送条件
+        // 1: 表示该消息在用户在线时推送个推通道,用户离线时推送厂商通道;
+        // 2: 表示该消息只通过厂商通道策略下发,不考虑用户是否在线;
+        // 3: 表示该消息只通过个推通道下发,不考虑用户是否在线;
+        // 4: 表示该消息优先从厂商通道下发,若消息内容在厂商通道代发失败后会从个推通道下发。
+        Strategy strategy = new Strategy();
+        strategy.setDef(1);
+        strategy.setSt(1);
+        Settings settings = new Settings();
+        settings.setStrategy(strategy);
+        pushDTO.setSettings(settings);
+        //消息有效期,走厂商消息需要设置该值
+        settings.setTtl(3600000);
+
+        //推送苹果离线通知标题内容
+        Alert alert = new Alert();
+        //苹果离线通知栏标题
+        alert.setTitle(msg.getTitle());
+        //苹果离线通知栏内容
+        alert.setBody(msg.getContent());
+        Aps aps = new Aps();
+        //1表示静默推送(无通知栏消息),静默推送时不需要填写其他参数。
+        //苹果建议1小时最多推送3条静默消息
+        aps.setContentAvailable(0);
+        aps.setSound("default");
+        aps.setAlert(alert);
+        IosDTO iosDTO = new IosDTO();
+        iosDTO.setAps(aps);
+        iosDTO.setType("notify");
+        iosDTO.setPayload(payload);
+        PushChannel pushChannel = new PushChannel();
+        pushChannel.setIos(iosDTO);
+        //安卓离线厂商通道推送消息体
+        AndroidDTO androidDTO = new AndroidDTO();
+        Ups ups = new Ups();
+        ThirdNotification notification1 = new ThirdNotification();
+        ups.setNotification(notification1);
+        //安卓离线展示的标题
+        notification1.setTitle(msg.getTitle());
+        //安卓离线展示的内容
+        notification1.setBody(msg.getContent());
+        notification1.setClickType("intent");
+        notification1.setIntent("intent:#Intent;action=android.intent.action.oppopush;launchFlags=0x14000000;component=com.weixiaobao/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=" + msg.getTitle() + ";S.content=" + msg.getContent() + ";S.payload=" + msg.getMsg() + ";end");
+        //各厂商自有功能单项设置
+        //ups.addOption("HW", "/message/android/notification/badge/class", "io.dcloud.PandoraEntry ");
+        //ups.addOption("HW", "/message/android/notification/badge/add_num", 1);
+        //ups.addOption("HW", "/message/android/notification/importance", "HIGH");
+        //ups.addOption("VV","classification",1);
+        androidDTO.setUps(ups);
+        pushChannel.setAndroid(androidDTO);
+        pushDTO.setPushChannel(pushChannel);
+
+        // PushMessage在线走个推通道才会起作用的消息体
+        PushMessage pushMessage = new PushMessage();
+        pushDTO.setPushMessage(pushMessage);
+        pushMessage.setTransmission(payload);
+        return pushDTO;
+    }
+
+    /**
+     * 单推
+     *
+     * @param msg
+     * @return
+     */
+    public void pushByUserId(Msg msg) {
+        try {
+            if (ObjectUtils.isEmpty(msg.getUid())) {
+                log.error("用户ID不能为空");
+                return;
+            }
+            //获取Push客户端ID:6a67d611f29e233492223e2de4d7a0c7
+            String clientId = RedisUtils.getCacheObject(getPushCid(String.valueOf(msg.getUid())));
+            if (StringUtils.isEmpty(clientId)) {
+                log.error("客户端ID没有获取到");
+                return;
+            }
+            msg.setCid(clientId);
+            //单推
+            this.pushToSingleByCid(msg);
+        } catch (Exception e) {
+            log.error("单推异常");
+        }
+    }
+
+    /**
+     * 批量推
+     *
+     * @param msg
+     * @return
+     */
+    public void pushByUserIds(Msg msg) {
+        try {
+            if (CollectionUtils.isEmpty(msg.getUidList())) {
+                log.error("用户ID不能为空");
+                return;
+            }
+            List<String> cidList = new ArrayList<>();
+            msg.getUidList().forEach(u -> {
+                //获取Push客户端ID:6a67d611f29e233492223e2de4d7a0c7
+                String clientId = RedisUtils.getCacheObject(getPushCid(String.valueOf(u)));
+                //去重
+                if (!StringUtils.isEmpty(clientId) && !cidList.contains(clientId)) {
+                    cidList.add(clientId);
+                }
+            });
+
+            if (CollectionUtils.isEmpty(cidList)) {
+                log.error("客户端ID没有获取到");
+                return;
+            }
+
+            if (cidList.size() > 0) {
+                //按每200个一组分割
+                List<List<String>> parts = Lists.partition(cidList, 200);
+                parts.forEach(list -> {
+                    msg.setCidList(list);
+                    //批量推
+                    this.pushListByCid(msg);
+                });
+            }
+
+        } catch (Exception e) {
+            log.error("批量推异常");
+        }
+    }
+
+    /**
+     * 查询用户状态
+     *
+     * @param cid 客户端标识
+     */
+    public void queryUserStatus(String cid) {
+        ApiResult<Map<String, CidStatusDTO>> mapApiResult = userApi.queryUserStatus(Sets.newHashSet(cid));
+        log.info(String.format("用户状态:%s", mapApiResult));//online在线 offline离线
+    }
+
+    /**
+     * 个推客户端ID
+     **/
+    public static String getPushCid(String key) {
+        return "push:cid:" + key;
+    }
+}

+ 52 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/push/Msg.java

@@ -0,0 +1,52 @@
+package com.ruoyi.common.utils.push;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class Msg {
+
+    /**
+     * 单推
+     */
+    private String cid;
+
+    /**
+     * 批量推
+     */
+    private List<String> cidList;
+
+    /**
+     * 用户ID
+     */
+    private Long uid;
+
+    /**
+     * 用户ID
+     */
+    private List<Long> uidList;
+
+    /**
+     * 标题
+     */
+    private String title;
+
+    /**
+     * 内容
+     */
+    private String content;
+
+    /**
+     * 消息
+     */
+    private Object msg;
+
+    private int type;
+}

+ 6 - 0
ruoyi-user/src/main/java/com/ruoyi/user/domain/bo/ThirdLoginBo.java

@@ -23,4 +23,10 @@ public class ThirdLoginBo implements Serializable {
     @ApiModelProperty(value = "第三方code", required = true)
     @NotBlank(message = "第三方code不能为空")
     private String code;
+
+    /**
+     * unipush的客户端ID,这个id定位了你的手机
+     */
+    @ApiModelProperty(value = "unipush的客户端ID")
+    private String cid;
 }

+ 6 - 0
ruoyi-user/src/main/java/com/ruoyi/user/domain/bo/ThirdRegisterBo.java

@@ -37,4 +37,10 @@ public class ThirdRegisterBo implements Serializable {
     @ApiModelProperty(value = "标识码", required = true)
     @NotBlank(message = "标识码不能为空")
     private String identityCode;
+
+    /**
+     * unipush的客户端ID,这个id定位了你的手机
+     */
+    @ApiModelProperty(value = "unipush的客户端ID")
+    private String cid;
 }

+ 6 - 0
ruoyi-user/src/main/java/com/ruoyi/user/domain/bo/UserLoginBo.java

@@ -22,4 +22,10 @@ public class UserLoginBo implements Serializable {
      */
     @ApiModelProperty(value = "验证码")
     private String captcha;
+
+    /**
+     * unipush的客户端ID,这个id定位了你的手机
+     */
+    @ApiModelProperty(value = "unipush的客户端ID")
+    private String cid;
 }