package cn.com.ctop.kuaishou.modules.ai.service.impl;

import cn.com.ctop.common.module.entity.CtopOauthToken;
import cn.com.ctop.common.module.service.ICtopOauthTokenService;
import cn.com.ctop.common.module.service.IMaterialCutFrameService;
import cn.com.ctop.common.module.utils.Check;
import cn.com.ctop.common.module.utils.HttpUtils;
import cn.com.ctop.common.module.utils.KuaishouInterfaceConstant;
import cn.com.ctop.common.module.utils.PropertiesUtils;
import cn.com.ctop.kuaishou.modules.ai.entity.AiKuaiShouAppInfo;
import cn.com.ctop.kuaishou.modules.ai.entity.AiKuaishouAccountAutoStrategy;
import cn.com.ctop.kuaishou.modules.ai.entity.AiKuaishouAccountAutoTarget;
import cn.com.ctop.kuaishou.modules.ai.entity.AiKuaishouAccountAutoVideo;
import cn.com.ctop.kuaishou.modules.ai.entity.AiKuaishouCampaignLevelOperationRecord;
import cn.com.ctop.kuaishou.modules.ai.entity.AiKuaishouCreativeLevelOperationRecord;
import cn.com.ctop.kuaishou.modules.ai.entity.AiKuaishouProgramCreativeLevelOperationRecord;
import cn.com.ctop.kuaishou.modules.ai.entity.AiKuaishouUnitLevelOperationRecord;
import cn.com.ctop.kuaishou.modules.ai.entity.KuaishouAccountCreativeOverrunInfo;
import cn.com.ctop.kuaishou.modules.ai.enums.KuaishouAutoTypeEnum;
import cn.com.ctop.kuaishou.modules.ai.enums.KuaishouCreativeAdsenceTypeEnum;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaiShouAppInfoService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouAccountAutoDoService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouAccountAutoTargetService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouAccountAutoVideoService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouCampaignLevelOperationRecordService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouCreativeLevelOperationRecordService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouProgramCreativeLevelOperationRecordService;
import cn.com.ctop.kuaishou.modules.ai.service.IAiKuaishouUnitLevelOperationRecordService;
import cn.com.ctop.kuaishou.modules.ai.service.IKuaiShouAppMultipleBidsService;
import cn.com.ctop.kuaishou.modules.ai.service.IKuaishouAccountCreativeOverrunInfoService;
import cn.com.ctop.kuaishou.modules.ai.service.IKuaishouLandpagePackageService;
import cn.com.ctop.kuaishou.modules.batch.entity.KuaiShouAppList;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouAppListService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouCampaignService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouGroupService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouGroupTemplateService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouImageGetService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouMaterialUploadService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouUpdateService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouVideoGetService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaishouInterfaceService;
import cn.com.ctop.kuaishou.modules.channel.entity.KuaishouChannel;
import cn.com.ctop.kuaishou.modules.channel.entity.KuaishouChannelItems;
import cn.com.ctop.kuaishou.modules.channel.service.IKuaishouChannelService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.RedisUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;

@Slf4j
@Service
public class AiKuaishouAccountAutoDoServiceImpl implements IAiKuaishouAccountAutoDoService {
    @Autowired
    private IKuaiShouGroupService groupService;
    @Autowired
    private IAiKuaishouAccountAutoTargetService autoTargetService;
    @Autowired
    private IKuaiShouGroupTemplateService groupTemplateService;
    @Autowired
    private ICtopOauthTokenService tokenService;
    @Autowired
    private IKuaishouInterfaceService kuaishouInterfaceService;
    @Autowired
    private IKuaiShouVideoGetService videoGetService;
    @Autowired
    private IAiKuaishouAccountAutoVideoService autoVideoService;
    @Autowired
    private IMaterialCutFrameService materialCutFrameService;
    @Autowired
    private IKuaiShouImageGetService imageGetService;
    @Autowired
    private IKuaiShouMaterialUploadService materialUploadService;
    @Autowired
    private IKuaishouAccountCreativeOverrunInfoService overrunInfoService;
    @Autowired
    private IAiKuaishouCampaignLevelOperationRecordService aiKuaishouCampaignLevelOperationRecordService;

    @Autowired
    private IAiKuaishouProgramCreativeLevelOperationRecordService kuaishouProgramCreativeLevelOperationRecordService;

    @Autowired
    private IAiKuaiShouAppInfoService appInfoService;
    @Autowired
    private IAiKuaishouUnitLevelOperationRecordService unitLevelOperationRecordService;
    @Autowired
    private IAiKuaishouCreativeLevelOperationRecordService creativeLevelOperationRecordService;
    @Autowired
    private IKuaishouLandpagePackageService landpagePackageService;
    @Autowired
    private IKuaiShouAppMultipleBidsService kuaiShouAppMultipleBidsService;
    @Autowired
    private IKuaishouChannelService channelService;
    @Autowired
    private IKuaiShouUpdateService updateService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private IKuaiShouAppListService appListService;

    @Autowired
    private IKuaiShouCampaignService campaignService;

    @Override
    public void createCustomUpNewCreative(AiKuaishouAccountAutoStrategy strategy) {
        Long accountId = strategy.getAccountId();
        //==========账户{}执行自定义上新自动创建==========start==========
        String date = DateUtils.formatDate(new Date());
        //当前需要创建广告组数量
        if (strategy.getCustomPlanCnt() == null || strategy.getCustomUnitCnt() == null) {
            return;
        }
        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;
        }
        log.info("----- （自定义上新）账户：{}，已建组数：{}；剩余待建组数：{}", strategy.getAccountId(), customUnitCnt, unitNum);
        Integer hours = 0;
        try {
            //当前剩余小时数 （2~21）
            hours = 22 - DateUtils.getHour(DateUtils.formatDateTime());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        JSONObject cnt = getCnt(strategy.getSourceMaterial(), unitNum, hours);
        Integer upNewCnt = cnt.getInteger("upNewCnt");
        if (upNewCnt == 0) {
            return;
        } else if (upNewCnt >= 3) {
            //上新每小时执行3次
            upNewCnt = upNewCnt / 3;
        } else {
            upNewCnt = 1;
        }
        if (upNewCnt > 0) {
            // 1上新
            log.info("----- （自定义上新）账户：{}，本次可建组数：{}", strategy.getAccountId(), upNewCnt);
            this.autoCreateCreative(strategy, 1, upNewCnt);
        }

    }

    /**
     * 创建自定义高质量、打捞、遗漏广告创意
     */
    @Override
    public void createCustomCreative(AiKuaishouAccountAutoStrategy strategy) {
        Long accountId = strategy.getAccountId();
        //==========账户{}执行自定义（高质量、打捞、遗漏）自动创建==========start==========
        String date = DateUtils.formatDate(new Date());
        //当前需要创建广告组数量
        if (strategy.getCustomPlanCnt() == null || strategy.getCustomUnitCnt() == null) {
            return;
        }
        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) {
            log.info("今日创建已达标");
            return;
        }
        log.info("----- （自定义）账户：{}，已建组数：{}；剩余待建组数：{}", strategy.getAccountId(), customUnitCnt, unitNum);
        Integer hours = 0;
        try {
            //当前剩余小时数 （2~21）
            hours = 22 - DateUtils.getHour(DateUtils.formatDateTime());
        } catch (ParseException e) {
            e.printStackTrace();
        }

