Browse Source

feat : 消息推送策略代码提交

bilingfeng 10 months ago
parent
commit
9299634550
38 changed files with 1340 additions and 20 deletions
  1. 63 0
      game-module/game-module-base/src/main/java/com/zanxiang/game/module/base/pojo/params/PushMsgParam.java
  2. 25 0
      game-module/game-module-base/src/main/java/com/zanxiang/game/module/base/rpc/IPushMsgRpc.java
  3. 1 1
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/ManageApplication.java
  4. 31 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/config/AsyncConfig.java
  5. 5 10
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/constant/RedisKeyConstant.java
  6. 34 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/enums/PushMsgAgentTypeEnum.java
  7. 34 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/enums/PushMsgStrategyTypeEnum.java
  8. 131 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/rpc/impl/PushMsgRpcImpl.java
  9. 14 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/ICpSendMsgLogService.java
  10. 25 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IPushMsgSendLogService.java
  11. 12 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IPushMsgSendResultService.java
  12. 21 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IPushMsgStrategyService.java
  13. 12 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IPushMsgTestRoleService.java
  14. 21 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IPushMsgTextStrategy.java
  15. 2 1
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/CpSendMsgLogServiceImpl.java
  16. 129 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/PushMsgSendLogServiceImpl.java
  17. 18 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/PushMsgSendResultServiceImpl.java
  18. 157 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/PushMsgStrategyServiceImpl.java
  19. 18 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/PushMsgTestRoleServiceImpl.java
  20. 33 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/PushMsgTextStrategyImpl.java
  21. 4 1
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/UserCardServiceImpl.java
  22. 71 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/PushMsgSendLog.java
  23. 66 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/PushMsgSendResult.java
  24. 108 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/PushMsgStrategy.java
  25. 56 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/PushMsgTestRole.java
  26. 78 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/PushMsgTextStrategy.java
  27. 12 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/PushMsgSendLogMapper.java
  28. 12 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/PushMsgSendResultMapper.java
  29. 12 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/PushMsgStrategyMapper.java
  30. 12 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/PushMsgTestRoleMapper.java
  31. 12 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/PushMsgTextStrategyMapper.java
  32. 1 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/SDKApplication.java
  33. 7 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/controller/UserController.java
  34. 6 4
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/listener/OrderPaySuccessListener.java
  35. 28 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IPushMsgService.java
  36. 4 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/LoginServiceImpl.java
  37. 61 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/PushMsgServiceImpl.java
  38. 4 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserServiceImpl.java

+ 63 - 0
game-module/game-module-base/src/main/java/com/zanxiang/game/module/base/pojo/params/PushMsgParam.java

@@ -0,0 +1,63 @@
+package com.zanxiang.game.module.base.pojo.params;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-27
+ * @description : 消息推送接口参数
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class PushMsgParam implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 充值消息
+     */
+    public static final String MSG_TYPE_RECHARGE = "MSG_TYPE_RECHARGE";
+
+    /**
+     * 等级消息
+     */
+    public static final String MSG_TYPE_LEVEL = "MSG_TYPE_LEVEL";
+
+    /**
+     * 订单id
+     */
+    private String orderId;
+
+    /**
+     * 玩家id
+     */
+    private Long userId;
+
+    /**
+     * 游戏id
+     */
+    private Long gameId;
+
+    /**
+     * 游戏服务器id
+     */
+    private String serverId;
+
+    /**
+     * 角色id
+     */
+    private String roleId;
+
+    /**
+     * 角色当前等级
+     */
+    private Long roleLevel;
+
+}

+ 25 - 0
game-module/game-module-base/src/main/java/com/zanxiang/game/module/base/rpc/IPushMsgRpc.java

@@ -0,0 +1,25 @@
+package com.zanxiang.game.module.base.rpc;
+
+import com.zanxiang.game.module.base.pojo.params.PushMsgParam;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-27
+ * @description : 消息推送
+ */
+public interface IPushMsgRpc {
+
+    /**
+     * 充值消息推送
+     *
+     * @param param : 参数
+     */
+    void pushMsgByRecharge(PushMsgParam param);
+
+    /**
+     * 等级更新消息推送
+     *
+     * @param param : 参数
+     */
+    void pushMsgByLevelUpdate(PushMsgParam param);
+}

+ 1 - 1
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/ManageApplication.java

