Browse Source

自动投放修改2.1

zhaoxian 3 years ago
parent
commit
205cc2c51c
13 changed files with 449 additions and 199 deletions
  1. 28 0
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/controller/AiKuaishouAccountAutoDoController.java
  2. 7 3
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/entity/AiKuaishouAccountAutoStrategy.java
  3. 8 2
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/entity/AiKuaishouAccountAutoVideo.java
  4. 3 0
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/mapper/AiKuaishouAccountAutoVideoMapper.java
  5. 26 9
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/mapper/xml/AiKuaishouAccountAutoVideoMapper.xml
  6. 3 2
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/service/IAiKuaishouAccountAutoDoService.java
  7. 325 150
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/service/impl/AiKuaishouAccountAutoDoServiceImpl.java
  8. 10 12
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/service/impl/AiKuaishouAccountAutoVideoServiceImpl.java
  9. 3 0
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/batch/mapper/KuaiShouCampaignMapper.java
  10. 27 19
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/batch/mapper/xml/KuaiShouCampaignMapper.xml
  11. 3 0
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/batch/service/IKuaiShouCampaignService.java
  12. 0 2
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/batch/service/impl/CrossAccountBatchServiceImpl.java
  13. 6 0
      jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/batch/service/impl/KuaiShouCampaignServiceImpl.java

+ 28 - 0
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/controller/AiKuaishouAccountAutoDoController.java

@@ -54,7 +54,35 @@ public class AiKuaishouAccountAutoDoController {
             returnJson.put("message", e.getMessage());
         }
         return returnJson;
+    }
 
+    /**
+     * 每日补充创建创意 、频率 每天22点05分钟执行1次
+     */
+    @GetMapping(value = "/supplementTodayCreatives")
+    public JSONObject supplementTodayCreatives(String id) {
+        JSONObject returnJson = new JSONObject();
+        try {
+            if (Check.isNull(id)) {
+                throw new Exception("主键id不能为空");
+            }
+            AiKuaishouAccountAutoStrategy strategy = strategyService.getById(id);
+            if (Check.isNull(strategy)) {
+                throw new Exception("根据id获取详细信息为空");
+            }
+            Boolean unitOverrun = autoDoService.getUnitOverrun(strategy.getAccountId());
+            if (unitOverrun) {
+                log.error("****** 广告组创建超限,accountId:{}", strategy.getAccountId());
+                throw new Exception("今日广告组创建已超限");
+            }
+            customService.submit(() -> autoDoService.supplementTodayCreatives(strategy));
+            returnJson.put("code", 0);
+            returnJson.put("message", "异步创建中");
+        } catch (Exception e) {
+            returnJson.put("code", -1);
+            returnJson.put("message", e.getMessage());
+        }
+        return returnJson;
     }
 
     /**

+ 7 - 3
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/entity/AiKuaishouAccountAutoStrategy.java

@@ -335,10 +335,10 @@ public class AiKuaishouAccountAutoStrategy {
     @ApiModelProperty(value = "创意标签")
     private String creativeTag;
     /**
-     * 物料关联的封面个数
+     * 自定义创意数量
      */
-    @Excel(name = "物料关联的封面个数", width = 15)
-    @ApiModelProperty(value = "物料关联的封面个数")
+    @Excel(name = "自定义创意数量", width = 15)
+    @ApiModelProperty(value = "自定义创意数量")
     private Integer imageCnt;
     /**
      * 程序化创意2.0-是否开启智能抽帧
@@ -497,4 +497,8 @@ public class AiKuaishouAccountAutoStrategy {
 
     @TableField(exist = false)
     private int programCreatedUnitCnt;
+
+    //待建广告组数
+    @TableField(exist = false)
+    private int toBeBuiltCnt;
 }

+ 8 - 2
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/entity/AiKuaishouAccountAutoVideo.java

@@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
 import com.google.common.base.Objects;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
@@ -24,6 +25,7 @@ import java.util.Date;
  * @date 2021-10-20
  */
 @Data
+@AllArgsConstructor
 @TableName("ctop_ai_kuaishou_account_auto_video")
 @EqualsAndHashCode(callSuper = false)
 @Accessors(chain = true)
