Explorar el Código

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

zhimo hace 1 año
padre
commit
82a7b8b394

+ 46 - 106
game-data/game-data-serve/src/main/java/com/zanxiang/game/data/serve/service/impl/AccountAgentDayServiceImpl.java

@@ -1107,42 +1107,16 @@ public class AccountAgentDayServiceImpl implements IAccountAgentDayService {
                 	round(if(sum(cost) > 0, sum(new_user_total_amount) / sum(cost), 0), 4) as today_recovery_rate,
                 	round(if(sum(cost) > 0, sum(buy_new_user_total_amount) / sum(cost), 0), 4) as buy_recovery_rate,
                 	sum(gross_profit) as gross_margin,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d1, '/', 6)) / sum(cost), 0), 4) as roi_day1,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d2, '/', 6)) / sum(cost), 0), 4) as roi_day2,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d3, '/', 6)) / sum(cost), 0), 4) as roi_day3,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d4, '/', 6)) / sum(cost), 0), 4) as roi_day4,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d5, '/', 6)) / sum(cost), 0), 4) as roi_day5,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d6, '/', 6)) / sum(cost), 0), 4) as roi_day6,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d7, '/', 6)) / sum(cost), 0), 4) as roi_day7,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d8, '/', 6)) / sum(cost), 0), 4) as roi_day8,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d9, '/', 6)) / sum(cost), 0), 4) as roi_day9,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d10, '/', 6)) / sum(cost), 0), 4) as roi_day10,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d11, '/', 6)) / sum(cost), 0), 4) as roi_day11,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d12, '/', 6)) / sum(cost), 0), 4) as roi_day12,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d13, '/', 6)) / sum(cost), 0), 4) as roi_day13,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d14, '/', 6)) / sum(cost), 0), 4) as roi_day14,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d15, '/', 6)) / sum(cost), 0), 4) as roi_day15,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d16, '/', 6)) / sum(cost), 0), 4) as roi_day16,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d17, '/', 6)) / sum(cost), 0), 4) as roi_day17,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d18, '/', 6)) / sum(cost), 0), 4) as roi_day18,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d19, '/', 6)) / sum(cost), 0), 4) as roi_day19,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d20, '/', 6)) / sum(cost), 0), 4) as roi_day20,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d21, '/', 6)) / sum(cost), 0), 4) as roi_day21,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d22, '/', 6)) / sum(cost), 0), 4) as roi_day22,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d23, '/', 6)) / sum(cost), 0), 4) as roi_day23,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d24, '/', 6)) / sum(cost), 0), 4) as roi_day24,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d25, '/', 6)) / sum(cost), 0), 4) as roi_day25,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d26, '/', 6)) / sum(cost), 0), 4) as roi_day26,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d27, '/', 6)) / sum(cost), 0), 4) as roi_day27,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d28, '/', 6)) / sum(cost), 0), 4) as roi_day28,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d29, '/', 6)) / sum(cost), 0), 4) as roi_day29,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_m1, '/', 6)) / sum(cost), 0), 4) as roi_day30,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_m2, '/', 6)) / sum(cost), 0), 4) as roi_day60,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_m3, '/', 6)) / sum(cost), 0), 4) as roi_day90,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_m6, '/', 6)) / sum(cost), 0), 4) as roi_day180,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_y1, '/', 6)) / sum(cost), 0), 4) as roi_year1,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_sum, '/', 6)) / sum(cost), 0), 4) as roi_total,
