Переглянути джерело

feat(push): 添加推送任务统计功能支持多渠道

- 集成 CommonPushTaskStatsDto 数据传输对象
- 添加 CommonPushTaskUserMapper 用于统计数据查询
- 扩展 queryChannelStats 方法以支持华为、荣耀、三星和 APNS 渠道
- 实现基于回调数据的统计聚合功能
- 添加华为、荣耀、三星和 APNS 渠道的统计查询方法
- 实现零值检查和数据转换工具方法
- 优化渠道统计查询逻辑并传入推送任务 ID 和设备类型参数
fcw 1 день тому
батько
коміт
13e8db0c87

+ 16 - 0
alien-entity/src/main/java/shop/alien/mapper/CommonPushTaskUserMapper.java

@@ -1,7 +1,23 @@
 package shop.alien.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 import shop.alien.entity.store.CommonPushTaskUser;
+import shop.alien.entity.store.dto.CommonPushTaskStatsDto;
 
 public interface CommonPushTaskUserMapper extends BaseMapper<CommonPushTaskUser> {
+
+    @Select("<script>" +
+            "SELECT " +
+            "IFNULL(SUM(CASE WHEN status IN (1, 2, 3) THEN 1 ELSE 0 END), 0) AS sentCount, " +
+            "IFNULL(SUM(CASE WHEN status IN (1, 3) THEN 1 ELSE 0 END), 0) AS deliveredCount, " +
+            "IFNULL(SUM(CASE WHEN show_info = 1 THEN 1 ELSE 0 END), 0) AS showCount, " +
+            "IFNULL(SUM(CASE WHEN user_add = 1 THEN 1 ELSE 0 END), 0) AS clickCount " +
+            "FROM common_push_task_user " +
+            "WHERE delete_flag = 0 AND push_task_id = #{pushTaskId} " +
+            "<if test='phoneType != null and phoneType != \"\"'>AND phone_type = #{phoneType} </if>" +
+            "</script>")
+    CommonPushTaskStatsDto selectStatsByTaskAndPhoneType(@Param("pushTaskId") Long pushTaskId,
+                                                         @Param("phoneType") String phoneType);
 }

+ 72 - 2
alien-store/src/main/java/shop/alien/store/service/impl/CommonPushTaskStatsServiceImpl.java

@@ -10,8 +10,10 @@ import shop.alien.entity.store.CommonPushChannelConfig;
 import shop.alien.entity.store.CommonPushTask;
 import shop.alien.entity.store.CommonPushTaskNum;
 import shop.alien.entity.store.CommonPushTaskStatus;
+import shop.alien.entity.store.dto.CommonPushTaskStatsDto;
 import shop.alien.mapper.CommonPushChannelConfigMapper;
 import shop.alien.mapper.CommonPushTaskMapper;
+import shop.alien.mapper.CommonPushTaskUserMapper;
 import shop.alien.store.dto.CommonPushChannelStatsDto;
 import shop.alien.store.service.CommonPushTaskNumService;
 import shop.alien.store.service.CommonPushTaskStatsService;
@@ -34,6 +36,7 @@ public class CommonPushTaskStatsServiceImpl implements CommonPushTaskStatsServic
     );
 
     private final CommonPushTaskMapper commonPushTaskMapper;
+    private final CommonPushTaskUserMapper commonPushTaskUserMapper;
     private final CommonPushTaskNumService commonPushTaskNumService;
     private final CommonPushChannelConfigMapper commonPushChannelConfigMapper;
     private final CommonPushVendorHttpClient commonPushVendorHttpClient;
@@ -85,7 +88,8 @@ public class CommonPushTaskStatsServiceImpl implements CommonPushTaskStatsServic
             if (credential == null) {
                 continue;
             }
-            CommonPushChannelStatsDto stats = queryChannelStats(channelCode, credential, vendorTaskIds, entry.getValue());
+            CommonPushChannelStatsDto stats = queryChannelStats(channelCode, credential, vendorTaskIds,
+                    entry.getValue(), task.getId(), phoneType);
             if (stats == null) {
                 continue;
             }