@@ -75,8 +77,8 @@ public class AiKuaishouAccountAutoVideo {
     /**
      * 使用时间
      */
-    @Excel(name = "使用时间", width = 15)
-    @ApiModelProperty(value = "使用时间")
+    @Excel(name = "素材创建时间", width = 15)
+    @ApiModelProperty(value = "素材创建时间")
     private String statDate;
     /**
      * MD5值
@@ -123,6 +125,10 @@ public class AiKuaishouAccountAutoVideo {
     @TableField(exist = false)
     private Long campaignId;
 
+    public AiKuaishouAccountAutoVideo() {
+
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) {

+ 3 - 0
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/mapper/AiKuaishouAccountAutoVideoMapper.java

@@ -1,6 +1,7 @@
 package cn.com.ctop.kuaishou.modules.ai.mapper;
 
 import cn.com.ctop.kuaishou.modules.ai.entity.AiKuaishouAccountAutoVideo;
+import com.alibaba.fastjson.JSONArray;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -30,4 +31,6 @@ public interface AiKuaishouAccountAutoVideoMapper extends BaseMapper<AiKuaishouA
     void addAutoVideoUsed(@Param("accountId") Long accountId, @Param("signature") String signature, @Param("videoType") Integer videoType, @Param("createType") String createType, @Param("usageTimes") Integer usageTimes, @Param("date") String date);
 
     void updateAutoVideoUsed(@Param("accountId") Long accountId, @Param("signature") String signature, @Param("videoType") Integer videoType, @Param("createType") String createType, @Param("usageTimes") Integer usageTimes, @Param("date") String date);
+
+    List<String> selectBatchByPhotoIds(@Param("details") JSONArray details, @Param("statDate") String statDate, @Param("accountId") Long accountId);
 }

+ 26 - 9
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/mapper/xml/AiKuaishouAccountAutoVideoMapper.xml

@@ -5,25 +5,27 @@
     <insert id="insertBatch">
         replace into ctop_ai_kuaishou_account_auto_video(
         account_id,
+        custom_status,
+        program_status,
         photo_id,
-        status,
+        photo_name,
         material_type,
-        stat_date,
         channel_type,
-        signature,
-        photo_name
+        stat_date,
+        signature
         )
         values
         <foreach collection="videoGetList" item="video" separator=",">
             (
             #{video.accountId},
+            #{video.customStatus},
+            #{video.programStatus},
             #{video.photoId},
-            #{video.status},
-            #{video.materialType},
-            #{video.statDate},
+            #{video.photoName},
+           #{video.materialType},
             #{video.channelType},
-            #{video.signature},
-            #{video.photoName}
+            #{video.statDate},
+            #{video.signature}
             )
         </foreach>
     </insert>
@@ -172,4 +174,19 @@
           AND video_type = #{videoType}
           AND create_type = #{createType}
     </update>
+
+    <select id="selectBatchByPhotoIds" resultType="java.lang.String">
+        SELECT photo_id
+        FROM ctop_ai_kuaishou_account_auto_video
+        WHERE account_id = #{accountId}
+          AND stat_date = #{statDate}
+        <if test="details!=null">
+            AND photo_id in
+            <foreach item="item" collection="details"
+                     open="(" separator="," close=")">
+                #{item.photo_id}
+            </foreach>
+          </if>
+    </select>
+
 </mapper>

+ 3 - 2
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/service/IAiKuaishouAccountAutoDoService.java

@@ -2,8 +2,6 @@ package cn.com.ctop.kuaishou.modules.ai.service;
 
 import cn.com.ctop.kuaishou.modules.ai.entity.AiKuaishouAccountAutoStrategy;
 
-import java.text.ParseException;
-
 public interface IAiKuaishouAccountAutoDoService {
 
     //创建自定义上新广告创意
@@ -18,4 +16,7 @@ public interface IAiKuaishouAccountAutoDoService {
     // 组创建是否超限
     Boolean getUnitOverrun(Long accountId);
 
+    //每日补齐创建
+    void supplementTodayCreatives(AiKuaishouAccountAutoStrategy strategy);
+
 }

+ 325 - 150
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/service/impl/AiKuaishouAccountAutoDoServiceImpl.java

@@ -121,7 +121,8 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
         //当前需要创建广告组数量
         int unitNum = strategy.getCustomPlanCnt() * strategy.getCustomUnitCnt();
         //获取自定义已创建广告组数量
-        Integer customUnitCnt = (Integer) redisUtil.get(date + "_auto_custom_cnt_" + accountId);
+        String customkey = date + "_auto_custom_cnt_" + accountId;
+        Integer customUnitCnt = (Integer) redisUtil.get(customkey);
         if (Check.isNull(customUnitCnt)) {
             if (unitNum > 600) {
                 unitNum = 600;
@@ -153,7 +154,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
         if (upNewCnt > 0) {
             // 1上新
             log.info("----- (自定义上新)账户:{},本次可建组数:{}", strategy.getAccountId(), upNewCnt);
-            unitNum = this.autoCreateCreative(strategy, 1, upNewCnt);
+            this.autoCreateCreative(strategy, 1, upNewCnt);
         }
 
     }
@@ -169,7 +170,8 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
         //当前需要创建广告组数量
         int unitNum = strategy.getCustomPlanCnt() * strategy.getCustomUnitCnt();
         //获取自定义已创建广告组数量
-        Integer customUnitCnt = (Integer) redisUtil.get(date + "_auto_custom_cnt_" + accountId);
+        String customkey = date + "_auto_custom_cnt_" + accountId;
+        Integer customUnitCnt = (Integer) redisUtil.get(customkey);
         if (Check.isNull(customUnitCnt)) {
             if (unitNum > 600) {
                 unitNum = 600;
@@ -177,7 +179,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
         } else {
             unitNum = unitNum - customUnitCnt;
         }
-        if (unitNum == 0) {
+        if (unitNum <= 0) {
             return;
         }
         log.info("----- (自定义)账户:{},已建组数:{};剩余待建组数:{}", strategy.getAccountId(), customUnitCnt, unitNum);
@@ -231,6 +233,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
     private Integer autoCreateCreative(AiKuaishouAccountAutoStrategy strategy, Integer videoType, Integer videoCnt) {
         //4-自定义
         strategy.setUnitType(4);
+        strategy.setToBeBuiltCnt(videoCnt);
         Long accountId = strategy.getAccountId();
         CtopOauthToken token = tokenService.getTokenByAccountId(accountId);
         if (null == token) {
@@ -260,16 +263,16 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
             KuaishouChannel usedchannel = null;
             try {
                 KuaishouChannel channel = null;
-                /*获取渠道号*/
                 Integer useChannel = Check.isNull(strategy.getUseChannel()) ? 0 : strategy.getUseChannel();//0不使用,1使用
                 if (useChannel == 1) {
+                    /*获取渠道号*/
                     channel = getChannel(level, accountId, appName);
                     if (Check.isNull(channel)) {
                         return videoCnt;
                     }
                 }
                 /*执行 创建计划or获取已创建计划Id*/
-                newCampaignId = getCampaignId(strategy, token, typeName, "TODAY", videoType, appId, strategyUuid, channel);
+                newCampaignId = getCampaignId(strategy, token, videoType, strategyUuid, channel);
                 if (null == newCampaignId) {
                     return videoCnt;
                 }
@@ -337,15 +340,21 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                         continue;
                     }
                     JSONObject unitParams = (JSONObject) result.getResult();
-                    log.info("--------------执行创建广告组,参数:{}", unitParams);
                     Map<String, Object> unitCreateResult = kuaishouInterfaceService.adUnitCreate(token.getAccessToken(), token.getAccountId(), unitParams, 1);
-                    log.info("--------------创建广告组结果,返回数据:{}", unitCreateResult.toString());
                     unitCode = (Integer) unitCreateResult.get("code");
                     unitMessage = (String) unitCreateResult.get("message");
                     /*获取广告组创建记录*/
