Explorar el Código

Merge branch 'package' of GameCenter/game-center into dev

zhimo hace 1 año
padre
commit
d1211bb415

+ 2 - 2
game-data/game-data-serve/src/main/java/com/zanxiang/game/data/serve/controller/AdsGameServerController.java

@@ -51,14 +51,14 @@ public class AdsGameServerController {
 
     @ApiOperation(value = "游戏区服每日数据")
     @PreAuthorize(permissionKey = "gameServer:GameServerSumDay:day")
-    @PostMapping("sum/day")
+    @PostMapping("/sum/day")
     public ResultVO<Page<GameServerSumDayVO>> getGameServerDataSumDay(@RequestBody GameServerSumDayDTO dto) {
         return ResultVO.ok(gameServerService.getGameServerDataSumDay(dto));
     }
 
     @ApiOperation(value = "游戏区服每日数据总计一栏")
     @PreAuthorize(permissionKey = "gameServer:GameServerSumDay:dayTotal")
-    @PostMapping("sum/day/total")
+    @PostMapping("/sum/day/total")
     public ResultVO<GameServerSumDayTotalVO> getGameServerDataSumDayTotal(@RequestBody GameServerSumDayTotalDTO dto) {
         return ResultVO.ok(gameServerService.getGameServerDataSumDayTotal(dto));
     }

+ 6 - 0
game-data/game-data-serve/src/main/java/com/zanxiang/game/data/serve/pojo/dto/GameDataWaterDTO.java

@@ -51,4 +51,10 @@ public class GameDataWaterDTO extends BasePage {
     @ApiModelProperty(notes = "排序方式:升序asc;降序desc")
     private String sortType;
 
+    /**
+     * 游戏维度:1-子游戏维度;2-父游戏维度;3-超父游戏维度
+     */
+    @ApiModelProperty(notes = "游戏维度:1-子游戏维度;2-父游戏维度")
+    private Long gameDimension;
+
 }

+ 6 - 0
game-data/game-data-serve/src/main/java/com/zanxiang/game/data/serve/pojo/vo/FirstNewUserAgainTrendVO.java

@@ -56,4 +56,10 @@ public class FirstNewUserAgainTrendVO {
     @ApiModelProperty(notes = "不展示")
     private String sourceSystem;
 
+    /**
+     * 数据归因:1-子游戏维度; 2-父游戏维度
+     */
+    @ApiModelProperty(notes = "数据归因:1-子游戏维度; 2-父游戏维度")
+    private Long gameDimension;
+
 }

+ 213 - 45
game-data/game-data-serve/src/main/java/com/zanxiang/game/data/serve/service/impl/GameDataServiceImpl.java

@@ -969,6 +969,20 @@ public class GameDataServiceImpl implements IGameDataService {
     public Page<GameDataWaterVO> getGameDataWater(GameDataWaterDTO dto) {
         com.github.sd4324530.jtuple.Tuple2<List<Long>, List<Long>> poerInfo = dataPowerComponent.getPowerInfo();
         List<Long> userGameIds = dto.getGameId() == null ? poerInfo.second : Collections.singletonList(dto.getGameId());
+
+        if (dto.getGameDimension() == null) {
+            //默认查询子游戏维度
+            dto.setGameDimension(1L);
+        }
+        //默认查询的字段
+        String gameColumn = "game_id";
+        String classifyColumn = "game_classify";
+        String gameNameColumn = "game_name";
+        if (dto.getGameDimension() == 2L) {
+            gameColumn = "parent_game_id";
+            classifyColumn = "parent_game_classify";
+            gameNameColumn = "parent_game_name";
+        }
         if (null == dto.getRechargeDate()) {
             dto.setRechargeDate(LocalDate.now());
         }
@@ -982,30 +996,37 @@ public class GameDataServiceImpl implements IGameDataService {
         // 创建一个 Criteria 接口实例
         SimpleCriteria cri = Cnd.cri();
         if (StringUtils.isNotBlank(dto.getGameName())) {
-            cri.where().andLike("game_name", dto.getGameName());
+            cri.where().andLike(gameNameColumn, dto.getGameName());
         }
         if (StringUtils.isNotBlank(dto.getGameClassify())) {
-            cri.where().andEquals("game_classify", dto.getGameClassify());
-        }
-        if (StringUtils.isNotBlank(dto.getGameClassify())) {
-            cri.where().andEquals("game_classify", dto.getGameClassify());
+            cri.where().andEquals(classifyColumn, dto.getGameClassify());
         }
         if (StringUtils.isNotBlank(dto.getSourceSystem())) {
             cri.where().andEquals("source_system", dto.getSourceSystem());
         }
-        if (null != userGameIds) {
-            cri.where().andInList("game_id", userGameIds);
+        if (CollectionUtils.isNotEmpty(userGameIds)) {
+            cri.where().andInList(gameColumn, userGameIds);
         }
         cri.where().and("dt", "=", dto.getRechargeDate());
-        cri.groupBy("source_system,game_id");
+        cri.groupBy("source_system", gameColumn);
         cri.orderBy(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, dto.getSortFiled()), dto.getSortType());
-        Sql sql = Sqls.queryEntity(waterSql() + "$condition");
+        Sql sql;
+        if (dto.getGameDimension() == 1L) {
+            sql = Sqls.queryEntity(waterSql() + "$condition");
+        } else {
+            sql = Sqls.queryEntity(waterSqlForParent() + "$condition");
+        }
         sql.setPager(pager);
         Entity<GameDataWaterVO> entity = dao.getEntity(GameDataWaterVO.class);
         sql.setEntity(entity).setCondition(cri);
         dao.execute(sql);
 
-        Sql sqlCount = Sqls.queryEntity("select count(*) from ads_everyday_water " + "$condition");
+        Sql sqlCount;
+        if (dto.getGameDimension() == 1L) {
+            sqlCount = Sqls.queryEntity("select count(*) from ads_everyday_water " + "$condition");
+        } else {
+            sqlCount = Sqls.queryEntity("select count(*) from game_ads_parent.ads_everyday_water_parent " + "$condition");
+        }
         sqlCount.setCondition(cri);
         pager.setRecordCount((int) Daos.queryCount(dao, sqlCount));
 
@@ -1018,7 +1039,7 @@ public class GameDataServiceImpl implements IGameDataService {
 
         SimpleCriteria templateCri = Cnd.cri();
         templateCri.where().and("dt", "=", dto.getRechargeDate());
-        templateCri.where().andInList("game_id", gameIds);
+        templateCri.where().andInList(gameColumn, gameIds);
         Sql templateSql = Sqls.queryEntity(waterTemplateSql() + "$condition");
         Entity<AdsEverydayWater> everydayWaterEntity = dao.getEntity(AdsEverydayWater.class);
         templateSql.setEntity(everydayWaterEntity).setCondition(templateCri);
@@ -1703,7 +1724,7 @@ public class GameDataServiceImpl implements IGameDataService {
      * 通过反射赋值游戏首日复充数据
      *
      */
-    private void formatFirstNewUserAgain(GameDataFirstNewUserAgainVO vo, Long[] usersId) {
+    private void formatFirstNewUserAgain(GameDataFirstNewUserAgainVO vo, Long[] usersId, GameDataFirstNewUserAgainDTO dto) {
         if (CollectionUtils.isEmpty(firstNewUserAgainFieldMapList)) {
             return;
         }
@@ -1718,6 +1739,7 @@ public class GameDataServiceImpl implements IGameDataService {
                         .usersId(usersId)
                         .timeType(name)
                         .sourceSystem(vo.getSourceSystem())
+                        .gameDimension(dto.getGameDimension() == null ? 1L : dto.getGameDimension())
                         .build());
             } catch (IllegalAccessException e) {
                 throw new RuntimeException(e);
@@ -1729,7 +1751,8 @@ public class GameDataServiceImpl implements IGameDataService {
      * 通过反射赋值游戏首日复充总计数据
      *
      */
-    private void formatFirstNewUserAgainTotal(GameDataFirstNewUserAgainTotalVO vo, Long[] usersIdTotal) {
+    private void formatFirstNewUserAgainTotal(GameDataFirstNewUserAgainTotalVO vo,
+                                              Long[] usersIdTotal, GameDataFirstNewUserAgainTotalDTO dto) {
         if (CollectionUtils.isEmpty(firstNewUserAgainTotalFieldMapList)) {
             return;
         }
@@ -1749,6 +1772,7 @@ public class GameDataServiceImpl implements IGameDataService {
                                 BigDecimal.valueOf(count.doubleValue() / da1Count.doubleValue()).setScale(4, RoundingMode.HALF_UP))
                         .timeType(name)
                         .usersId(usersIdTotal)
+                        .gameDimension(dto.getGameDimension() == null ? 1L : dto.getGameDimension())
                         .build());
             } catch (IllegalAccessException e) {
                 throw new RuntimeException(e);
@@ -4247,7 +4271,7 @@ public class GameDataServiceImpl implements IGameDataService {
         }
         //新增查询条件
         Criteria cri = Cnd.cri();
-        if (userGameIds != null) {
+        if (CollectionUtils.isNotEmpty(userGameIds)) {
             //拼接游戏id
             cri.where().andInList(gameColumn, userGameIds);
         }
@@ -4388,7 +4412,8 @@ public class GameDataServiceImpl implements IGameDataService {
                         .toArray(Long[]::new);
             }
             //将string转成 FirstNewUserAgainTrendVO 对象
-            formatFirstNewUserAgain(vo, usersID);
+            formatFirstNewUserAgain(vo, usersID, dto);
+
             return vo;
         }).collect(Collectors.toList());
 
@@ -4482,7 +4507,7 @@ public class GameDataServiceImpl implements IGameDataService {
         //voAgainData有值时进行映射的取值处理
         if (StringUtils.isNotBlank(voAgainData.getDa1())) {
             //计算游戏首日复充总计
-            formatFirstNewUserAgainTotal(voAgainData, usersIdTotal);
+            formatFirstNewUserAgainTotal(voAgainData, usersIdTotal, dto);
         }
         //将两个对象的属性结合
         copyNullProperties(voAgainData, voGameData);
@@ -4501,6 +4526,10 @@ public class GameDataServiceImpl implements IGameDataService {
 
         //得到需要查询的usersID内容
         FirstNewUserAgainTrendVO againTrendVO = dto.getAgainTrendVO();
+        if (againTrendVO.getGameDimension() == null) {
+            //默认子游戏维度
+            againTrendVO.setGameDimension(1L);
+        }
         //处理时间条件
         String timeType = againTrendVO.getTimeType();
         //用来处理日期的字符串
@@ -4529,35 +4558,41 @@ public class GameDataServiceImpl implements IGameDataService {
         }
         //拼接排序条件
         //如果没有排序条件给默认值
-        if (StringUtils.isBlank(dto.getSortType())) {
-            dto.setSortType(OrderByEnum.DESC.getOrderType());
-        }
-        if (StringUtils.isBlank(dto.getSortFiled())) {
-            queryStr.append(" ORDER BY today_total_amount DESC, player_id DESC");
-        } else {
-            queryStr.append("""
+        if (againTrendVO.getGameDimension() == 1L) {
+            if (StringUtils.isBlank(dto.getSortType())) {
+                dto.setSortType(OrderByEnum.DESC.getOrderType());
+            }
+            if (StringUtils.isBlank(dto.getSortFiled())) {
+                queryStr.append(" ORDER BY today_total_amount DESC, player_id DESC");
+            } else {
+                queryStr.append("""
                      ORDER BY %s %s, player_id DESC
                     """.formatted(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, dto.getSortFiled()),
-                    dto.getSortType()));
+                        dto.getSortType()));
+            }
+        } else {
+            //父游戏维度
+            if (StringUtils.isBlank(dto.getSortType())) {
+                dto.setSortType(OrderByEnum.DESC.getOrderType());
+            }
+            if (StringUtils.isBlank(dto.getSortFiled())) {
+                queryStr.append(" ORDER BY today_total_amount DESC, parent_player_id DESC");
+            } else {
+                queryStr.append("""
+                     ORDER BY %s %s, parent_player_id DESC
+                    """.formatted(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, dto.getSortFiled()),
+                        dto.getSortType()));
+            }
         }
         //得到用户id数组
         Long[] usersId = againTrendVO.getUsersId();
         //查询具体的数据
-        Sql userDetilsSql = Sqls.create("""
-                SELECT
-                    player_id,
-                    reg_agent_id,
-                    reg_agent_name,
-                    server_name,
-                    role_name,
-                    $amount as today_total_amount,
-                    player_os
-                FROM
-                    game_ads.ads_player_recharge_ranking
-                WHERE player_id IN (@usersId)
-                and dt = DATE_ADD(DATE(reg_user_time),
-                """ + queryStr
-                );
+        Sql userDetilsSql;
+        if (againTrendVO.getGameDimension() == 1L) {
+            userDetilsSql = Sqls.create(userDetailSql() + queryStr);
+        } else {
+            userDetilsSql = Sqls.create(userDetailSqlForParent() + queryStr);
+        }
         //设置参数
         if ("total".equals(timeType)) {
             userDetilsSql.vars().set("amount", "total_amount");
@@ -4591,15 +4626,28 @@ public class GameDataServiceImpl implements IGameDataService {
      * @return map
      */
     private Map findUsersId(GameDataFirstNewUserAgainDTO dto) {
+
+        //默认查询子游戏维度
+        if (dto.getGameDimension() == null) {
+            dto.setGameDimension(1L);
+        }
+        //默认查询字段
+        String gameColumn = "reg_game_id";
+        String classifyColumn = "classify";
+        if (dto.getGameDimension() == 2L) {
+            gameColumn = "parent_game_id";
+            classifyColumn = "parent_game_classify";
+        }
+
         //查询每个游戏的注册首日充值用户id,在玩家充值排行榜里查找
         Criteria findUsersCri = Cnd.cri();
         //拼接游戏ID
         if (dto.getGameId() != null) {
-            findUsersCri.where().andInList("reg_game_id", dto.getGameId());
+            findUsersCri.where().andInList(gameColumn, dto.getGameId());
         }
         //拼接游戏应用类型
         if (dto.getClassify() != null) {
-            findUsersCri.where().andEquals("classify", dto.getClassify());
+            findUsersCri.where().andEquals(classifyColumn, dto.getClassify());
         }
         //拼接SDK来源
         if (StringUtils.isNotBlank(dto.getSourceSystem())) {
@@ -4615,7 +4663,12 @@ public class GameDataServiceImpl implements IGameDataService {
         } else if ("nature".equals(dto.getTableTypes())) {
             findUsersCri.where().andEquals("reg_agent_id", 0);
         }
-        Sql findUsersIdSql = Sqls.create(findUsersIdSql(findUsersCri));
+        Sql findUsersIdSql;
+        if (dto.getGameDimension() == 1L) {
+            findUsersIdSql = Sqls.create(findUsersIdSql(findUsersCri));
+        } else {
+            findUsersIdSql = Sqls.create(findUsersIdSqlForParent(findUsersCri));
+        }
         //自定义回传
         findUsersIdSql.setCallback((connection, resultSet, sql) -> {
             Map<String, String> tempMap = new HashMap<>();
@@ -4637,15 +4690,28 @@ public class GameDataServiceImpl implements IGameDataService {
      * @return map
      */
     private String findUsersIdTotal(GameDataFirstNewUserAgainTotalDTO dto) {
+
+        //默认查询子游戏维度
+        if (dto.getGameDimension() == null) {
+            dto.setGameDimension(1L);
+        }
+        //默认查询字段
+        String gameColumn = "reg_game_id";
+        String classifyColumn = "classify";
+        if (dto.getGameDimension() == 2L) {
+            gameColumn = "parent_game_id";
+            classifyColumn = "parent_game_classify";
+        }
+
         //查询每个游戏的注册首日充值用户id,在玩家充值排行榜里查找
         Criteria findUsersCri = Cnd.cri();
         //拼接游戏ID
         if (dto.getGameId() != null) {
-            findUsersCri.where().andEquals("reg_game_id", dto.getGameId());
+            findUsersCri.where().andInList(gameColumn, dto.getGameId());
         }
         //拼接游戏应用类型
         if (dto.getClassify() != null) {
-            findUsersCri.where().andEquals("classify", dto.getClassify());
+            findUsersCri.where().andEquals(classifyColumn, dto.getClassify());
         }
         //拼接SDK来源
         if (StringUtils.isNotBlank(dto.getSourceSystem())) {
@@ -4661,7 +4727,12 @@ public class GameDataServiceImpl implements IGameDataService {
         } else if ("nature".equals(dto.getTableTypes())) {
             findUsersCri.where().andEquals("reg_agent_id", 0);
         }
-        Sql findUsersIdTotalSql = Sqls.create(findUsersIdTotalSql(findUsersCri));
+        Sql findUsersIdTotalSql;
+        if (dto.getGameDimension() == 1L) {
+            findUsersIdTotalSql = Sqls.create(findUsersIdTotalSql(findUsersCri));
+        } else {
+            findUsersIdTotalSql = Sqls.create(findUsersIdTotalSqlForParent(findUsersCri));
+        }
         //自定义回传
         findUsersIdTotalSql.setCallback(Sqls.callback.str());
         //运行sql
@@ -8244,6 +8315,25 @@ public class GameDataServiceImpl implements IGameDataService {
                         """;
     }
 
+    /**
+     * 找出每个游戏注册时间内的注册当天且充值的用户id-父游戏维度
+     *
+     * @return String
+     */
+    private String findUsersIdSqlForParent(Criteria cri) {
+        return """
+                SELECT
+                    CONCAT(parent_game_id,'/',DATE(reg_user_time)) as game_id_date,
+                    GROUP_CONCAT(CONVERT (parent_player_id, varchar), "/") as amount_users_id
+                FROM
+                    game_ads_parent.ads_player_recharge_ranking_parent
+                """ + cri +
+                """
+                    AND dt = DATE(reg_user_time)
+                GROUP BY parent_game_id, DATE(reg_user_time)
+                        """;
+    }
+
     /**
      * 找出每个游戏注册时间内的注册当天且充值的用户id(总计一栏)
      *
@@ -8261,6 +8351,23 @@ public class GameDataServiceImpl implements IGameDataService {
                 """;
     }
 
+    /**
+     * 找出每个游戏注册时间内的注册当天且充值的用户id(总计一栏)-父游戏维度
+     *
+     * @return String
+     */
+    private String findUsersIdTotalSqlForParent(Criteria cri) {
+        return """
+                SELECT
+                    GROUP_CONCAT(CONVERT (parent_player_id, varchar), "/") as amount_users_id
+                FROM
+                    game_ads_parent.ads_player_recharge_ranking_parent
+                """ + cri +
+                """
+                    AND dt = DATE(reg_user_time)
+                """;
+    }
+
     private List<H5NatureUserVO> getH5NatureUserVOList(GameDataH5DTO dto, Map<String, Object> importDayNMap, GameDataH5VO item) {
         Sql natureGameSql = Sqls.queryEntity("""
                 select
@@ -8736,6 +8843,9 @@ public class GameDataServiceImpl implements IGameDataService {
                 """;
     }
 
+    /**
+     * 子游戏维度
+     */
     private String waterSql() {
         return """
                 select
@@ -8751,6 +8861,24 @@ public class GameDataServiceImpl implements IGameDataService {
                 """;
     }
 
+    /**
+     * 父游戏维度
+     */
+    private String waterSqlForParent() {
+        return """
+                select
+                parent_game_id as id,
+                source_system,
+                parent_game_id as game_id,
+                max(parent_game_name) as game_name,
+                max(parent_game_classify) as game_classify,
+                max(amount) as amount,
+                max(buy_amount) as buy_amount,
+                max(nature_amount) as nature_amount
+                from game_ads_parent.ads_everyday_water_parent
+                """;
+    }
+
     /**
      * 流水临时sql
      *
@@ -8774,6 +8902,46 @@ public class GameDataServiceImpl implements IGameDataService {
                 """;
     }
 
+    /**
+     * 用户详情sql-子游戏维度
+     */
+    private String userDetailSql() {
+        return """
+                SELECT
+                    player_id,
+                    reg_agent_id,
+                    reg_agent_name,
+                    server_name,
+                    role_name,
+                    $amount as today_total_amount,
+                    player_os
+                FROM
+                    game_ads.ads_player_recharge_ranking
+                WHERE player_id IN (@usersId)
+                and dt = DATE_ADD(DATE(reg_user_time),
+                """;
+    }
+
+    /**
+     * 用户详情sql-子游戏维度
+     */
+    private String userDetailSqlForParent() {
+        return """
+                SELECT
+                    parent_player_id as player_id,
+                    reg_agent_id,
+                    reg_agent_name,
+                    server_name,
+                    role_name,
+                    $amount as today_total_amount,
+                    player_os
+                FROM
+                    game_ads_parent.ads_player_recharge_ranking_parent
+                WHERE parent_player_id IN (@usersId)
+                and dt = DATE_ADD(DATE(reg_user_time),
+                """;
+    }
+
     /**
      * DayN内部类
      */

+ 11 - 9
game-data/game-data-serve/src/main/java/com/zanxiang/game/data/serve/service/impl/IActiveDataServiceImpl.java

@@ -165,15 +165,15 @@ public class IActiveDataServiceImpl implements IActiveDataService {
             classifyColumn = "parent_game_classify";
             switch (dto.getActiveTypes()) {
                 case "reg" -> {
-                    queryTableName = "ads_game_active_reg_day_parent";
+                    queryTableName = "game_ads_parent.ads_game_active_reg_day_parent";
                     tempClazz = AdsGameActiveRegDayParent.class;
                 }
                 case "role" -> {
-                    queryTableName = "ads_game_active_role_day_parent";
+                    queryTableName = "game_ads_parent.ads_game_active_role_day_parent";
                     tempClazz = AdsGameActiveRoleDayParent.class;
                 }
                 case "amount" -> {
-                    queryTableName = "ads_game_active_order_day_parent";
+                    queryTableName = "game_ads_parent.ads_game_active_order_day_parent";
                     tempClazz = AdsGameActiveOrderDayParent.class;
                 }
             }
@@ -279,9 +279,9 @@ public class IActiveDataServiceImpl implements IActiveDataService {
             classifyColumn = "parent_game_classify";
             tableName = "game_ads_parent.ads_game_day_parent";
             switch (dto.getActiveTypes()) {
-                case "reg" -> queryTableName = "ads_game_active_reg_day_parent";
-                case "role" -> queryTableName = "ads_game_active_role_day_parent";
-                case "amount" -> queryTableName = "ads_game_active_order_day_parent";
+                case "reg" -> queryTableName = "game_ads_parent.ads_game_active_reg_day_parent";
+                case "role" -> queryTableName = "game_ads_parent.ads_game_active_role_day_parent";
+                case "amount" -> queryTableName = "game_ads_parent.ads_game_active_order_day_parent";
             }
         }
         //创建查询条件
@@ -390,7 +390,8 @@ public class IActiveDataServiceImpl implements IActiveDataService {
         //拼接查询条件
         StringBuilder trendDay = new StringBuilder(StringUtils.EMPTY);
         trendDay.append("SELECT ");
-        if ("ads_game_active_reg_day".equals(tableName)) {
+        if ("ads_game_active_reg_day".equals(tableName) ||
+                "game_ads_parent.ads_game_active_reg_day_parent".equals(tableName)) {
             //90天数据
             for (int day = 1; day <= 90; day++) {
                 trendDay.append("""
@@ -413,7 +414,8 @@ public class IActiveDataServiceImpl implements IActiveDataService {
                             SUM(IF(DATE_ADD(dt, INTERVAL 12 month) <= Local.now(), %sreg_num, 0))
                     ) AS m12
                        """.formatted(type, type));
-        } else if ("ads_game_active_order_day".equals(tableName)) {
+        } else if ("ads_game_active_order_day".equals(tableName) ||
+                "game_ads_parent.ads_game_active_role_day_parent".equals(tableName)) {
             // 第N天新增付费用户数/第N天老活跃用户数/累计到第N天的付费用户数/累计到第N天的活跃人数
             //90天数据
             for (int day = 1; day <= 90; day++) {
@@ -474,7 +476,7 @@ public class IActiveDataServiceImpl implements IActiveDataService {
                     ) AS m12
                        """.formatted(type, type, type, type));
         }
-        trendDay.append("FROM game_ads.");
+        trendDay.append("FROM ");
         trendDay.append(tableName);
 
         return trendDay.toString();

+ 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) {
         SpringApplication.run(ManageApplication.class, args);
-        System.out.println("赞象Manage服务启动成功 <小程序监听修改上线> ( ´・・)ノ(._.`) \n" +
+        System.out.println("赞象Manage服务启动成功 <导量逻辑> ( ´・・)ノ(._.`) \n" +
                 "___  ___  ___   _   _   ___  _____  _____ \n" +
                 "|  \\/  | / _ \\ | \\ | | / _ \\|  __ \\|  ___|\n" +
                 "| .  . |/ /_\\ \\|  \\| |/ /_\\ \\ |  \\/| |__  \n" +

+ 6 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/constant/RedisKeyConstant.java

@@ -22,4 +22,10 @@ public class RedisKeyConstant {
      */
     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_";
+
+
 }

+ 70 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/enums/ExpireTimeEnum.java

@@ -0,0 +1,70 @@
+package com.zanxiang.game.module.manage.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author : lingfeng
+ * @time : 2021-11-16
+ * @description : 时间常量
+ */
+@Getter
+@AllArgsConstructor
+public enum ExpireTimeEnum {
+
+    /**
+     * 无固定期限
+     */
+    NONE(0, "无固定期限"),
+
+    /**
+     * 1分钟
+     */
+    ONE_MIN(60, "1分钟"),
+
+    /**
+     * 5分钟
+     */
+    FIVE_MIN(5 * 60, "5分钟"),
+
+    /**
+     * 30分钟
+     */
+    HALF_HOUR(30 * 60, "30分钟"),
+
+    /**
+     * 1小时
+     */
+    ONE_HOUR(60 * 60, "1小时"),
+
+    /**
+     * 1天
+     */
+    ONE_DAY(24 * 60 * 60, "1天"),
+
+    /**
+     * 一周
+     */
+    ONE_WEEK(24 * 60 * 60 * 7, "一周"),
+
+    /**
+     * 1个月
+     */
+    ONE_MON(30 * 24 * 60 * 60, "1个月"),
+
+    /**
+     * 1年
+     */
+    ONE_YEAR(365 * 24 * 60 * 60, "1年");
+
+    /**
+     * 时间
+     */
+    private final long time;
+
+    /**
+     * 描述
+     */
+    private final String desc;
+
+}  

+ 26 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/AppletCheckServiceImpl.java

@@ -12,7 +12,9 @@ import com.zanxiang.game.module.base.pojo.enums.PayApplicationTypeEnum;
 import com.zanxiang.game.module.base.pojo.enums.StatusEnum;
 import com.zanxiang.game.module.base.rpc.IWxApiServiceRpc;
 import com.zanxiang.game.module.manage.constant.RedisKeyConstant;
+import com.zanxiang.game.module.manage.enums.ExpireTimeEnum;
 import com.zanxiang.game.module.manage.service.*;
+import com.zanxiang.game.module.manage.utils.RedisUtil;
 import com.zanxiang.game.module.mybatis.entity.GamePayWay;
 import com.zanxiang.game.module.mybatis.entity.ListenCall;
 import com.zanxiang.game.module.mybatis.entity.PayApplication;
@@ -54,6 +56,9 @@ public class AppletCheckServiceImpl implements IAppletCheckService {
     @DubboReference(providedBy = ServerInfo.SERVER_SDK_DUBBO_NAME)
     private IWxApiServiceRpc wxApiServiceRpc;
 
+    @Autowired
+    private RedisUtil<Integer> redisUtil;
+
     @Autowired
     private IPayBoxService payBoxService;
 
@@ -93,6 +98,11 @@ public class AppletCheckServiceImpl implements IAppletCheckService {
 
     @Override
     public void payApplicationCheck(PayApplication payApplication) {
+        //异常计数器, 返回false的时候过滤
+        if (!this.countUpdate(payApplication.getAppId())) {
+            return;
+        }
+        //连续三次报警, 进行通知
         try {
             //更新游戏支付
             this.gamePayUpdate(payApplication);
@@ -113,6 +123,22 @@ public class AppletCheckServiceImpl implements IAppletCheckService {
         }
     }
 
+    private boolean countUpdate(String appId) {
+        String key = RedisKeyConstant.APPLET_ERROR_COUNT + appId;
+        Integer count = redisUtil.getCache(key);
+        if (count != null && count >= 2) {
+            redisUtil.deleteCache(key);
+            return Boolean.TRUE;
+        }
+        if (count == null) {
+            count = 1;
+        } else {
+            count += 1;
+        }
+        redisUtil.setCache(key, count, ExpireTimeEnum.HALF_HOUR.getTime());
+        return Boolean.FALSE;
+    }
+
     private void gamePayUpdate(PayApplication payApplication) {
         //修改支付应用状态
         log.error("支付应用异常 - 修改支付应用状态, appName : {}", payApplication.getAppName());

+ 11 - 4
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/UserServiceImpl.java

@@ -108,12 +108,19 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
         if (gameDTO == null) {
             throw new BaseException("参数错误, 关联导量游戏信息不存在");
         }
+        //判断该玩家是否已经导过该游戏
+        if (super.count(new LambdaQueryWrapper<User>()
+                .eq(User::getGameId, gameDTO.getId())
+                .eq(User::getRelationUserId, userId)
+        ) > 0) {
+            throw new BaseException("玩家已经导到该游戏端, 请不要重复操作");
+        }
         //判断手机号是否被该游戏其他用户绑定
-        int count = super.count(new LambdaQueryWrapper<User>()
+        if (super.count(new LambdaQueryWrapper<User>()
                 .eq(User::getGameId, gameDTO.getId())
-                .eq(User::getMobile, mobile));
-        if (count > 0) {
-            throw new BaseException("参数错误, 该手机号已被该游戏其他玩家信息绑定");
+                .eq(User::getMobile, mobile)
+        ) > 0) {
+            throw new BaseException("手机号已被游戏其他玩家绑定");
         }
         //复制用户信息
         User h5User = BeanUtil.copy(user, User.class);

+ 10 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/task/PayApplicationTask.java

@@ -5,6 +5,7 @@ import com.zanxiang.game.module.manage.service.IAppletCheckService;
 import com.zanxiang.module.redis.service.IDistributedLockComponent;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
@@ -26,11 +27,20 @@ public class PayApplicationTask {
     @Autowired
     private IAppletCheckService appletCheckService;
 
+    /**
+     * 服务器域名
+     */
+    @Value("${taskRun}")
+    private Boolean taskRun;
+
     /**
      * 小程序每10分钟检查
      */
     @Scheduled(cron = "0 0/10 * * * ?")
     public void payApplicationCheck() {
+        if (!taskRun) {
+            return;
+        }
         //上锁
         if (!distributedLockComponent.doLock(RedisKeyConstant.PAY_APP_CHECK_LOCK, 0L, 15L, TimeUnit.MINUTES)) {
             return;

+ 272 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/utils/RedisUtil.java

@@ -0,0 +1,272 @@
+package com.zanxiang.game.module.manage.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.*;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author : lingfeng
+ * @time : 2021-11-23
+ * @description : redis缓存通用前缀
+ */
+@Component
+@Slf4j
+public class RedisUtil<T> {
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    /**
+     * 设置缓存
+     *
+     * @param key  : 缓存key
+     * @param t    : 缓存对象
+     * @param time : 过期时间
+     * @return : 返回是否设置成功
+     */
+    public boolean setCache(String key, T t, long time) {
+        try {
+            ValueOperations<String, T> operations = redisTemplate.opsForValue();
+            operations.set(key, t, time, TimeUnit.SECONDS);
+            return true;
+        } catch (Exception e) {
+            log.error("缓存添加异常, key : {}, value : {}, e : {}", key, t, e.getMessage());
+        }
+        return false;
+    }
+
+    /**
+     * 获取缓存方法
+     *
+     * @param key : 缓存key
+     * @return : 返回缓存对象
+     */
+    public T getCache(String key) {
+        try {
+            if (redisTemplate.hasKey(key)) {
+                ValueOperations<String, T> operations = redisTemplate.opsForValue();
+                return operations.get(key);
+            }
+        } catch (Exception e) {
+            log.error("获取缓存异常, key : {}, e : {}", key, e.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * redis 事物锁设置方法
+     *
+     * @param key  : 缓存key
+     * @param t    : 缓存对象
+     * @param time : 过期时间
+     * @return : 返回是否设置成功
+     */
+    public boolean setIfAbsent(String key, T t, long time) {
+        try {
+            ValueOperations<String, T> operations = redisTemplate.opsForValue();
+            return operations.setIfAbsent(key, t, time, TimeUnit.SECONDS);
+        } catch (Exception e) {
+            log.error("缓存添加异常, key : {}, value : {}, e : {}", key, t, e.getMessage());
+        }
+        return false;
+    }
+
+    /**
+     * 删除缓存的方法
+     *
+     * @param key : 缓存key
+     * @return : 返回缓存对象
+     */
+    public boolean deleteCache(String key) {
+        try {
+            if (redisTemplate.hasKey(key)) {
+                return redisTemplate.delete(key);
+            }
+        } catch (Exception e) {
+            log.error("删除缓存异常, key : {}, e : {}", key, e.getMessage());
+        }
+        return false;
+    }
+
+    /**
+     * set集合添加元素
+     *
+     * @param key : 缓存key
+     * @param t   : 需要添加的元素
+     * @return : 返回是否添加成功
+     */
+    public boolean addToSet(String key, T... t) {
+        try {
+            SetOperations<String, T> setOperations = redisTemplate.opsForSet();
+            Long add = setOperations.add(key, t);
+            return add != null && add >= 1;
+        } catch (Exception e) {
+            log.error("Set添加失败,key : {}, e : {}", key, e.getMessage());
+        }
+        return false;
+    }
+
+    /**
+     * set集合删除元素
+     *
+     * @param key : 缓存key
+     * @param t   : 需要删除的元素
+     * @return : 返回是否删除成功
+     */
+    public boolean removeOfSet(String key, T... t) {
+        try {
+            SetOperations<String, T> setOperations = redisTemplate.opsForSet();
+            Long remove = setOperations.remove(key, t);
+            return remove != null && remove >= 1;
+        } catch (Exception e) {
+            log.error("Set删除失败,key : {}, e : {}", key, e.getMessage());
+        }
+        return false;
+    }
+
+    /**
+     * 判断元素是否存在于set集合
+     *
+     * @param key : 缓存key
+     * @param t   : 需要判断的元素
+     * @return : 判断元素是否存在
+     */
+    public boolean isMemberInSet(String key, T t) {
+        try {
+            SetOperations<String, T> setOperations = redisTemplate.opsForSet();
+            return setOperations.isMember(key, t);
+        } catch (Exception e) {
+            log.error("Set元素是否存在判断异常, key : {}, e : {}", key, e.getMessage());
+        }
+        return false;
+    }
+
+    /**
+     * 添加单个缓存到list
+     *
+     * @param key   : 缓存key
+     * @param t     : 缓存元素
+     * @param isTop : isTop :  真:前 假:后
+     * @return : 返回添加结果
+     */
+    public boolean setListCache(String key, T t, boolean isTop) {
+        try {
+            ListOperations<String, T> listOperations = redisTemplate.opsForList();
+            if (isTop) {
+                listOperations.leftPush(key, t);
+            } else {
+                listOperations.rightPush(key, t);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error("redis添加队列元素失败,key : {}, e : {}", key, e.getMessage());
+        }
+        return false;
+    }
+
+    /**
+     * 获取列表中的前or后的值
+     *
+     * @param key   : 缓存key
+     * @param isTop :  真:前 假:后
+     * @return : 返回缓存数据
+     */
+    public T getListOneCache(String key, boolean isTop) {
+        try {
+            if (redisTemplate.hasKey(key)) {
+                ListOperations<String, T> listOperations = redisTemplate.opsForList();
+                T t = null;
+                if (isTop) {
+                    t = listOperations.leftPop(key);
+                } else {
+                    t = listOperations.rightPop(key);
+                }
+                return t;
+            }
+
+        } catch (Exception e) {
+            log.error("redis获取队列元素失败,key : {}, e : {}", key, e.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * 添加单个元素进入有序集合
+     *
+     * @param key   : 缓存key
+     * @param t     : 缓存值
+     * @param score : 分数
+     * @return : 返回是否添加成功
+     */
+    public boolean addZSet(String key, T t, double score) {
+        try {
+            ZSetOperations zSet = redisTemplate.opsForZSet();
+            Boolean add = zSet.add(key, t, score);
+            return add == null ? false : add;
+        } catch (Exception e) {
+            log.error("ZSet添加失败元素失败, key : {}, t : {}, score : {}, e : {}", key, t.toString(), score, e.getMessage());
+        }
+        return false;
+    }
+
+    /**
+     * 获取分数区间
+     *
+     * @param key : 缓存key
+     * @param min : 最小值
+     * @param max : 最大值
+     * @return {@link Set}<{@link T}>
+     */
+    public Set<T> getZSetByScore(String key, Double min, Double max) {
+        try {
+            ZSetOperations<String, T> zSet = redisTemplate.opsForZSet();
+            Set<T> set = zSet.rangeByScore(key, min, max);
+            return set;
+        } catch (Exception e) {
+            log.error("ZSet获取元素失败, key : {}, min : {}, max : {}, e : {}", key, min, max, e.getMessage());
+        }
+        return null;
+    }
+
+
+    /**
+     * 按照索引区间获取(按分数正序排序)
+     * (0, -1)表示获取全部
+     *
+     * @param key   : 缓存key
+     * @param start : 开始
+     * @param end   : 结束
+     * @return {@link Set}<{@link T}>
+     */
+    public Set<T> rangeZSet(String key, long start, long end) {
+        try {
+            ZSetOperations<String, T> zSet = redisTemplate.opsForZSet();
+            return zSet.range(key, start, end);
+        } catch (Exception e) {
+            log.error("ZSet获取元素失败, key : {}, start : {}, end : {}, e : {}", key, start, end, e.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * 按照缓存值删除
+     *
+     * @param key : 缓存key
+     * @param t   : 缓存值
+     * @return boolean : 删除结果
+     */
+    public boolean delZSetValue(String key, T t) {
+        try {
+            ZSetOperations<String, T> zSet = redisTemplate.opsForZSet();
+            Long remove = zSet.remove(key, t);
+            return remove != null && remove == 1;
+        } catch (Exception e) {
+            log.error("ZSet删除元素失败, key : {}, t : {}, , e : {}", key, t, e.getMessage());
+        }
+        return false;
+    }
+}