@@ -23,7 +23,7 @@ public class ManageApplication {
 
 
     public static void main(String[] args) {
     public static void main(String[] args) {
         SpringApplication.run(ManageApplication.class, args);
         SpringApplication.run(ManageApplication.class, args);
-        System.out.println("赞象Manage服务启动成功 < (角色授权分组接口修改´3・・)ノ(._.`) \n" +
+        System.out.println("赞象Manage服务启动成功 < (消息推送策略・・)ノ(._.`) \n" +
                 "___  ___  ___   _   _   ___  _____  _____ \n" +
                 "___  ___  ___   _   _   ___  _____  _____ \n" +
                 "|  \\/  | / _ \\ | \\ | | / _ \\|  __ \\|  ___|\n" +
                 "|  \\/  | / _ \\ | \\ | | / _ \\|  __ \\|  ___|\n" +
                 "| .  . |/ /_\\ \\|  \\| |/ /_\\ \\ |  \\/| |__  \n" +
                 "| .  . |/ /_\\ \\|  \\| |/ /_\\ \\ |  \\/| |__  \n" +

+ 31 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/config/AsyncConfig.java

@@ -0,0 +1,31 @@
+package com.zanxiang.game.module.manage.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.AsyncConfigurer;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-27
+ * @description : 异步配置
+ */
+@Configuration
+@EnableAsync
+public class AsyncConfig implements AsyncConfigurer {
+
+    @Override
+    public Executor getAsyncExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        //设置核心线程数
+        executor.setCorePoolSize(15);
+        //设置最大线程数
+        executor.setMaxPoolSize(50);
+        //设置队列容量
+        executor.setQueueCapacity(20);
+        executor.initialize();
+        return executor;
+    }
+}

+ 5 - 10
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/constant/RedisKeyConstant.java

@@ -17,16 +17,6 @@ public class RedisKeyConstant {
      */
      */
     public static final String PHONE_CALL_LOCK = RedisKeyConstant.REDIS_PREFIX + "phoneCall_lock_";
     public static final String PHONE_CALL_LOCK = RedisKeyConstant.REDIS_PREFIX + "phoneCall_lock_";
 
 
-    /**
-     * 小程序支付应用任务锁
-     */
-    public static final String PAY_APP_CHECK_LOCK = RedisKeyConstant.REDIS_PREFIX + "payApplicationCheck_lock";
-
-    /**
-     * 小程序失败计数器
-     */
-    public static final String APPLET_ERROR_COUNT = RedisKeyConstant.REDIS_PREFIX + "applet_error_count_";
-
     /**
     /**
      * 客服消息redis监听器消息频道
      * 客服消息redis监听器消息频道
      */
      */
@@ -47,4 +37,9 @@ public class RedisKeyConstant {
      */
      */
     public static final String KF_MSG_USER_SESSION_UPDATE = RedisKeyConstant.REDIS_PREFIX + "kf_msg_user_session_update_";
     public static final String KF_MSG_USER_SESSION_UPDATE = RedisKeyConstant.REDIS_PREFIX + "kf_msg_user_session_update_";
 
 
+    /**
+     * 小程序支付应用任务锁
+     */
+    public static final String GAME_USER_ROLE_UP_LOCK = RedisKeyConstant.REDIS_PREFIX + "gameUserRoleUpLock_";
+
 }
 }

+ 34 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/enums/PushMsgAgentTypeEnum.java

@@ -0,0 +1,34 @@
+package com.zanxiang.game.module.manage.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-27
+ * @description : 消息推送渠道类型
+ */
+@Getter
+@AllArgsConstructor
+public enum PushMsgAgentTypeEnum {
+
+    /**
+     * 所有渠道
+     */
+    PUSH_MSG_AGENT_ALL("PUSH_MSG_AGENT_ALL"),
+
+    /**
+     * 自然量渠道
+     */
+    PUSH_MSG_AGENT_DEFAULT("PUSH_MSG_AGENT_DEFAULT"),
+
+    /**
+     * 非自然量渠道
+     */
+    PUSH_MSG_AGENT_UN_DEFAULT("PUSH_MSG_AGENT_UN_DEFAULT");
+
+    /**
+     * 消息推送渠道类型
+     */
+    private String value;
+}

+ 34 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/enums/PushMsgStrategyTypeEnum.java

@@ -0,0 +1,34 @@
+package com.zanxiang.game.module.manage.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-27
+ * @description : 消息推送策略类型
+ */
+@Getter
+@AllArgsConstructor
+public enum PushMsgStrategyTypeEnum {
+
+    /**
+     * 全局配置
+     */
+    PUSH_MSG_STRATEGY_DEFAULT("PUSH_MSG_STRATEGY_DEFAULT"),
+
+    /**
+     * 充值策略
+     */
+    PUSH_MSG_STRATEGY_RECHARGE("PUSH_MSG_STRATEGY_RECHARGE"),
+
+    /**
+     * 等级策略
+     */
+    PUSH_MSG_STRATEGY_LEVEL("PUSH_MSG_STRATEGY_RECHARGE");
+
+    /**
+     * 消息推送策略类型
+     */
+    private String value;
+}

+ 131 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/rpc/impl/PushMsgRpcImpl.java

@@ -0,0 +1,131 @@
+package com.zanxiang.game.module.manage.rpc.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.zanxiang.game.module.base.pojo.params.PushMsgParam;
+import com.zanxiang.game.module.base.rpc.IPushMsgRpc;
+import com.zanxiang.game.module.manage.constant.RedisKeyConstant;
+import com.zanxiang.game.module.manage.enums.CpSendRoleResultEnum;
+import com.zanxiang.game.module.manage.enums.PushMsgStrategyTypeEnum;
+import com.zanxiang.game.module.manage.service.IPushMsgSendResultService;
+import com.zanxiang.game.module.manage.service.IPushMsgStrategyService;
+import com.zanxiang.game.module.mybatis.entity.PushMsgSendResult;
+import com.zanxiang.game.module.mybatis.entity.PushMsgStrategy;
+import com.zanxiang.module.redis.service.IDistributedLockComponent;
+import com.zanxiang.module.util.JsonUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-24
+ * @description : 消息推送
+ */
+@Slf4j
+@DubboService
+public class PushMsgRpcImpl implements IPushMsgRpc {
+
+    @Autowired
+    private IPushMsgStrategyService pushMsgStrategyService;
+
+    @Autowired
+    private IPushMsgSendResultService pushMsgSendResultService;
+
+    @Autowired
+    private IDistributedLockComponent distributedLockComponent;
+
+    @Async
+    @Override
+    public void pushMsgByRecharge(PushMsgParam param) {
+        log.error("消息推送, 接收到充值消息, param : {}", JsonUtil.toString(param));
+        //全局策略控制是否限制
+        if (this.defaultStrategyCheck(param)) {
+            log.error("充值消息, 全局策略判断不执行, param : {}", JsonUtil.toString(param));
+            return;
+        }
+        //查询充值策略
+        PushMsgStrategy pushMsgStrategy = pushMsgStrategyService.getOne(new LambdaQueryWrapper<PushMsgStrategy>()
+                .eq(PushMsgStrategy::getStrategyType, PushMsgStrategyTypeEnum.PUSH_MSG_STRATEGY_RECHARGE.getValue())
+                .eq(PushMsgStrategy::getGameId, param.getGameId())
+                .orderByDesc(PushMsgStrategy::getCreateTime)
+                .last("limit 1"));
+        if (pushMsgStrategy == null) {
+            log.error("充值消息, 查询策略为空, param : {}", JsonUtil.toString(param));
+            return;
+        }
+        pushMsgStrategyService.strategyPushMsgRun(pushMsgStrategy, param);
+    }
+
+    @Async
+    @Override
+    public void pushMsgByLevelUpdate(PushMsgParam param) {
+        log.error("消息推送,, 接收到等级更新消息, param : {}", JsonUtil.toString(param));
+        //等级变更, 过滤战力变化, 1小时执行一次
+        String lock = RedisKeyConstant.GAME_USER_ROLE_UP_LOCK + param.getUserId() + "_" + param.getGameId() + "_"
+                + param.getServerId() + "_" + param.getRoleId() + "_" + param.getRoleLevel();
+        if (!distributedLockComponent.doLock(lock, 0L, 1L, TimeUnit.HOURS)) {
+            log.error("消息推送, 等级变化碰撞线程锁, 消息不执行 param : {}", JsonUtil.toString(param));
+            return;
+        }
+        //全局策略控制是否限制
+        if (this.defaultStrategyCheck(param)) {
+            log.error("等级更新消息, 全局策略判断不执行, param : {}", JsonUtil.toString(param));
+            return;
+        }
+        //查询等级策略
+        PushMsgStrategy pushMsgStrategy = pushMsgStrategyService.getOne(new LambdaQueryWrapper<PushMsgStrategy>()
+                .eq(PushMsgStrategy::getStrategyType, PushMsgStrategyTypeEnum.PUSH_MSG_STRATEGY_LEVEL.getValue())
+                .eq(PushMsgStrategy::getGameId, param.getGameId())
+                .le(PushMsgStrategy::getRoleLevelMin, param.getRoleLevel())
+                .ge(PushMsgStrategy::getRoleLevelMax, param.getRoleLevel())
+                .orderByDesc(PushMsgStrategy::getCreateTime)
+                .last("limit 1"));
+        if (pushMsgStrategy == null) {
+            log.error("等级更新消息, 查询策略为空, param : {}", JsonUtil.toString(param));
+            return;
+        }
+        pushMsgStrategyService.strategyPushMsgRun(pushMsgStrategy, param);
+    }
+
+    private boolean defaultStrategyCheck(PushMsgParam param) {
+        //查询全局策略
+        PushMsgStrategy defaultStrategy = pushMsgStrategyService.getOne(new LambdaQueryWrapper<PushMsgStrategy>()
+                .eq(PushMsgStrategy::getStrategyType, PushMsgStrategyTypeEnum.PUSH_MSG_STRATEGY_DEFAULT.getValue())
+                .eq(PushMsgStrategy::getGameId, param.getGameId())
+                .orderByDesc(PushMsgStrategy::getCreateTime)
+                .last("limit 1"));
+        //判断今日推送次数是否已限制
+        if (defaultStrategy.getSendCountMax() != null && pushMsgSendResultService.count(new LambdaQueryWrapper<PushMsgSendResult>()
+                .eq(PushMsgSendResult::getGameId, param.getGameId())
+                .eq(PushMsgSendResult::getServerId, param.getServerId())
+                .eq(PushMsgSendResult::getRoleId, param.getRoleId())
+                .ge(PushMsgSendResult::getCreateTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT))
+                .eq(PushMsgSendResult::getSendStatus, CpSendRoleResultEnum.CP_SEND_ROLE_RESULT_SUCCESS.getValue())
+        ) >= defaultStrategy.getSendCountMax()) {
+            return Boolean.TRUE;
+        }
+        //判断间隔时间是否已限制
+        if (defaultStrategy.getSendTimeInterval() != null) {
+            PushMsgSendResult pushMsgSendResult = pushMsgSendResultService.getOne(new LambdaQueryWrapper<PushMsgSendResult>()
+                    .select(PushMsgSendResult::getCreateTime)
+                    .eq(PushMsgSendResult::getGameId, param.getGameId())
+                    .eq(PushMsgSendResult::getServerId, param.getServerId())
+                    .eq(PushMsgSendResult::getRoleId, param.getRoleId())
+                    .eq(PushMsgSendResult::getSendStatus, CpSendRoleResultEnum.CP_SEND_ROLE_RESULT_SUCCESS.getValue())
+                    .orderByDesc(PushMsgSendResult::getCreateTime)
+                    .last("limit 1"));
+            Duration duration = Duration.between(pushMsgSendResult.getCreateTime(), LocalDateTime.now());
+            if (duration.toMinutes() <= defaultStrategy.getSendTimeInterval()) {
+                return Boolean.TRUE;
+            }
+        }
+        return Boolean.FALSE;
+    }
+}

+ 14 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/ICpSendMsgLogService.java

@@ -1,7 +1,9 @@
 package com.zanxiang.game.module.manage.service;
 package com.zanxiang.game.module.manage.service;
 
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.manage.pojo.dto.CpSendMsgResultDTO;
 import com.zanxiang.game.module.mybatis.entity.CpSendMsgLog;
 import com.zanxiang.game.module.mybatis.entity.CpSendMsgLog;
+import com.zanxiang.game.module.mybatis.entity.GameSupper;
 
 
 import java.util.List;
 import java.util.List;
 
 
@@ -21,4 +23,16 @@ public interface ICpSendMsgLogService extends IService<CpSendMsgLog> {
      * @param roleIdList : 角色列表
      * @param roleIdList : 角色列表
      */
      */
     void cpSendMsg(Long taskId, Long gameId, String text, List<String> roleIdList);
     void cpSendMsg(Long taskId, Long gameId, String text, List<String> roleIdList);
+
+    /**
+     * CP发送消息API
+     *
+     * @param gameSupper : 超父游戏
+     * @param msgId      : 消息id
+     * @param serverId   : 区服id
+     * @param text       : 文本
+     * @param roleIdList : 角色id列表
+     * @return : 返回发送结果
+     */
+    CpSendMsgResultDTO cpSendMsgApi(GameSupper gameSupper, String msgId, String serverId, String text, List<String> roleIdList);
 }
 }

+ 25 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IPushMsgSendLogService.java

@@ -0,0 +1,25 @@
+package com.zanxiang.game.module.manage.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.GameUserRole;
+import com.zanxiang.game.module.mybatis.entity.PushMsgSendLog;
+
+import java.util.List;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-27
+ * @description : 消息推送日志
+ */
+public interface IPushMsgSendLogService extends IService<PushMsgSendLog> {
+
+    /**
+     * 推送消息
+     *
+     * @param gameId           : 游戏od
+     * @param strategyId       : 策略id
+     * @param text             : 文本内容
+     * @param gameUserRoleList : 角色列表
+     */
+    void pushMsg(Long gameId, Long strategyId, String text, List<GameUserRole> gameUserRoleList);
+}

+ 12 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IPushMsgSendResultService.java

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.manage.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.PushMsgSendResult;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-24
+ * @description : 消息推送结果
+ */
+public interface IPushMsgSendResultService extends IService<PushMsgSendResult> {
+}

+ 21 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IPushMsgStrategyService.java

@@ -0,0 +1,21 @@
+package com.zanxiang.game.module.manage.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.base.pojo.params.PushMsgParam;
+import com.zanxiang.game.module.mybatis.entity.PushMsgStrategy;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-24
+ * @description : 消息推送策略
+ */
+public interface IPushMsgStrategyService extends IService<PushMsgStrategy> {
+
+    /**
+     * 策略推送消息
+     *
+     * @param pushMsgStrategy : 策略
+     * @param param           : 参数
+     */
+    void strategyPushMsgRun(PushMsgStrategy pushMsgStrategy, PushMsgParam param);
+}

+ 12 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IPushMsgTestRoleService.java

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.manage.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.PushMsgTestRole;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-24
+ * @description : 消息推送测试角色
+ */
+public interface IPushMsgTestRoleService extends IService<PushMsgTestRole> {
+}

+ 21 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IPushMsgTextStrategy.java

@@ -0,0 +1,21 @@
+package com.zanxiang.game.module.manage.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.GameUserRole;
+import com.zanxiang.game.module.mybatis.entity.PushMsgTextStrategy;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-24
+ * @description : 消息推送文本策略
+ */
+public interface IPushMsgTextStrategy extends IService<PushMsgTextStrategy> {
+
+    /**
+     * 查询角色对应的文本策略
+     *
+     * @param gameUserRole : 角色信息
+     * @return : 返回文本
+     */
+    String getStrategyText(GameUserRole gameUserRole);
+}

+ 2 - 1
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/CpSendMsgLogServiceImpl.java

@@ -146,7 +146,8 @@ public class CpSendMsgLogServiceImpl extends ServiceImpl<CpSendMsgLogMapper, CpS
                 .build();
                 .build();
     }
     }
 
 