-                         """
+                	"""
+                +roiDay()+
+                """
+                     round(if(SUM(IF(DATE_ADD(dt, INTERVAL 29 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_m1, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 29 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_day30,
+                     round(if(SUM(IF(DATE_ADD(dt, INTERVAL 59 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_m2, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 59 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_day60,
+                     round(if(SUM(IF(DATE_ADD(dt, INTERVAL 89 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_m3, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 89 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_day90,
+                     round(if(SUM(IF(DATE_ADD(dt, INTERVAL 179 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_m6, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 179 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_day180,
+                     round(if(SUM(IF(DATE_ADD(dt, INTERVAL 359 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_y1, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 359 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_year1,
+                     round(if(SUM(cost) > 0, sum(SPLIT_PART(amount_sum, '/', 6)) / SUM(cost), 0), 4) as roi_total,
+                        """
                 +trendDay()+
                         """
                          CONCAT(SUM(CAST(SPLIT_PART(amount_m1 , '/', 1) AS DECIMAL(10, 2))),'/',SUM(CAST(SPLIT_PART(amount_m1, '/', 6) AS DECIMAL(10, 2))),'/',SUM(CAST(SPLIT_PART(amount_m1, '/', 2) AS BIGINT)),'/',
@@ -1201,6 +1175,18 @@ public class AccountAgentDayServiceImpl implements IAccountAgentDayService {
         return trendDay.toString();
     }
 
+    private String roiDay() {
+        StringBuilder roiDay = new StringBuilder(StringUtils.EMPTY);
+        LocalDate now = LocalDate.now();
+        for (int day = 1; day <= 29; day++) {
+            roiDay.append("""
+                    round(if(SUM(IF(DATE_ADD(dt, INTERVAL %s DAY) > '%s', 0, cost)) > 0, sum(SPLIT_PART(amount_d%s, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL %s DAY) > '%s', 0, cost)), 0), 4) as roi_day%s,
+                    """
+                    .formatted(day - 1, now,day,day-1,now,day));
+        }
+        return roiDay.toString();
+    }
+
     /**
      * 推广总数据SQL
      * @return
@@ -1265,41 +1251,18 @@ public class AccountAgentDayServiceImpl implements IAccountAgentDayService {
                 	round(if(sum(a.cost) > 0, sum(a.first_new_user_amount) / sum(a.cost), 0), 4) as first_recovery_rate,
                 	round(if(sum(a.cost) > 0, sum(a.new_user_total_amount) / sum(a.cost), 0), 4) as today_recovery_rate,
                 	sum(a.gross_profit) as gross_margin,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d1, '/', 6)) / sum(cost), 0), 4) as roi_day1,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d2, '/', 6)) / sum(cost), 0), 4) as roi_day2,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d3, '/', 6)) / sum(cost), 0), 4) as roi_day3,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d4, '/', 6)) / sum(cost), 0), 4) as roi_day4,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d5, '/', 6)) / sum(cost), 0), 4) as roi_day5,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d6, '/', 6)) / sum(cost), 0), 4) as roi_day6,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d7, '/', 6)) / sum(cost), 0), 4) as roi_day7,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d8, '/', 6)) / sum(cost), 0), 4) as roi_day8,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d9, '/', 6)) / sum(cost), 0), 4) as roi_day9,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d10, '/', 6)) / sum(cost), 0), 4) as roi_day10,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d11, '/', 6)) / sum(cost), 0), 4) as roi_day11,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d12, '/', 6)) / sum(cost), 0), 4) as roi_day12,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d13, '/', 6)) / sum(cost), 0), 4) as roi_day13,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d14, '/', 6)) / sum(cost), 0), 4) as roi_day14,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d15, '/', 6)) / sum(cost), 0), 4) as roi_day15,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d16, '/', 6)) / sum(cost), 0), 4) as roi_day16,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d17, '/', 6)) / sum(cost), 0), 4) as roi_day17,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d18, '/', 6)) / sum(cost), 0), 4) as roi_day18,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d19, '/', 6)) / sum(cost), 0), 4) as roi_day19,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d20, '/', 6)) / sum(cost), 0), 4) as roi_day20,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d21, '/', 6)) / sum(cost), 0), 4) as roi_day21,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d22, '/', 6)) / sum(cost), 0), 4) as roi_day22,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d23, '/', 6)) / sum(cost), 0), 4) as roi_day23,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d24, '/', 6)) / sum(cost), 0), 4) as roi_day24,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d25, '/', 6)) / sum(cost), 0), 4) as roi_day25,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d26, '/', 6)) / sum(cost), 0), 4) as roi_day26,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d27, '/', 6)) / sum(cost), 0), 4) as roi_day27,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d28, '/', 6)) / sum(cost), 0), 4) as roi_day28,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d29, '/', 6)) / sum(cost), 0), 4) as roi_day29,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_m1, '/', 6)) / sum(cost), 0), 4) as roi_day30,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_m2, '/', 6)) / sum(cost), 0), 4) as roi_day60,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_m3, '/', 6)) / sum(cost), 0), 4) as roi_day90,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_m6, '/', 6)) / sum(cost), 0), 4) as roi_day180,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_y1, '/', 6)) / sum(cost), 0), 4) as roi_year1,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_sum, '/', 6)) / sum(cost), 0), 4) as roi_total,
+                         """
+                +roiDay()+
+                  """
+                     round(if(SUM(IF(DATE_ADD(dt, INTERVAL 29 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_m1, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 29 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_day30,
+                     round(if(SUM(IF(DATE_ADD(dt, INTERVAL 59 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_m2, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 59 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_day60,
+                     round(if(SUM(IF(DATE_ADD(dt, INTERVAL 89 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_m3, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 89 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_day90,
+                     round(if(SUM(IF(DATE_ADD(dt, INTERVAL 179 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_m6, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 179 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_day180,
+                     round(if(SUM(IF(DATE_ADD(dt, INTERVAL 359 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_y1, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 359 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_year1,
+                     round(if(SUM(cost) > 0, sum(SPLIT_PART(amount_sum, '/', 6)) / SUM(cost), 0), 4) as roi_total,
+                 """
+                +
+                  """
                 	round(if(sum(a.register_num) > 0, sum(a.first_new_user_amount_num) / sum(a.register_num), 0), 4) as first_recharge_rate,
                 	round(if(sum(a.register_num) > 0, sum(a.new_user_total_amount_num) / sum(a.register_num), 0), 4) as today_recharge_rate,
                 	round(if(sum(a.first_new_user_amount_count) > 0, sum(a.first_new_user_amount) / sum(a.first_new_user_amount_count), 0), 4) as avg_first_user_recharge,
@@ -1380,41 +1343,18 @@ public class AccountAgentDayServiceImpl implements IAccountAgentDayService {
                 	round(if(sum(a.cost) > 0, sum(a.first_new_user_amount) / sum(a.cost), 0), 4) as first_recovery_rate,
                 	round(if(sum(a.cost) > 0, sum(a.new_user_total_amount) / sum(a.cost), 0), 4) as today_recovery_rate,
                 	sum(a.gross_profit) as gross_margin,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d1, '/', 6)) / sum(cost), 0), 4) as roi_day1,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d2, '/', 6)) / sum(cost), 0), 4) as roi_day2,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d3, '/', 6)) / sum(cost), 0), 4) as roi_day3,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d4, '/', 6)) / sum(cost), 0), 4) as roi_day4,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d5, '/', 6)) / sum(cost), 0), 4) as roi_day5,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d6, '/', 6)) / sum(cost), 0), 4) as roi_day6,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d7, '/', 6)) / sum(cost), 0), 4) as roi_day7,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d8, '/', 6)) / sum(cost), 0), 4) as roi_day8,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d9, '/', 6)) / sum(cost), 0), 4) as roi_day9,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d10, '/', 6)) / sum(cost), 0), 4) as roi_day10,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d11, '/', 6)) / sum(cost), 0), 4) as roi_day11,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d12, '/', 6)) / sum(cost), 0), 4) as roi_day12,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d13, '/', 6)) / sum(cost), 0), 4) as roi_day13,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d14, '/', 6)) / sum(cost), 0), 4) as roi_day14,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d15, '/', 6)) / sum(cost), 0), 4) as roi_day15,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d16, '/', 6)) / sum(cost), 0), 4) as roi_day16,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d17, '/', 6)) / sum(cost), 0), 4) as roi_day17,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d18, '/', 6)) / sum(cost), 0), 4) as roi_day18,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d19, '/', 6)) / sum(cost), 0), 4) as roi_day19,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d20, '/', 6)) / sum(cost), 0), 4) as roi_day20,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d21, '/', 6)) / sum(cost), 0), 4) as roi_day21,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d22, '/', 6)) / sum(cost), 0), 4) as roi_day22,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d23, '/', 6)) / sum(cost), 0), 4) as roi_day23,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d24, '/', 6)) / sum(cost), 0), 4) as roi_day24,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d25, '/', 6)) / sum(cost), 0), 4) as roi_day25,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d26, '/', 6)) / sum(cost), 0), 4) as roi_day26,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d27, '/', 6)) / sum(cost), 0), 4) as roi_day27,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d28, '/', 6)) / sum(cost), 0), 4) as roi_day28,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_d29, '/', 6)) / sum(cost), 0), 4) as roi_day29,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_m1, '/', 6)) / sum(cost), 0), 4) as roi_day30,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_m2, '/', 6)) / sum(cost), 0), 4) as roi_day60,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_m3, '/', 6)) / sum(cost), 0), 4) as roi_day90,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_m6, '/', 6)) / sum(cost), 0), 4) as roi_day180,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_y1, '/', 6)) / sum(cost), 0), 4) as roi_year1,
-                         round(if(sum(cost) > 0, sum(SPLIT_PART(amount_sum, '/', 6)) / sum(cost), 0), 4) as roi_total,
+                         """
+                +roiDay()+
+                """
+                   round(if(SUM(IF(DATE_ADD(dt, INTERVAL 29 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_m1, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 29 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_day30,
+                   round(if(SUM(IF(DATE_ADD(dt, INTERVAL 59 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_m2, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 59 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_day60,
+                   round(if(SUM(IF(DATE_ADD(dt, INTERVAL 89 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_m3, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 89 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_day90,
+                   round(if(SUM(IF(DATE_ADD(dt, INTERVAL 179 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_m6, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 179 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_day180,
+                   round(if(SUM(IF(DATE_ADD(dt, INTERVAL 359 DAY) > CURRENT_DATE(), 0, cost)) > 0, sum(SPLIT_PART(amount_y1, '/', 6)) / SUM(IF(DATE_ADD(dt, INTERVAL 359 DAY) > CURRENT_DATE(), 0, cost)), 0), 4) as roi_year1,
+                   round(if(SUM(cost) > 0, sum(SPLIT_PART(amount_sum, '/', 6)) / SUM(cost), 0), 4) as roi_total,
+               """
+                +
+                """
                 	round(if(sum(a.register_num) > 0, sum(a.first_new_user_amount_num) / sum(a.register_num), 0), 4) as first_recharge_rate,
                 	round(if(sum(a.register_num) > 0, sum(a.new_user_total_amount_num) / sum(a.register_num), 0), 4) as today_recharge_rate,
                 	round(if(sum(a.first_new_user_amount_count) > 0, sum(a.first_new_user_amount) / sum(a.first_new_user_amount_count), 0), 4) as avg_first_user_recharge,

+ 43 - 22
game-data/game-data-serve/src/main/java/com/zanxiang/game/data/serve/service/impl/GameDataServiceImpl.java

@@ -831,23 +831,23 @@ public class GameDataServiceImpl implements IGameDataService {
         Criteria criRc = Cnd.cri();
         if (StringUtils.isNotBlank(vo.getGameName())) {
             //拼接游戏名称查询条件
-            criRc.where().andEquals("b.game_name", vo.getGameName());
+            criRc.where().andEquals("game_name", vo.getGameName());
         }
         if (vo.getGameClassify() != null) {
             //拼接游戏类型查询条件
-            criRc.where().andEquals("b.game_classify", vo.getGameClassify());
+            criRc.where().andEquals("game_classify", vo.getGameClassify());
         }
         if (vo.getGameId() != null) {
             //拼接游戏id查询条件
-            criRc.where().andEquals("b.game_id", vo.getGameId());
+            criRc.where().andEquals("game_id", vo.getGameId());
         }
         if (dto.getRechargeBeginDate() != null && dto.getRechargeEndDate() != null) {
             //拼接充值日期查询条件
-            criRc.where().andBetween("b.dt", dto.getRechargeBeginDate(), dto.getRechargeEndDate());
+            criRc.where().andBetween("dt", dto.getRechargeBeginDate(), dto.getRechargeEndDate());
         }
         if (StringUtils.isNotBlank(dto.getSourceSystem())) {
             //拼接SDK来源
-            criRc.where().andEquals("b.source_system", dto.getSourceSystem());
+            criRc.where().andEquals("source_system", dto.getSourceSystem());
         }
         //创建sql语句
         Sql sqlWithRechargeDate = Sqls.create(gameDataTotalSqlRecharge() + criRc);
@@ -905,7 +905,7 @@ public class GameDataServiceImpl implements IGameDataService {
         //创建查询条件
         Criteria cri = Cnd.cri();
         if (dayNMap.get("registerBeginDate") != null && dayNMap.get("registerEndDate") != null) {
-            //拼接充值时间查询条件
+            //拼接注册时间查询条件
             cri.where().andBetween("dt", dayNMap.get("registerBeginDate"), dayNMap.get("registerEndDate"));
         }
         if (dayNMap.get("gameId") != null) {
@@ -983,6 +983,27 @@ public class GameDataServiceImpl implements IGameDataService {
                         .build();
             }
         }
+
+        /*//查询新用户充值人数
+        Criteria newUserAmountNumCri = cri;
+        if (dayNMap.get("rechargeBeginDate") != null && dayNMap.get("rechargeEndDate") != null) {
+            //拼接充值时间查询条件
+            newUserAmountNumCri.where().andBetween("dt", dayNMap.get("rechargeBeginDate"), dayNMap.get("rechargeEndDate"));
+        }
+        Sql newUserAmountNumSql = Sqls.create("""
+                    SELECT
+                        DISTINCT(user_id)
+                    FROM
+                        game_ads.ads_information
+                """ + newUserAmountNumCri);
+        newUserAmountNumSql.setCallback(Sqls.callback.longValue());
+        //执行sql
+        dao.execute(newUserAmountNumSql);
+        //得到结果 新用户充值人数
+        Long newUserAmountNum = newUserAmountNumSql.getLong();
+        //设置新用户充值人数
+        dayN.setRechargeNum(newUserAmountNum);*/
+
         return dayN;
     }
 
@@ -2012,25 +2033,25 @@ public class GameDataServiceImpl implements IGameDataService {
     private String gameDataTotalSqlRecharge() {
         return """
                 SELECT