-                    AiKuaishouUnitLevelOperationRecord unitRecord = new AiKuaishouUnitLevelOperationRecord(strategy, unitParams, unitCode, unitMessage, strategyUuid, videoItem.getAutoTargetId(), channel.getId());
+
+                    Long channelId = null;
+                    if (!Check.isNull(channel)) {
+                        channelId = channel.getId();
+                    }
+                    AiKuaishouUnitLevelOperationRecord unitRecord = new AiKuaishouUnitLevelOperationRecord(strategy, unitParams, unitCode, unitMessage, strategyUuid, videoItem.getAutoTargetId(), channelId);
                     if (unitCode != 0) {
-                        log.error("****** 账户({})组创建失败,message:{}", accountId, unitMessage);
+                        log.error("****** 账户({})组创建失败,入参:{},message:{}", accountId, unitParams, unitMessage);
+                        String redisKey = strategy.getAccountId() + "_" + newCampaignId + DateUtils.formatDate(new Date(), "yy-MMdd");
+                        Integer redisCnt = (Integer) redisUtil.get(redisKey);
+                        redisUtil.set(redisKey, redisCnt - 1, 100000L);
                         unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                         if (!Check.isNull(usedchannel)) {
                             channelService.callBackUpdateChannelRel(level, usedchannel.getChannelCode(), accountId, newCampaignId, null, 0);
@@ -359,13 +368,17 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                         channelService.callBackUpdateChannelRel(level, usedchannel.getChannelCode(), accountId, newCampaignId, unitId, 1);
                     }
                     videoCnt--;
-                    successVideoList.add(videoItem);
-                    //添加素材使用记录
-                    updateVideoUsed(videoType, null, "custom", videoItem);
+                    //更新自定义创建组数量
+                    updateRedisCnt(statDate + "_auto_custom_cnt_" + accountId);
                     //获取创意参数
                     JSONObject creativeParams = getCreativeParams(token, unitId, videoItem, strategy, usedchannel, null);
                     //执行创建创意
-                    createCreative(token, creativeParams, strategy, unitId, strategyUuid);
+                    boolean flag = createCreative(token, creativeParams, strategy, unitId, strategyUuid);
+                    if (flag) {
+                        //创意成功,则素材置为不可用,添加素材使用记录
+                        updateVideoUsed(videoType, null, "custom", videoItem);
+                        successVideoList.add(videoItem);
+                    }
                     if (videoCnt <= 0) {
                         removeRedisVideos(successVideoList, videoKey);
                         return 0;
@@ -388,6 +401,30 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
     }
 
     /**
+     * 更新广告组数
+     */
+    private void updateRedisCnt(String key) {
+        Integer unitCnt = (Integer) redisUtil.get(key);
+        if (Check.isNull(unitCnt)) {
+            redisUtil.set(key, 1, 100000L);
+        } else {
+            redisUtil.set(key, unitCnt + 1, 100000L);
+        }
+    }
+
+    /**
+     * 更新广告组数
+     */
+    private void updateVideoStatus(String key) {
+        Integer unitCnt = (Integer) redisUtil.get(key);
+        if (Check.isNull(unitCnt)) {
+            redisUtil.set(key, 1, 100000L);
+        } else {
+            redisUtil.set(key, unitCnt + 1, 100000L);
+        }
+    }
+
+    /**
      * 自定义多应用创建逻辑
      */
     private Integer autoCreateCustomMoreApp(AiKuaishouAccountAutoStrategy strategy, Integer videoType, CtopOauthToken token, String typeName, Integer videoCnt) {
@@ -434,7 +471,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                     continue;
                 }
                 /*执行 创建计划or获取已创建计划Id*/
-                Long campaignId = getCampaignId(strategy, token, typeName, "ALL_CAMPAIGN", videoType, appId, strategyUuid, null);
+                Long campaignId = getCampaignId(strategy, token, videoType, strategyUuid, null);
                 if (campaignId == null) {
                     continue;
                 }
@@ -477,6 +514,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                 log.info("--------------(多应用)创建广告组结果,返回数据:{}", unitCreateResult.toString());
                 Integer unitCode = (Integer) unitCreateResult.get("code");
                 String unitMessage = (String) unitCreateResult.get("message");
+
                 /*获取广告组创建记录*/
                 AiKuaishouUnitLevelOperationRecord unitRecord = new AiKuaishouUnitLevelOperationRecord(strategy, unitParams, unitCode, unitMessage, strategyUuid, videoItem.getAutoTargetId(), null);
                 if (unitCode != 0) {
@@ -523,7 +561,8 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
         //当前需要创建广告组数量
         int unitNum = strategy.getProgramPlanCnt() * strategy.getProgramUnitCnt();
         //获取自定义已创建广告组数量
-        Integer programUnitCnt = (Integer) redisUtil.get(date + "_auto_program_cnt_" + accountId);
+        String programkey = date + "_auto_program_cnt_" + accountId;
+        Integer programUnitCnt = (Integer) redisUtil.get(programkey);
         if (Check.isNull(programUnitCnt)) {
             if (unitNum > 300) {
                 unitNum = 300;
@@ -579,6 +618,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
         String uuid = UUID.randomUUID().toString();
         //4-自定义
         strategy.setUnitType(7);
+        strategy.setToBeBuiltCnt(videoCnt);
         Long accountId = strategy.getAccountId();
         CtopOauthToken token = tokenService.getTokenByAccountId(accountId);
         if (null == token) {
@@ -623,7 +663,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                     }
                 }
                 /*执行 创建计划or获取已创建计划Id*/
-                newCampaignId = getCampaignId(strategy, token, typeName, "TODAY", videoType, appId, uuid, channel);
+                newCampaignId = getCampaignId(strategy, token, videoType, uuid, channel);
                 if (null == newCampaignId) {
                     return videoCnt;
                 }
@@ -662,21 +702,25 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                     if (allVideos.size() < 3) {
                         log.error("****** {}账户({})执行程序化创建,获取素材小于3({})", statDate, accountId, typeName);
                     } else {
-                        List<List<AiKuaishouAccountAutoVideo>> splitVideos = Lists.partition(allVideos, 10);
-                        int size = splitVideos.size() - 1;
+                        List<List<AiKuaishouAccountAutoVideo>> splitVideos = Lists.newArrayList(Lists.partition(allVideos, 10));
+                        List<List<AiKuaishouAccountAutoVideo>> newVideos = new ArrayList<>();
+                        for (List<AiKuaishouAccountAutoVideo> splitVideo : splitVideos) {
+                            newVideos.add(Lists.newArrayList(splitVideo));
+                        }
+                        int size = newVideos.size() - 1;
                         if (size > 0) {
                             //判断最后一个集合小于3
-                            List<AiKuaishouAccountAutoVideo> lastVideos = splitVideos.get(size);
+                            List<AiKuaishouAccountAutoVideo> lastVideos = newVideos.get(size);
                             if (lastVideos.size() < 3) {
                                 //小于3 则去除
                                 for (int i = 0; i < size; i++) {
-                                    proVideos.add(splitVideos.get(i));
+                                    proVideos.add(newVideos.get(i));
                                 }
                             } else {
-                                proVideos = splitVideos;
+                                proVideos = newVideos;
                             }
                         } else {
-                            proVideos = splitVideos;
+                            proVideos = newVideos;
                         }
                     }
                 }
@@ -719,8 +763,13 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                     log.info("--------------创建程序化广告组结果,返回数据:{}", unitCreateResult.toString());
                     unitCode = (Integer) unitCreateResult.get("code");
                     unitMessage = (String) unitCreateResult.get("message");
+
+                    Long channelId = null;
+                    if (!Check.isNull(channel)) {
+                        channelId = channel.getId();
+                    }
                     /*获取广告组创建记录*/
-                    AiKuaishouUnitLevelOperationRecord unitRecord = new AiKuaishouUnitLevelOperationRecord(strategy, unitParams, unitCode, unitMessage, uuid, videos.get(0).getAutoTargetId(), channel.getId());
+                    AiKuaishouUnitLevelOperationRecord unitRecord = new AiKuaishouUnitLevelOperationRecord(strategy, unitParams, unitCode, unitMessage, uuid, videos.get(0).getAutoTargetId(), channelId);
                     if (unitCode != 0) {
                         log.error("****** 程序化账户({})组创建失败,message:{}", accountId, unitMessage);
                         unitLevelOperationRecordService.saveOrUpdate(unitRecord);
@@ -736,13 +785,19 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                         channelService.callBackUpdateChannelRel(level, usedchannel.getChannelCode(), accountId, newCampaignId, unitId, 1);
                     }
                     videoCnt--;
-                    successVideoList.add(videos);
-                    //添加素材使用记录
-                    updateVideoUsed(videoType, videos, "program", null);
+                    //更新程序化创建组数量
+                    updateRedisCnt(statDate + "_auto_program_cnt_" + accountId);
+
                     //获取创意参数
                     JSONObject creativeParams = getProgramCreativeParams(token, unitId, videos, strategy, videoCnt, null, usedchannel);
                     //执行创建创意
-                    createProgramCreatives(token, creativeParams, strategy);
+                    boolean flag = createProgramCreatives(token, creativeParams, strategy);
+                    if (flag) {
+                        //创意成功,则素材置为不可用,添加素材使用记录
+                        updateVideoUsed(videoType, videos, "program", null);
+                        successVideoList.add(videos);
+                    }
+
                     if (videoCnt <= 0) {
                         removeProRedisVideos(successVideoList, videoKey);
                         return 0;
@@ -836,7 +891,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                 }
 
                 /*执行 创建计划or获取已创建计划Id*/
-                Long campaignId = getCampaignId(strategy, token, typeName, "ALL_CAMPAIGN", videoType, appId, strategyUuid, null);
+                Long campaignId = getCampaignId(strategy, token, videoType, strategyUuid, null);
                 if (campaignId == null) {
                     continue;
                 }
@@ -974,15 +1029,17 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                 continue;
             }
             JSONObject unitParams = (JSONObject) result.getResult();
-            log.info("--------------执行创建广告组,参数:{}", unitParams);
             Map<String, Object> unitCreateResult = kuaishouInterfaceService.adUnitCreate(token.getAccessToken(), token.getAccountId(), unitParams, 1);
-            log.info("--------------创建广告组结果,返回数据:{}", unitCreateResult.toString());
             unitCode = (Integer) unitCreateResult.get("code");
             unitMessage = (String) unitCreateResult.get("message");
+            Long channelId = null;
+            if (!Check.isNull(channel)) {
+                channelId = channel.getId();
+            }
             /*获取广告组创建记录*/
-            AiKuaishouUnitLevelOperationRecord unitRecord = new AiKuaishouUnitLevelOperationRecord(strategy, unitParams, unitCode, unitMessage, strategyUuid, videoItem.getAutoTargetId(), channel.getId());
+            AiKuaishouUnitLevelOperationRecord unitRecord = new AiKuaishouUnitLevelOperationRecord(strategy, unitParams, unitCode, unitMessage, strategyUuid, videoItem.getAutoTargetId(), channelId);
             if (unitCode != 0) {
-                log.error("****** 账户({})组创建失败,message:{}", accountId, unitMessage);
+                log.error("****** 账户({})组创建失败,入参:{},message:{}", accountId, unitParams.toString(), unitMessage);
                 unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                 if (!Check.isNull(channel)) {
                     channelService.callBackUpdateChannelRel("group", channel.getChannelCode(), accountId, newCampaignId, null, 0);
@@ -996,13 +1053,18 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                 channelService.callBackUpdateChannelRel("group", channel.getChannelCode(), accountId, newCampaignId, unitId, 1);
             }
             videoCnt--;
-            successVideoList.add(videoItem);
-            //添加素材使用记录
-            updateVideoUsed(videoType, null, "custom", videoItem);
+            //更新自定义创建组数量
+            updateRedisCnt(date + "_auto_custom_cnt_" + accountId);
+
             //获取创意参数
             JSONObject creativeParams = getCreativeParams(token, unitId, videoItem, strategy, channel, null);
             //执行创建创意
-            createCreative(token, creativeParams, strategy, unitId, strategyUuid);
+            boolean flag = createCreative(token, creativeParams, strategy, unitId, strategyUuid);
+            if (flag) {
+                //创意成功,则素材置为不可用,添加素材使用记录
+                updateVideoUsed(videoType, null, "custom", videoItem);
+                successVideoList.add(videoItem);
+            }
             if (videoCnt <= 0) {
                 removeRedisVideos(successVideoList, videoKey);
                 return 0;
@@ -1100,15 +1162,17 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                 continue;
             }
             JSONObject unitParams = (JSONObject) result.getResult();
-            log.info("--------------执行创建程序化广告组,参数:{}", unitParams);
             Map<String, Object> unitCreateResult = kuaishouInterfaceService.adUnitCreate(token.getAccessToken(), token.getAccountId(), unitParams, 1);
-            log.info("--------------创建程序化广告组结果,返回数据:{}", unitCreateResult.toString());
             unitCode = (Integer) unitCreateResult.get("code");
             unitMessage = (String) unitCreateResult.get("message");
+            Long channelId = null;
+            if (!Check.isNull(channel)) {
+                channelId = channel.getId();
+            }
             /*获取广告组创建记录*/
-            AiKuaishouUnitLevelOperationRecord unitRecord = new AiKuaishouUnitLevelOperationRecord(strategy, unitParams, unitCode, unitMessage, uuid, videos.get(0).getAutoTargetId(), channel.getId());
+            AiKuaishouUnitLevelOperationRecord unitRecord = new AiKuaishouUnitLevelOperationRecord(strategy, unitParams, unitCode, unitMessage, uuid, videos.get(0).getAutoTargetId(), channelId);
             if (unitCode != 0) {
-                log.error("****** 程序化账户({})组创建失败,message:{}", accountId, unitMessage);
+                log.error("****** 程序化账户({})组创建失败,入参:{},message:{}", accountId, unitParams, unitMessage);
                 unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                 if (!Check.isNull(channel)) {
                     channelService.callBackUpdateChannelRel("group", channel.getChannelCode(), accountId, newCampaignId, null, 0);
@@ -1122,13 +1186,18 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                 channelService.callBackUpdateChannelRel("group", channel.getChannelCode(), accountId, newCampaignId, unitId, 1);
             }
             videoCnt--;
-            successVideoList.add(videos);
-            //添加素材使用记录
-            updateVideoUsed(videoType, videos, "program", null);
+            //更新程序化创建组数量
+            updateRedisCnt(date + "_auto_program_cnt_" + accountId);
+
             //获取创意参数
             JSONObject creativeParams = getProgramCreativeParams(token, unitId, videos, strategy, videoCnt, null, channel);
             //执行创建创意
-            createProgramCreatives(token, creativeParams, strategy);
+            boolean flag = createProgramCreatives(token, creativeParams, strategy);
+            if (flag) {
+                successVideoList.add(videos);
+                //创意成功,则素材置为不可用,添加素材使用记录
+                updateVideoUsed(videoType, videos, "program", null);
+            }
             if (videoCnt <= 0) {
                 removeProRedisVideos(successVideoList, videoKey);
                 return 0;
@@ -1228,7 +1297,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                 if (videoCnt == 0) {
                     return null;
                 }
-                return redisVideos.subList(0, videoCnt);
+                return new ArrayList<>(redisVideos.subList(0, videoCnt));
             }
         }
         Integer matchType = strategy.getMatchType();//1定向匹配。2素材匹配
@@ -1250,7 +1319,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
         if (!Check.isNull(redisVideos) && !redisVideos.isEmpty()) {
             redisUtil.set(key, redisVideos, 100000L);
             if (redisVideos.size() > videoCnt) {
-                autoVideos = redisVideos.subList(0, videoCnt);
+                autoVideos = new ArrayList<>(redisVideos.subList(0, videoCnt));
             } else {
                 autoVideos = redisVideos;
             }
@@ -1320,7 +1389,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
      * 根据匹配逻辑,定向包和素材进行匹配,获取匹配后的素材(程序化)
      */
     private List<List<AiKuaishouAccountAutoVideo>> getProTargetsAndVideos(AiKuaishouAccountAutoStrategy strategy, List<List<AiKuaishouAccountAutoVideo>> proVideos, String key, Integer videoCnt, Long appId, Long campaignId, String appVersion) {
-        List<List<AiKuaishouAccountAutoVideo>> redisVideos = (List<List<AiKuaishouAccountAutoVideo>>) redisUtil.get(key);
+        List<List<AiKuaishouAccountAutoVideo>> redisVideos = (List) redisUtil.get(key);
         if (Check.isNull(redisVideos) || redisVideos.isEmpty()) {
             if (Check.isNull(proVideos) || proVideos.isEmpty()) {
                 log.error("****** 缺少素材,不执行创建操作");
@@ -1332,7 +1401,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                 if (videoCnt == 0) {
                     return null;
                 }
-                return redisVideos.subList(0, videoCnt);
+                return new ArrayList<>(redisVideos.subList(0, videoCnt));
             }
         }
         Integer matchType = strategy.getMatchType();//1定向匹配。2素材匹配
@@ -1355,7 +1424,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
         if (!Check.isNull(redisVideos) && !redisVideos.isEmpty()) {
             redisUtil.set(key, redisVideos, 100000L);
             if (redisVideos.size() > videoCnt) {
-                autoVideos = redisVideos.subList(0, videoCnt);
+                autoVideos = new ArrayList<>(redisVideos.subList(0, videoCnt));
             } else {
                 autoVideos = redisVideos;
             }
@@ -1460,7 +1529,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                         log.info("------获取渠道号:{}", channel.getChannelCode());
                     }
                 } else {
-                    log.error("****** 查询渠道号异常!{}", result.getMessage());
+                    log.error("******  账户({})根据应用名:{},未查询渠道号!{}", accountId, appName, result.getMessage());
                 }
             }
         } catch (Exception e) {
@@ -1582,7 +1651,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
     /**
      * 执行创建程序化创意
      */
-    private void createProgramCreatives(CtopOauthToken token, JSONObject creativeParams, AiKuaishouAccountAutoStrategy strategy) {
+    private boolean createProgramCreatives(CtopOauthToken token, JSONObject creativeParams, AiKuaishouAccountAutoStrategy strategy) {
         try {
             Map<String, String> headers = new HashMap<>();
             headers.put("Content-Type", "application/json");
@@ -1593,7 +1662,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
             log.info("创建程序化创意返回结果:{}", resultJson);
             if (Check.isNull(resultJson)) {
                 log.error("程序化创意创意异常:api接口返回为空");
-                return;
+                return false;
             }
             Integer code = resultJson.getInteger("code");
             String message = resultJson.getString("message");
@@ -1609,11 +1678,14 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                     redisUtil.set(programKey, 1, 100000L);
                     updateService.updateUnitStatus(token.getAccessToken(), token.getAccountId(), unitId, 3, "e9ca23d68d884d4ebb19d07889727dae");
                 }
+                return false;
             }
         } catch (Exception e) {
             e.printStackTrace();
             log.error("执行创建程序化创意异常", e);
+            return false;
         }
+        return true;
     }
 
     /**
@@ -1673,57 +1745,96 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
     /**
      * 创建计划or获取已创建计划Id
      */
-    private Long getCampaignId(AiKuaishouAccountAutoStrategy strategy, CtopOauthToken token, String typeName, String checkType, Integer videoType, Long appId, String uuid, KuaishouChannel channel) {
-        Long campaignId = null;
-        if ("ALL_CAMPAIGN".equals(checkType)) {
-            campaignId = campaignService.getCampaignIdByAccountAndName(strategy.getAccountId(), typeName);
-        } else if ("TODAY".equals(checkType)) {
-            String nowDate = DateUtils.getNowDate("yyyy-MM-dd") + " 00:00:01";
-            campaignId = campaignService.getCampaignIdByAccountAndNameAndDate(strategy.getAccountId(), typeName, nowDate);
+    private Long getCampaignId(AiKuaishouAccountAutoStrategy strategy, CtopOauthToken token, Integer videoType, String uuid, KuaishouChannel channel) {
+        String nowDate = DateUtils.getNowDate("yyyy-MM-dd") + " 00:00:01";
+        String typeNa = "程序化";
+        //计划数
+        Integer planCnt = strategy.getProgramPlanCnt();
+        //组数
+        Integer unitCnt = strategy.getProgramUnitCnt();
+        if (strategy.getUnitType() == 4) {
+            planCnt = strategy.getCustomPlanCnt();
+            unitCnt = strategy.getCustomUnitCnt();
+            typeNa = "自定义";
+        }
+        List<JSONObject> datas = campaignService.getCampaignIdsByParameters(strategy.getAccountId(), typeNa, nowDate);
+        int toBeBuiltCnt = 0;
+        if (!Check.isNull(datas)) {
+            for (JSONObject data : datas) {
+                //计划下已建广告组数
+                Integer count = data.getInteger("count");
+                if (count >= unitCnt) {
+                    continue;
+                }
+                //计划下还可建的广告组数
+                toBeBuiltCnt = unitCnt - count;
+                //若比本次待建数少,则用本计划建满,剩余不做创建,下次执行
+                if (toBeBuiltCnt < strategy.getToBeBuiltCnt()) {
+                    strategy.setToBeBuiltCnt(toBeBuiltCnt);
+                }
+                return data.getLong("campaignId");
+            }
+            //判断是否扔可用创建计划
+            if (planCnt > datas.size()) {
+                return createCampaign(strategy, token, videoType, uuid, channel);
+            }
+        } else {
+            return createCampaign(strategy, token, videoType, uuid, channel);
         }
-        if (!Check.isNull(campaignId)) {
+        return null;
+    }
+
+    private Long createCampaign(AiKuaishouAccountAutoStrategy strategy, CtopOauthToken token, Integer videoType, String uuid, KuaishouChannel channel) {
+        try {
+            Long campaignId = null;
+            //创建一个计划
+            JSONObject campaignParams = new JSONObject();
+            campaignParams.put("advertiser_id", token.getAccountId());
+            String campaignName = getName(strategy.getCampaignName(), strategy, videoType, null, "plan", null, null, channel);
+            campaignParams.put("campaign_name", campaignName);
+            campaignParams.put("type", strategy.getCampaignType());
+            if (null != strategy.getCampaignDayBudget() && strategy.getCampaignDayBudget().compareTo(BigDecimal.ZERO) != 0) {
+                campaignParams.put("day_budget", strategy.getCampaignDayBudget());
+            }
+            Map<String, Object> campaignCreateResult = kuaishouInterfaceService.campaignCreate(token.getAccessToken(), token.getAccountId(), campaignParams);
+            Integer code = (Integer) campaignCreateResult.get("code");
+            String message = (String) campaignCreateResult.get("message");
+            //添加创建记录
+            AiKuaishouCampaignLevelOperationRecord record = new AiKuaishouCampaignLevelOperationRecord(strategy, null, code, message, campaignName, uuid);
+            if (code != 0) {
+                if (strategy.getCampaignName().contains("{{序号}}")) {
+                    String redisKey = token.getAccountId() + "_planCnt";
+                    Integer redisValue = (Integer) redisUtil.get(redisKey);
+                    redisUtil.set(redisKey, redisValue - 1);
+                }
+                log.error("****** 自动投放重构创建计划失败,入参:{},message:{}", campaignParams.toJSONString(), message);
+                record.setCampaignId(0L);
+                aiKuaishouCampaignLevelOperationRecordService.saveOrUpdate(record);
+                if (!Check.isNull(channel)) {
+                    channelService.callBackUpdateChannelRel("plan", channel.getChannelCode(), token.getAccountId(), null, null, 0);
+                }
+                return null;
+            }
+            campaignId = (Long) campaignCreateResult.get("campaignId");
+            //回滚channel使用锁
             if (!Check.isNull(channel)) {
                 channelService.callBackUpdateChannelRel("plan", channel.getChannelCode(), token.getAccountId(), campaignId, null, 1);
             }
-            return campaignId;
-        }
-
-        // 不存在“上新”计划,则创建一个计划
-        JSONObject campaignParams = new JSONObject();
-        campaignParams.put("advertiser_id", token.getAccountId());
-        String campaignName = getName(strategy.getCampaignName(), strategy, videoType, null, "plan", null, null, channel);
-        campaignParams.put("campaign_name", campaignName);
-        campaignParams.put("type", strategy.getCampaignType());
-        if (null != strategy.getCampaignDayBudget() && strategy.getCampaignDayBudget().compareTo(BigDecimal.ZERO) != 0) {
-            campaignParams.put("day_budget", strategy.getCampaignDayBudget());
-        }
-        Map<String, Object> campaignCreateResult = kuaishouInterfaceService.campaignCreate(token.getAccessToken(), token.getAccountId(), campaignParams);
-        Integer code = (Integer) campaignCreateResult.get("code");
-        String message = (String) campaignCreateResult.get("message");
-        AiKuaishouCampaignLevelOperationRecord record = new AiKuaishouCampaignLevelOperationRecord(strategy, null, code, message, campaignName, uuid);
-        if (code != 0) {
-            log.error("****** 计划创建失败,message:{}", message);
-            record.setCampaignId(0L);
+            record.setCampaignId(campaignId);
             aiKuaishouCampaignLevelOperationRecordService.saveOrUpdate(record);
-            if (!Check.isNull(channel)) {
-                channelService.callBackUpdateChannelRel("plan", channel.getChannelCode(), token.getAccountId(), null, null, 0);
+            Integer campaignStatus = strategy.getCampaignStatus();
+            if (!Check.isNull(campaignStatus) && campaignStatus == 2) {
+                updateService.updateCampaignStatus(token.getAccessToken(), token.getAccountId(), campaignId, campaignStatus, "Auto");
             }
-            return null;
-        }
-        campaignId = (Long) campaignCreateResult.get("campaignId");
-        //回滚channel使用锁
-        if (!Check.isNull(channel)) {
-            channelService.callBackUpdateChannelRel("plan", channel.getChannelCode(), token.getAccountId(), campaignId, null, 1);
-        }
-        record.setCampaignId(campaignId);
-        aiKuaishouCampaignLevelOperationRecordService.saveOrUpdate(record);
-        Integer campaignStatus = strategy.getCampaignStatus();
-        if (!Check.isNull(campaignStatus) && campaignStatus == 2) {
-            updateService.updateCampaignStatus(token.getAccessToken(), token.getAccountId(), campaignId, campaignStatus, "Auto");
+            return campaignId;
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("创建计划异常", e);
         }
-        return campaignId;
+        return null;
     }
 
+
     /**
      * 整理广告组参数
      */
@@ -2157,8 +2268,11 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
             if (null != strategy.getCreativeTag() && !"".equals(strategy.getCreativeTag())) {
                 creativeParams.put("creative_tag", JSONArray.parseArray(strategy.getCreativeTag()));
             }
-            //根据封面数,创建创意个数
+            //自定义创意数量,创建创意个数
             Integer count = strategy.getImageCnt();
+            if (Check.isNull(count) || count == 0) {
+                count = 1;
+            }
             //广告语集 
             JSONArray descList = JSONArray.parseArray(strategy.getDescription());
             JSONArray creatives = new JSONArray();
@@ -2169,15 +2283,15 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                     creativeName = "自定义创意_" + (i + 1);
                 }
                 //创意名称
-                creativeParams.put("creative_name", creativeName);
-                creativeParams.put("photo_id", videoItem.getPhotoId());
+                creative.put("creative_name", creativeName);
+                creative.put("photo_id", videoItem.getPhotoId());
                 //素材类型 1:竖版视频 2:横版视频
-                creativeParams.put("creative_material_type", videoItem.getMaterialType());
+                creative.put("creative_material_type", videoItem.getMaterialType());
                 //行动号召按钮文案
-                creativeParams.put("action_bar_text", strategy.getActionBarText());
+                creative.put("action_bar_text", strategy.getActionBarText());
                 //广告语
                 int index = new Random().nextInt(descList.size());
-                creativeParams.put("description", descList.getString(index));
+                creative.put("description", descList.getString(index));
 
                 //便利贴创意短广告语,便利贴4时必填
                 if ("4".equals(videoItem.getMaterialType())) {
@@ -2187,25 +2301,25 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                 }
                 // 封面广告语标题
                 if (!Check.isNull(strategy.getStickerTitle())) {
-                    creativeParams.put("sticker_title", strategy.getStickerTitle());
+                    creative.put("sticker_title", strategy.getStickerTitle());
                 }
                 // 贴纸样式类型
                 if (!Check.isNull(strategy.getOverlayType())) {
-                    creativeParams.put("overlay_type", strategy.getOverlayType());
+                    creative.put("overlay_type", strategy.getOverlayType());
                 }
                 // 广告标签
                 if (!Check.isNull(strategy.getExposeTag())) {
-                    creativeParams.put("expose_tag", strategy.getExposeTag());
+                    creative.put("expose_tag", strategy.getExposeTag());
                 }
                 // 广告标签2期
                 JSONArray newExposeTag = JSONArray.parseArray(strategy.getNewExposeTag());
                 if (!Check.isNull(newExposeTag) && !newExposeTag.isEmpty()) {
-                    creativeParams.put("new_expose_tag", newExposeTag);
+                    creative.put("new_expose_tag", newExposeTag);
                 }
                 // 安卓下载中间页ID
                 String siteId = strategy.getSiteId();
                 if (!Check.isNull(siteId) && !"-1".equals(siteId)) {
-                    creativeParams.put("site_id", siteId);
+                    creative.put("site_id", siteId);
                 }
                 creatives.add(creative);
             }
@@ -2222,7 +2336,7 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
     /**
      * 执行创建自定义创意
      */
-    private void createCreative(CtopOauthToken token, JSONObject creativeParams, AiKuaishouAccountAutoStrategy strategy, Long unitId, String uuid) {
+    private boolean createCreative(CtopOauthToken token, JSONObject creativeParams, AiKuaishouAccountAutoStrategy strategy, Long unitId, String uuid) {
         try {
             Map<String, Object> creativeCreateResult = kuaishouInterfaceService.batchCreativeCreate(token.getAccessToken(), token.getAccountId(), creativeParams, 1, null);
             Integer creativeCode = (Integer) creativeCreateResult.get("code");
@@ -2246,18 +2360,21 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
             if (creativeCode != 0) {
                 creativeLevelOperationRecordService.saveOrUpdate(record);
                 log.info("创意创建失败=>message:{}", creativeMessage);
-                return;
+                return false;
             }
         } catch (Exception e) {
             e.printStackTrace();
             log.error("执行创建自定义创意异常", e);
+            return false;
         }
+        return true;
     }
 
     /**
      * 替换通配符({{***}})获取有效的计划名称/组名称
      */
-    private String getName(String wildcard, AiKuaishouAccountAutoStrategy strategy, Integer videoType, String materialName, String type, Long campaignId, Long appId, KuaishouChannel channel) {
+    private String getName(String wildcard, AiKuaishouAccountAutoStrategy strategy, Integer videoType,
+                           String materialName, String type, Long campaignId, Long appId, KuaishouChannel channel) {
         if (wildcard.contains("{{渠道号}}") || wildcard.contains("{{渠道号名称}}")) {
             if (Check.isNull(channel)) {
                 wildcard = wildcard.replace("{{渠道号}}", "");
@@ -2286,20 +2403,6 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
             }
         }
 
-        if (wildcard.contains("{{序号}}")) {
-            int count = 0;
-            String redisKey = strategy.getAccountId() + "_planName";
-            Object redisValue = redisUtil.get(redisKey);
-            if (Check.isNull(redisValue)) {
-                count = 1;
-                redisUtil.set(redisKey, count);
-            } else {
-                count = (Integer) redisValue + 1;
-                redisUtil.set(redisKey, count);
-            }
-            wildcard = wildcard.replace("{{序号}}", count + "");
-        }
-
         if (wildcard.contains("{{创意制作方式}}")) {
             if (strategy.getUnitType() == 4) {
                 wildcard = wildcard.replace("{{创意制作方式}}", "自定义");
@@ -2320,48 +2423,53 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
                 wildcard = wildcard.replace("{{素材名称}}", materialName);
             }
         }
-
         if (!wildcard.contains("{{自定义}}")) {
-            wildcard = wildcard + "{{自定义}}";
+            wildcard = wildcard + "-{{自定义}}";
         }
-
-        String targetName = KuaishouAutoTypeEnum.getNameByType(strategy.getUnitType().toString() + videoType.toString());
-        wildcard = wildcard.replace("{{自定义}}", targetName);
         String nowDate = DateUtils.formatDate(new Date(), "yy-MMdd");
         if ("plan".equals(type)) {
+            if (wildcard.contains("{{序号}}")) {
+                Integer count = 0;
+                String redisKey = strategy.getAccountId() + "_planCnt";
+                Integer redisValue = (Integer) redisUtil.get(redisKey);
+                if (Check.isNull(redisValue)) {
+                    count = 1;
+                    redisUtil.set(redisKey, count);
+                } else {
+                    count = redisValue + 1;
+                    redisUtil.set(redisKey, count);
+                }
+                wildcard = wildcard.replace("{{序号}}", count + "");
+            }
+            //创意制作方式
+            if (strategy.getUnitType() == 4) {
+                wildcard = wildcard.replace("{{自定义}}", "自定义");
+            } else {
+                wildcard = wildcard.replace("{{自定义}}", "程序化");
+            }
             if (wildcard.contains("{{日期}}")) {
                 wildcard = wildcard.replace("{{日期}}", nowDate);
             } else {
                 wildcard = wildcard + "-" + nowDate;
             }
         } else if ("unit".equals(type)) {
+            String targetName = KuaishouAutoTypeEnum.getNameByType(strategy.getUnitType().toString() + videoType.toString());
+            wildcard = wildcard.replace("{{自定义}}", targetName);
             wildcard = wildcard.replace("{{日期}}", nowDate);
         }
-
+        //组序号,必填
         if (!Check.isNull(campaignId)) {
-            Integer count = null;
-            if (null != strategy.getSingleAppid() && strategy.getSingleAppid() == 0 && strategy.getMultipleBids() == 0) {
-                String redisKey = strategy.getAccountId() + "_" + campaignId;
-                Object redisValue = redisUtil.get(redisKey);
-                if (Check.isNull(redisValue)) {
-                    count = 1;
-                    redisUtil.set(redisKey, count);
-                } else {
-                    count = (Integer) redisValue + 1;
-                    redisUtil.set(redisKey, count);
-                }
+            Integer cnt = null;
+            String redisKey = strategy.getAccountId() + "_" + campaignId + nowDate;
+            Integer redisCnt = (Integer) redisUtil.get(redisKey);
+            if (Check.isNull(redisCnt)) {
+                cnt = 1;
+                redisUtil.set(redisKey, cnt, 100000L);
             } else {
-                String redisKey = strategy.getAccountId() + "_" + campaignId + nowDate;
-                Object redisValue = redisUtil.get(redisKey);
-                if (Check.isNull(redisValue)) {
-                    count = 1;
-                    redisUtil.set(redisKey, count, 100000L);
-                } else {
-                    count = (Integer) redisValue + 1;
-                    redisUtil.set(redisKey, count, 100000L);
-                }
+                cnt = redisCnt + 1;
+                redisUtil.set(redisKey, cnt, 100000L);
             }
-            wildcard = wildcard + "-" + count;
+            wildcard = wildcard + "-" + cnt;
         }
         wildcard = wildcard.replace("--", "-");
         wildcard = wildcard.replace("--", "-");
@@ -2414,6 +2522,73 @@ public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAut
         }
     }
 
+    @Override
+    public void supplementTodayCreatives(AiKuaishouAccountAutoStrategy strategy) {
+        Long accountId = strategy.getAccountId();
+        //==========账户{}执行自定义上新自动创建==========start==========
+        String date = DateUtils.formatDate(new Date());
+        //当前需要创建广告组数量
+        int unitNum = strategy.getCustomPlanCnt() * strategy.getCustomUnitCnt();
+        //获取自定义已创建广告组数量
+        String customkey = date + "_auto_custom_cnt_" + accountId;
+        Integer customUnitCnt = (Integer) redisUtil.get(customkey);
+        if (Check.isNull(customUnitCnt)) {
+            if (unitNum > 600) {
+                unitNum = 600;
+            }
+        } else {
+            unitNum = unitNum - customUnitCnt;
+        }
+        if (unitNum == 0) {
+            return;
+        }
+        if (unitNum > 0) {
+            // 1上新
+            log.info("-----每日补充创建 (自定义上新)账户:{},本次可建组数:{}", strategy.getAccountId(), unitNum);
+            this.autoCreateCreative(strategy, 1, unitNum);
+        }
+        String material = strategy.getSourceMaterial();
+        if (unitNum > 0 && material.contains("2")) {
+            //2高质量素材
+            log.info("-----每日补充创建 (自定义高质量)账户:{},本次可建组数:{}", strategy.getAccountId(), unitNum);
+            unitNum = this.autoCreateCreative(strategy, 2, unitNum);
+        }
+        if (unitNum > 0 && material.contains("3")) {
+            //3历史遗漏
+            log.info("-----每日补充创建 (自定义历史遗漏)账户:{},本次可建组数:{}", strategy.getAccountId(), unitNum);
+            unitNum = this.autoCreateCreative(strategy, 3, unitNum);
+        }
+        if (unitNum > 0 && material.contains("4")) {
+            //4历史打捞素材
+            log.info("-----每日补充创建 (自定义历史打捞)账户:{},本次可建组数:{}", strategy.getAccountId(), unitNum);
+            this.autoCreateCreative(strategy, 4, unitNum);
+        }
+
+        int punitNum = strategy.getProgramPlanCnt() * strategy.getProgramUnitCnt();
+        String programkey = date + "_auto_program_cnt_" + accountId;
+        Integer programUnitCnt = (Integer) redisUtil.get(programkey);
+        if (Check.isNull(programUnitCnt)) {
+            if (punitNum > 300) {
+                punitNum = 300;
+            }
+        } else {
+            punitNum = punitNum - programUnitCnt;
+        }
+        if (punitNum == 0) {
+            return;
+        }
+        if (punitNum > 0) {
+            //1上新
+            log.info("-----每日补充创建 (程序化上新)账户:{},本次可建组数:{}", strategy.getAccountId(), punitNum);
+            unitNum = this.autoCreateProgramCreative(strategy, 1, punitNum);
+        }
+        if (punitNum > 0 && material.contains("2")) {
+            log.info("-----每日补充创建 (程序化高质量)账户:{},本次可建组数:{}", strategy.getAccountId(), punitNum);
+            //2高质量素材
+            unitNum = this.autoCreateProgramCreative(strategy, 2, punitNum);
+        }
+    }
+
 
     /**
      * 获取创建比例

+ 10 - 12
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/ai/service/impl/AiKuaishouAccountAutoVideoServiceImpl.java

@@ -49,17 +49,17 @@ public class AiKuaishouAccountAutoVideoServiceImpl extends ServiceImpl<AiKuaisho
 
     @Override
     public List<AiKuaishouAccountAutoVideo> getHistoryTopVideos(Long accountId, Integer channelType, String keyword, Integer videoCnt, String statDate, String createType) {
-        return autoVideoMapper.getHistoryTopVideos(accountId, channelType, keyword, videoCnt, createType,statDate);
+        return autoVideoMapper.getHistoryTopVideos(accountId, channelType, keyword, videoCnt, createType, statDate);
     }
 
     @Override
     public List<AiKuaishouAccountAutoVideo> getMissVideoByParams(Long accountId, Integer channelType, String keyword, Integer videoCnt, String statDate) {
-        return autoVideoMapper.getZeroVideoByParams(accountId, channelType, keyword, videoCnt,statDate);
+        return autoVideoMapper.getZeroVideoByParams(accountId, channelType, keyword, videoCnt, statDate);
     }
 
     @Override
     public List<AiKuaishouAccountAutoVideo> getHistoryCatchVideos(Long accountId, Integer channelType, String keyword, Integer videoCnt, String statDate) {
-        return autoVideoMapper.getHistoryCatchVideos(accountId, channelType, keyword, videoCnt,statDate);
+        return autoVideoMapper.getHistoryCatchVideos(accountId, channelType, keyword, videoCnt, statDate);
     }
 
 
@@ -94,8 +94,8 @@ public class AiKuaishouAccountAutoVideoServiceImpl extends ServiceImpl<AiKuaisho
             param.put("advertiser_id", token.getAccountId());
             param.put("page_size", pageSize);
             param.put("page", page);
-            param.put("start_date", date);
-            param.put("end_date", date);
+//            param.put("start_date", date);
+//            param.put("end_date", date);
             String result = HttpUtils.httpPostRequest(url, param, headers);
             JSONObject resultJson = JSONObject.parseObject(result);
             if (Check.isNull(resultJson)) {
@@ -118,25 +118,23 @@ public class AiKuaishouAccountAutoVideoServiceImpl extends ServiceImpl<AiKuaisho
             }
 
             List<AiKuaishouAccountAutoVideo> videoGetList = new ArrayList<>();
+
+            Long accountId = token.getAccountId();
+            List<String> photoIds = autoVideoMapper.selectBatchByPhotoIds(details, date, accountId);
             for (int i = 0; i < details.size(); i++) {
                 JSONObject detailJson = details.getJSONObject(i);
                 if (Check.isNull(detailJson)) {
                     continue;
                 }
                 String photoId = detailJson.getString("photo_id");
-                Long accountId = token.getAccountId();
-                QueryWrapper<AiKuaishouAccountAutoVideo> queryWrapper = new QueryWrapper<>();
-                queryWrapper.eq("photo_id", photoId);
-                queryWrapper.eq("account_id", accountId).last("limit 1");
-                AiKuaishouAccountAutoVideo one = this.getOne(queryWrapper);
-                if (!Check.isNull(one)) {
+                if (!Check.isNull(photoIds) && photoIds.contains(photoId)) {
                     continue;
                 }
                 AiKuaishouAccountAutoVideo videoGet = new AiKuaishouAccountAutoVideo();
                 videoGet.setAccountId(accountId);
                 videoGet.setPhotoId(photoId);
                 videoGet.setPhotoName(detailJson.getString("photo_name"));
-                videoGet.setStatDate(date);
+                videoGet.setStatDate(DateUtils.formatDate(detailJson.getDate("create_time"), DateUtils.WEB_FORMAT));
                 String signature = detailJson.getString("signature");
                 videoGet.setSignature(signature);
                 Integer source = detailJson.getInteger("source");

+ 3 - 0
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/batch/mapper/KuaiShouCampaignMapper.java

@@ -1,6 +1,7 @@
 package cn.com.ctop.kuaishou.modules.batch.mapper;
 
 import cn.com.ctop.kuaishou.modules.batch.entity.KuaiShouCampaign;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -25,4 +26,6 @@ public interface KuaiShouCampaignMapper extends BaseMapper<KuaiShouCampaign> {
     Long getCampaignIdByAccountAndName(@Param("accountId") Long accountId, @Param("name") String replaceString);
 
     Long getCampaignIdByAccountAndNameAndDate(@Param("accountId") Long accountId, @Param("name") String replaceString, @Param("nowDate") String nowDate);
+
+    List<JSONObject> getCampaignIdsByParameters(@Param("accountId") Long accountId, @Param("typeName") String typeName, @Param("nowDate") String nowDate);
 }

+ 27 - 19
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/batch/mapper/xml/KuaiShouCampaignMapper.xml

@@ -36,38 +36,46 @@
 
     <select id="queryIdsByPutStatus" resultType="java.lang.Long">
         SELECT campaign_id
-        FROM
-        ctop_kuaishou_campaign
+        FROM ctop_kuaishou_campaign
         WHERE account_id = #{accountId}
-        AND put_status = #{putStatus}
+          AND put_status = #{putStatus}
     </select>
     <select id="queryCampaignIdByAccountId" resultType="java.lang.Long">
         SELECT campaign_id
-        FROM
-        ctop_kuaishou_campaign
+        FROM ctop_kuaishou_campaign
         WHERE account_id = #{accountId}
     </select>
     <select id="getNewDataByAccountIdAndDate" resultType="java.lang.Integer">
-     select
-     count(1)
-     from ctop_kuaishou_campaign
-     where account_id = #{accountId}
-     and DATE_FORMAT(put_create_time,'%Y-%m-%d') = #{statDate}
+        select count(1)
+        from ctop_kuaishou_campaign
+        where account_id = #{accountId}
+          and DATE_FORMAT(put_create_time, '%Y-%m-%d') = #{statDate}
     </select>
     <select id="getCampaignIdByAccountAndName" resultType="java.lang.Long">
-        select campaign_id  from  ctop_kuaishou_campaign where  account_id = #{accountId}
-        and campaign_name like CONCAT('%',CONCAT(#{name},'%'))
-        order by put_create_time DESC
-        limit 1
+        select campaign_id
+        from ctop_kuaishou_campaign
+        where account_id = #{accountId}
+          and campaign_name like CONCAT('%', CONCAT(#{name}, '%'))
+        order by put_create_time DESC limit 1
 
 
     </select>
     <select id="getCampaignIdByAccountAndNameAndDate" resultType="java.lang.Long">
-          select campaign_id  from  ctop_kuaishou_campaign where  account_id = #{accountId}
-        and campaign_name like CONCAT('%',CONCAT(#{name},'%'))
-        and put_create_time &gt;= #{nowDate}
-        order by put_create_time DESC
-        limit 1
+        select campaign_id
+        from ctop_kuaishou_campaign
+        where account_id = #{accountId}
+          and campaign_name like CONCAT('%', CONCAT(#{name}, '%'))
+          and put_create_time &gt;= #{nowDate}
+        order by put_create_time DESC limit 1
 
     </select>
+
+    <select id="getCampaignIdsByParameters" resultType="com.alibaba.fastjson.JSONObject">
+        SELECT campaign_id as 'campaignId',
+               (SELECT COUNT(1) FROM ctop_kuaishou_group WHERE campaign_id = t.campaign_id) AS 'count'
+        FROM ctop_kuaishou_campaign t
+        WHERE account_id = #{accountId}
+          AND campaign_name LIKE CONCAT('%', CONCAT(#{typeName}, '%'))
+          AND put_create_time &gt;= #{nowDate}
+    </select>
 </mapper>

+ 3 - 0
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/batch/service/IKuaiShouCampaignService.java

@@ -2,6 +2,7 @@ package cn.com.ctop.kuaishou.modules.batch.service;
 
 import cn.com.ctop.kuaishou.modules.batch.entity.KuaiShouCampaign;
 import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.extension.service.IService;
 
 import java.util.List;
@@ -27,4 +28,6 @@ public interface IKuaiShouCampaignService extends IService<KuaiShouCampaign> {
     Long getCampaignIdByAccountAndName(Long accountId, String replaceString);
 
     Long getCampaignIdByAccountAndNameAndDate(Long accountId, String replaceString, String nowDate);
+
+    List<JSONObject> getCampaignIdsByParameters(Long accountId, String typeName, String nowDate);
 }

+ 0 - 2
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/batch/service/impl/CrossAccountBatchServiceImpl.java

@@ -500,9 +500,7 @@ public class CrossAccountBatchServiceImpl implements ICrossAccountBatchService {
                         };
                         thread2.start();
                     }
-
                 } else {
-
                     if (code == 500000 && count <= 4) {
                         adUnitCreate(requestJson, count + 1);
                     }

+ 6 - 0
jeecg-boot-module-system/src/main/java/cn/com/ctop/kuaishou/modules/batch/service/impl/KuaiShouCampaignServiceImpl.java

@@ -5,6 +5,7 @@ import cn.com.ctop.kuaishou.modules.batch.entity.KuaiShouCampaign;
 import cn.com.ctop.kuaishou.modules.batch.mapper.KuaiShouCampaignMapper;
 import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouCampaignService;
 import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -91,4 +92,9 @@ public class KuaiShouCampaignServiceImpl extends ServiceImpl<KuaiShouCampaignMap
         return campaignMapper.getCampaignIdByAccountAndNameAndDate(accountId, replaceString, nowDate);
     }
 
+    @Override
+    public List<JSONObject> getCampaignIdsByParameters(Long accountId, String typeName, String nowDate) {
+        return campaignMapper.getCampaignIdsByParameters(accountId, typeName, nowDate);
+    }
+
 }