-    private CpSendMsgResultDTO cpSendMsgApi(GameSupper gameSupper, String msgId, String serverId, String text, List<String> roleIdList) {
+    @Override
+    public CpSendMsgResultDTO cpSendMsgApi(GameSupper gameSupper, String msgId, String serverId, String text, List<String> roleIdList) {
         long time = System.currentTimeMillis() / 1000;
         long time = System.currentTimeMillis() / 1000;
         Map<String, Object> param = new HashMap<>(8);
         Map<String, Object> param = new HashMap<>(8);
         param.put("msgId", msgId);
         param.put("msgId", msgId);

+ 129 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/PushMsgSendLogServiceImpl.java

@@ -0,0 +1,129 @@
+package com.zanxiang.game.module.manage.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.manage.enums.CpSendRoleResultEnum;
+import com.zanxiang.game.module.manage.pojo.dto.CpSendMsgResultDTO;
+import com.zanxiang.game.module.manage.pojo.dto.GameDTO;
+import com.zanxiang.game.module.manage.service.*;
+import com.zanxiang.game.module.mybatis.entity.GameSupper;
+import com.zanxiang.game.module.mybatis.entity.GameUserRole;
+import com.zanxiang.game.module.mybatis.entity.PushMsgSendLog;
+import com.zanxiang.game.module.mybatis.entity.PushMsgSendResult;
+import com.zanxiang.game.module.mybatis.mapper.PushMsgSendLogMapper;
+import com.zanxiang.module.util.JsonUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-27
+ * @description : 消息推送日志
+ */
+@Slf4j
+@Service
+public class PushMsgSendLogServiceImpl extends ServiceImpl<PushMsgSendLogMapper, PushMsgSendLog> implements IPushMsgSendLogService {
+
+    @Autowired
+    private IGameService gameService;
+
+    @Autowired
+    private IGameSupperService gameSupperService;
+
+    @Autowired
+    private TransactionTemplate transactionTemplate;
+
+    @Autowired
+    private ICpSendMsgLogService cpSendMsgLogService;
+
+    @Autowired
+    private IPushMsgSendResultService pushMsgSendResultService;
+
+    @Override
+    public void pushMsg(Long gameId, Long strategyId, String text, List<GameUserRole> gameUserRoleList) {
+        //查询游戏
+        GameDTO gameDTO = gameService.getById(gameId);
+        GameSupper gameSupper = gameSupperService.getById(gameDTO.getSuperGameId());
+        //消息id
+        String msgId = UUID.randomUUID().toString().replace("-", "");
+        //保存发送记录
+        PushMsgSendLog pushMsgSendLog = this.transform(msgId, gameId, strategyId, text, gameUserRoleList.size());
+        super.save(pushMsgSendLog);
+        log.error("消息推送成功保存日志, gameId : {}, strategyId : {}, text : {}, gameUserRoleList : {}",
+                gameId, strategyId, text, JsonUtil.toString(gameUserRoleList));
+        //角色信息按区服分组
+        Map<String, List<GameUserRole>> serverIdRoleMap = gameUserRoleList.stream()
+                .collect(Collectors.groupingBy(GameUserRole::getServerId));
+        serverIdRoleMap.forEach((serverId, roleList) -> {
+
+            log.error("消息推送单组执行, gameId : {}, strategyId : {}, text : {}, serverId : {}, roleList : {}",
+                    gameId, strategyId, text, serverId, JsonUtil.toString(roleList));
+
+            List<String> serverRoleIdList = roleList.stream().map(GameUserRole::getRoleId).collect(Collectors.toList());
+            try {
+                CpSendMsgResultDTO result = cpSendMsgLogService.cpSendMsgApi(gameSupper, msgId, serverId, text, serverRoleIdList);
+                this.resultHandle(pushMsgSendLog, result, serverId, serverRoleIdList);
+            } catch (Exception e) {
+                log.error("消息推送, CP消息发送API调用异常, gameId : {}, strategyId : {}, serverId : {}, roleList : {}, e : {}",
+                        gameId, strategyId, serverId, roleList, e.getMessage(), e);
+            }
+        });
+    }
+
+    private PushMsgSendLog transform(String msgId, Long gameId, Long strategyId, String text, Integer roleCount) {
+        return PushMsgSendLog.builder()
+                .msgId(msgId)
+                .gameId(gameId)
+                .strategyId(strategyId)
+                .text(text)
+                .roleCount(roleCount)
+                .successCount(0L)
+                .failCount(0L)
+                .createTime(LocalDateTime.now())
+                .updateTime(LocalDateTime.now())
+                .build();
+    }
+
+    private void resultHandle(PushMsgSendLog pushMsgSendLog, CpSendMsgResultDTO result, String serverId, List<String> roleList) {
+        //结果列表
+        List<PushMsgSendResult> resultList = new ArrayList<>();
+        roleList.forEach(roleId -> {
+            //状态判断
+            CpSendRoleResultEnum resultEnum = result.getFailList().contains(roleId) ?
+                    CpSendRoleResultEnum.CP_SEND_ROLE_RESULT_FAIL : CpSendRoleResultEnum.CP_SEND_ROLE_RESULT_SUCCESS;
+            resultList.add(this.transform(pushMsgSendLog, resultEnum.getValue(), serverId, roleId));
+        });
+        //在同一个事物中进行表更新
+        transactionTemplate.execute(status -> {
+            pushMsgSendResultService.saveBatch(resultList);
+            super.update(new LambdaUpdateWrapper<PushMsgSendLog>()
+                    .setSql("success_count=success_count+" + result.getSucessCount())
+                    .setSql("fail_count=fail_count+" + result.getFailList().size())
+                    .set(PushMsgSendLog::getUpdateTime, LocalDateTime.now())
+                    .eq(PushMsgSendLog::getMsgId, pushMsgSendLog.getMsgId())
+            );
+            return Boolean.TRUE;
+        });
+    }
+
+    private PushMsgSendResult transform(PushMsgSendLog pushMsgSendLog, String sendStatus, String serverId, String roleId) {
+        return PushMsgSendResult.builder()
+                .msgId(pushMsgSendLog.getMsgId())
+                .gameId(pushMsgSendLog.getGameId())
+                .strategyId(pushMsgSendLog.getStrategyId())
+                .serverId(serverId)
+                .roleId(roleId)
+                .sendStatus(sendStatus)
+                .createTime(LocalDateTime.now())
+                .build();
+    }
+}

+ 18 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/PushMsgSendResultServiceImpl.java

@@ -0,0 +1,18 @@
+package com.zanxiang.game.module.manage.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.manage.service.IPushMsgSendResultService;
+import com.zanxiang.game.module.mybatis.entity.PushMsgSendResult;
+import com.zanxiang.game.module.mybatis.mapper.PushMsgSendResultMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-24
+ * @description : 消息推送结果
+ */
+@Slf4j
+@Service
+public class PushMsgSendResultServiceImpl extends ServiceImpl<PushMsgSendResultMapper, PushMsgSendResult> implements IPushMsgSendResultService {
+}

+ 157 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/PushMsgStrategyServiceImpl.java

@@ -0,0 +1,157 @@
+package com.zanxiang.game.module.manage.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.base.pojo.params.PushMsgParam;
+import com.zanxiang.game.module.manage.enums.CpSendRoleResultEnum;
+import com.zanxiang.game.module.manage.enums.PushMsgAgentTypeEnum;
+import com.zanxiang.game.module.manage.pojo.dto.UserDTO;
+import com.zanxiang.game.module.manage.service.*;
+import com.zanxiang.game.module.mybatis.entity.*;
+import com.zanxiang.game.module.mybatis.mapper.PushMsgStrategyMapper;
+import com.zanxiang.module.util.JsonUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.logging.log4j.util.Strings;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.Objects;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-24
+ * @description : 消息推送策略
+ */
+@Slf4j
+@Service
+public class PushMsgStrategyServiceImpl extends ServiceImpl<PushMsgStrategyMapper, PushMsgStrategy> implements IPushMsgStrategyService {
+
+    @Autowired
+    private IUserService userService;
+
+    @Autowired
+    private IRoleOperateService roleOperateService;
+
+    @Autowired
+    private IGameUserRoleService gameUserRoleService;
+
+    @Autowired
+    private IPushMsgTextStrategy pushMsgTextStrategy;
+
+    @Autowired
+    private IPushMsgTestRoleService pushMsgTestRoleService;
+
+    @Autowired
+    private IPushMsgSendLogService pushMsgSendLogService;
+
+    @Autowired
+    private IPushMsgSendResultService pushMsgSendResultService;
+
+    @Override
+    public void strategyPushMsgRun(PushMsgStrategy pushMsgStrategy, PushMsgParam param) {
+        log.error("消息推送执行策略, pushMsgStrategyId : {}, param : {}", pushMsgStrategy.getId(), JsonUtil.toString(param));
+        //查询玩家
+        UserDTO userDTO = userService.getById(param.getUserId());
+        //不存在等级策略, 或者玩家为空
+        if (userDTO == null) {
+            log.error("消息推送玩家信息为空, pushMsgStrategyId : {}, param : {}", pushMsgStrategy.getId(), JsonUtil.toString(param));
+            return;
+        }
+        //策略开启测试, 查询是否在测试名单中, 不在测试名单中, 直接结束, 不执行
+        if (Objects.equals(pushMsgStrategy.getRoleTestSwitch(), Boolean.TRUE)
+                && pushMsgTestRoleService.count(new LambdaQueryWrapper<PushMsgTestRole>()
+                .eq(PushMsgTestRole::getGameId, param.getGameId())
+                .eq(PushMsgTestRole::getServerId, param.getServerId())
+                .eq(PushMsgTestRole::getRoleId, param.getRoleId())
+        ) <= 0) {
+            log.error("消息推送开启测试无测试人员, pushMsgStrategyId : {}, param : {}", pushMsgStrategy.getId(), JsonUtil.toString(param));
+            return;
+        }
+        //判断是否满足策略执行条件
+        if (!this.strategyCheck(pushMsgStrategy, param, userDTO)) {
+            log.error("消息推送不满足策略条件, pushMsgStrategyId : {}, param : {}", pushMsgStrategy.getId(), JsonUtil.toString(param));
+            return;
+        }
+        //查询策略执行角色
+        GameUserRole gameUserRole = gameUserRoleService.getOne(new LambdaQueryWrapper<GameUserRole>()
+                .eq(GameUserRole::getGameId, param.getGameId())
+                .eq(GameUserRole::getUserId, param.getUserId())
+                .eq(GameUserRole::getRoleId, param.getRoleId()));
+        //查询策略执行文本
+        String strategyText = pushMsgTextStrategy.getStrategyText(gameUserRole);
+        if (Strings.isBlank(strategyText)) {
+            log.error("消息推送文本内容为空, pushMsgStrategyId : {}, param : {}", pushMsgStrategy.getId(), JsonUtil.toString(param));
+            return;
+        }
+        //执行发送
+        pushMsgSendLogService.pushMsg(param.getGameId(), pushMsgStrategy.getId(), strategyText, Collections.singletonList(gameUserRole));
+    }
+
+    private boolean strategyCheck(PushMsgStrategy pushMsgStrategy, PushMsgParam param, UserDTO userDTO) {
+        //渠道限制不为空, 且不是全渠道
+        if (pushMsgStrategy.getAgentType() != null
+                && !Objects.equals(pushMsgStrategy.getAgentType(), PushMsgAgentTypeEnum.PUSH_MSG_AGENT_ALL.getValue())) {
+            //只允许自然量
+            if (Objects.equals(pushMsgStrategy.getAgentType(), PushMsgAgentTypeEnum.PUSH_MSG_AGENT_DEFAULT.getValue())
+                    && !Objects.equals(userDTO.getAgentId(), Agent.DEFAULT_AGENT)) {
+                return Boolean.FALSE;
+            }
+            //只允许非自然量
+            if (Objects.equals(pushMsgStrategy.getAgentType(), PushMsgAgentTypeEnum.PUSH_MSG_AGENT_UN_DEFAULT.getValue())
+                    && Objects.equals(userDTO.getAgentId(), Agent.DEFAULT_AGENT)) {
+                return Boolean.FALSE;
+            }
+        }
+        //判断注册时间, 是否在最大限制之内
+        if (pushMsgStrategy.getRegDayMax() != null
+                && userDTO.getCreateTime().plusDays(pushMsgStrategy.getRegDayMax()).isBefore(LocalDateTime.now())) {
+            return Boolean.FALSE;
+        }
+        //首个角色限制
+        if (Objects.equals(pushMsgStrategy.getFirstRoleSwitch(), Boolean.TRUE)) {
+            GameUserRole gameUserRole = gameUserRoleService.getOne(new LambdaQueryWrapper<GameUserRole>()
+                    .eq(GameUserRole::getUserId, param.getUserId())
+                    .eq(GameUserRole::getGameId, param.getGameId())
+                    .orderByAsc(GameUserRole::getCreateTime)
+                    .last("limit 1"));
+            if (gameUserRole == null || !Objects.equals(gameUserRole.getRoleId(), param.getRoleId())) {
+                return Boolean.FALSE;
+            }
+        }
+        //判断最小充值金额
+        if (pushMsgStrategy.getRechargeMoneyMin() != null) {
+            GameUserRole gameUserRole = gameUserRoleService.getOne(new LambdaQueryWrapper<GameUserRole>()
+                    .eq(GameUserRole::getUserId, param.getUserId())
+                    .eq(GameUserRole::getGameId, param.getGameId())
+                    .eq(GameUserRole::getRoleId, param.getRoleId()));
+            if (gameUserRole.getRechargeMoney() == null
+                    || gameUserRole.getRechargeMoney().longValue() < pushMsgStrategy.getRechargeMoneyMin()) {
+                return Boolean.FALSE;
+            }
+        }
+        //判断是否已加企微
+        if (Objects.equals(pushMsgStrategy.getExcludeAddWechat(), Boolean.TRUE)) {
+            RoleOperate roleOperate = roleOperateService.getOne(new LambdaQueryWrapper<RoleOperate>()
+                    .eq(RoleOperate::getUserId, param.getUserId())
+                    .eq(RoleOperate::getGameId, param.getGameId())
+                    .eq(RoleOperate::getServerId, param.getServerId())
+                    .eq(RoleOperate::getRoleId, param.getRoleId()));
+            if (Objects.equals(roleOperate.getIsAddCorpWechat(), 1)) {
+                return Boolean.FALSE;
+            }
+        }
+        //单策略推送次数
+        if (pushMsgStrategy.getSendCountMax() != null && pushMsgSendResultService.count(new LambdaQueryWrapper<PushMsgSendResult>()
+                .eq(PushMsgSendResult::getGameId, param.getGameId())
+                .eq(PushMsgSendResult::getStrategyId, pushMsgStrategy.getId())
+                .eq(PushMsgSendResult::getServerId, param.getServerId())
+                .eq(PushMsgSendResult::getRoleId, param.getRoleId())
+                .eq(PushMsgSendResult::getSendStatus, CpSendRoleResultEnum.CP_SEND_ROLE_RESULT_SUCCESS.getValue())
+        ) >= pushMsgStrategy.getSendCountMax()) {
+            return Boolean.FALSE;
+        }
+        return Boolean.TRUE;
+    }
+}

+ 18 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/PushMsgTestRoleServiceImpl.java

@@ -0,0 +1,18 @@
+package com.zanxiang.game.module.manage.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.manage.service.IPushMsgTestRoleService;
+import com.zanxiang.game.module.mybatis.entity.PushMsgTestRole;
+import com.zanxiang.game.module.mybatis.mapper.PushMsgTestRoleMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-24
+ * @description : 消息推送测试角色
+ */
+@Slf4j
+@Service
+public class PushMsgTestRoleServiceImpl extends ServiceImpl<PushMsgTestRoleMapper, PushMsgTestRole> implements IPushMsgTestRoleService {
+}

+ 33 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/PushMsgTextStrategyImpl.java

@@ -0,0 +1,33 @@
+package com.zanxiang.game.module.manage.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.manage.service.IPushMsgTextStrategy;
+import com.zanxiang.game.module.mybatis.entity.GameUserRole;
+import com.zanxiang.game.module.mybatis.entity.PushMsgTextStrategy;
+import com.zanxiang.game.module.mybatis.mapper.PushMsgTextStrategyMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-24
+ * @description : 消息推送文本策略
+ */
+@Slf4j
+@Service
+public class PushMsgTextStrategyImpl extends ServiceImpl<PushMsgTextStrategyMapper, PushMsgTextStrategy> implements IPushMsgTextStrategy {
+
+    @Override
+    public String getStrategyText(GameUserRole gameUserRole) {
+        PushMsgTextStrategy pushMsgTextStrategy = super.getOne(new LambdaQueryWrapper<PushMsgTextStrategy>()
+                .eq(PushMsgTextStrategy::getGameId, gameUserRole.getGameId())
+                .le(PushMsgTextStrategy::getRoleLevelMin, gameUserRole.getRoleLevel())
+                .ge(PushMsgTextStrategy::getRoleLevelMax, gameUserRole.getRoleLevel())
+                .le(PushMsgTextStrategy::getRechargeMoneyMin, gameUserRole.getRechargeMoney())
+                .ge(PushMsgTextStrategy::getRechargeMoneyMax, gameUserRole.getRechargeMoney())
+                .orderByDesc(PushMsgTextStrategy::getCreateTime)
+                .last("limit 1"));
+        return pushMsgTextStrategy == null ? null : pushMsgTextStrategy.getText();
+    }
+}

+ 4 - 1
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/UserCardServiceImpl.java

@@ -104,7 +104,10 @@ public class UserCardServiceImpl extends ServiceImpl<UserCardMapper, UserCard> i
     @Override
     @Override
     public UserCardVO getByUserId(Long userId) {
     public UserCardVO getByUserId(Long userId) {
         UserCard userCard = this.getOne(new LambdaQueryWrapper<UserCard>()
         UserCard userCard = this.getOne(new LambdaQueryWrapper<UserCard>()
-                .eq(UserCard::getUserId, userId));
+                .eq(UserCard::getUserId, userId)
+                .orderByDesc(UserCard::getCreateTime)
+                .last("limit 1")
+        );
         return this.toVo(userCard);
         return this.toVo(userCard);
     }
     }
 
 

+ 71 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/PushMsgSendLog.java

@@ -0,0 +1,71 @@
+package com.zanxiang.game.module.mybatis.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-27
+ * @description : 玩家消息推送执行日志
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+@Builder
+@TableName("t_push_msg_send_log")
+public class PushMsgSendLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "msg_id", type = IdType.INPUT)
+    private String msgId;
+
+    /**
+     * 游戏id
+     */
+    private Long gameId;
+
+    /**
+     * 策略id
+     */
+    private Long strategyId;
+
+    /**
+     * 文本内容
+     */
+    private String text;
+
+    /**
+     * 角色执行人数
+     */
+    private Integer roleCount;
+
+    /**
+     * 成功人数
+     */
+    private Long successCount;
+
+    /**
+     * 失败人数
+     */
+    private Long failCount;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+}