-                    ifnull(sum(b.buy_amount_count),0) buy_amount_count,
-                    ifnull(sum(b.buy_amount_num),0) buy_amount_num,
-                    ifnull(sum(b.buy_amount),0) buy_amount,
-                    ifnull(sum(b.nature_amount_count),0) nature_amount_count,
-                    ifnull(sum(b.nature_amount_num),0) nature_amount_num,
-                    ifnull(sum(b.nature_amount),0) nature_amount,
-                    ifnull(sum(b.amount_count),0) amount_count,
-                    ifnull(sum(b.amount_num),0) amount_num,
-                    ifnull(sum(b.amount),0) amount,
+                    ifnull(sum(buy_amount_count),0) buy_amount_count,
+                    ifnull(sum(buy_amount_num),0) buy_amount_num,
+                    ifnull(sum(buy_amount),0) buy_amount,
+                    ifnull(sum(nature_amount_count),0) nature_amount_count,
+                    ifnull(sum(nature_amount_num),0) nature_amount_num,
+                    ifnull(sum(nature_amount),0) nature_amount,
+                    ifnull(sum(amount_count),0) amount_count,
+                    ifnull(sum(amount_num),0) amount_num,
+                    ifnull(sum(amount),0) amount,
                     
-                    round(if(sum(b.buy_amount_count) > 0, sum(b.buy_amount) / sum(b.buy_amount_count), 0), 2) buy_avg_amount,
-                    round(if(sum(b.nature_amount_count) > 0, sum(b.nature_amount) / sum(b.nature_amount_count), 0), 2) nature_avg_amount,
-                    round(if(sum(b.amount_count) > 0, sum(b.amount) / sum(b.amount_count), 0), 2) avg_amount,
-                    round(if(sum(b.buy_amount_num) > 0 , sum(b.buy_amount) / sum(b.buy_amount_num), 0), 2) buy_amount_arpu,
-                    round(if(sum(b.nature_amount_num) > 0 , sum(b.nature_amount) / sum(b.nature_amount_num), 0), 2) nature_amount_arpu,
-                    round(if(sum(b.amount_num) > 0 , sum(b.amount) / sum(b.amount_num), 0), 2) amount_arpu
+                    round(if(sum(buy_amount_count) > 0, sum(buy_amount) / sum(buy_amount_count), 0), 2) buy_avg_amount,
+                    round(if(sum(nature_amount_count) > 0, sum(nature_amount) / sum(nature_amount_count), 0), 2) nature_avg_amount,
+                    round(if(sum(amount_count) > 0, sum(amount) / sum(amount_count), 0), 2) avg_amount,
+                    round(if(sum(buy_amount_num) > 0 , sum(buy_amount) / sum(buy_amount_num), 0), 2) buy_amount_arpu,
+                    round(if(sum(nature_amount_num) > 0 , sum(nature_amount) / sum(nature_amount_num), 0), 2) nature_amount_arpu,
+                    round(if(sum(amount_num) > 0 , sum(amount) / sum(amount_num), 0), 2) amount_arpu
                     
                 FROM
