Przeglądaj źródła

feat: 小说服务同步广告离线保存接口

hidewnd 3 tygodni temu
rodzic
commit
e47cc1ea82

+ 10 - 1
yt-novel/yt-novel-service/src/main/java/com/ytpm/novel/controller/AdController.java

@@ -2,6 +2,7 @@ package com.ytpm.novel.controller;
 
 import com.ytpm.agent.view.AgentAdGroupStaticsVO;
 import com.ytpm.agent.view.AgentTopCountView;
+import com.ytpm.app.param.DyzAdRecordDelayParam;
 import com.ytpm.app.param.DyzAdRecordParam;
 import com.ytpm.general.Result;
 import com.ytpm.middle.view.DashboardRankingListVO;
@@ -39,10 +40,18 @@ public class AdController {
      */
     @ApiOperation("保存")
     @PostMapping("/saveRecord")
-    public Result<?> saveRecord(@RequestBody DyzAdRecordParam param) {
+    public Result<String> saveRecord(@RequestBody DyzAdRecordParam param) {
         return adService.saveRecord(param);
     }
 
+    /**
+     * 保存因离线而暂存的广告记录
+     */
+    @ApiOperation("延迟保存广告记录")
+    @PostMapping("/saveRecord/delay")
+    public Result<String> delaySaveRecord(@RequestBody DyzAdRecordDelayParam param) {
+        return adService.delaySaveRecord(param);
+    }
     /**
      * 根据应用ID查询广告数
      */

+ 15 - 0
yt-novel/yt-novel-service/src/main/java/com/ytpm/novel/dao/AdRecordMapper.java

@@ -19,11 +19,26 @@ public interface AdRecordMapper {
      */
     void addOne(YtDyzAdRecord adRecord);
 
+    /**
+     * 批量新增广告记录
+     */
+    void batchAdd(@Param("adRecords") List<YtDyzAdRecord> adRecords);
+
     /**
      * 保存游客广告记录
      */
     void addOneVisitor(YtDyzAdRecord adRecord);
 
+    /**
+     * 批量新增游客广告记录
+     */
+    void batchAddVisitor(@Param("adRecords") List<YtDyzAdRecord> adRecords);
+
+    /**
+     * 根据 json id字段查询广告记录
+     */
+    YtDyzAdRecord getByTakuAdId(@Param("userId") String userId, @Param("id") String id);
+
     /**
      * 查询用户的广告记录
      */

+ 14 - 2
yt-novel/yt-novel-service/src/main/java/com/ytpm/novel/service/AdService.java

@@ -3,8 +3,8 @@ package com.ytpm.novel.service;
 import com.ytpm.agent.view.AgentAdGroupStaticsVO;
 import com.ytpm.agent.view.AgentTopCountView;
 import com.ytpm.app.model.YtDyzAdRecord;
-import com.ytpm.app.model.YtDyzUser;
 import com.ytpm.app.model.YtNovelUser;
+import com.ytpm.app.param.DyzAdRecordDelayParam;
 import com.ytpm.app.param.DyzAdRecordParam;
 import com.ytpm.general.Result;
 import com.ytpm.middle.view.DashboardRankingListVO;
@@ -20,7 +20,12 @@ public interface AdService {
     /**
      * 保存广告记录
      */
-    Result<?> saveRecord(DyzAdRecordParam param);
+    Result<String> saveRecord(DyzAdRecordParam param);
+
+    /**
+     * 批量保存离线广告记录
+     */
+    Result<String> delaySaveRecord(DyzAdRecordDelayParam param);
 
     /**
      * 根据应用查询广告数量
@@ -52,8 +57,15 @@ public interface AdService {
      */
     List<AgentAdGroupStaticsVO> getAgentProfit(String appIds);
 
+    /**
+     * 保存记录
+     * 始终创建新的事务以保障子方法的独立事务
+     */
     String saveRecordAndChangeUser(DyzAdRecordParam param, YtNovelUser user);
 
+    /**
+     * 根据ID查询多条广告记录
+     */
     List<YtDyzAdRecord> queryRecordByIds(List<String> adRecordIds);
 
     /**

+ 90 - 15
yt-novel/yt-novel-service/src/main/java/com/ytpm/novel/service/impl/AdServiceImpl.java

@@ -1,9 +1,12 @@
 package com.ytpm.novel.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSONObject;
 import com.ytpm.advertise.enums.AdPlatformTypeEnum;
 import com.ytpm.advertise.enums.AdSourceTypeEnum;
 import com.ytpm.agent.enums.AdRecordEnum;
@@ -13,6 +16,7 @@ import com.ytpm.agent.view.AgentTopCountView;
 import com.ytpm.app.model.YtDyzAdRecord;
 import com.ytpm.app.model.YtNovelAdRecord;
 import com.ytpm.app.model.YtNovelUser;
+import com.ytpm.app.param.DyzAdRecordDelayParam;
 import com.ytpm.app.param.DyzAdRecordParam;
 import com.ytpm.feign.RiskFeign;
 import com.ytpm.general.RepMessage;
@@ -31,9 +35,10 @@ import com.ytpm.novel.dao.AdRecordMapper;
 import com.ytpm.novel.dao.AppUserMapper;
 import com.ytpm.novel.dao.NovelAdRecordMapper;
 import com.ytpm.novel.model.param.NovelAdRecordParam;
-import com.ytpm.novel.service.AdService;
 import com.ytpm.novel.model.view.AgentNetworkAgg;
+import com.ytpm.novel.service.AdService;
 import com.ytpm.novel.service.AppUserService;
+import com.ytpm.util.DateUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -79,7 +84,7 @@ public class AdServiceImpl implements AdService {
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public Result<?> saveRecord(DyzAdRecordParam param) {
+    public Result<String> saveRecord(DyzAdRecordParam param) {
         YtNovelUser user = appUserMapper.selectPrimaryKey(param.getUserId());
         log.debug(StrUtil.format("[saveRecord] userId:{} revenue:{} ", param.getUserId(), param.getRevenue()));
         if (Objects.isNull(user)) {
@@ -93,7 +98,7 @@ public class AdServiceImpl implements AdService {
         if (AdSourceTypeEnum.rewarded_video.getAdSourceType() == param.getAdSourceType()) {
             Result<?> result = riskFeign.checkAdRisk(user);
             if (result.getCode() != 200) {
-                Result<Object> resultObj = new Result<>(StatusCode.ACCESS_ERR, getTipsMsg());
+                Result<String> resultObj = new Result<>(StatusCode.ACCESS_ERR, getTipsMsg());
                 if ("766".contains(result.getMessage())) {
                     // 成本收益限制处理返回值 766
                     resultObj.setData(result.getMessage());
@@ -113,6 +118,84 @@ public class AdServiceImpl implements AdService {
         return split[RandomUtil.randomInt(split.length)];
     }
 
+    @Override
+    public Result<String> delaySaveRecord(DyzAdRecordDelayParam param) {
+        YtNovelUser user = appUserMapper.selectPrimaryKey(param.getUserId());
+        if (Objects.isNull(user)) {
+            return Result.resultOk(RepMessage.SAVE_SUCCESS);
+        }
+        if (!UserStatusEnum.NORMAL.getCode().equals(user.getUserStatus())) {
+            return new Result<>(StatusCode.ACCESS_ERR, getTipsMsg());
+        }
+        log.info("[delaySaveRecord] userId:{}", param.getUserId());
+        if (CollectionUtil.isNotEmpty(param.getAdRecords())) {
+            YtDyzAdRecord insertEntity;
+            List<String> filter = new ArrayList<>();
+            List<YtDyzAdRecord> saveList = new ArrayList<>();
+            BigDecimal totalRevenue = BigDecimal.ZERO;
+            for (DyzAdRecordParam adParam : param.getAdRecords()) {
+                handleTimeNull(adParam);
+                String takuAdId = null;
+                if (StrUtil.isNotEmpty(adParam.getResultJson())) {
+                    JSONObject json = JSONObject.parseObject(adParam.getResultJson());
+                    takuAdId = json.getString("id");
+                }
+                // 广告数据校验 重复数据过滤
+                if (StrUtil.isNotEmpty(takuAdId)) {
+                    if (filter.contains(takuAdId)) {
+                        continue;
+                    }
+                    YtDyzAdRecord record = adRecordMapper.getByTakuAdId(user.getUserId(), takuAdId);
+                    if (record != null) {
+                        continue;
+                    }
+                    filter.add(takuAdId);
+                }
+                insertEntity = new YtDyzAdRecord();
+                BeanUtils.copyProperties(adParam, insertEntity);
+                insertEntity.setRecordId(IdUtil.fastSimpleUUID());
+                insertEntity.setUserId(user.getUserId());
+                insertEntity.setNetworkName(AdPlatformTypeEnum.getDesc(Integer.parseInt(adParam.getNetworkFormId())));
+                saveList.add(insertEntity);
+                if (AdRecordEnum.LOGIN_BEFORE.getCode().equals(param.getLoginStatus())
+                        && AdSourceTypeEnum.rewarded_video.getAdSourceType() == adParam.getAdSourceType()) {
+                    continue;
+                }
+                if (insertEntity.getRevenue() != null) {
+                    totalRevenue = totalRevenue.add(insertEntity.getRevenue());
+                }
+            }
+            if (CollUtil.isNotEmpty(saveList)) {
+                log.info("[delaySaveRecord] saveCount:{}, {}", saveList.size(), saveList.stream().map(YtDyzAdRecord::getRecordId).collect(Collectors.joining(",")));
+                if (param.getLoginStatus() != null && AdRecordEnum.LOGIN_BEFORE.getCode().equals(param.getLoginStatus())) {
+                    adRecordMapper.batchAddVisitor(saveList);
+                } else {
+                    adRecordMapper.batchAdd(saveList);
+                }
+                appUserMapper.updateTotal(user.getUserId(), saveList.size(), totalRevenue);
+            }
+        }
+        return Result.resultOk(RepMessage.SAVE_SUCCESS);
+    }
+
+    /**
+     * 处理传入广告记录 beginTime FinishTime可能为null的情况
+     */
+    private void handleTimeNull(DyzAdRecordParam param) {
+        param.setFinishTime(verifyTimeParam("finishTime", param.getFinishTime(), null));
+        param.setBeginTime(verifyTimeParam("beginTime", param.getBeginTime(), param.getFinishTime()));
+    }
+
+    private String verifyTimeParam(String fieldName, String value, String defaultValue) {
+        if (Objects.isNull(value) || "null".equals(value)) {
+            if ("null".equals(value)) {
+                log.warn("param[{}] value '{}' is null !", fieldName, value);
+            }
+            return StrUtil.isEmpty(defaultValue) ? DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss") : defaultValue;
+        }
+        return value;
+    }
+
     /**
      * 根据应用查询广告数量
      */
@@ -290,12 +373,7 @@ public class AdServiceImpl implements AdService {
     public String saveRecordAndChangeUser(DyzAdRecordParam param, YtNovelUser user) {
         //增加广告记录
         YtDyzAdRecord adRecord = new YtDyzAdRecord();
-        if (Objects.isNull(param.getBeginTime()) || "null".equals(param.getBeginTime())) {
-            if ("null".equals(param.getBeginTime())) {
-                log.warn("param beginTime is null !");
-            }
-            param.setBeginTime(param.getFinishTime());
-        }
+        handleTimeNull(param);
         BeanUtils.copyProperties(param, adRecord);
         adRecord.setRecordId(IdUtil.fastSimpleUUID());
         adRecord.setNetworkName(AdPlatformTypeEnum.getDesc(Integer.parseInt(param.getNetworkFormId())));
@@ -304,12 +382,9 @@ public class AdServiceImpl implements AdService {
         } else {
             adRecordMapper.addOne(adRecord);
         }
-        boolean updateUserTotal = true;
+        boolean updateUserTotal = !AdRecordEnum.LOGIN_BEFORE.getCode().equals(param.getLoginStatus())
+                || AdSourceTypeEnum.rewarded_video.getAdSourceType() != param.getAdSourceType();
         // 游客登陆下 激励视频广告记录不算入用户统计
-        if (AdRecordEnum.LOGIN_BEFORE.getCode().equals(param.getLoginStatus())
-                && AdSourceTypeEnum.rewarded_video.getAdSourceType() == param.getAdSourceType()) {
-            updateUserTotal = false;
-        }
         if (updateUserTotal) {
             //修改用户信息, 广告次数+1  总收益 + revenue
             if (param.getRevenue() == null) {
@@ -342,7 +417,7 @@ public class AdServiceImpl implements AdService {
         BigDecimal redPackRevenue = BigDecimal.ZERO;
         if (userRevenueRate != null) {
             record.setRevenueRate(userRevenueRate.toPlainString());
-            if(record.getRevenue() != null && record.getRevenue().compareTo(BigDecimal.ZERO) > 0){
+            if (record.getRevenue() != null && record.getRevenue().compareTo(BigDecimal.ZERO) > 0) {
                 redPackRevenue = record.getRevenue().multiply(userRevenueRate).setScale(5, RoundingMode.HALF_UP);
             }
         }

+ 94 - 0
yt-novel/yt-novel-service/src/main/resources/mapper/AdRecordMapper.xml

@@ -42,6 +42,48 @@
                 #{appId}
             )
     </insert>
+    <insert id="batchAdd">
+        insert into yt_dyz_ad_record
+        (
+        record_id,
+        user_id,
+        nick_name,
+        placement_id,
+        ad_source_id,
+        revenue,
+        network_form_id,
+        network_name,
+        network_placement_id,
+        begin_time,
+        finish_time,
+        result_json,
+        ad_source_index,
+        ad_source_type,
+        ecpm,
+        app_id
+        )
+        values
+        <foreach collection="adRecords" item="item" separator=",">
+            (
+            #{item.recordId},
+            #{item.userId},
+            #{item.nickName},
+            #{item.placementId},
+            #{item.adSourceId},
+            #{item.revenue},
+            #{item.networkFormId},
+            #{item.networkName},
+            #{item.networkPlacementId},
+            ifnull(#{item.beginTime}, now()),
+            ifnull(#{item.finishTime}, now()),
+            #{item.resultJson},
+            #{item.adSourceIndex},
+            #{item.adSourceType},
+            #{item.ecpm},
+            #{item.appId}
+            )
+        </foreach>
+    </insert>
     <insert id="addOneVisitor">
         insert into yt_dyz_ad_record_visitor
         (
@@ -82,6 +124,49 @@
                 #{appId}
             )
     </insert>
+    <insert id="batchAddVisitor">
+        insert into yt_dyz_ad_record_visitor
+        (
+        record_id,
+        user_id,
+        nick_name,
+        placement_id,
+        ad_source_id,
+        revenue,
+        network_form_id,
+        network_name,
+        network_placement_id,
+        begin_time,
+        finish_time,
+        result_json,
+        ad_source_index,
+        ad_source_type,
+        ecpm,
+        app_id
+        )
+        values
+        <foreach collection="adRecords" item="item" separator=",">
+            (
+            #{item.recordId},
+            #{item.userId},
+            #{item.nickName},
+            #{item.placementId},
+            #{item.adSourceId},
+            #{item.revenue},
+            #{item.networkFormId},
+            #{item.networkName},
+            #{item.networkPlacementId},
+            ifnull(#{item.beginTime}, now()),
+            ifnull(#{item.finishTime}, now()),
+            #{item.resultJson},
+            #{item.adSourceIndex},
+            #{item.adSourceType},
+            #{item.ecpm},
+            #{item.appId}
+            )
+        </foreach>
+
+    </insert>
     <select id="countByAppIds" resultType="java.lang.Integer">
         select
         count(record_id)
@@ -285,4 +370,13 @@
             AND DATE(finish_time) = DATE(NOW())
         </if>
     </select>
+    <select id="getByTakuAdId" resultType="com.ytpm.app.model.YtDyzAdRecord">
+        select
+            record_id, user_id,app_id, nick_name, placement_id, ad_source_id, revenue,
+            network_form_id, network_name, network_placement_id, finish_time, begin_time,
+            result_json,ad_source_type,ad_source_index,ecpm
+        from yt_dyz_ad_record
+        where user_id = #{userId} and JSON_EXTRACT(result_json, '$.id') = #{id}
+        limit 1
+    </select>
 </mapper>

+ 9 - 0
yt-question/yt-question-service/src/main/java/com/ytpm/question/dao/AdRecordMapper.java

@@ -19,6 +19,9 @@ public interface AdRecordMapper {
      */
     void addOne(YtDyzAdRecord adRecord);
 
+    /**
+     * 批量新增广告记录
+     */
     void batchAdd(@Param("adRecords") List<YtDyzAdRecord> adRecords);
 
     /**
@@ -26,8 +29,14 @@ public interface AdRecordMapper {
      */
     void addOneVisitor(YtDyzAdRecord adRecord);
 
+    /**
+     * 根据 json id字段查询广告记录
+     */
     YtDyzAdRecord getByTakuAdId(@Param("userId") String userId, @Param("id") String id);
 
+    /**
+     * 批量新增游客广告记录
+     */
     void batchAddVisitor(@Param("adRecords") List<YtDyzAdRecord> adRecords);
 
     /**

+ 10 - 0
yt-question/yt-question-service/src/main/java/com/ytpm/question/service/AdService.java

@@ -21,6 +21,9 @@ public interface AdService {
      */
     Result<String> saveRecord(DyzAdRecordParam param);
 
+    /**
+     * 批量保存离线广告记录
+     */
     Result<String> delaySaveRecord(DyzAdRecordDelayParam param);
 
     /**
@@ -53,7 +56,14 @@ public interface AdService {
      */
     List<AgentAdGroupStaticsVO> getAgentProfit(String appIds);
 
+    /**
+     * 保存记录
+     * 始终创建新的事务以保障子方法的独立事务
+     */
     String saveRecordAndChangeUser(DyzAdRecordParam param, YtDyzUser user);
 
+    /**
+     * 根据ID查询多条广告记录
+     */
     List<YtDyzAdRecord> queryRecordByIds(List<String> adRecordIds);
 }

+ 2 - 5
yt-question/yt-question-service/src/main/java/com/ytpm/question/service/impl/AdServiceImpl.java

@@ -393,12 +393,9 @@ public class AdServiceImpl implements AdService {
         } else {
             adRecordMapper.addOne(adRecord);
         }
-        boolean updateUserTotal = true;
+        boolean updateUserTotal = !AdRecordEnum.LOGIN_BEFORE.getCode().equals(param.getLoginStatus())
+                || AdSourceTypeEnum.rewarded_video.getAdSourceType() != param.getAdSourceType();
         // 游客登陆下 激励视频广告记录不算入用户统计
-        if (AdRecordEnum.LOGIN_BEFORE.getCode().equals(param.getLoginStatus())
-                && AdSourceTypeEnum.rewarded_video.getAdSourceType() == param.getAdSourceType()) {
-            updateUserTotal = false;
-        }
         if (updateUserTotal) {
             //修改用户信息, 广告次数+1  总收益 + revenue
             if (param.getRevenue() == null) {