+ 66 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/PushMsgSendResult.java

@@ -0,0 +1,66 @@
+package com.zanxiang.game.module.mybatis.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-23
+ * @description : 玩家消息推送执行结果
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+@Builder
+@TableName("t_push_msg_send_result")
+public class PushMsgSendResult implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 消息id
+     */
+    private String msgId;
+
+    /**
+     * 游戏id
+     */
+    private Long gameId;
+
+    /**
+     * 策略id
+     */
+    private Long strategyId;
+
+    /**
+     * 区服id
+     */
+    private String serverId;
+
+    /**
+     * 角色id
+     */
+    private String roleId;
+
+    /**
+     * 发送状态
+     */
+    private String sendStatus;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+}

+ 108 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/PushMsgStrategy.java

@@ -0,0 +1,108 @@
+package com.zanxiang.game.module.mybatis.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-23
+ * @description : 玩家消息推送执行策略
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+@Builder
+@TableName("t_push_msg_strategy")
+public class PushMsgStrategy implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 游戏id
+     */
+    private Long gameId;
+
+    /**
+     * 策略类型
+     */
+    private String strategyType;
+
+    /**
+     * 渠道类型
+     */
+    private String agentType;
+
+    /**
+     * 最大注册天数
+     */
+    private Integer regDayMax;
+
+    /**
+     * 最大发送次数
+     */
+    private Integer sendCountMax;
+
+    /**
+     * 最小充值金额
+     */
+    private Long rechargeMoneyMin;
+
+    /**
+     * 发送时间间隔(分钟)
+     */
+    private Integer sendTimeInterval;
+
+    /**
+     * 是否排除已加企微
+     */
+    private Boolean excludeAddWechat;
+
+    /**
+     * 角色最小等级
+     */
+    private Integer roleLevelMin;
+
+    /**
+     * 角色最大等级
+     */
+    private Integer roleLevelMax;
+
+    /**
+     * 首角色限定开关
+     */
+    private Boolean firstRoleSwitch;
+
+    /**
+     * 是否开启角色测试
+     */
+    private Boolean roleTestSwitch;
+
+    /**
+     * 1:删除,0: 正常
+     */
+    @TableLogic
+    private Integer isDelete;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+}