-                    ads_game_day b
+                    game_ads.ads_game_day
                                 
                 """;
     }

+ 23 - 2
game-data/game-data-serve/src/main/java/com/zanxiang/game/data/serve/service/impl/PromotionDayServiceImpl.java

@@ -539,7 +539,17 @@ public class PromotionDayServiceImpl implements IAdsPromotionDayService {
                 	SUM(d3) as d3,
                 	SUM(d7) as d7,
                 	SUM(d15) as d15,
-                	SUM(active) as active
+                	IFNULL(SUM(active), 0) as active,
+                    ROUND(IF(SUM(active) > 0, SUM(today_cost) / SUM(active), 0), 2) as active_cost,
+                    IFNULL(SUM(first_convert_count), 0) as first_convert_count,
+                    ROUND(IF(SUM(first_convert_count) > 0, SUM(today_cost) / SUM(first_convert_count), 0), 2) as first_convert_cost,
+                    ROUND(IF(SUM(active) > 0, SUM(game_pay_count) / SUM(active), 0), 4) as first_active_pay_rate,
+                    SUM(first_attribution_game_in_app_ltv1day) as first_attribution_game_in_app_ltv1day,
+                    ROUND(IF(SUM(today_cost) > 0 , SUM(first_attribution_game_in_app_ltv1day) / SUM(today_cost), 0), 4) as first_attribution_game_in_app_roi1day,
+                    SUM(game_pay_count) as game_pay_count,
+                    ROUND(IF(SUM(reg_num) > 0, SUM(first_attribution_game_in_app_ltv1day) / SUM(reg_num), 0), 2) as ltv_day1,
+                    (SUM(first_new_user_amount_count) - SUM(game_pay_count)) as sub_order_num,
+                    (SUM(first_new_user_amount) - SUM(first_attribution_game_in_app_ltv1day)) as sub_order_amount
                 FROM
                 	game_ads.ads_promotion_day
                 """;