@@ -96,7 +100,8 @@ public class CommonPushTaskStatsServiceImpl implements CommonPushTaskStatsServic
     }
 
     private CommonPushChannelStatsDto queryChannelStats(String channelCode, JSONObject credential,
-                                                          Map<String, String> vendorTaskIds, String taskIdValue) {
+                                                          Map<String, String> vendorTaskIds, String taskIdValue,
+                                                          Long pushTaskId, String phoneType) {
         if (StringUtils.isBlank(taskIdValue)) {
             return null;
         }
@@ -110,12 +115,77 @@ public class CommonPushTaskStatsServiceImpl implements CommonPushTaskStatsServic
                 return commonPushVendorHttpClient.queryVivoStatistics(credential, firstTaskId);
             case "xiaomi":
                 return commonPushVendorHttpClient.queryXiaomiStatistics(credential, firstTaskId);
+            case "huawei":
+                return queryHuaweiStatistics(pushTaskId, phoneType);
+            case "honor":
+                return queryHonorStatistics(pushTaskId, phoneType);
+            case "samsung":
+                return querySamsungStatistics(pushTaskId, phoneType);
+            case "apns":
+                return queryApnsStatistics(pushTaskId, phoneType);
             default:
                 log.debug("渠道暂无统计查询接口, channelCode={}, taskId={}", channelCode, firstTaskId);
                 return null;
         }
     }
 
+    /**
+     * 华为无厂商侧统计拉取 API,基于 common_push_task_user 回调数据聚合。
+     */
+    private CommonPushChannelStatsDto queryHuaweiStatistics(Long pushTaskId, String phoneType) {
+        return aggregateCallbackStatistics(pushTaskId, phoneType, "huawei");
+    }
+
+    private CommonPushChannelStatsDto queryHonorStatistics(Long pushTaskId, String phoneType) {
+        return aggregateCallbackStatistics(pushTaskId, phoneType, "honor");
+    }
+
+    private CommonPushChannelStatsDto querySamsungStatistics(Long pushTaskId, String phoneType) {
+        return aggregateCallbackStatistics(pushTaskId, phoneType, "samsung");
+    }
+
+    private CommonPushChannelStatsDto queryApnsStatistics(Long pushTaskId, String phoneType) {
+        return aggregateCallbackStatistics(pushTaskId, phoneType, "apns");
+    }
+
+    private CommonPushChannelStatsDto aggregateCallbackStatistics(Long pushTaskId, String phoneType,
+                                                                    String channelCode) {
+        if (pushTaskId == null) {
+            return null;
+        }
+        CommonPushTaskStatsDto stats = commonPushTaskUserMapper.selectStatsByTaskAndPhoneType(pushTaskId, phoneType);
+        if (isZeroStats(stats) && StringUtils.isNotBlank(phoneType)) {
+            stats = commonPushTaskUserMapper.selectStatsByTaskAndPhoneType(pushTaskId, null);
+        }
+        if (stats == null || isZeroStats(stats)) {
+            log.debug("回调统计无数据, channelCode={}, pushTaskId={}, phoneType={}", channelCode, pushTaskId, phoneType);
+            return null;
+        }
+        CommonPushChannelStatsDto result = new CommonPushChannelStatsDto();
+        result.setRealSend(longToString(stats.getSentCount()));
+        result.setRealDelivered(longToString(stats.getDeliveredCount()));
+        result.setShowSum(longToString(stats.getShowCount()));
+        result.setClickSum(longToString(stats.getClickCount()));
+        log.info("回调统计聚合完成, channelCode={}, pushTaskId={}, phoneType={}, stats={}",
+                channelCode, pushTaskId, phoneType, result);
+        return result;
+    }
+
+    private boolean isZeroStats(CommonPushTaskStatsDto stats) {
+        return longValue(stats.getSentCount()) == 0
+                && longValue(stats.getDeliveredCount()) == 0
+                && longValue(stats.getShowCount()) == 0
+                && longValue(stats.getClickCount()) == 0;
+    }
+
+    private long longValue(Long value) {
+        return value == null ? 0L : value;
+    }
+
+    private String longToString(Long value) {
+        return value == null ? null : String.valueOf(value);
+    }
+
     private void upsertTaskNum(CommonPushTask task, String phoneType, CommonPushChannelStatsDto stats) {
         CommonPushTaskNum existing = commonPushTaskNumService.getOne(new LambdaQueryWrapper<CommonPushTaskNum>()
                 .eq(CommonPushTaskNum::getPushTaskId, task.getId())