+ 56 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/PushMsgTestRole.java

@@ -0,0 +1,56 @@
+package com.zanxiang.game.module.mybatis.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-23
+ * @description : 玩家消息推送测试角色
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+@Builder
+@TableName("t_push_msg_test_role")
+public class PushMsgTestRole implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 游戏id
+     */
+    private Long gameId;
+
+    /**
+     * 区服id
+     */
+    private String serverId;
+
+    /**
+     * 角色id
+     */
+    private String roleId;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+}

+ 78 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/PushMsgTextStrategy.java

@@ -0,0 +1,78 @@
+package com.zanxiang.game.module.mybatis.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-23
+ * @description : 玩家消息推送话术策略
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+@Builder
+@TableName("t_push_msg_text_strategy")
+public class PushMsgTextStrategy implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 游戏id
+     */
+    private Long gameId;
+
+    /**
+     * 最小充值金额
+     */
+    private Long rechargeMoneyMin;
+
+    /**
+     * 最大充值金额
+     */
+    private Long rechargeMoneyMax;
+
+    /**
+     * 角色最小等级
+     */
+    private Integer roleLevelMin;
+
+    /**
+     * 角色最大等级
+     */
+    private Integer roleLevelMax;
+
+    /**
+     * 话术文本
+     */
+    private String text;
+
+    /**
+     * 1:删除,0: 正常
+     */
+    @TableLogic
+    private Integer isDelete;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+}