        //高质量待建数
        int highCnt = 0;
        //遗漏
        int omitCnt = 0;
        //打捞
        int salvageCnt = 0;
        //1-上新素材，2-高质量素材，3-遗漏素材，4-历史打捞
        String material = strategy.getSourceMaterial();
        if (hours > 0) {
            JSONObject cnt = getCnt(material, unitNum, hours);
            highCnt = cnt.getInteger("highCnt");
            omitCnt = cnt.getInteger("omitCnt");
            salvageCnt = cnt.getInteger("salvageCnt");
        }
        if (omitCnt > 0 && material.contains("3")) {
            //3历史遗漏
            log.info("----- （自定义历史遗漏）账户：{}，本次可建组数：{}", strategy.getAccountId(), omitCnt);
            unitNum = this.autoCreateCreative(strategy, 3, omitCnt);
        }
        if (highCnt > 0 && material.contains("2")) {
            //2高质量素材
            log.info("----- （自定义高质量）账户：{}，本次可建组数：{}", strategy.getAccountId(), highCnt);
            unitNum = this.autoCreateCreative(strategy, 2, highCnt);
        }
        if (salvageCnt > 0 && material.contains("4")) {
            //4历史打捞素材
            log.info("----- （自定义历史打捞）账户：{}，本次可建组数：{}", strategy.getAccountId(), salvageCnt);
            this.autoCreateCreative(strategy, 4, salvageCnt);
        }
    }


    /**
     * 执行创建自定义创意
     *
     * @param strategy  账户配置信息
     * @param videoType 创意创建类型 1:素材上新 2：历史高质量 3:历史遗漏素材 4:历史打捞
     * @param videoCnt  视频数量
     */
    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) {
            log.error("****** accountId:{}获取token失败", accountId);
            return videoCnt;
        }
        //创意创建类型
        String typeName = KuaishouAutoTypeEnum.getNameByType(strategy.getUnitType().toString() + videoType.toString());
        String statDate = DateUtils.formatDate(new Date());
        //判定账户是否单一应用
        Integer singleAppid = strategy.getSingleAppid();
        if (null != singleAppid && singleAppid == 1) {
            String strategyUuid = UUID.randomUUID().toString();
            /**============================= 单应用 逻辑 =============================*/
            log.info("--------------------账户（{}）执行自定义自动投放搭建:{}-----------------", accountId, typeName);

            /*获取应用*/
            Long appId = strategy.getAppId();
            KuaiShouAppList appInfo = appListService.getAppInfo(accountId, appId);
            if (Check.isNull(appInfo)) {
                log.error("****** 获取应用失败，AppId={}", appId);
                return videoCnt;
            }
            String appName = appInfo.getAppName();
            Long newCampaignId = null;
            String level = strategy.getUsageLevel();//渠道号应用层级，account-账户，plan-计划，group-组
            KuaishouChannel usedchannel = null;
            try {
                KuaishouChannel channel = null;
                //0不使用，1使用
                Integer useChannel = Check.isNull(strategy.getUseChannel()) ? 0 : strategy.getUseChannel();
                if (useChannel == 1 && !"group".equals(level)) {
                    /*获取渠道号*/
                    channel = getChannel(level, accountId, appName);
                    if (Check.isNull(channel)) {
                        return videoCnt;
                    }
                }
                /*执行 创建计划or获取已创建计划Id*/
                newCampaignId = getCampaignId(strategy, token, videoType, strategyUuid, channel);
                if (null == newCampaignId) {
                    return videoCnt;
                }
                //赋值本次执行可创建数
                videoCnt = strategy.getToBeBuiltCnt();
                log.info("---------获取计划成功，计划id:{}", newCampaignId);
                // 1 ：使用渠道号
                if (useChannel == 1) {
                    //素材关键词置空
                    strategy.setMaterialKeyword(null);
                    //根据组级渠道号进行创建自定义
                    if ("group".equals(level)) {
                        return addCustomByGroupChannel(strategy, appName, videoType, token, newCampaignId, appId, videoCnt, strategyUuid);
                    } else {
                        if ("plan".equals(level)) {
                            usedchannel = channelService.queryUsedChannel(accountId, newCampaignId, level);
                            if (Check.isNull(usedchannel)) {
                                log.error("****** 获取已创建的计划所使用的渠道号失败");
                                return videoCnt;
                            }
                            log.info("------因计划层级，更新为已创建的计划所使用的渠道号：{}", usedchannel.getChannelCode());
                        } else {
                            usedchannel = channel;
                        }
                        //设置素材关键词
                        List<KuaishouChannelItems> items = usedchannel.getItems();
                        if (!Check.isNull(items) && !items.isEmpty()) {
                            strategy.setMaterialKeyword(items.get(0).getKeyword());
                        }
                    }
                }

                /*获取素材*/
                List<AiKuaishouAccountAutoVideo> newVideos = getVideosByParams(strategy, statDate, null, videoType, "custom", videoCnt);
                if (null == newVideos || newVideos.isEmpty()) {
                    log.error("****** {}账户（{}）执行自定义创建，未获取到素材({})", statDate, accountId, typeName);
                }

                //待建素材集 redis key值
                String videoKey = accountId + statDate + "_list_" + videoType;
                /*根据匹配逻辑，定向包和素材进行匹配，获取匹配后的素材*/
                List<AiKuaishouAccountAutoVideo> targetsAndVideos = getTargetsAndVideos(strategy, newVideos, videoKey, null, null, null, videoCnt);
                if (Check.isNull(targetsAndVideos)) {
                    return videoCnt;
                }

                //记录创建成功的素材，待最后执行删除redis
                List<AiKuaishouAccountAutoVideo> successVideoList = new ArrayList<>();
                for (AiKuaishouAccountAutoVideo videoItem : targetsAndVideos) {
                    boolean unitOverrun = getUnitOverrun(accountId);
                    if (unitOverrun) {
                        log.error("****** 账户（{}）创建广告组超限", accountId);
                        removeRedisVideos(successVideoList, videoKey);
                        return 0;
                    }
                    KuaishouAccountCreativeOverrunInfo overrunInfo = overrunInfoService.getByParams(accountId, statDate);
                    if (null != overrunInfo) {
                        log.error("****** 账户（{}）创建广告创意超限", accountId);
                        removeRedisVideos(successVideoList, videoKey);
                        return 0;
                    }
                    Integer unitCode = null;
                    String unitMessage = null;
                    String unitName = getName(strategy.getGroupName(), strategy, videoType, videoItem.getPhotoName(), "unit", newCampaignId, appId, videoItem.getAutoTargetId(), usedchannel);
                    Result<Object> result = createUnitParams(token, newCampaignId, strategy, unitName, appId, usedchannel, videoItem.getAutoTargetId());
                    if (result.getCode() != CommonConstant.SC_OK_200) {
                        //广告组数据获取失败
                        continue;
                    }
                    JSONObject unitParams = (JSONObject) result.getResult();
                    Map<String, Object> unitCreateResult = kuaishouInterfaceService.adUnitCreate(token.getAccessToken(), token.getAccountId(), unitParams, 1);
                    Thread.sleep(1000);
                    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(), channelId);
                    if (unitCode != 0) {
                        rollbackPlayId(unitParams, strategy);
                        log.error("****** 账户（{}）组创建失败，入参：{}，message:{}", accountId, unitParams, unitMessage);
                        String redisKey = strategy.getAccountId() + "_" + newCampaignId + DateUtils.formatDate(new Date(), "yyMMdd");
                        Integer redisCnt = (Integer) redisUtil.get(redisKey);
                        redisUtil.set(redisKey, redisCnt - 1, 100000L);
                        unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                        continue;
                    }
                    Long unitId = (Long) unitCreateResult.get("unitId");
                    unitRecord.setUnitId(unitId);
                    unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                    if (!Check.isNull(usedchannel)) {
                        channelService.callBackUpdateChannelRel(level, usedchannel.getChannelCode(), accountId, newCampaignId, unitId);
                    }
                    videoCnt--;
                    //更新自定义创建组数量
                    updateRedisCnt(statDate + "_auto_custom_cnt_" + accountId);
                    //获取创意参数
                    JSONObject creativeParams = getCreativeParams(token, unitId, videoItem, strategy, usedchannel, null);
                    //执行创建创意
                    boolean flag = createCreative(token, creativeParams, strategy, newCampaignId, unitId, strategyUuid);
                    if (flag) {
                        //创意成功，则素材置为不可用，添加素材使用记录
                        updateVideoUsed(videoType, null, "custom", videoItem);
                        successVideoList.add(videoItem);
                    }
                    if (videoCnt <= 0) {
                        removeRedisVideos(successVideoList, videoKey);
                        return 0;
                    }
                }
                //删除已使用的素材
                removeRedisVideos(successVideoList, videoKey);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("(单应用)创建自定义广告组异常", e);
            }
        } else {
            //多应用
            return autoCreateCustomMoreApp(strategy, videoType, token, typeName, videoCnt);
        }
        return videoCnt;
    }

    /**
     * 更新广告组数
     */
    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) {
        String strategyUuid = UUID.randomUUID().toString();
        Long accountId = strategy.getAccountId();
        String statDate = DateUtils.formatDate(new Date());
        String appIds = strategy.getAppIdArray();
        if (Check.isNull(appIds)) {
            log.error("****** 未获取到应用appIds");
            return videoCnt;
        }
        JSONArray appIdArray = JSONArray.parseArray(appIds);
        if (Check.isNull(appIdArray) || appIdArray.isEmpty()) {
            log.error("****** 应用appIds参数格式错误");
            return videoCnt;
        }

        //待建素材集 redis key值
        String videoKey = accountId + statDate + "_more_list_" + videoType;
        //判断 单/多出价
        Integer isMoreBids = strategy.getMultipleBids();
        /*单出价*/
        if (Check.isNull(isMoreBids) && isMoreBids == 0) {
            for (int i = 0; i < appIdArray.size(); i++) {
                /*获取应用*/
                Long appId = appIdArray.getLong(i);
                AiKuaiShouAppInfo appInfo = appInfoService.getByParams(accountId, appId);
                if (Check.isNull(appInfo)) {
                    log.error("****** 获取应用失败，AppId={}", appId);
                    continue;
                }
                //是否通用监测链接
                if (null != strategy.getGeneralTrack() && strategy.getGeneralTrack() == 0) {
                    strategy.setClickTrackUrl(appInfo.getTrackUrl());
                    strategy.setImpressionUrl(appInfo.getTrackUrl());
                }
                //重置命名，appversion唯一
                typeName = appInfo.getAppVersion() + "-" + typeName;

                /*获取素材*/
                List<AiKuaishouAccountAutoVideo> allVideos = getVideosByParams(strategy, statDate, appInfo.getAppVersion(), videoType, "custom", videoCnt);
                if (null == allVideos || allVideos.isEmpty()) {
                    log.error("****** {}账户（{}）执行自定义创建，未获取到素材({})", statDate, accountId, typeName);
                    continue;
                }
                /*执行 创建计划or获取已创建计划Id*/
                Long campaignId = getCampaignId(strategy, token, videoType, strategyUuid, null);
                if (campaignId == null) {
                    continue;
                }
                //赋值本次执行可创建数
                videoCnt = strategy.getToBeBuiltCnt();
                /*根据匹配逻辑，定向包和素材进行匹配，获取匹配后的素材*/
                getTargetsAndVideos(strategy, allVideos, videoKey, appId, campaignId, appInfo.getAppVersion(), 0);
            }
            /*从Redis获取待创建的素材*/
            List<AiKuaishouAccountAutoVideo> redisVideos = (List) redisUtil.get(videoKey);
            if (Check.isNull(redisVideos) || redisVideos.isEmpty()) {
                return videoCnt;
            }
            //记录创建成功的素材，待最后执行删除redis
            List<AiKuaishouAccountAutoVideo> successVideoList = new ArrayList<>();
            for (AiKuaishouAccountAutoVideo videoItem : redisVideos) {
                Long appId = videoItem.getAppId();
                Long campaignId = videoItem.getCampaignId();
                boolean unitOverrun = getUnitOverrun(accountId);
                if (unitOverrun) {
                    log.error("****** 账户（{}）创建广告组超限", accountId);
                    removeRedisVideos(successVideoList, videoKey);
                    return 0;
                }
                KuaishouAccountCreativeOverrunInfo overrunInfo = overrunInfoService.getByParams(accountId, statDate);
                if (null != overrunInfo) {
                    log.error("****** 账户（{}）创建广告创意超限", accountId);
                    removeRedisVideos(successVideoList, videoKey);
                    return 0;
                }

                /*开始执行创建组、创意*/
                String unitName = getName(strategy.getGroupName(), strategy, videoType, videoItem.getPhotoName(), "unit", campaignId, appId, videoItem.getAutoTargetId(), null);
                Result<Object> result = createUnitParams(token, campaignId, strategy, unitName, appId, null, videoItem.getAutoTargetId());
                if (result.getCode() != CommonConstant.SC_OK_200) {
                    //广告组数据获取失败
                    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());
                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) {
                    log.error("****** 账户（{}）组创建失败，message:{}", accountId, unitMessage);
                    unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                    continue;
                }
                Long unitId = (Long) unitCreateResult.get("unitId");
                unitRecord.setUnitId(unitId);
                //添加广告组创建记录
                unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                videoCnt--;
                successVideoList.add(videoItem);
                //添加素材使用记录
                updateVideoUsed(videoType, null, "custom", videoItem);
                //获取创意参数
                JSONObject creativeParams = getCreativeParams(token, unitId, videoItem, strategy, null, videoItem.getAppVersion());
                //执行创建创意
                createCreative(token, creativeParams, strategy, campaignId, unitId, strategyUuid);
                if (videoCnt <= 0) {
                    removeRedisVideos(successVideoList, videoKey);
                    return 0;
                }
            }
            removeRedisVideos(successVideoList, videoKey);

        } else if (isMoreBids == 1) {
            //多出价 借鉴 AiKuaishouCreateCreativeServiceImpl
        }
        return videoCnt;
    }


    /**
     * 创建程序化广告创意
     *
     * @param strategy 账户配置信息
     */
    @Override
    public void createProgramCreative(AiKuaishouAccountAutoStrategy strategy) {
        Long accountId = strategy.getAccountId();
        //==========账户{}执行自定义（高质量、打捞、遗漏）自动创建==========start==========
        String date = DateUtils.formatDate(new Date());
        //当前需要创建广告组数量
        if (strategy.getProgramPlanCnt() == null || strategy.getProgramUnitCnt() == null) {
            return;
        }
        int unitNum = strategy.getProgramPlanCnt() * strategy.getProgramUnitCnt();
        //获取自定义已创建广告组数量
        String programkey = date + "_auto_program_cnt_" + accountId;
        Integer programUnitCnt = (Integer) redisUtil.get(programkey);
        if (Check.isNull(programUnitCnt)) {
            if (unitNum > 300) {
                unitNum = 300;
            }
        } else {
            unitNum = unitNum - programUnitCnt;
        }
        if (unitNum == 0) {
            log.info("今日创建已达标");
            return;
        }
        log.info("----- （程序化）账户：{}，已建组数：{}；剩余待建组数：{}", strategy.getAccountId(), programUnitCnt, unitNum);
        Integer hours = 0;
        try {
            //当前剩余小时数 （2~21）
            hours = 22 - DateUtils.getHour(DateUtils.formatDateTime());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        int highCnt = 0;
        int upNewCnt = unitNum;
        String matchType = strategy.getSourceMaterial();
        //包含高质量，则按照 上新：高质量 = 75%:25% 分配
        if (matchType.contains("2")) {
            highCnt = (int) (unitNum * 0.25);
            upNewCnt = (int) (unitNum * 0.75);
        }
        if (hours > upNewCnt) {
            upNewCnt = 1;
        } else {
            highCnt = highCnt / hours;
            upNewCnt = upNewCnt / hours;
        }
        if (upNewCnt > 0) {
            //1上新
            log.info("----- （程序化上新）账户：{}，本次可建组数：{}", strategy.getAccountId(), upNewCnt);
            unitNum = this.autoCreateProgramCreative(strategy, 1, upNewCnt);
        }
        if (highCnt > 0 && matchType.contains("2")) {
            log.info("----- （程序化高质量）账户：{}，本次可建组数：{}", strategy.getAccountId(), highCnt);
            //2高质量素材
            unitNum = this.autoCreateProgramCreative(strategy, 2, highCnt);
        }
    }

    /**
     * 执行创建程序化创意
     *
     * @param strategy  策略对象
     * @param videoType 创意创建类型 1:素材上新 2：历史高质量 3:历史遗漏素材 4:历史打捞
     * @param videoCnt  视频数量
     */
    private Integer autoCreateProgramCreative(AiKuaishouAccountAutoStrategy strategy, Integer videoType, Integer videoCnt) {
        String uuid = UUID.randomUUID().toString();
        //4-自定义
        strategy.setUnitType(7);
        strategy.setToBeBuiltCnt(videoCnt);
        Long accountId = strategy.getAccountId();
        CtopOauthToken token = tokenService.getTokenByAccountId(accountId);
        if (null == token) {
            log.error("****** accountId:{}获取token失败", accountId);
            return videoCnt;
        }

        //创意创建类型
        String typeName = KuaishouAutoTypeEnum.getNameByType(strategy.getUnitType().toString() + videoType.toString());
        String statDate = DateUtils.formatDate(new Date());

        //程序化创建创意超限标识，redis key值
        String programKey = accountId + "_pcreativecount_" + statDate;
        //待建素材集 redis 程序化单应用 key值
        String videoKey = accountId + statDate + "_op_plist_" + videoType;
        //判定账户是否单一应用
        Integer singleAppid = strategy.getSingleAppid();
        if (null != singleAppid && singleAppid == 1) {
            /**============================= 单应用 逻辑 =============================*/
            log.info("--------------------账户（{}）执行程序化单应用自动投放搭建:{}-----------------", accountId, typeName);

            /*获取应用*/
            Long appId = strategy.getAppId();
            KuaiShouAppList appInfo = appListService.getAppInfo(accountId, appId);
            if (Check.isNull(appInfo)) {
                log.error("****** 获取应用失败，AppId={}", appId);
                return videoCnt;
            }
            String appName = appInfo.getAppName();
            Long newCampaignId = null;
            String level = strategy.getUsageLevel();//渠道号应用层级，account-账户，plan-计划，group-组
            KuaishouChannel usedchannel = null;
            try {
                KuaishouChannel channel = null;
                /*获取渠道号*/
                Integer useChannel = Check.isNull(strategy.getUseChannel()) ? 0 : strategy.getUseChannel();//0不使用，1使用
                // 1 ：使用渠道号
                if (useChannel == 1) {
                    channel = getChannel(level, accountId, appName);
                    if (Check.isNull(channel)) {
                        return videoCnt;
                    }
                }
                /*执行 创建计划or获取已创建计划Id*/
                newCampaignId = getCampaignId(strategy, token, videoType, uuid, channel);
                if (null == newCampaignId) {
                    return videoCnt;
                }
                //赋值本次执行可创建数
                videoCnt = strategy.getToBeBuiltCnt();
                log.info("---------获取计划成功，计划id:{}", newCampaignId);

                if (!Check.isNull(channel)) {
                    //素材关键词置空
                    strategy.setMaterialKeyword(null);
                    //根据组级渠道号进行创建自定义
                    if ("group".equals(level)) {
                        return addProgramByGroupChannel(strategy, appName, videoType, token, newCampaignId, appId, videoCnt, uuid);
                    } else {
                        if ("plan".equals(level)) {
                            usedchannel = channelService.queryUsedChannel(accountId, newCampaignId, level);
                            if (Check.isNull(usedchannel)) {
                                log.error("****** 获取已创建的计划所使用的渠道号失败");
                                return videoCnt;
                            }
                        } else {
                            usedchannel = channel;
                        }
                        //设置素材关键词
                        List<KuaishouChannelItems> items = usedchannel.getItems();
                        if (!Check.isNull(items) && !items.isEmpty()) {
                            strategy.setMaterialKeyword(items.get(0).getKeyword());
                        }
                    }
                }

                /*获取素材*/
                List<List<AiKuaishouAccountAutoVideo>> proVideos = new ArrayList<>();
                List<AiKuaishouAccountAutoVideo> allVideos = getVideosByParams(strategy, statDate, null, videoType, "program", videoCnt * 10);
                if (null == allVideos || allVideos.isEmpty()) {
                    log.error("****** {}账户（{}）执行程序化创建，未获取到素材({})", statDate, accountId, typeName);
                } else {
                    if (allVideos.size() < 3) {
                        log.error("****** {}账户（{}）执行程序化创建，获取素材小于3({})", statDate, accountId, typeName);
                    } else {
                        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 = newVideos.get(size);
                            if (lastVideos.size() < 3) {
                                //小于3 则去除
                                for (int i = 0; i < size; i++) {
                                    proVideos.add(newVideos.get(i));
                                }
                            } else {
                                proVideos = newVideos;
                            }
                        } else {
                            proVideos = newVideos;
                        }
                    }
                }

                /*根据匹配逻辑，定向包和素材进行匹配，获取匹配后的素材*/
                List<List<AiKuaishouAccountAutoVideo>> targetsAndVideos = getProTargetsAndVideos(strategy, proVideos, videoKey, videoCnt, null, null, null);
                if (Check.isNull(targetsAndVideos)) {
                    return videoCnt;
                }

                //记录创建成功的素材，待最后执行删除redis
                List<List<AiKuaishouAccountAutoVideo>> successVideoList = new ArrayList<>();
                for (List<AiKuaishouAccountAutoVideo> videos : targetsAndVideos) {
                    boolean unitOverrun = getUnitOverrun(accountId);
                    if (unitOverrun) {
                        log.error("****** 账户（{}）创建广告组超限", accountId);
                        removeProRedisVideos(successVideoList, videoKey);
                        return 0;
                    }
                    Object redisObj = redisUtil.get(programKey);
                    if (!Check.isNull(redisObj)) {
                        Integer value = (Integer) redisObj;
                        if (value == 1) {
                            log.error("****** 账户（{}）创建程序化创意超限", strategy.getAccountId());
                            removeProRedisVideos(successVideoList, videoKey);
                            return 0;
                        }
                    }
                    Integer unitCode = null;
                    String unitMessage = null;
                    String unitName = getName(strategy.getGroupName(), strategy, videoType, null, "unit", newCampaignId, appId, videos.get(0).getAutoTargetId(), usedchannel);
                    Result<Object> result = createUnitParams(token, newCampaignId, strategy, unitName, appId, usedchannel, videos.get(0).getAutoTargetId());
                    if (result.getCode() != CommonConstant.SC_OK_200) {
                        //广告组数据获取失败
                        continue;
                    }
                    JSONObject unitParams = (JSONObject) result.getResult();
//                    log.info("--------------执行创建程序化广告组，参数：{}", unitParams);
                    Map<String, Object> unitCreateResult = kuaishouInterfaceService.adUnitCreate(token.getAccessToken(), token.getAccountId(), unitParams, 1);
                    Thread.sleep(1000);
                    //                    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(), channelId);
                    if (unitCode != 0) {
                        log.error("****** 程序化账户（{}）组创建失败，message:{}", accountId, unitMessage);
                        unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                        continue;
                    }
                    Long unitId = (Long) unitCreateResult.get("unitId");
                    unitRecord.setUnitId(unitId);
                    unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                    if (!Check.isNull(usedchannel)) {
                        channelService.callBackUpdateChannelRel(level, usedchannel.getChannelCode(), accountId, newCampaignId, unitId);
                    }
                    videoCnt--;
                    //更新程序化创建组数量
                    updateRedisCnt(statDate + "_auto_program_cnt_" + accountId);

                    //获取创意参数
                    JSONObject creativeParams = getProgramCreativeParams(token, unitId, videos, strategy, videoCnt, null, usedchannel);
                    //执行创建创意
                    boolean flag = createProgramCreatives(token, creativeParams, strategy, newCampaignId, uuid, 1);
                    if (flag) {
                        //创意成功，则素材置为不可用，添加素材使用记录
                        updateVideoUsed(videoType, videos, "program", null);
                        successVideoList.add(videos);
                    }

                    if (videoCnt <= 0) {
                        removeProRedisVideos(successVideoList, videoKey);
                        return 0;
                    }
                }
                //删除已使用的素材
                removeProRedisVideos(successVideoList, videoKey);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("(单应用)创建自定义广告组异常", e);
            }
        } else {
            log.info("--------------------账户（{}）执行程序化多应用自动投放搭建:{}-----------------", accountId, typeName);
            //多应用
            autoCreateProgramMoreApp(strategy, videoType, token, typeName, videoCnt);
        }
        return videoCnt;
    }

    /**
     * 程序化多应用创建逻辑
     */
    private Integer autoCreateProgramMoreApp(AiKuaishouAccountAutoStrategy strategy, Integer videoType, CtopOauthToken token, String typeName, Integer videoCnt) {
        String strategyUuid = UUID.randomUUID().toString();
        Long accountId = strategy.getAccountId();
        String date = DateUtils.formatDate(new Date());
        String appIds = strategy.getAppIdArray();
        if (Check.isNull(appIds)) {
            log.error("****** 未获取到应用appIds");
            return videoCnt;
        }
        JSONArray appIdArray = JSONArray.parseArray(appIds);
        if (Check.isNull(appIdArray) || appIdArray.isEmpty()) {
            log.error("****** 应用appIds参数格式错误");
            return videoCnt;
        }

        //程序化创建创意超限标识，redis key值
        String programKey = accountId + "_pcreativecount_" + date;
        //待建素材集 redis 程序化多应用 key值
        String videoKey = accountId + date + "_mp_plist_" + videoType;

        //判断 单/多出价
        Integer isMoreBids = strategy.getMultipleBids();
        /*单出价*/
        if (Check.isNull(isMoreBids) && isMoreBids == 0) {
            for (int i = 0; i < appIdArray.size(); i++) {
                /*获取应用*/
                Long appId = appIdArray.getLong(i);
                AiKuaiShouAppInfo appInfo = appInfoService.getByParams(accountId, appId);
                if (Check.isNull(appInfo)) {
                    log.error("****** 获取应用失败，AppId={}", appId);
                    continue;
                }
                //是否通用监测链接
                if (null != strategy.getGeneralTrack() && strategy.getGeneralTrack() == 0) {
                    strategy.setClickTrackUrl(appInfo.getTrackUrl());
                    strategy.setImpressionUrl(appInfo.getTrackUrl());
                }
                //重置命名，appversion唯一
                typeName = appInfo.getAppVersion() + "-" + typeName;

                /*获取素材*/
                List<List<AiKuaishouAccountAutoVideo>> proVideos = new ArrayList<>();
                List<AiKuaishouAccountAutoVideo> allVideos = getVideosByParams(strategy, date, appInfo.getAppVersion(), videoType, "program", videoCnt * 10);
                if (Check.isNull(allVideos) || allVideos.isEmpty()) {
                    log.error("****** {}账户（{}）通过组级渠道号关键词{}，未获取到素材（程序化）", date, accountId, strategy.getMaterialKeyword());
                    continue;
                } else if (allVideos.size() < 3) {
                    continue;
                }

                List<List<AiKuaishouAccountAutoVideo>> splitVideos = Lists.partition(allVideos, 10);
                int size = splitVideos.size() - 1;
                if (size > 0) {
                    //判断最后一个集合小于3
                    List<AiKuaishouAccountAutoVideo> lastVideos = splitVideos.get(size);
                    if (lastVideos.size() < 3) {
                        //小于3 则去除
                        for (int j = 0; j < size; j++) {
                            proVideos.add(splitVideos.get(j));
                        }
                    } else {
                        proVideos = splitVideos;
                    }
                } else {
                    proVideos = splitVideos;
                }

                /*执行 创建计划or获取已创建计划Id*/
                Long campaignId = getCampaignId(strategy, token, videoType, strategyUuid, null);
                if (campaignId == null) {
                    continue;
                }
                //赋值本次执行可创建数
                videoCnt = strategy.getToBeBuiltCnt();
                /*根据匹配逻辑，定向包和素材进行匹配，获取匹配后的素材*/
                getProTargetsAndVideos(strategy, proVideos, programKey, 0, appId, campaignId, appInfo.getAppVersion());
            }
            /*从Redis获取待创建的素材*/
            List<List<AiKuaishouAccountAutoVideo>> redisVideos = (List<List<AiKuaishouAccountAutoVideo>>) redisUtil.get(videoKey);
            if (Check.isNull(redisVideos) || redisVideos.isEmpty()) {
                return videoCnt;
            }
            //记录创建成功的素材，待最后执行删除redis
            List<List<AiKuaishouAccountAutoVideo>> successVideoList = new ArrayList<>();
            for (List<AiKuaishouAccountAutoVideo> videos : redisVideos) {
                Long appId = videos.get(0).getAppId();
                Long campaignId = videos.get(0).getCampaignId();
                Long autoTargetId = videos.get(0).getAutoTargetId();
                boolean unitOverrun = getUnitOverrun(accountId);
                if (unitOverrun) {
                    log.error("****** 账户（{}）创建广告组超限", accountId);
                    removeProRedisVideos(successVideoList, videoKey);
                    return 0;
                }

                Object redisObj = redisUtil.get(programKey);
                if (!Check.isNull(redisObj)) {
                    Integer value = (Integer) redisObj;
                    if (value == 1) {
                        log.error("****** 账户（{}）创建程序化创意超限", strategy.getAccountId());
                        removeProRedisVideos(successVideoList, videoKey);
                        return 0;
                    }
                }

                /*开始执行创建组、创意*/
                String unitName = getName(strategy.getGroupName(), strategy, videoType, null, "unit", campaignId, appId, autoTargetId, null);
                Result<Object> result = createUnitParams(token, campaignId, strategy, unitName, appId, null, autoTargetId);
                if (result.getCode() != CommonConstant.SC_OK_200) {
                    //广告组数据获取失败
                    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());
                Integer unitCode = (Integer) unitCreateResult.get("code");
                String unitMessage = (String) unitCreateResult.get("message");
                /*获取广告组创建记录*/
                AiKuaishouUnitLevelOperationRecord unitRecord = new AiKuaishouUnitLevelOperationRecord(strategy, unitParams, unitCode, unitMessage, strategyUuid, autoTargetId, null);
                if (unitCode != 0) {
                    log.error("****** 程序化账户（{}）组创建失败，message:{}", accountId, unitMessage);
                    unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                    continue;
                }
                Long unitId = (Long) unitCreateResult.get("unitId");
                unitRecord.setUnitId(unitId);
                //添加广告组创建记录
                unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                videoCnt--;
                successVideoList.add(videos);
                //添加素材使用记录
                updateVideoUsed(videoType, videos, "program", null);
                //获取创意参数
                JSONObject creativeParams = getProgramCreativeParams(token, unitId, videos, strategy, videoCnt, videos.get(0).getAppVersion(), null);
                //执行创建创意
//                createProgramCreatives(token, creativeParams, strategy, 1);
                if (videoCnt <= 0) {
                    removeProRedisVideos(successVideoList, videoKey);
                    return 0;
                }
            }
            removeProRedisVideos(successVideoList, videoKey);
        } else if (isMoreBids == 1) {
            //多出价 借鉴 AiKuaishouCreateCreativeServiceImpl
        }
        return videoCnt;
    }

    /**
     * 根据组级渠道号进行创建(自定义)
     */
    private Integer addCustomByGroupChannel(AiKuaishouAccountAutoStrategy strategy, String appName, Integer videoType, CtopOauthToken token, Long newCampaignId, Long appId, Integer videoCnt, String strategyUuid) throws Exception {
        Long accountId = strategy.getAccountId();
        KuaishouChannel channel = null;
        /*获取组级渠道号*/
         /*
        channel = getChannel("group", accountId, appName);
        if (Check.isNull(channel)) {
            log.error("****** 获取组级渠道号失败");
            return videoCnt;
        }
        //设置素材关键词
        List<KuaishouChannelItems> items = channel.getItems();
        if (!Check.isNull(items) && !items.isEmpty()) {
            strategy.setMaterialKeyword(items.get(0).getKeyword());
        }
        */

        //待建素材集 redis key值
        String date = DateUtils.formatDate(new Date());
        String videoKey = accountId + date + "_cg_list_" + videoType;

        /*获取素材*/
        List<AiKuaishouAccountAutoVideo> newVideos = getVideosByParams(strategy, date, null, videoType, "custom", videoCnt);
        if (null == newVideos || newVideos.isEmpty()) {
            log.error("****** {}账户（{}），通过组级渠道号关键词（{}）未获取到素材", date, accountId, strategy.getMaterialKeyword());
        }

        /*根据匹配逻辑，定向包和素材进行匹配，获取匹配后的素材*/
        List<AiKuaishouAccountAutoVideo> targetsAndVideos = getTargetsAndVideos(strategy, newVideos, videoKey, null, null, null, videoCnt);
        if (Check.isNull(targetsAndVideos)) {
            return videoCnt;
        }

        //记录创建成功的素材，待最后执行删除redis
        List<AiKuaishouAccountAutoVideo> successVideoList = new ArrayList<>();
        Integer unitCode = null;
        String unitMessage = null;
        for (AiKuaishouAccountAutoVideo videoItem : targetsAndVideos) {
            channel = getChannel("group", accountId, appName);
            if (Check.isNull(channel)) {
                log.error("****** 获取组级渠道号失败");
                return videoCnt;
            }
            boolean unitOverrun = getUnitOverrun(accountId);
            if (unitOverrun) {
                log.error("****** 账户（{}）创建广告组超限", accountId);
                removeRedisVideos(successVideoList, videoKey);
                return 0;
            }
            KuaishouAccountCreativeOverrunInfo overrunInfo = overrunInfoService.getByParams(accountId, date);
            if (null != overrunInfo) {
                log.error("****** 账户（{}）创建广告创意超限", accountId);
                removeRedisVideos(successVideoList, videoKey);
                return 0;
            }


            String unitName = getName(strategy.getGroupName(), strategy, videoType, videoItem.getPhotoName(), "unit", newCampaignId, videoItem.getAutoTargetId(), appId, channel);
            Result<Object> result = createUnitParams(token, newCampaignId, strategy, unitName, appId, channel, videoItem.getAutoTargetId());
            if (result.getCode() != CommonConstant.SC_OK_200) {
                //广告组数据获取失败
                continue;
            }
            JSONObject unitParams = (JSONObject) result.getResult();
            Map<String, Object> unitCreateResult = kuaishouInterfaceService.adUnitCreate(token.getAccessToken(), token.getAccountId(), unitParams, 1);
            Thread.sleep(1000);
            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(), channelId);
            if (unitCode != 0) {
                log.error("****** 账户（{}）组创建失败，入参：{},message:{}", accountId, unitParams.toString(), unitMessage);
                unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                continue;
            }
            Long unitId = (Long) unitCreateResult.get("unitId");
            unitRecord.setUnitId(unitId);
            unitLevelOperationRecordService.saveOrUpdate(unitRecord);
            if (!Check.isNull(channel)) {
                channelService.callBackUpdateChannelRel("group", channel.getChannelCode(), accountId, newCampaignId, unitId);
            }
            videoCnt--;
            //更新自定义创建组数量
            updateRedisCnt(date + "_auto_custom_cnt_" + accountId);

            //获取创意参数
            JSONObject creativeParams = getCreativeParams(token, unitId, videoItem, strategy, channel, null);
            //执行创建创意
            boolean flag = createCreative(token, creativeParams, strategy, newCampaignId, unitId, strategyUuid);
            if (flag) {
                //创意成功，则素材置为不可用，添加素材使用记录
                updateVideoUsed(videoType, null, "custom", videoItem);
                successVideoList.add(videoItem);
            }
            if (videoCnt <= 0) {
                removeRedisVideos(successVideoList, videoKey);
                return 0;
            }
        }
        removeRedisVideos(successVideoList, videoKey);
        if (videoCnt > 0 && unitCode == 0) {
            return addCustomByGroupChannel(strategy, appName, videoType, token, newCampaignId, appId, videoCnt, strategyUuid);
        }
        return videoCnt;
    }


    /**
     * 根据组级渠道号进行创建(程序化)
     */
    private Integer addProgramByGroupChannel(AiKuaishouAccountAutoStrategy strategy, String appName, Integer videoType, CtopOauthToken token, Long newCampaignId, Long appId, Integer videoCnt, String uuid) throws Exception {
        Long accountId = strategy.getAccountId();
        /*获取组级渠道号*/
        /*KuaishouChannel channel = getChannel("group", accountId, appName);
        if (Check.isNull(channel)) {
            log.error("****** 获取组级渠道号失败");
            return videoCnt;
        }
        //设置素材关键词
        List<KuaishouChannelItems> items = channel.getItems();
        if (!Check.isNull(items) && !items.isEmpty()) {
            strategy.setMaterialKeyword(items.get(0).getKeyword());
        }
        */
        String date = DateUtils.formatDate(new Date());

        //程序化创建创意超限标识，redis key值
        String programKey = accountId + "_pcreativecount_" + date;
        //待建素材集 redis 程序化单应用 key值
        String videoKey = accountId + date + "_op_plist_" + videoType;

        /*获取素材*/
        List<List<AiKuaishouAccountAutoVideo>> proVideos = new ArrayList<>();
        List<AiKuaishouAccountAutoVideo> allVideos = getVideosByParams(strategy, date, null, videoType, "program", videoCnt * 10);
        if (Check.isNull(allVideos) || allVideos.isEmpty()) {
            log.error("****** {}账户（{}）通过组级渠道号关键词{}，未获取到素材（程序化）", date, accountId, strategy.getMaterialKeyword());
        } else {
            if (allVideos.size() >= 3) {
                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 = newVideos.get(size);
                    if (lastVideos.size() < 3) {
                        //小于3 则去除
                        for (int i = 0; i < size; i++) {
                            proVideos.add(newVideos.get(i));
                        }
                    } else {
                        proVideos = newVideos;
                    }
                } else {
                    proVideos = newVideos;
                }
            }
        }

        /*根据匹配逻辑，定向包和素材进行匹配，获取匹配后的素材*/
        List<List<AiKuaishouAccountAutoVideo>> targetsAndVideos = getProTargetsAndVideos(strategy, proVideos, videoKey, videoCnt, null, null, null);
        if (Check.isNull(targetsAndVideos)) {
            return videoCnt;
        }

        //记录创建成功的素材，待最后执行删除redis
        List<List<AiKuaishouAccountAutoVideo>> successVideoList = new ArrayList<>();
        Integer unitCode = null;
        String unitMessage = null;
        for (List<AiKuaishouAccountAutoVideo> videos : targetsAndVideos) {
            boolean unitOverrun = getUnitOverrun(accountId);
            if (unitOverrun) {
                log.error("****** 账户（{}）创建广告组超限", accountId);
                removeProRedisVideos(successVideoList, videoKey);
                return 0;
            }
            /*获取组级渠道号*/
            KuaishouChannel channel = getChannel("group", accountId, appName);
            if (Check.isNull(channel)) {
                log.error("****** 获取组级渠道号失败");
                return videoCnt;
            }

            Object redisObj = redisUtil.get(programKey);
            if (!Check.isNull(redisObj)) {
                Integer value = (Integer) redisObj;
                if (value == 1) {
                    log.error("****** 账户（{}）创建程序化创意超限", strategy.getAccountId());
                    removeProRedisVideos(successVideoList, videoKey);
                    return 0;
                }
            }


            String unitName = getName(strategy.getGroupName(), strategy, videoType, null, "unit", newCampaignId, appId, videos.get(0).getAutoTargetId(), channel);
            Result<Object> result = createUnitParams(token, newCampaignId, strategy, unitName, appId, channel, videos.get(0).getAutoTargetId());
            if (result.getCode() != CommonConstant.SC_OK_200) {
                //广告组数据获取失败
                continue;
            }
            JSONObject unitParams = (JSONObject) result.getResult();
            Map<String, Object> unitCreateResult = kuaishouInterfaceService.adUnitCreate(token.getAccessToken(), token.getAccountId(), unitParams, 1);
            Thread.sleep(1000);
            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(), channelId);
            if (unitCode != 0) {
                log.error("****** 程序化账户（{}）组创建失败，入参：{}，message:{}", accountId, unitParams, unitMessage);
                unitLevelOperationRecordService.saveOrUpdate(unitRecord);
                continue;
            }
            Long unitId = (Long) unitCreateResult.get("unitId");
            unitRecord.setUnitId(unitId);
            unitLevelOperationRecordService.saveOrUpdate(unitRecord);
            if (!Check.isNull(channel)) {
                channelService.callBackUpdateChannelRel("group", channel.getChannelCode(), accountId, newCampaignId, unitId);
            }
            videoCnt--;
            //更新程序化创建组数量
            updateRedisCnt(date + "_auto_program_cnt_" + accountId);

            //获取创意参数
            JSONObject creativeParams = getProgramCreativeParams(token, unitId, videos, strategy, videoCnt, null, channel);
            //执行创建创意
            boolean flag = createProgramCreatives(token, creativeParams, strategy, newCampaignId, uuid, 1);
            if (flag) {
                successVideoList.add(videos);
                //创意成功，则素材置为不可用，添加素材使用记录
                updateVideoUsed(videoType, videos, "program", null);
            }
            if (videoCnt <= 0) {
                removeProRedisVideos(successVideoList, videoKey);
                return 0;
            }
        }
        //删除已使用的素材
        removeProRedisVideos(successVideoList, videoKey);
        if (videoCnt > 0 && unitCode == 0) {
            return addProgramByGroupChannel(strategy, appName, videoType, token, newCampaignId, appId, videoCnt, uuid);
        }
        return videoCnt;
    }


    /**
     * 添加素材使用记录
     */
    private void updateVideoUsed(Integer videoType, List<AiKuaishouAccountAutoVideo> videos, String type, AiKuaishouAccountAutoVideo one) {
        try {
            if (videos == null) {
                videos = new ArrayList<>();
                videos.add(one);
            }
            for (AiKuaishouAccountAutoVideo video : videos) {
                if (videoType == 1 || videoType == 3) {
                    AiKuaishouAccountAutoVideo v = new AiKuaishouAccountAutoVideo();
                    v.setId(video.getId());
                    if ("custom".equals(type)) {
                        v.setCustomStatus(0);//无效
                    } else {
                        v.setProgramStatus(0);//无效
                    }
                    autoVideoService.updateById(v);
                } else {
                    autoVideoService.saveOrUpdateAutoVideoUsedCount(video.getAccountId(), video.getSignature(), videoType, type);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("添加素材使用记录异常", e);
        }
    }

    /**
     * 删除redis中创建成功的匹配素材（自定义）
     */
    private void removeRedisVideos(List<AiKuaishouAccountAutoVideo> successVideoList, String key) {
        //redis key值
        try {
            if (!Check.isNull(successVideoList) && !successVideoList.isEmpty()) {
                List<AiKuaishouAccountAutoVideo> redisVideos = (List) redisUtil.get(key);
                if (!Check.isNull(redisVideos) && !redisVideos.isEmpty()) {
                    redisVideos.removeAll(successVideoList);
                    redisUtil.set(key, redisVideos, 100000L);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("****** 删除redis中创建成功的匹配素材失败", e);
        }
    }

    /**
     * 删除redis中创建成功的匹配素材（程序化）
     */
    private void removeProRedisVideos(List<List<AiKuaishouAccountAutoVideo>> successVideoList, String key) {
        //redis key值
        try {
            if (!Check.isNull(successVideoList) && !successVideoList.isEmpty()) {
                List<List<AiKuaishouAccountAutoVideo>> redisVideos = (List) redisUtil.get(key);
                if (!Check.isNull(redisVideos) && !redisVideos.isEmpty()) {
                    redisVideos.removeAll(successVideoList);
                    redisUtil.set(key, redisVideos, 100000L);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("****** 删除redis中创建成功的匹配素材失败（程序化）");
        }
    }


    /**
     * 根据匹配逻辑，定向包和素材进行匹配，获取匹配后的素材（自定义）
     */
    private List<AiKuaishouAccountAutoVideo> getTargetsAndVideos(AiKuaishouAccountAutoStrategy strategy, List<AiKuaishouAccountAutoVideo> newVideos, String key, Long appId, Long campaignId, String appVersion, Integer videoCnt) {
        List<AiKuaishouAccountAutoVideo> autoVideos = new ArrayList<>();
        List<AiKuaishouAccountAutoVideo> redisVideos = (List) redisUtil.get(key);
        if (Check.isNull(redisVideos) || redisVideos.isEmpty()) {
            if (Check.isNull(newVideos) || newVideos.isEmpty()) {
                log.error("****** 缺少素材，不执行创建操作");
                return null;
            }
            redisVideos = new ArrayList<>();
        } else {
            if (redisVideos.size() >= 600) {
                if (videoCnt == 0) {
                    return null;
                }
                return new ArrayList<>(redisVideos.subList(0, videoCnt));
            }
        }
        Integer matchType = strategy.getMatchType();//1定向匹配。2素材匹配
        List<Long> targets = JSONArray.parseArray(strategy.getAutoTargetArray(), Long.class);
        if (Check.isNull(targets) || targets.isEmpty()) {
            log.error("****** 获取策略中本地定向数据失败");
            return null;
        }
        if (matchType == 1) {
            directionalMatch(targets, newVideos, redisVideos, appId, campaignId, appVersion);
        } else {
            videoMatch(targets, newVideos, redisVideos, appId, campaignId, appVersion);
        }
        /*多应用*/
        if (videoCnt == 0) {
            return null;
        }

        if (!Check.isNull(redisVideos) && !redisVideos.isEmpty()) {
            redisUtil.set(key, redisVideos, 100000L);
            if (redisVideos.size() > videoCnt) {
                autoVideos = new ArrayList<>(redisVideos.subList(0, videoCnt));
            } else {
                autoVideos = redisVideos;
            }
        }
        return autoVideos;
    }

    /**
     * 定向匹配（自定义）
     */
    private void directionalMatch(List<Long> targets, List<AiKuaishouAccountAutoVideo> newVideos, List<AiKuaishouAccountAutoVideo> redisVideos, Long appId, Long campaignId, String appVersion) {
        int size = targets.size();
        if (newVideos.size() > size) {
            List<List<AiKuaishouAccountAutoVideo>> splitVideos = Lists.partition(newVideos, size);
            for (List<AiKuaishouAccountAutoVideo> videos : splitVideos) {
                for (int i = 0; i < videos.size(); i++) {
                    AiKuaishouAccountAutoVideo video = videos.get(i);
                    video.setAutoTargetId(targets.get(i));
                    if (!Check.isNull(appId)) {
                        video.setAppId(appId);
                        video.setCampaignId(campaignId);
                        video.setAppVersion(appVersion);
                    }
                    if (!redisVideos.contains(video)) {
                        redisVideos.add(video);
                    }
                }
            }
        } else {
            for (int i = 0; i < newVideos.size(); i++) {
                AiKuaishouAccountAutoVideo video = newVideos.get(i);
                video.setAutoTargetId(targets.get(i));
                if (!Check.isNull(appId)) {
                    video.setAppId(appId);
                    video.setCampaignId(campaignId);
                    video.setAppVersion(appVersion);
                }
                if (!redisVideos.contains(video)) {
                    redisVideos.add(video);
                }
            }
        }
    }

    /**
     * 素材匹配(自定义）
     */
    private void videoMatch(List<Long> targets, List<AiKuaishouAccountAutoVideo> newVideos, List<AiKuaishouAccountAutoVideo> redisVideos, Long appId, Long campaignId, String appVersion) {
        for (Long targetId : targets) {
            for (AiKuaishouAccountAutoVideo newVideo : newVideos) {
                AiKuaishouAccountAutoVideo video = new AiKuaishouAccountAutoVideo();
                newVideo.setAutoTargetId(targetId);
                if (!Check.isNull(appId)) {
                    newVideo.setAppId(appId);
                    newVideo.setCampaignId(campaignId);
                    newVideo.setAppVersion(appVersion);
                }
                BeanUtils.copyProperties(newVideo, video);
                if (!redisVideos.contains(newVideo)) {
                    redisVideos.add(video);
                }
            }
        }
    }

    /**
     * 根据匹配逻辑，定向包和素材进行匹配，获取匹配后的素材(程序化）
     */
    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) redisUtil.get(key);
        if (Check.isNull(redisVideos) || redisVideos.isEmpty()) {
            if (Check.isNull(proVideos) || proVideos.isEmpty()) {
                log.error("****** 缺少素材，不执行创建操作");
                return null;
            }
            redisVideos = new ArrayList<>();
        } else {
            if (redisVideos.size() >= 300) {
                if (videoCnt == 0) {
                    return null;
                }
                return new ArrayList<>(redisVideos.subList(0, videoCnt));
            }
        }
        Integer matchType = strategy.getMatchType();//1定向匹配。2素材匹配
        List<Long> targets = JSONArray.parseArray(strategy.getAutoTargetArray(), Long.class);
        if (Check.isNull(targets) || targets.isEmpty()) {
            log.error("****** 获取策略中本地定向数据失败");
            return null;
        }
        if (matchType == 1) {
            directionalMatchPro(targets, proVideos, redisVideos, appId, campaignId, appVersion);
        } else {
            videoMatchPro(targets, proVideos, redisVideos, appId, campaignId, appVersion);
        }

        /*多应用*/
        if (videoCnt == 0) {
            return null;
        }
        List<List<AiKuaishouAccountAutoVideo>> autoVideos = new ArrayList<>();
        if (!Check.isNull(redisVideos) && !redisVideos.isEmpty()) {
            redisUtil.set(key, redisVideos, 100000L);
            if (redisVideos.size() > videoCnt) {
                autoVideos = new ArrayList<>(redisVideos.subList(0, videoCnt));
            } else {
                autoVideos = redisVideos;
            }
        }
        return autoVideos;

    }

    /**
     * 素材匹配(程序化）
     */
    private void videoMatchPro(List<Long> targets, List<List<AiKuaishouAccountAutoVideo>> proVideos, List<List<AiKuaishouAccountAutoVideo>> redisVideos, Long appId, Long campaignId, String appVersion) {
        List<List<AiKuaishouAccountAutoVideo>> havaList = new ArrayList<>();
        for (Long targetId : targets) {
            for (List<AiKuaishouAccountAutoVideo> newVideo : proVideos) {
                for (AiKuaishouAccountAutoVideo video : newVideo) {
                    if (!Check.isNull(appId)) {
                        video.setAppId(appId);
                        video.setCampaignId(campaignId);
                        video.setAppVersion(appVersion);
                    }
                    video.setAutoTargetId(targetId);
                }
                for (List<AiKuaishouAccountAutoVideo> redisVideo : redisVideos) {
                    if (redisVideo.size() == newVideo.size() && redisVideo.containsAll(newVideo)) {
                        havaList.add(newVideo);
                    }
                }
            }
        }
        if (havaList.size() > 0) {
            proVideos.removeAll(havaList);
        }
        redisVideos.addAll(proVideos);
    }

    /**
     * 定向匹配（程序化）
     */
    private void directionalMatchPro(List<Long> targets, List<List<AiKuaishouAccountAutoVideo>> proVideos, List<List<AiKuaishouAccountAutoVideo>> redisVideos, Long appId, Long campaignId, String appVersion) {
        List<List<AiKuaishouAccountAutoVideo>> havaList = new ArrayList<>();
        int size = targets.size();
        if (proVideos.size() > size) {
            List<List<List<AiKuaishouAccountAutoVideo>>> lists = Lists.partition(proVideos, size);
            for (List<List<AiKuaishouAccountAutoVideo>> videos : lists) {
                for (int i = 0; i < videos.size(); i++) {
                    List<AiKuaishouAccountAutoVideo> video = videos.get(i);
                    for (AiKuaishouAccountAutoVideo vide : video) {
                        vide.setAutoTargetId(targets.get(i));
                        if (!Check.isNull(appId)) {
                            vide.setAppId(appId);
                            vide.setCampaignId(campaignId);
                            vide.setAppVersion(appVersion);
                        }
                    }
                    for (List<AiKuaishouAccountAutoVideo> redisVideo : redisVideos) {
                        if (redisVideo.size() == video.size() && redisVideo.containsAll(video)) {
                            havaList.add(video);
                        }
                    }
                }
            }
        } else {
            for (int i = 0; i < proVideos.size(); i++) {
                List<AiKuaishouAccountAutoVideo> video = proVideos.get(i);
                for (AiKuaishouAccountAutoVideo vide : video) {
                    vide.setAutoTargetId(targets.get(i));
                    if (!Check.isNull(appId)) {
                        vide.setAppId(appId);
                        vide.setCampaignId(campaignId);
                        vide.setAppVersion(appVersion);
                    }
                }
                for (List<AiKuaishouAccountAutoVideo> redisVideo : redisVideos) {
                    if (redisVideo.size() == video.size() && redisVideo.containsAll(video)) {
                        havaList.add(video);
                    }
                }
            }
        }
        if (havaList.size() > 0) {
            proVideos.removeAll(havaList);
        }
        redisVideos.addAll(proVideos);
    }


    /**
     * 获取渠道号
     */
    private KuaishouChannel getChannel(String level, Long accountId, String appName) {
        KuaishouChannel channel = null;
        try {
            channel = null;
            Result<Object> result = channelService.queryChannel(accountId, level, appName, null);
            if (result.getCode() == 200) {
                channel = (KuaishouChannel) result.getResult();
                if (Check.isNull(channel)) {
                    log.error("****** 账户（{}）未获取到渠道号，{}", accountId, result.getMessage());
                } else {
                    log.info("------获取渠道号：{}", channel.getChannelCode());
                }
            } else {
                log.error("******  账户（{}）根据应用名：{}，未查询渠道号！{}", accountId, appName, result.getMessage());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取渠道号异常", e);
        }
        return channel;
    }


    /**
     * 绑定落地页与广告组、创意的关系
     */
    private void createLandPageAndUnit(JSONObject programJson, JSONObject data) {
        if (!Check.isNull(data) && !Check.isNull(programJson.getLong("site_id"))) {
            Thread thread = new Thread() {
                @Override
                public void run() {
                    landpagePackageService.createLandPageAndUnit(programJson.getLong("site_id"), data.getLong("creative_id"), programJson.getLong("unit_id"), programJson.getLong("advertiser_id"));
                }
            };
            thread.start();
        }
    }

    /**
     * 整理程序化创意参数
     */
    private JSONObject getProgramCreativeParams(CtopOauthToken token, Long unitId, List<AiKuaishouAccountAutoVideo> videos, AiKuaishouAccountAutoStrategy strategy, Integer creativeCnt, String appVersion, KuaishouChannel channel) {
        JSONObject programJson = null;
        try {
            programJson = new JSONObject();
            programJson.put("advertiser_id", token.getAccountId());
            programJson.put("unit_id", unitId);
            Integer singleAppid = strategy.getSingleAppid();
            if (!Check.isNull(singleAppid) && singleAppid == 2 && !Check.isNull(appVersion)) {  // 多应用情况下 创意名称已app-versionMI命名
                programJson.put("package_name", appVersion);
            } else {
                programJson.put("package_name", "程序化创意" + creativeCnt);
            }

            //素材列表
            JSONArray photoList = new JSONArray();
            List<String> vids = new ArrayList<>();//竖版视频ID
            List<String> hids = new ArrayList<>();//横版视频ID
            for (AiKuaishouAccountAutoVideo video : videos) {
                Integer type = video.getMaterialType();
                String photoId = video.getPhotoId();
                JSONObject photo = new JSONObject();
                photo.put("photo_id", photoId);//视频 ID
                photo.put("cover_image_token", null);// 封面图片 token,通过上传图片接口获得，不传值则直接使用视频的首帧作为封面图片
                photo.put("creative_material_type", type);//1：竖版视频 2：横版视频
                photoList.add(photo);
                if (!Check.isNull(type) && type == 1) {
                    vids.add(photoId);
                } else {
                    hids.add(photoId);
                }
            }
            programJson.put("photo_list", photoList);
            programJson.put("vids", vids);
            programJson.put("hids", hids);
            //落地页 ID
            if (!Check.isNull(strategy.getSiteId()) && !"-1".equals(strategy.getSiteId())) {
                programJson.put("site_id", strategy.getSiteId());
            }
            //封面贴纸
            if (!Check.isNull(strategy.getStickerTitle())) {
                programJson.put("sticker_styles", strategy.getStickerTitle());
            }

            //行动号召按钮
            if (!Check.isNull(strategy.getActionBarText())) {
                programJson.put("action_bar", getActionBar(strategy.getActionBarText()));
            }

            //第三方点击检测链接
            if (!Check.isNull(strategy.getClickTrackUrl())) {
                programJson.put("click_url", strategy.getClickTrackUrl());
            }
            if (!Check.isNull(channel) && !Check.isNull(channel.getClickTrackUrl())) {
                programJson.put("click_url", channel.getClickTrackUrl());
            }

            //第三方 ActionBar 点击监控链接
            if (!Check.isNull(strategy.getActionbarClickUrl())) {
                programJson.put("actionbar_click_url", strategy.getActionbarClickUrl());
            }
            if (!Check.isNull(channel) && !Check.isNull(channel.getActionbarClickUrl())) {
                programJson.put("actionbar_click_url", channel.getActionbarClickUrl());
            }

            //作品广告语
            programJson.put("captions", getCaptions(strategy.getDescription()));

            if (!Check.isNull(strategy.getCreativeCategory())) {
                //创意分类 与创意标签同时传或同时不传
                programJson.put("creative_category", strategy.getCreativeCategory());
                //创意标签
                programJson.put("creative_tag", JSONArray.parseArray(strategy.getCreativeTag()));
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("整理程序化创意参数异常", e);
        }
        return programJson;
    }

    /**
     * 获取程序化广告语
     */
    private JSONArray getCaptions(String description) {
        JSONArray jsonArray = JSONArray.parseArray(description);
        if (jsonArray.size() <= 3) {
            return jsonArray;
        }
        //重置顺序
        Collections.shuffle(jsonArray);
        JSONArray captions = new JSONArray();
        for (int i = 0; i < jsonArray.size(); i++) {
            captions.add(jsonArray.getString(i));
            if (captions.size() == 3) {
                break;
            }
        }
        return captions;
    }

    /**
     * 执行创建程序化创意
     */
    private boolean createProgramCreatives(CtopOauthToken token, JSONObject creativeParams, AiKuaishouAccountAutoStrategy strategy, Long campaignId, String uuid, int count) {
        try {
            Map<String, String> headers = new HashMap<>();
            headers.put("Content-Type", "application/json");
            headers.put("Access-Token", token.getAccessToken());
            String url = PropertiesUtils.getConfig("kuaishou_api_url") + KuaishouInterfaceConstant.PROGRAM_CREATE;
            String result = HttpUtils.kuaiShouhttpPostRequest(url, creativeParams.toJSONString(), headers);
            Thread.sleep(1000);
            JSONObject resultJson = JSON.parseObject(result);
            log.info("创建程序化创意返回结果：{}", resultJson);
            if (Check.isNull(resultJson)) {
                log.error("程序化创意创意异常：api接口返回为空");
                return false;
            }
            Integer code = resultJson.getInteger("code");
            String message = resultJson.getString("message");
            Long unitId = creativeParams.getLong("unit_id");
            AiKuaishouProgramCreativeLevelOperationRecord record = new AiKuaishouProgramCreativeLevelOperationRecord(creativeParams, campaignId, unitId, uuid, message);
            if (code == 0) {
                createLandPageAndUnit(creativeParams, resultJson.getJSONObject("data"));
                record.setStatus(0);
                kuaishouProgramCreativeLevelOperationRecordService.saveOrUpdate(record);
            } else {
                log.error("快手创建程序化创意失败，accountId:{},unitId:{}，返回信息:{},入参：{}", token.getAccountId(), unitId, message, creativeParams.toJSONString());
                if ("系统错误".equals(message) && count <= 3) {
                    log.info("第{}次重新创建程序化创意", count);
                    //系统错误，则重新执行
                    return createProgramCreatives(token, creativeParams, strategy, campaignId, uuid, count + 1);
                } else if (code == 400001 && "/rest/openapi/v2/creative/advanced/program/create调用已超日限，次日可恢复调用".equals(message)) {
                    String statDate = DateUtils.formatDate(new Date());
                    String programKey = token.getAccountId() + "_pcreativecount_" + statDate;
                    redisUtil.set(programKey, 1, 100000L);
                    updateService.updateUnitStatus(token.getAccessToken(), token.getAccountId(), unitId, 3, "e9ca23d68d884d4ebb19d07889727dae");
                }
                record.setStatus(-1);
                kuaishouProgramCreativeLevelOperationRecordService.saveOrUpdate(record);
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("执行创建程序化创意异常", e);
            return false;
        }
        return true;
    }

    /**
     * 获取素材
     *
     * @param strategy   策略对象
     * @param statDate   获取当前时间的素材
     * @param appInfo    应用，只针对对应用
     * @param videoType  创意创建类型 1:素材上新 2：历史高质量 3:历史遗漏素材 4：历史素材打捞
     * @param createType 创建类型 custom：自定义，program:程序化
     * @param videoCnt   获取素材数
     */
    private List<AiKuaishouAccountAutoVideo> getVideosByParams(AiKuaishouAccountAutoStrategy strategy, String statDate, String appVersion, Integer videoType, String createType, Integer videoCnt) {
        //素材关键词
        List<String> keyword = new ArrayList<>();
        if (Check.isNull(strategy.getMaterialKeyword()) && !Check.isNull(appVersion)) {
            String[] split = appVersion.split("-");
            if (split.length > 1) {
                keyword.add(split[1]);
            } else {
                return null;
            }
        } else {
            String materialKeyword = strategy.getMaterialKeyword();
            if (materialKeyword != null && materialKeyword.contains("-")) {
                keyword = Arrays.asList(materialKeyword.split("-"));
            }
        }
        Long accountId = strategy.getAccountId();
        Integer channelType = strategy.getChannelType();
        if (videoType == 1) {
            //获取上新素材
            return autoVideoService.getUpNewVideos(accountId, channelType, keyword, videoCnt, statDate, createType);
            /*if (!Check.isNull(appVersion)) {
                //多应用需要查询素材名包含“all"的素材
                List<AiKuaishouAccountAutoVideo> videos = autoVideoService.getUpNewVideos(accountId, channelType, "all", videoCnt, statDate, createType);
                if (null == upNewVideos || upNewVideos.isEmpty()) {
                    return videos;
                } else if (null == videos || videos.isEmpty()) {
                    return upNewVideos;
                } else {
                    upNewVideos.addAll(videos);
                    return upNewVideos;
                }
            } else {
                return upNewVideos;
            }*/
        } else if (videoType == 2) {
            //历史高质量
            return autoVideoService.getHistoryTopVideos(accountId, channelType, keyword, videoCnt, statDate, createType);
        } else if (videoType == 3) {
            //获取遗漏素材：7天内未被创建过的素材（自动投放账户级重构）
            return autoVideoService.getMissVideoByParams(accountId, channelType, keyword, videoCnt, statDate);
        } else {
            //历史素材打捞
            return autoVideoService.getHistoryCatchVideos(accountId, channelType, keyword, videoCnt, statDate);
        }
    }

    /**
     * 创建计划or获取已创建计划Id
     */
    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 > 0 && toBeBuiltCnt < strategy.getToBeBuiltCnt()) {
                    strategy.setToBeBuiltCnt(toBeBuiltCnt);
                }
                return data.getLong("campaignId");
            }
            //判断是否扔可创建计划
            if (planCnt > datas.size()) {
                return createCampaign(strategy, token, videoType, uuid, channel);
            } else {
                log.info("当日创建量已达标");
            }
        } else {
            return createCampaign(strategy, token, videoType, uuid, channel);
        }
        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, 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);
                return null;
            }
            campaignId = (Long) campaignCreateResult.get("campaignId");

            if (!Check.isNull(channel)) {
                channelService.callBackUpdateChannelRel("plan", channel.getChannelCode(), token.getAccountId(), campaignId, null);
            }
            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 null;
    }


    /**
     * 整理广告组参数
     */
    private Result<Object> createUnitParams(CtopOauthToken token, Long campaignId, AiKuaishouAccountAutoStrategy strategy, String unitName, Long appId, KuaishouChannel channel, Long autoTargetId) {
        // 拼装创建组的入参 json
        try {
            JSONObject unitParams = new JSONObject();
            Integer unitType = strategy.getUnitType();
            //创意制作方式
            unitParams.put("unit_type", unitType);
            unitParams.put("advertiser_id", token.getAccountId());
            unitParams.put("campaign_id", campaignId);
            unitParams.put("unit_name", unitName);
            //出价类型
            unitParams.put("bid_type", strategy.getBidType());
            if (!Check.isNull(strategy.getGroupStatus()) && strategy.getGroupStatus() == 2) {
                unitParams.put("put_status", strategy.getGroupStatus());
            }
            //组单日预算
            if (null != strategy.getUnitDayBudget() && strategy.getUnitDayBudget().compareTo(BigDecimal.ZERO) != 0) {
                unitParams.put("day_budget", strategy.getUnitDayBudget());
            } else {
                unitParams.put("day_budget", 0);
            }
            //应用商店列表
            unitParams.put("ocpx_action_type", strategy.getOcpxActionType());
            //出价
            if (null != strategy.getBid() && strategy.getBid().compareTo(BigDecimal.ZERO) != 0) {
                unitParams.put("bid", strategy.getBid());
            }
            if (!Check.isNull(strategy.getCpaBid()) && !"0".equals((strategy.getCpaBid()))) {
                Long cpdBid = getBidByString(strategy.getCpaBid());
                unitParams.put("cpa_bid", cpdBid);
            }
            //优先低成本
            if (null != strategy.getSmartBid() && strategy.getSmartBid().compareTo(BigDecimal.ZERO) != 0) {
                unitParams.put("smart_bid", strategy.getSmartBid());
            }
            //优化目标
            if (!Check.isNull(strategy.getOcpxActionType())) {
                unitParams.put("ocpx_action_type", strategy.getOcpxActionType());
            }
            //深度转化目标
            if (!Check.isNull(strategy.getDeepConversionType())) {
                unitParams.put("deep_conversion_type", strategy.getDeepConversionType());
            }
            //付费ROI系数
            if (!Check.isNull(strategy.getRoiRatio())) {
                unitParams.put("roi_ratio", strategy.getRoiRatio());
            }
            //深度转化目标出价
            if (!Check.isNull(strategy.getDeepConversionBid())) {
                Long cpdBid = getBidByString(strategy.getDeepConversionBid());
                unitParams.put("deep_conversion_bid", cpdBid);
            }
            Integer sceneId = null;
            String scenceIdString = strategy.getSceneId();

            if (!Check.isNull(scenceIdString) && !Check.isNull(JSONArray.parseArray(scenceIdString))) {
                JSONArray scenceArray = JSONArray.parseArray(scenceIdString);
                sceneId = scenceArray.getInteger(0);
                unitParams.put("scene_id", scenceArray);
            }
            Integer campaignType = strategy.getCampaignType();
            if (campaignType == 2) { // 提升应用安装
                //应用ID
                if (!Check.isNull(appId)) {
                    unitParams.put("app_id", appId);
                }
                if (!Check.isNull(channel) && !Check.isNull(channel.getAppId())) {
                    unitParams.put("app_id", channel.getAppId());
                }
                // 直达链接
                if (!Check.isNull(strategy.getSchemaUri())) {
                    unitParams.put("schema_uri", strategy.getSchemaUri());
                }
                if (!Check.isNull(channel) && !Check.isNull(channel.getSchemaUri())) {
                    unitParams.put("schema_uri", channel.getSchemaUri());
                }
                // 优先从系统应用商店下载
                if (null != strategy.getUseAppMarket()) {
                    unitParams.put("use_app_market", strategy.getUseAppMarket());
                }
                if (null != strategy.getAppStore() && !"".equals(strategy.getAppStore())) {
                    unitParams.put("app_store", JSONArray.parseArray(strategy.getAppStore()));
                }
            } else if (campaignType == 3) { //  获取电商下单
                //url类型
                if (!Check.isNull(strategy.getUrlType())) {
                    unitParams.put("url_type", strategy.getUrlType());
                }
                if (!Check.isNull(strategy.getUrl())) {
                    unitParams.put("url", strategy.getUrl());
                }
            } else if (campaignType == 5) { //收集销售线索
                //url类型
                if (!Check.isNull(strategy.getWebUriType())) {
                    unitParams.put("web_uri_type", strategy.getWebUriType());
                }
                //url
                if (!Check.isNull(strategy.getUrl())) {
                    unitParams.put("url", strategy.getUrl());
                }
            } else if (campaignType == 7) { //  提高应用活跃
                //  appId
                if (!Check.isNull(appId)) {
                    unitParams.put("app_id", appId);
                }
                if (!Check.isNull(channel) && !Check.isNull(channel.getAppId())) {
                    unitParams.put("app_id", channel.getAppId());
                }
                //  url
                if (!Check.isNull(strategy.getUrl())) {
                    unitParams.put("url", strategy.getUrl());
                }
                //调起链接
                if (!Check.isNull(strategy.getSchemaUri())) {
                    unitParams.put("schema_uri", strategy.getSchemaUri());
                }
                if (!Check.isNull(channel) && !Check.isNull(channel.getSchemaUri())) {
                    unitParams.put("schema_uri", channel.getSchemaUri());
                }
            }
            //投放开始时间
            if (!Check.isNull(strategy.getBeginTime())) {
                unitParams.put("begin_time", strategy.getBeginTime());
            } else {
                unitParams.put("begin_time", DateUtils.formatDate());
            }
            //投放结束时间
            if (!Check.isNull(strategy.getEndTime())) {
                unitParams.put("end_time", strategy.getEndTime());
            }
            //投放时间段
            if (!Check.isNull(strategy.getScheduleTime())) {
                unitParams.put("schedule_time", strategy.getScheduleTime());
            }
            //转化目标ID
            if (!Check.isNull(strategy.getConvertId())) {
                unitParams.put("convert_id", strategy.getConvertId());
            }
            //创意展现方式
            if (!Check.isNull(strategy.getShowMode())) {
                unitParams.put("show_mode", strategy.getShowMode());
            }
            //投放方式
            if (!Check.isNull(strategy.getSpeed())) {
                unitParams.put("speed", strategy.getSpeed());
            }
            //预约广告
            if (!Check.isNull(strategy.getSiteType())) {
                unitParams.put("site_type", strategy.getSiteType());
            }
            //智能定向
            if (!Check.isNull(strategy.getAutoTarget())) {
                unitParams.put("auto_target", strategy.getAutoTarget());
            }
            //行为意向-系统优选
            if (!Check.isNull(strategy.getIntentionTarget()) && strategy.getIntentionTarget() != 0) {
                unitParams.put("intention_target", true);
            }
            //试玩 ID
            if (!Check.isNull(strategy.getPlayableIds())) {
                unitParams.put("playable_id", getPlayId(strategy.getPlayableIds(), strategy.getId(), campaignId));
                unitParams.put("play_button", strategy.getPlayButton());
            }
            //是否开启自动生成视频
            if (!Check.isNull(strategy.getAutoCreatePhoto())) {
                unitParams.put("auto_create_photo", strategy.getAutoCreatePhoto());
            }
            if (unitType == 7) {
                //是否开启智能抽帧
                if (!Check.isNull(strategy.getSmartCover())) {
                    unitParams.put("smart_cover", strategy.getSmartCover());
                }
                //程序化创意2.0素材挖掘

                if (!Check.isNull(strategy.getAssetMining())) {
                    if (strategy.getAssetMining() == 1) {
                        unitParams.put("asset_mining", true);
                    } else if (strategy.getAssetMining() == 0) {
                        unitParams.put("asset_mining", false);
                    }
                }
            }
            // 定向数据
            JSONObject target = getTarget(sceneId, autoTargetId);
            if (Check.isNull(target)) {
                return Result.error("整理本地定向数据失败");
            }
            unitParams.put("target", target);
            return Result.ok(unitParams);
        } catch (Exception e) {
            log.error("整理广告组数据异常", e);
            e.printStackTrace();
            return Result.error(e.getMessage());
        }
    }


    /**
     * 获取出价参数
     */
    private Long getBidByString(String cpaBid) {
        if (Check.isNull(cpaBid)) {
            return null;
        }
        String bidString = null;
        if (cpaBid.contains("/")) { // 阶梯出价
            String[] split = cpaBid.split("/");
            int random = new Random().nextInt(split.length);
            bidString = split[random];

        } else if (cpaBid.contains("-")) { // 随机出价
            String[] split = cpaBid.split("-");
            Double min = Double.valueOf(split[0]) / 1000;
            Double max = Double.valueOf(split[1]) / 1000;
            Double randomNumber = new BigDecimal(min + Math.random() * (max - min)).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
            bidString = String.valueOf(new Double(randomNumber * 1000).longValue());

        } else {
            bidString = cpaBid;
        }

        return Long.valueOf(bidString);
    }

    /**
     * 整理本地定向参数
     */
    private JSONObject getTarget(Integer sceneId, Long autoTargetId) {
        JSONObject target = new JSONObject();
        try {
            AiKuaishouAccountAutoTarget templateTarget = autoTargetService.getById(autoTargetId);
            if (Check.isNull(templateTarget)) {
                log.error("****** 未曾查到本地定向，id={}", autoTargetId);
                throw new Exception("未曾查到本地定向，id=" + autoTargetId);
            }
            // 地域
            JSONArray region = JSONArray.parseArray(templateTarget.getRegion());
            if (!Check.isNull(region) && !region.isEmpty()) {
                target.put("region", region);
            }
            // 商圈定向
            JSONArray districtIds = JSONArray.parseArray(templateTarget.getDistrictIds());
            if (!Check.isNull(districtIds) && !districtIds.isEmpty()) {
                target.put("district_ids", districtIds);
            }
            //用户类型	0：实时；1：常驻；2：不限
            if (!Check.isNull(templateTarget.getUserType())) {
                target.put("user_type", templateTarget.getUserType());
            }
            //自定义年龄段
            if (!Check.isNull(templateTarget.getMax()) && !Check.isNull(templateTarget.getMin())) {
                JSONObject ageJson = new JSONObject();
                Integer max = templateTarget.getMax();
                Integer min = templateTarget.getMin();
                if (!Check.isNull(max) && max != -1) {
                    ageJson.put("max", max);
                }
                if (!Check.isNull(min) && min != -1) {
                    ageJson.put("min", min);
                }
                if (!ageJson.isEmpty()) {
                    target.put("age", ageJson);
                }
            }
            // 固定年龄段
            JSONArray agesRange = JSONArray.parseArray(templateTarget.getAgesRange());
            if (!Check.isNull(agesRange) && !agesRange.isEmpty()) {
                target.put("ages_range", agesRange);
            }
            // 性别
            if (!Check.isNull(templateTarget.getGender())) {
                target.put("gender", templateTarget.getGender());
            }
            // 操作系统
            Integer platformOs = templateTarget.getPlatformOs();
            if (!Check.isNull(platformOs)) {
                target.put("platform_os", templateTarget.getPlatformOs());
                // Android版本
                if (platformOs == 1 && !Check.isNull(templateTarget.getAndroidOsv())) {
                    target.put("android_osv", templateTarget.getAndroidOsv());
                }
                // Ios版本
                if (platformOs == 2 && !Check.isNull(templateTarget.getIosOsv())) {
                    target.put("ios_osv", templateTarget.getIosOsv());
                }
            }
            // 网络环境
            if (!Check.isNull(templateTarget.getNetwork())) {
                target.put("network", templateTarget.getNetwork());
            }
            // 设备品牌
            JSONArray deviceBrand = JSONArray.parseArray(templateTarget.getDeviceBrand());
            if (!Check.isNull(deviceBrand) && !deviceBrand.isEmpty()) {
                target.put("device_brand", deviceBrand);
            }
            // 设备价格
            JSONArray devicePrice = JSONArray.parseArray(templateTarget.getDevicePrice());
            if (!Check.isNull(devicePrice) && !devicePrice.isEmpty()) {
                target.put("device_price", devicePrice);
            }
            // APP行为-按分类
            JSONArray appInterest = JSONArray.parseArray(templateTarget.getAppInterest());
            if (!Check.isNull(appInterest) && !appInterest.isEmpty()) {
                target.put("app_interest", appInterest);
            }
            // APP行为-按APP名称
            JSONArray appIds = JSONArray.parseArray(templateTarget.getAppIds());
            if (!Check.isNull(appIds) && !appIds.isEmpty()) {
                target.put("app_ids", appIds);
            }
            // 人群包定向
            JSONArray population = JSONArray.parseArray(templateTarget.getPopulation());
            if (!Check.isNull(population) && !population.isEmpty()) {
                target.put("population", population);
            }
            // 人群包排除
            JSONArray excludePopulation = JSONArray.parseArray(templateTarget.getExcludePopulation());
            if (!Check.isNull(excludePopulation) && !excludePopulation.isEmpty()) {
                target.put("exclude_population", excludePopulation);
            }
            // 付费人群包id
            JSONArray paidAudience = JSONArray.parseArray(templateTarget.getPaidAudience());
            if (!Check.isNull(paidAudience) && !paidAudience.isEmpty()) {
                target.put("paid_audience", paidAudience);
            }

            // 过滤已转化人群
            if (!Check.isNull(templateTarget.getFilterConvertedLevel())) {
                target.put("filter_converted_level", templateTarget.getFilterConvertedLevel());
            }

            if (sceneId != 5) {
                // 商业兴趣类型
                if (null != templateTarget.getBusinessInterestType()) {
                    target.put("business_interest_type", templateTarget.getBusinessInterestType());
                }
                // 商业兴趣
                JSONArray businessInterest = JSONArray.parseArray(templateTarget.getBusinessInterest());
                if (!Check.isNull(businessInterest) && !businessInterest.isEmpty()) {
                    target.put("business_interest", businessInterest);
                }
                // 网红粉丝
                JSONArray fansStarString = JSONArray.parseArray(templateTarget.getFansStar());
                if (!Check.isNull(fansStarString) && !fansStarString.isEmpty()) {
                    target.put("fans_star", fansStarString);
                }
                // 兴趣视频用户
                JSONArray interestVideoString = JSONArray.parseArray(templateTarget.getInterestVideo());
                if (!Check.isNull(interestVideoString) && !interestVideoString.isEmpty()) {
                    target.put("interest_video", interestVideoString);
                }

                // 开启智能扩量
                Integer isOpen = templateTarget.getIsOpen();
                if (!Check.isNull(isOpen) && isOpen == 1) {
                    JSONObject intelliExtend = new JSONObject();
                    intelliExtend.put("is_open", isOpen);
                    //  不可突破年龄
                    if (!Check.isNull(templateTarget.getNoAgeBreak())) {
                        intelliExtend.put("no_age_break", templateTarget.getNoAgeBreak());
                    }
                    //  不可突破性别
                    if (!Check.isNull(templateTarget.getNoGenderBreak())) {
                        intelliExtend.put("no_gender_break", templateTarget.getNoGenderBreak());
                    }
                    //  不可突破地域
                    if (!Check.isNull(templateTarget.getNoAreaBreak())) {
                        intelliExtend.put("no_area_break", templateTarget.getNoAreaBreak());
                    }
                    // 智能扩量
                    target.put("intelli_extend", intelliExtend);
                }
            }

            //-------------行为兴趣定向
            JSONObject behaviorInterest = new JSONObject();
            JSONObject behavior = new JSONObject();
            JSONObject interest = new JSONObject();

            String keyword = templateTarget.getKeyword();
            if (!Check.isNull(keyword) && !keyword.isEmpty()) {
                behavior.put("keyword", JSONArray.parseArray(templateTarget.getKeyword()));
            }
            String label = templateTarget.getLabel();
            if (!Check.isNull(label) && !label.isEmpty()) {
                behavior.put("label", JSONArray.parseArray(templateTarget.getLabel()));
            }
            String interestLabel = templateTarget.getInterestLabel();
            if (!Check.isNull(interestLabel) && !interestLabel.isEmpty()) {
                interest.put("label", JSONArray.parseArray(interestLabel));
            }
            String sceneType = templateTarget.getSceneType();
            if (!Check.isNull(templateTarget.getTimeType()) && !Check.isNull(sceneType) && !sceneType.isEmpty() && !Check.isNull(templateTarget.getInterestLabel())) {
                behavior.put("time_type", templateTarget.getTimeType());
                behavior.put("scene_type", JSONArray.parseArray(sceneType));
                behavior.put("strength_type", templateTarget.getStrengthType());
                behaviorInterest.put("behavior", behavior);
                interest.put("scene_type", JSONArray.parseArray(sceneType));
                behaviorInterest.put("interest", interest);
                target.put("behavior_interest", behaviorInterest);
            }
            return target;
        } catch (Exception e) {
            log.error("整合本地定向数据异常", e);
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 整理自定义创意参数
     */
    private JSONObject getCreativeParams(CtopOauthToken token, Long unitId, AiKuaishouAccountAutoVideo videoItem, AiKuaishouAccountAutoStrategy strategy, KuaishouChannel channel, String appVersion) {
        JSONObject creativeParams = null;
        try {
            creativeParams = new JSONObject();
            creativeParams.put("advertiser_id", token.getAccountId());
            creativeParams.put("unit_id", unitId);
            //第三方点击检测链接
            if (null != strategy.getClickTrackUrl() && !"".equals(strategy.getClickTrackUrl())) {
                creativeParams.put("click_track_url", strategy.getClickTrackUrl());
            }
            if (!Check.isNull(channel) && !Check.isNull(channel.getClickTrackUrl())) {
                creativeParams.put("click_track_url", channel.getClickTrackUrl());
            }
            //第三方开始播放监测链接
            if (null != strategy.getImpressionUrl() && !"".equals(strategy.getImpressionUrl())) {
                creativeParams.put("impression_url", strategy.getImpressionUrl());
            }
            // 第三方有效播放监测链接
            if (!Check.isNull(strategy.getAdPhotoPlayedT3sUrl())) {
                creativeParams.put("ad_photo_played_t3s_url", strategy.getAdPhotoPlayedT3sUrl());
            }
            //第三方点击按钮监测链接
            if (null != strategy.getActionbarClickUrl() && !"".equals(strategy.getActionbarClickUrl())) {
                creativeParams.put("actionbar_click_url", strategy.getActionbarClickUrl());
            }
            if (!Check.isNull(channel) && !Check.isNull(channel.getActionbarClickUrl())) {
                creativeParams.put("actionbar_click_url", channel.getActionbarClickUrl());
            }
            //创意分类
            if (null != strategy.getCreativeCategory()) {
                creativeParams.put("creative_category", strategy.getCreativeCategory());
            }
            //创意标签
            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();
            for (int i = 0; i < count; i++) {
                JSONObject creative = new JSONObject();
                String creativeName = appVersion + "_" + (i + 1);
                if (Check.isNull(appVersion)) {
                    creativeName = "自定义创意_" + (i + 1);
                }
                //创意名称
                creative.put("creative_name", creativeName);
                creative.put("photo_id", videoItem.getPhotoId());
                //素材类型 1：竖版视频 2：横版视频
                creative.put("creative_material_type", videoItem.getMaterialType());
                //行动号召按钮文案
                creative.put("action_bar_text", getActionBar(strategy.getActionBarText()));
                //广告语
                int index = new Random().nextInt(descList.size());
                creative.put("description", descList.getString(index));

                //便利贴创意短广告语,便利贴4时必填
                if ("4".equals(videoItem.getMaterialType())) {
                    if (!Check.isNull("shortSlogan")) {
                        creative.put("short_slogan", strategy.getShortSlogan());
                    }
                }
                // 封面广告语标题
                if (!Check.isNull(strategy.getStickerTitle())) {
                    creative.put("sticker_title", strategy.getStickerTitle());
                }
                // 贴纸样式类型
                if (!Check.isNull(strategy.getOverlayType())) {
                    creative.put("overlay_type", strategy.getOverlayType());
                }
                // 广告标签
                if (!Check.isNull(strategy.getExposeTag())) {
                    creative.put("expose_tag", strategy.getExposeTag());
                }
                // 广告标签2期
                JSONArray newExposeTag = JSONArray.parseArray(strategy.getNewExposeTag());
                if (!Check.isNull(newExposeTag) && !newExposeTag.isEmpty()) {
                    creative.put("new_expose_tag", newExposeTag);
                }
                // 安卓下载中间页ID
                String siteId = strategy.getSiteId();
                if (!Check.isNull(siteId) && !"-1".equals(siteId)) {
                    creative.put("site_id", siteId);
                }
                creatives.add(creative);
            }
            //批量参数
            creativeParams.put("creatives", creatives);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("整理自定义创意参数异常", e);
        }
        return creativeParams;
    }


    /**
     * 执行创建自定义创意
     */
    private boolean createCreative(CtopOauthToken token, JSONObject creativeParams, AiKuaishouAccountAutoStrategy strategy, Long newCampaignId, Long unitId, String uuid) {
        try {
            Map<String, Object> creativeCreateResult = kuaishouInterfaceService.batchCreativeCreate(token.getAccessToken(), token.getAccountId(), creativeParams, 1, null);
            Thread.sleep(1000);
            Integer creativeCode = (Integer) creativeCreateResult.get("code");
            String creativeMessage = (String) creativeCreateResult.get("message");

            if (creativeCode == -1 && "/rest/openapi/v2/creative/create调用已超日限，次日可恢复调用".equals(creativeMessage)) {
                updateService.updateUnitStatus(token.getAccessToken(), token.getAccountId(), unitId, 3, "e9ca23d68d884d4ebb19d07889727dae");
            } else if (creativeCode == -1 && creativeMessage.contains("超过可关联的有效创意的上限")) {
                updateService.updateUnitStatus(token.getAccessToken(), token.getAccountId(), unitId, 3, "e9ca23d68d884d4ebb19d07889727dae");
            } else if (creativeCode == -1 && creativeMessage.contains("视频不存在")) {
                updateService.updateUnitStatus(token.getAccessToken(), token.getAccountId(), unitId, 3, "e9ca23d68d884d4ebb19d07889727dae");
            }
            AiKuaishouCreativeLevelOperationRecord record = new AiKuaishouCreativeLevelOperationRecord(strategy, creativeCode, creativeParams, creativeMessage, newCampaignId, unitId, uuid);
            if (creativeCode == 0) {
                JSONArray addCreativeIds = (JSONArray) creativeCreateResult.get("ids");
                for (int i = 0; i < addCreativeIds.size(); i++) {
                    record.setCreativeId(addCreativeIds.getLong(i));
                    creativeLevelOperationRecordService.saveOrUpdate(record);
                }
            }
            if (creativeCode != 0) {
                creativeLevelOperationRecordService.saveOrUpdate(record);
                log.info("创意创建失败=>message:{}", creativeMessage);
                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, Long targetId, KuaishouChannel channel) {
        if (wildcard.contains("-{{渠道号}}-") || wildcard.contains("-{{渠道号名称}}-")) {
            if (Check.isNull(channel)) {
                wildcard = wildcard.replace("-{{渠道号}}-", "");
                wildcard = wildcard.replace("-{{渠道号名称}}-", "");
            } else {
                wildcard = wildcard.replace("-{{渠道号}}-", channel.getChannelCode());
                wildcard = wildcard.replace("-{{渠道号名称}}-", channel.getChannelName());
            }
        }

        if (wildcard.contains("-{{定向包名}}-")) {
            AiKuaishouAccountAutoTarget templateTarget = autoTargetService.getById(targetId);
            if (!Check.isNull(templateTarget)) {
                wildcard = wildcard.replace("-{{定向包名}}-", templateTarget.getTargetName());
            } else {
                wildcard = wildcard.replace("-{{定向包名}}-", "");
            }
        }

        if (wildcard.contains("-{{应用名称}}-") || wildcard.contains("-{{应用标记}}-") || wildcard.contains("-{{应用包名}}-")) {
            KuaiShouAppList getAppInfo = appListService.getAppInfo(strategy.getAccountId(), appId);
            if (!Check.isNull(getAppInfo)) {
                wildcard = wildcard.replace("-{{应用名称}}-", getAppInfo.getAppName());
                wildcard = wildcard.replace("-{{应用标记}}-", getAppInfo.getAppVersion());
                wildcard = wildcard.replace("-{{应用包名}}-", getAppInfo.getPackageName());
            } else {
                wildcard = wildcard.replace("-{{应用名称}}-", "");
                wildcard = wildcard.replace("-{{应用标记}}-", "");
                wildcard = wildcard.replace("-{{应用包名}}-", "");
            }
        }

        if (wildcard.contains("-{{创意制作方式}}-")) {
            if (strategy.getUnitType() == 4) {
                wildcard = wildcard.replace("-{{创意制作方式}}-", "自定义");
            } else if (strategy.getUnitType() == 7) {
                wildcard = wildcard.replace("-{{创意制作方式}}-", "程序化");
            }
        }

        if (wildcard.contains("-{{广告位置}}-")) {
            String adsence = getAdSenceByParams(strategy.getSceneId());
            wildcard = wildcard.replace("-{{广告位置}}-", adsence);
        }

        if (wildcard.contains("-{{素材名称}}-")) {
            if (Check.isNull(materialName)) {
                wildcard = wildcard.replace("-{{素材名称}}-", "");
            } else {
                wildcard = wildcard.replace("-{{素材名称}}-", materialName);
            }
        }
        if (!wildcard.contains("-{{自定义}}-")) {
            wildcard = wildcard + "-{{自定义}}-";
        }
        String nowDate = DateUtils.formatDate(new Date(), "yyMMdd");
        if ("plan".equals(type)) {
            //创意制作方式
            if (strategy.getUnitType() == 4) {
                wildcard = wildcard.replace("-{{自定义}}-", "自定义新");
            } else {
                wildcard = wildcard.replace("-{{自定义}}-", "程序化新");
            }
            if (wildcard.contains("-{{日期}}-")) {
                wildcard = wildcard.replace("-{{日期}}-", nowDate);
            } else {
                wildcard = wildcard + "-" + nowDate;
            }
            if (!wildcard.contains("-{{序号}}-")) {
                wildcard = wildcard + "-{{序号}}-";
            }
            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 + "");
        } 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 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 {
                cnt = redisCnt + 1;
                redisUtil.set(redisKey, cnt, 100000L);
            }
            wildcard = wildcard + "-" + cnt;
        }
        wildcard = wildcard.replace("--", "-");
        wildcard = wildcard.replace("--", "-");

        if (wildcard.startsWith("-")) {
            wildcard = wildcard.substring(1, wildcard.length());
        }

        if (wildcard.endsWith("-")) {
            wildcard = wildcard.substring(0, wildcard.length() - 1);
        }
        return wildcard;
    }

    /**
     * 获取广告位置
     */
    private String getAdSenceByParams(String sceneIdString) {
        String adsence = "";
        if (null == sceneIdString || "".equals(sceneIdString)) {
            return adsence;
        }
        JSONArray sceneIdArray = JSONArray.parseArray(sceneIdString);
        if (null == sceneIdArray || sceneIdArray.isEmpty()) {
            return adsence;
        }
        for (int i = 0; i < sceneIdArray.size(); i++) {
            adsence += KuaishouCreativeAdsenceTypeEnum.getNameByType(sceneIdArray.getInteger(i)) + ",";
        }
        return adsence.substring(0, adsence.length() - 1);
    }


    /**
     * 组 创建是否超限
     */
    @Override
    public Boolean getUnitOverrun(Long accountId) {
        String nowDate = DateUtils.getNowDate("yyyy-MM-dd");
        String key = accountId + "_" + nowDate + "unit_create_overrun";
        Object redisObj = redisUtil.get(key);
        if (Check.isNull(redisObj)) {
            return false;
        }
        Integer value = (Integer) redisObj;
        if (value == 1) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public void supplementTodayCreatives(AiKuaishouAccountAutoStrategy strategy) {
        Long accountId = strategy.getAccountId();
        //==========账户{}执行自定义上新自动创建==========start==========
        String date = DateUtils.formatDate(new Date());
        String material = strategy.getSourceMaterial();

        if (strategy.getCustomPlanCnt() != null && strategy.getCustomUnitCnt() != null) {
            //当前需要创建广告组数量
            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);
            }

            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);
//            }
        }

        if (strategy.getProgramPlanCnt() != null && strategy.getProgramUnitCnt() != null) {

            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);
                punitNum = this.autoCreateProgramCreative(strategy, 1, punitNum);
            }
            if (punitNum > 0 && material.contains("2")) {
                log.info("-----每日补充创建 （程序化高质量）账户：{}，本次可建组数：{}", strategy.getAccountId(), punitNum);
                //2高质量素材
                this.autoCreateProgramCreative(strategy, 2, punitNum);
            }
        }
    }


    /**
     * 获取创建比例
     */
    private JSONObject getCnt(String material, int unitNum, int hours) {
        JSONObject object = new JSONObject();
        int upNewCnt = unitNum;
        //高质量待建数
        int highCnt = 0;
        //遗漏
        int omitCnt = 0;
        //打捞
        int salvageCnt = 0;

        //如果当前待建数低于循环次数（hours),则待建数 = 1
        if (unitNum < hours) {
            unitNum = 1;
        }
        /*素材优先级以及百分比：1上新、2高质量、3遗漏、4打捞
         *全选的情况：上新（70%）：高质量（20%）：遗漏（5%）：打捞（5%）
         *部分选择：上新：高质量=75%:25%；上新：遗漏/+打捞=85%：15%
         *上新：高质量：遗漏/打捞=70%:20%:10% */
        if (material.contains("2")) {
            if (material.contains("3") && material.contains("4")) {
                /*----------全选的情况：上新（70%）：高质量（20%）：遗漏（5%）：打捞（5%）----------*/
                //上新
                upNewCnt = getRealCnt(unitNum, hours, 0.7);
                //高质量
                highCnt = getRealCnt(unitNum, hours, 0.2);
                //遗漏
                omitCnt = getRealCnt(unitNum, hours, 0.05);
                //打捞
                salvageCnt = getRealCnt(unitNum, hours, 0.05);
            } else if (material.contains("3") || material.contains("4")) {
                /*----------上新：高质量：遗漏/打捞=70%:20%:10% ----------*/
                //上新
                upNewCnt = getRealCnt(unitNum, hours, 0.7);
                //高质量
                highCnt = getRealCnt(unitNum, hours, 0.2);
                //遗漏
                omitCnt = getRealCnt(unitNum, hours, 0.1);
                //打捞
                salvageCnt = getRealCnt(unitNum, hours, 0.1);
            } else {
                /*----------部分选择：上新：高质量=75%:25%；----------*/
                //上新
                upNewCnt = getRealCnt(unitNum, hours, 0.75);
                //高质量
                highCnt = getRealCnt(unitNum, hours, 0.25);
            }
        } else {
            if (material.contains("3") && material.contains("4")) {
                /*---------- 上新：遗漏:打捞=80%：10%：10%----------*/
                //上新
                upNewCnt = getRealCnt(unitNum, hours, 0.8);
                //遗漏
                omitCnt = getRealCnt(unitNum, hours, 0.1);
                //打捞
                salvageCnt = getRealCnt(unitNum, hours, 0.1);

            } else if (material.contains("3") || material.contains("4")) {
                /*---------- 上新：遗漏/+打捞=85%：15%----------*/
                //上新
                upNewCnt = getRealCnt(unitNum, hours, 0.85);
                //遗漏
                omitCnt = getRealCnt(unitNum, hours, 0.15);
                //打捞
                salvageCnt = getRealCnt(unitNum, hours, 0.15);
            } else {
                /*---------- 100%上新----------*/
                //上新
                upNewCnt = getRealCnt(unitNum, hours, 1);
            }
        }
        object.put("upNewCnt", upNewCnt);
        object.put("highCnt", highCnt);
        object.put("omitCnt", omitCnt);
        object.put("salvageCnt", salvageCnt);
        System.out.println("upNewCnt：" + upNewCnt + "，highCnt：" + highCnt + "omitCnt：" + omitCnt + "，salvageCnt：" + salvageCnt);
        return object;
    }

    private int getRealCnt(int unitNum, int hours, double i) {
        //上新总的待建数
        int upNewCnt = (int) (unitNum * i);
        if (upNewCnt > hours) {
            //总待建数大于循环次数
            upNewCnt = upNewCnt / hours;
        } else if (upNewCnt != 0) {
            //总待建数大于1时，本次执行创建一个高质量
            upNewCnt = 1;
        } else {
            upNewCnt = 0;
        }
        return upNewCnt;
    }

    public static String getActionBar(String actionBarText) {
        if (Check.isNull(actionBarText)) {
            return null;
        }
        String[] split = actionBarText.split(",");
        int i = getrandom(0, split.length - 1);
        return split[i];
    }

    public static int getrandom(int start, int end) {
        int num = (int) (Math.random() * (end - start + 1) + start);
        return num;
    }

    /**
     * 轮询使用 playableIds 值，playableIds string格式：123,456,789,122
     */
    public Long getPlayId(String playableIds, Long strategyId, Long campaignId) {
        JSONObject obj = JSONObject.parseObject(playableIds);
        String playIds = obj.getString("ids");
        String playId = null;
        Map<String, Object> map = new HashMap<>();
        String redisKey = strategyId + "-play-" + campaignId + "-" + DateUtils.formatDate(new Date(), "yyMMdd");
        Map<Object, Object> mget = redisUtil.hmget(redisKey);
        if (Check.isNullMap(mget)) {
            String[] ids = playIds.split(",");
            playId = ids[0];
            if (ids.length > 1) {
                String newValues = playIds.replace(playId + ",", "");
                map.put(String.valueOf(0), newValues);
            }
            map.put(String.valueOf(1), playId);
            redisUtil.hmset(redisKey, map, 100000L);
        } else {
            for (Object key : mget.keySet()) {
                String keyIndex = String.valueOf(key);
                String values = String.valueOf(mget.get(keyIndex));
                String[] ids = values.split(",");
                playId = ids[0];
                if (ids.length > 1) {
                    String newValues = values.replace(playId + ",", "");
                    map.put(keyIndex, newValues);
                } else {
                    redisUtil.hdel(redisKey, keyIndex);
                }
                int upIndex = Integer.valueOf(keyIndex) + 1;
                String upValues = String.valueOf(mget.get(String.valueOf(upIndex)));
                if ("null".equals(upValues)) {
                    map.put(String.valueOf(upIndex), playId);
                } else {
                    map.put(String.valueOf(upIndex), upValues + "," + playId);
                }
                break;
            }
            redisUtil.hmset(redisKey, map, 100000L);
        }
        return Long.valueOf(playId);
    }

    /**
     * 回滚 playableIds 值
     */
    public void rollbackPlayId(JSONObject unitParams, AiKuaishouAccountAutoStrategy strategy) {
        Long playableId = unitParams.getLong("playable_id");
        if (Check.isNull(playableId)) {
            return;
        }
        Map<String, Object> map = new HashMap<>();
        String redisKey = strategy.getId() + "-play-" + unitParams.getLong("campaign_id") + "-" + DateUtils.formatDate(new Date(), "yyMMdd");
        Map<Object, Object> mget = redisUtil.hmget(redisKey);
        if (!Check.isNullMap(mget)) {
            for (Object key : mget.keySet()) {
                String keyIndex = String.valueOf(key);
                String values = String.valueOf(mget.get(keyIndex));
                if (values.contains(playableId + "")) {
                    break;
                }
                values = playableId + "," + values;
                map.put(keyIndex, values);

                String upIndex = Integer.valueOf(keyIndex) + 1 + "";
                String upValues = String.valueOf(mget.get(upIndex));
                if (!"null".equals(upValues)) {
                    if (String.valueOf(playableId).equals(upValues)) {
                        redisUtil.hdel(redisKey, upIndex);
                    } else {
                        String newValues = upValues.replace("," + playableId, "");
                        map.put(upIndex, newValues);
                    }
                }
                break;
            }
            redisUtil.hmset(redisKey, map, 100000L);
        }
    }
}