@@ -647,7 +657,18 @@ public class PromotionDayServiceImpl implements IAdsPromotionDayService {
                 	IFNULL(SUM(d2), 0) as d2,
                 	IFNULL(SUM(d3), 0) as d3,
                 	IFNULL(SUM(d7), 0) as d7,
-                	IFNULL(SUM(d15), 0) as d15
+                	IFNULL(SUM(d15), 0) as d15,
+                	IFNULL(SUM(active), 0) as active,
+                    ROUND(IF(SUM(active) > 0, SUM(today_cost) / SUM(active), 0), 2) as active_cost,
+                    IFNULL(SUM(first_convert_count), 0) as first_convert_count,
+                    ROUND(IF(SUM(first_convert_count) > 0, SUM(today_cost) / SUM(first_convert_count), 0), 2) as first_convert_cost,
+                    ROUND(IF(SUM(active) > 0, SUM(game_pay_count) / SUM(active), 0), 4) as first_active_pay_rate,
+                    SUM(first_attribution_game_in_app_ltv1day) as first_attribution_game_in_app_ltv1day,
+                    ROUND(IF(SUM(today_cost) > 0 , SUM(first_attribution_game_in_app_ltv1day) / SUM(today_cost), 0), 4) as first_attribution_game_in_app_roi1day,
+                    SUM(game_pay_count) as game_pay_count,
+                    ROUND(IF(SUM(reg_num) > 0, SUM(first_attribution_game_in_app_ltv1day) / SUM(reg_num), 0), 2) as ltv_day1,
+                    (SUM(first_new_user_amount_count) - SUM(game_pay_count)) as sub_order_num,
+                    (SUM(first_new_user_amount) - SUM(first_attribution_game_in_app_ltv1day)) as sub_order_amount
                 FROM
                 	game_ads.ads_promotion_day
                 """;

+ 56 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/GameBackLog.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 : 2023-08-02
+ * @description : 回传日志
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+@Builder
+@TableName("t_game_back_log")
+public class GameBackLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 用户id
+     */
+    private Long userId;
+
+    /**
+     * 订单id
+     */
+    private String orderId;
+
+    /**
+     * 接口描述
+     */
+    private String describe;
+
+    /**
+     * 回传调接口参数
+     */
+    private String callBackParam;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+}

+ 12 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/GameBackLogMapper.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.GameBackLog;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-08-02
+ * @description : ${description}
+ */
+public interface GameBackLogMapper extends BaseMapper<GameBackLog> {
+}

+ 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) {
         SpringApplication.run(SDKApplication.class, args);
-        System.out.println("赞象SDK服务启动成功 <dubbo升级3.0, 小游戏导量H5上线> ( ´・・)ノ(._.`) \n" +
+        System.out.println("赞象SDK服务启动成功 <H5导量兼容账号密码, 回传调接口增加日志> ( ´・・)ノ(._.`) \n" +
                 " ___________ _   __\n" +
                 "/  ___|  _  \\ | / /\n" +
                 "\\ `--.| | | | |/ / \n" +

+ 22 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IGameBackLogService.java

@@ -0,0 +1,22 @@
+package com.zanxiang.game.module.sdk.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.GameBackLog;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-08-02
+ * @description : 游戏回传日志
+ */
+public interface IGameBackLogService extends IService<GameBackLog> {
+
+    /**
+     * 添加日志
+     *
+     * @param userId   用户id
+     * @param orderId  订单id
+     * @param describe 描述
+     * @param param    参数
+     */
+    void addLog(Long userId, String orderId, String describe, String param);
+}

+ 9 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IGameUserRoleService.java

@@ -2,6 +2,7 @@ package com.zanxiang.game.module.sdk.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.zanxiang.game.module.mybatis.entity.GameUserRole;
+import com.zanxiang.game.module.mybatis.entity.User;
 import com.zanxiang.game.module.sdk.pojo.param.GameUserRoleUpdateParam;
 import com.zanxiang.game.module.sdk.pojo.param.UserData;
 
@@ -17,6 +18,14 @@ public interface IGameUserRoleService extends IService<GameUserRole> {
      */
     Boolean updateUserGameRole(GameUserRoleUpdateParam param, UserData userData);
 
+    /**
+     * 用户指南游戏角色检查
+     *
+     * @param user         用户
+     * @param relationUser 用户关系
+     */
+    void userGuideGameRoleCheck(User user, User relationUser);
+
     /**
      * 通知监听服务
      *

+ 9 - 6
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/CallBackServiceImpl.java

@@ -55,6 +55,9 @@ public class CallBackServiceImpl implements ICallBackService {
     @Autowired
     private IGameExtService gameExtService;
 
+    @Autowired
+    private IGameBackLogService gameBackLogService;
+
     @Override
     public void userCallBack(User user, Map<String, String> urlParamMap) {
         //判断游戏是否开启广告回传, 未开启, 不回传
@@ -80,8 +83,8 @@ public class CallBackServiceImpl implements ICallBackService {
             //腾讯H5回传
             if (Objects.equals(agent.getAccountType(), AccountTypeEnum.TENCENT_H5.getValue())) {
                 TencentUserDTO tencentUserDTO = this.transform(user, agent, gameApplet);
-                log.error("用户注册 --> 腾讯H5回传提交, tencentUserDTO : {}", JsonUtil.toString(tencentUserDTO));
                 tencentUserActionBackRpc.backUser(tencentUserDTO);
+                gameBackLogService.addLog(user.getId(), null, "腾讯H5用户回传提交", JsonUtil.toString(tencentUserDTO));
             }
             //腾讯小游戏回传
             if (Objects.equals(agent.getAccountType(), AccountTypeEnum.TENCENT_MINI_GAME.getValue())) {
@@ -89,8 +92,8 @@ public class CallBackServiceImpl implements ICallBackService {
                 //解析设置clickId
                 Map<String, String> channelMap = agentService.channelTransform(user.getChannel());
                 tencentUserDTO.setClickId(channelMap.get("gdt_vid"));
-                log.error("用户注册 --> 腾讯小游戏回传提交, tencentUserDTO : {}", JsonUtil.toString(tencentUserDTO));
                 tencentMiniGameBackRpc.backUser(tencentUserDTO);
+                gameBackLogService.addLog(user.getId(), null, "腾讯小游戏用户回传提交", JsonUtil.toString(tencentUserDTO));
             }
             //头条回传
             if (CollectionUtils.isNotEmpty(urlParamMap) && Objects.equals(agent.getAccountType(), AccountTypeEnum.BYTE.getValue())) {
@@ -98,8 +101,8 @@ public class CallBackServiceImpl implements ICallBackService {
                 //判断是微信小游戏
                 if (Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_WX_APPLET.getId())) {
                     TtUserActiveRpcDTO activeReportRpcDTO = this.transform(user, agent, gameApplet, urlParamMap);
-                    log.error("用户注册 --> 头条回传提交, activeReportRpcDTO : {}", JsonUtil.toString(activeReportRpcDTO));
                     ttMiniGameBackRpc.userActiveReport(activeReportRpcDTO);
+                    gameBackLogService.addLog(user.getId(), null, "头条用户回传提交", JsonUtil.toString(activeReportRpcDTO));
                 }
             }
         } catch (Exception e) {
@@ -130,8 +133,8 @@ public class CallBackServiceImpl implements ICallBackService {
             //腾讯H5回传
             if (Objects.equals(agent.getAccountType(), AccountTypeEnum.TENCENT_H5.getValue())) {
                 TencentOrderDTO tencentOrderDTO = this.transform(platformOrderDTO, user, agent, gameApplet);
-                log.error("用户下单 --> 腾讯H5回传提交, tencentOrderDTO : {}", JsonUtil.toString(tencentOrderDTO));
                 tencentUserActionBackRpc.backOrder(tencentOrderDTO);
+                gameBackLogService.addLog(user.getId(), platformOrderDTO.getOrderId(), "腾讯H5订单回传提交", JsonUtil.toString(tencentOrderDTO));
             }
             //腾讯小游戏回传
             if (Objects.equals(agent.getAccountType(), AccountTypeEnum.TENCENT_MINI_GAME.getValue())) {
@@ -139,8 +142,8 @@ public class CallBackServiceImpl implements ICallBackService {
                 //解析设置clickId
                 Map<String, String> channelMap = agentService.channelTransform(user.getChannel());
                 tencentOrderDTO.setClickId(channelMap.get("gdt_vid"));
-                log.error("用户下单 --> 腾讯小游戏回传提交, tencentOrderDTO : {}", JsonUtil.toString(tencentOrderDTO));
                 tencentMiniGameBackRpc.backOrder(tencentOrderDTO);
+                gameBackLogService.addLog(user.getId(), platformOrderDTO.getOrderId(), "腾讯小游戏订单回传提交", JsonUtil.toString(tencentOrderDTO));
             }
             //头条回传
             if (Objects.equals(agent.getAccountType(), AccountTypeEnum.BYTE.getValue())) {
@@ -148,8 +151,8 @@ public class CallBackServiceImpl implements ICallBackService {
                 Game game = gameService.getById(platformOrderDTO.getGameId());
                 if (Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_WX_APPLET.getId())) {
                     TtOrderRpcDTO ttOrderRpcDTO = this.transform(platformOrderDTO, user.getOpenId(), agent, gameApplet, user.getCreateTime());
-                    log.error("用户下单 --> 头条回传提交, orderReportRpcDTO : {}", JsonUtil.toString(ttOrderRpcDTO));
                     ttMiniGameBackRpc.orderReport(ttOrderRpcDTO);
+                    gameBackLogService.addLog(user.getId(), platformOrderDTO.getOrderId(), "头条订单回传提交", JsonUtil.toString(ttOrderRpcDTO));
                 }
             }
         } catch (Exception e) {

+ 36 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameBackLogServiceImpl.java

@@ -0,0 +1,36 @@
+package com.zanxiang.game.module.sdk.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.mybatis.entity.GameBackLog;
+import com.zanxiang.game.module.mybatis.mapper.GameBackLogMapper;
+import com.zanxiang.game.module.sdk.service.IGameBackLogService;
+import com.zanxiang.module.util.JsonUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-08-02
+ * @description : 游戏回传日志
+ */
+@Slf4j
+@Service
+public class GameBackLogServiceImpl extends ServiceImpl<GameBackLogMapper, GameBackLog> implements IGameBackLogService {
+
+    @Override
+    public void addLog(Long userId, String orderId, String describe, String param) {
+        try {
+            super.save(GameBackLog.builder()
+                    .userId(userId)
+                    .orderId(orderId)
+                    .describe(describe)
+                    .callBackParam(JsonUtil.toString(param))
+                    .createTime(LocalDateTime.now())
+                    .build());
+        } catch (Exception e) {
+            log.error("回传接口日志保存异常, param : {}, e : {}", param, e.getMessage());
+        }
+    }
+}

+ 60 - 3
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameUserRoleServiceImpl.java

@@ -2,6 +2,7 @@ package com.zanxiang.game.module.sdk.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zanxiang.game.module.mybatis.entity.GameUser;
 import com.zanxiang.game.module.mybatis.entity.GameUserRole;
@@ -26,9 +27,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.client.RestTemplate;
 
 import java.time.LocalDateTime;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -158,6 +157,64 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
         distributedLockComponent.unlock(RedisKeyConstant.ROLE_UPDATE_KEY + "_" + userData.getUserId());
     }
 
+    @Override
+    public void userGuideGameRoleCheck(User user, User relationUser) {
+        //查询H5游戏角色列表
+        List<GameUserRole> relationUserRoleList = super.list(new LambdaQueryWrapper<GameUserRole>()
+                .eq(GameUserRole::getUserId, relationUser.getId())
+                .eq(GameUserRole::getGameId, relationUser.getGameId()));
+        //查询小游戏角色列表
+        List<GameUserRole> userRoleList = super.list(new LambdaQueryWrapper<GameUserRole>()
+                .eq(GameUserRole::getUserId, user.getId())
+                .eq(GameUserRole::getGameId, user.getGameId()));
+        //角色数量一致, 结束
+        if (userRoleList.size() == relationUserRoleList.size()) {
+            return;
+        }
+        //查询游戏玩家信息
+        GameUser gameUser = gameUserService.getOne(new LambdaQueryWrapper<GameUser>()
+                .eq(GameUser::getUserId, user.getId())
+                .eq(GameUser::getGameId, user.getGameId()));
+        if (gameUser == null) {
+            return;
+        }
+        //循环
+        List<GameUserRole> gameUserRoleList = new ArrayList<>();
+        relationUserRoleList.forEach(gameUserRole -> {
+            String roleId = gameUserRole.getRoleId();
+            GameUserRole userRole = userRoleList.stream()
+                    .filter(role -> Objects.equals(role.getRoleId(), roleId))
+                    .findFirst().orElse(null);
+            //缺少角色, 说明转到H5之后又重新在小程序登录创建了角色
+            if (userRole == null) {
+                gameUserRoleList.add(this.transform(user, gameUserRole, gameUser));
+            }
+        });
+        if (CollectionUtils.isNotEmpty(gameUserRoleList)) {
+            super.saveBatch(gameUserRoleList);
+        }
+    }
+
+    private GameUserRole transform(User user, GameUserRole gameUserRole, GameUser gameUser) {
+        return GameUserRole.builder()
+                .userId(user.getId())
+                .gameUserId(gameUser.getId())
+                .gameId(user.getGameId())
+                .serverId(gameUserRole.getServerId())
+                .serverName(gameUserRole.getServerName())
+                .roleId(gameUserRole.getRoleId())
+                .roleName(gameUserRole.getRoleName())
+                .roleLevel(gameUserRole.getRoleLevel())
+                .roleVipLevel(gameUserRole.getRoleVipLevel())
+                .rolePower(gameUserRole.getRolePower())
+                .os(gameUserRole.getOs())
+                .regTime(gameUserRole.getCreateTime())
+                .createTime(gameUserRole.getCreateTime())
+                .updateTime(gameUserRole.getUpdateTime())
+                .lastLoginTime(gameUserRole.getLastLoginTime())
+                .build();
+    }
+
     @Override
     public void callListenIn(GameUserRoleUpdateParam param, UserData userData) {
         Map<String, String> map = new HashMap<>(2);

+ 45 - 11
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/LoginServiceImpl.java

@@ -24,7 +24,9 @@ import org.apache.logging.log4j.util.Strings;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import reactor.util.function.Tuple2;
 import reactor.util.function.Tuple3;
+import reactor.util.function.Tuples;
 
 import java.time.LocalDateTime;
 import java.util.Map;
@@ -90,6 +92,9 @@ public class LoginServiceImpl implements IRegisterLoginService {
     @Autowired
     private IGameService gameService;
 
+    @Autowired
+    private IGameUserRoleService gameUserRoleService;
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public ResultVO<UserLoginVO> loginWxCode(LoginVxCodeParam param, UserData userData) {
@@ -178,11 +183,18 @@ public class LoginServiceImpl implements IRegisterLoginService {
             }
             //渠道更新和回传判断
             agentService.userAgentUpdate(user, userData.getChannel());
+            //登录信息
+            UserLoginVO userLoginVO = this.createUserLoginVO(user, userData);
+            Tuple2<Boolean, Long> tuple2 = this.userGuideCheck(user);
+            //导量用户返回原有的用户id
+            if (tuple2.getT1()) {
+                userLoginVO.setUserId(tuple2.getT2());
+            }
             //返回登录信息
-            return ResultVO.ok(this.createUserLoginVO(user, userData));
+            return ResultVO.ok(userLoginVO);
         }
         //用户注册, 用户名密码校验
-        HttpStatusEnum checkRegisterEnum = this.checkRegister(username, password);
+        HttpStatusEnum checkRegisterEnum = this.checkRegister(userData.getGameId(), username, password);
         if (!Objects.equals(checkRegisterEnum, HttpStatusEnum.SUCCESS)) {
             return ResultVO.fail(checkRegisterEnum.getMsg());
         }
@@ -218,14 +230,11 @@ public class LoginServiceImpl implements IRegisterLoginService {
             //渠道更新和回传判断
             agentService.userAgentUpdate(user, userData.getChannel());
             UserLoginVO userLoginVO = this.createUserLoginVO(user, userData);
-            Game game = gameService.getById(user.getGameId());
             //判断是否导量用户
-            if (user.getRelationUserId() != null && game.getGuideGameId() != null) {
-                User relationUser = userService.getById(user.getRelationUserId());
-                if (relationUser != null) {
-                    //导量用户返回原有的用户id
-                    userLoginVO.setUserId(relationUser.getId());
-                }
+            Tuple2<Boolean, Long> tuple2 = this.userGuideCheck(user);
+            //导量用户返回原有的用户id
+            if (tuple2.getT1()) {
+                userLoginVO.setUserId(tuple2.getT2());
             }
             //返回登录信息
             return ResultVO.ok(userLoginVO);
@@ -236,6 +245,28 @@ public class LoginServiceImpl implements IRegisterLoginService {
         return ResultVO.ok(this.createUserLoginVO(user, userData));
     }
 
+    private Tuple2<Boolean, Long> userGuideCheck(User user) {
+        //游戏信息
+        Game game = gameService.getById(user.getGameId());
+        //非导量用户
+        if (user.getRelationUserId() == null || game.getGuideGameId() == null) {
+            return Tuples.of(Boolean.FALSE, 0L);
+        }
+        //导量用户信息
+        User relationUser = userService.getById(user.getRelationUserId());
+        //确定导量匹配
+        if (relationUser != null && Objects.equals(relationUser.getRelationUserId(), user.getId())) {
+            try {
+                //监测角色是否导全, 或者导过之后又跑去小程序创建了其他角色
+                gameUserRoleService.userGuideGameRoleCheck(user, relationUser);
+            } catch (Exception ignored) {
+            }
+            return Tuples.of(Boolean.TRUE, relationUser.getId());
+        }
+        //非导量兜底逻辑
+        return Tuples.of(Boolean.FALSE, 0L);
+    }
+
     private User userCreateSave(UserData userData, String userName, String password, String mobile, String openId, String sessionKey) {
         //渠道id, 链接参数, 分享人id
         Tuple3<Long, Map<String, String>, String> tuple3 = agentService.getUserAgentId(userData);
@@ -333,13 +364,16 @@ public class LoginServiceImpl implements IRegisterLoginService {
         return Boolean.TRUE;
     }
 
-    private HttpStatusEnum checkRegister(String username, String password) {
+    private HttpStatusEnum checkRegister(Long gameId, String username, String password) {
         //判断用户名是否存在敏感词
         if (wordCheckService.hasWord(username)) {
             return HttpStatusEnum.USERNAME_SENSITIVE;
         }
         //判断用户名是否已存在
-        if (userService.count(new LambdaQueryWrapper<User>().eq(User::getUsername, username)) > 0) {
+        if (userService.count(new LambdaQueryWrapper<User>()
+                .eq(User::getGameId, gameId)
+                .eq(User::getUsername, username)
+        ) > 0) {
             return HttpStatusEnum.USERNAME_EXISTS;
         }
         //用户名合规检测