+ 12 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/PushMsgSendLogMapper.java

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.mybatis.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.module.mybatis.entity.PushMsgSendLog;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-27
+ * @description : ${description}
+ */
+public interface PushMsgSendLogMapper extends BaseMapper<PushMsgSendLog> {
+}

+ 12 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/PushMsgSendResultMapper.java

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.mybatis.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.module.mybatis.entity.PushMsgSendResult;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-23
+ * @description : ${description}
+ */
+public interface PushMsgSendResultMapper extends BaseMapper<PushMsgSendResult> {
+}

+ 12 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/PushMsgStrategyMapper.java

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.mybatis.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.module.mybatis.entity.PushMsgStrategy;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-23
+ * @description : ${description}
+ */
+public interface PushMsgStrategyMapper extends BaseMapper<PushMsgStrategy> {
+}

+ 12 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/PushMsgTestRoleMapper.java

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.mybatis.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.module.mybatis.entity.PushMsgTestRole;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-23
+ * @description : ${description}
+ */
+public interface PushMsgTestRoleMapper extends BaseMapper<PushMsgTestRole> {
+}

+ 12 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/PushMsgTextStrategyMapper.java

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.mybatis.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.module.mybatis.entity.PushMsgTextStrategy;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-23
+ * @description : ${description}
+ */
+public interface PushMsgTextStrategyMapper extends BaseMapper<PushMsgTextStrategy> {
+}

+ 1 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/SDKApplication.java

@@ -23,7 +23,7 @@ public class SDKApplication {
 
 
     public static void main(String[] args) {
     public static void main(String[] args) {
         SpringApplication.run(SDKApplication.class, args);
         SpringApplication.run(SDKApplication.class, args);
-        System.out.println("赞象SDK服务启动成功 <头条投放虚拟游戏兼容H5和APP> ( ´・・)ノ(._.`) \n" +
+        System.out.println("赞象SDK服务启动成功 <消息推送策略> ( ´・・)ノ(._.`) \n" +
                 " ___________ _   __\n" +
                 " ___________ _   __\n" +
                 "/  ___|  _  \\ | / /\n" +
                 "/  ___|  _  \\ | / /\n" +
                 "\\ `--.| | | | |/ / \n" +
                 "\\ `--.| | | | |/ / \n" +

+ 7 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/controller/UserController.java

@@ -46,6 +46,9 @@ public class UserController {
     @Autowired
     @Autowired
     private IUserShareService userShareService;
     private IUserShareService userShareService;
 
 
+    @Autowired
+    private IPushMsgService pushMsgService;
+
     @Autowired
     @Autowired
     private IGameShellLogService gameShellLogService;
     private IGameShellLogService gameShellLogService;
 
 
@@ -118,6 +121,10 @@ public class UserController {
         if (Objects.equals(param.getDataType(), DataTypeEnum.TYPE_CREATE_ROLE.getDateType())) {
         if (Objects.equals(param.getDataType(), DataTypeEnum.TYPE_CREATE_ROLE.getDateType())) {
             gameUserRoleService.callListenIn(param, userData);
             gameUserRoleService.callListenIn(param, userData);
         }
         }
+        //等级提升, 消息推送
+        if (Objects.equals(param.getDataType(), DataTypeEnum.TYPE_LEVEL_UP.getDateType())) {
+            pushMsgService.pushMsgByLevelUpdate(param, userData);
+        }
         return ResultVO.ok(result);
         return ResultVO.ok(result);
     }
     }
 
 

+ 6 - 4
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/listener/OrderPaySuccessListener.java

@@ -3,10 +3,7 @@ package com.zanxiang.game.module.sdk.listener;
 import com.zanxiang.game.module.base.pojo.enums.PayDeviceEnum;
 import com.zanxiang.game.module.base.pojo.enums.PayDeviceEnum;
 import com.zanxiang.game.module.sdk.enums.KafkaEventTrackEnum;
 import com.zanxiang.game.module.sdk.enums.KafkaEventTrackEnum;
 import com.zanxiang.game.module.sdk.pojo.dto.PlatformOrderDTO;
 import com.zanxiang.game.module.sdk.pojo.dto.PlatformOrderDTO;
-import com.zanxiang.game.module.sdk.service.ICallBackService;
-import com.zanxiang.game.module.sdk.service.IKafkaService;
-import com.zanxiang.game.module.sdk.service.IOrderService;
-import com.zanxiang.game.module.sdk.service.IPerformOrderService;
+import com.zanxiang.game.module.sdk.service.*;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.JsonUtil;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.logging.log4j.util.Strings;
 import org.apache.logging.log4j.util.Strings;
@@ -36,6 +33,9 @@ public class OrderPaySuccessListener {
     @Resource
     @Resource
     private IOrderService orderService;
     private IOrderService orderService;
 
 
+    @Autowired
+    private IPushMsgService pushMsgService;
+
     @Resource
     @Resource
     private IPerformOrderService performOrderService;
     private IPerformOrderService performOrderService;
 
 
@@ -77,6 +77,8 @@ public class OrderPaySuccessListener {
         callBackService.orderCallBack(platformOrderDTO);
         callBackService.orderCallBack(platformOrderDTO);
         //订单支付埋点数据发送到卡夫卡
         //订单支付埋点数据发送到卡夫卡
         kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_ORDER_PAY, JsonUtil.toString(platformOrderDTO));
         kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_ORDER_PAY, JsonUtil.toString(platformOrderDTO));
+        //订单充值消息推送
+        pushMsgService.pushMsgByRecharge(platformOrderDTO);
         log.info("订单:{} 支付成功履约监听逻辑 ------end---------", event.getOrderId());
         log.info("订单:{} 支付成功履约监听逻辑 ------end---------", event.getOrderId());
     }
     }
 }
 }

+ 28 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IPushMsgService.java

@@ -0,0 +1,28 @@
+package com.zanxiang.game.module.sdk.service;
+
+import com.zanxiang.game.module.sdk.pojo.dto.PlatformOrderDTO;
+import com.zanxiang.game.module.sdk.pojo.param.GameUserRoleUpdateParam;
+import com.zanxiang.game.module.sdk.pojo.param.UserData;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-28
+ * @description : 消息推送
+ */
+public interface IPushMsgService {
+
+    /**
+     * 充值消息推送
+     *
+     * @param platformOrderDTO : 订单信息
+     */
+    void pushMsgByRecharge(PlatformOrderDTO platformOrderDTO);
+
+    /**
+     * 等级提升推送消息
+     *
+     * @param param    : 参数
+     * @param userData : 玩家信息
+     */
+    void pushMsgByLevelUpdate(GameUserRoleUpdateParam param, UserData userData);
+}

+ 4 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/LoginServiceImpl.java

@@ -356,7 +356,10 @@ public class LoginServiceImpl implements IRegisterLoginService {
         //插入用户登录记录
         //插入用户登录记录
         userLoginLogService.createUserLoginLog(userData, LoginTypeEnum.LOGIN_REG.getLoginType());
         userLoginLogService.createUserLoginLog(userData, LoginTypeEnum.LOGIN_REG.getLoginType());
         //查询用户实名信息
         //查询用户实名信息
-        UserCard userCard = userCardService.getOne(new LambdaQueryWrapper<UserCard>().eq(UserCard::getUserId, user.getId()));
+        UserCard userCard = userCardService.getOne(new LambdaQueryWrapper<UserCard>()
+                .eq(UserCard::getUserId, user.getId())
+                .orderByDesc(UserCard::getCreateTime)
+                .last("limit 1"));
         //查询游戏
         //查询游戏
         GameExt gameExt = gameExtService.getByGameId(userData.getGameId());
         GameExt gameExt = gameExtService.getByGameId(userData.getGameId());
         //构造用户登录信息
         //构造用户登录信息

+ 61 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/PushMsgServiceImpl.java

@@ -0,0 +1,61 @@
+package com.zanxiang.game.module.sdk.service.impl;
+
+import com.zanxiang.game.module.base.ServerInfo;
+import com.zanxiang.game.module.base.pojo.params.PushMsgParam;
+import com.zanxiang.game.module.base.rpc.IPushMsgRpc;
+import com.zanxiang.game.module.sdk.pojo.dto.PlatformOrderDTO;
+import com.zanxiang.game.module.sdk.pojo.param.GameUserRoleUpdateParam;
+import com.zanxiang.game.module.sdk.pojo.param.UserData;
+import com.zanxiang.game.module.sdk.service.IPushMsgService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-05-28
+ * @description : 消息推送
+ */
+@Slf4j
+@Service
+public class PushMsgServiceImpl implements IPushMsgService {
+
+    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
+    private IPushMsgRpc pushMsgRpc;
+
+    @Override
+    public void pushMsgByRecharge(PlatformOrderDTO platformOrderDTO) {
+        try {
+            pushMsgRpc.pushMsgByRecharge(this.transform(platformOrderDTO));
+        } catch (Exception ignored) {
+        }
+    }
+
+    @Override
+    public void pushMsgByLevelUpdate(GameUserRoleUpdateParam param, UserData userData) {
+        try {
+            pushMsgRpc.pushMsgByLevelUpdate(this.transform(param, userData));
+        } catch (Exception ignored) {
+        }
+    }
+
+    private PushMsgParam transform(PlatformOrderDTO platformOrderDTO) {
+        return PushMsgParam.builder()
+                .orderId(platformOrderDTO.getOrderId())
+                .userId(platformOrderDTO.getUserId())
+                .gameId(platformOrderDTO.getGameId())
+                .serverId(platformOrderDTO.getServerId())
+                .roleId(platformOrderDTO.getRoleId())
+                .build();
+    }
+
+    private PushMsgParam transform(GameUserRoleUpdateParam param, UserData userData) {
+        return PushMsgParam.builder()
+                .userId(userData.getUserId())
+                .gameId(userData.getGameId())
+                .serverId(param.getServerId())
+                .roleId(param.getRoleId())
+                .roleLevel(param.getRoleLevel())
+                .build();
+    }
+}

+ 4 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserServiceImpl.java

@@ -174,7 +174,10 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
         //查询用户信息
         //查询用户信息
         User user = this.getById(userId);
         User user = this.getById(userId);
         //查询用户实名信息
         //查询用户实名信息
-        UserCard userCard = userCardService.getOne(new LambdaQueryWrapper<UserCard>().eq(UserCard::getUserId, userId));
+        UserCard userCard = userCardService.getOne(new LambdaQueryWrapper<UserCard>()
+                .eq(UserCard::getUserId, userId)
+                .orderByDesc(UserCard::getCreateTime)
+                .last("limit 1"));
         //查询游戏
         //查询游戏
         GameExt gameExt = gameExtService.getByGameId(userData.getGameId());
         GameExt gameExt = gameExtService.getByGameId(userData.getGameId());
         //构造用户登录信息
         //构造用户登录信息