package cn.com.ctop.alarm.modules.service.impl;

import cn.com.ctop.alarm.modules.constant.MatchLogic;
import cn.com.ctop.alarm.modules.entity.AlarmEventSend;
import cn.com.ctop.alarm.modules.entity.AlarmRuleOperationRecord;
import cn.com.ctop.alarm.modules.entity.AlarmRuleRecord;
import cn.com.ctop.alarm.modules.entity.RuleAccountTemplate;
import cn.com.ctop.alarm.modules.entity.RuleAccountThreshold;
import cn.com.ctop.alarm.modules.entity.RuleBase;
import cn.com.ctop.alarm.modules.entity.RuleGroup;
import cn.com.ctop.alarm.modules.entity.RuleIndicator;
import cn.com.ctop.alarm.modules.entity.RuleTemplate;
import cn.com.ctop.alarm.modules.enums.ConditEnum;
import cn.com.ctop.alarm.modules.mapper.AlarmEventSendMapper;
import cn.com.ctop.alarm.modules.mapper.AlarmRuleOperationRecordMapper;
import cn.com.ctop.alarm.modules.mapper.RuleAccountTemplateMapper;
import cn.com.ctop.alarm.modules.mapper.RuleAccountThresholdMapper;
import cn.com.ctop.alarm.modules.mapper.RuleBaseMapper;
import cn.com.ctop.alarm.modules.mapper.RuleGroupMapper;
import cn.com.ctop.alarm.modules.mapper.RuleIndicatorMapper;
import cn.com.ctop.alarm.modules.mapper.RuleTemplateMapper;
import cn.com.ctop.alarm.modules.service.ExternalLinkService;
import cn.com.ctop.alarm.modules.service.IAlarmRuleRecordService;
import cn.com.ctop.alarm.modules.service.IRuleAccountTemplateService;
import cn.com.ctop.alarm.modules.service.IRuleGroupService;
import cn.com.ctop.common.module.entity.CtopOauthToken;
import cn.com.ctop.common.module.entity.UserAllocation;
import cn.com.ctop.common.module.mapper.SysUserMapper;
import cn.com.ctop.common.module.mapper.UserAllocationMapper;
import cn.com.ctop.common.module.service.ICtopOauthTokenService;
import cn.com.ctop.common.module.service.IRuleDataAccountKuaishouService;
import cn.com.ctop.common.module.service.IRuleDataAccountService;
import cn.com.ctop.common.module.service.ISendMessageService;
import cn.com.ctop.common.module.utils.Check;
import cn.com.ctop.common.module.utils.SendMailUtil;
import cn.com.ctop.kuaishou.modules.batch.entity.KuaiShouGroup;
import cn.com.ctop.kuaishou.modules.batch.entity.KuaiShouGroupBid;
import cn.com.ctop.kuaishou.modules.batch.mapper.KuaiShouGroupBidMapper;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouGroupService;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouUpdateService;
import cn.com.ctop.toutiao.modules.material.entity.ByteDanceAdvertisePlan;
import cn.com.ctop.toutiao.modules.material.service.IByteDanceAdvertisePlanService;
import cn.com.ctop.toutiao.modules.material.service.IByteDanceCreativeService;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xxl.job.core.enums.NoEn;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 规则组
 *
 * @author jeecg-boot
 * @version V1.0
 * @date 2020-11-15
 */
@Slf4j
@Service
public class RuleGroupServiceImpl extends ServiceImpl<RuleGroupMapper, RuleGroup> implements IRuleGroupService {

    //修改头条CPABID出价,投放时间
    private static final String URL_UPDATE_CPABID = "";
    //修改头条预算
    private static final String URL_UPDATE_BUDGET = "";

    @Autowired
    private RuleBaseMapper ruleBaseMapper;
    @Autowired
    private RuleGroupMapper ruleGroupMapper;
    @Autowired
    private RuleIndicatorMapper ruleIndicatorMapper;
    @Autowired
    private RuleAccountThresholdMapper ruleAccountThresholdMapper;
    @Autowired
    private RuleAccountTemplateMapper ruleAccountTemplateMapper;
    @Autowired
    private RuleTemplateMapper ruleTemplateMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private IRuleGroupService ruleGroupService;
    @Autowired
    private IRuleAccountTemplateService ruleAccountTemplateService;
    @Autowired
    private ISendMessageService sendMessageService;
    @Autowired
    private IKuaiShouUpdateService kuaiShouUpdateService;
    @Autowired
    private IRuleDataAccountService ruleDataAccountService;
    @Autowired
    private IRuleDataAccountKuaishouService ruleDataAccountKuaishouService;
    @Autowired
    private AlarmEventSendMapper alarmEventSendMapper;
    @Autowired
    private UserAllocationMapper userAllocationMapper;
    @Autowired
    private IByteDanceAdvertisePlanService byteDanceAdvertisePlanService;
    @Autowired
    private IByteDanceCreativeService byteDanceCreativeService;
    @Autowired
    private IAlarmRuleRecordService alarmRuleRecordService;
    @Autowired
    private ICtopOauthTokenService tokenService;
    @Autowired
    private IKuaiShouGroupService kuaiShouGroupService;
    @Resource
    private KuaiShouGroupBidMapper groupBidMapper;
    @Resource
    private AlarmRuleOperationRecordMapper ruleOperationRecordMapper;
    @Resource
    private ExternalLinkService externalLinkService;

    private JSONObject userObj = null;
    private final static String ACCOUNT = "account";
    private final static String UNIT = "unit";
    private final static String PLAN = "plan";
    private final static String CREATIVE = "creative";

    static ExecutorService executorService = Executors.newFixedThreadPool(5);

    @Override
    public void checkRulesByAccountId(Long accountId) {
        try {
            QueryWrapper<RuleAccountTemplate> wrapper = new QueryWrapper<>();
            //0状态为启动
            wrapper.eq("account_id", accountId);
            wrapper.eq("template_status", 0);
            wrapper.last("limit 1");
            RuleAccountTemplate ruleAccountTemplate = ruleAccountTemplateMapper.selectOne(wrapper);
            if (Check.isNull(ruleAccountTemplate)) {
                log.error("查询账户绑定过的规则模板失败");
                return;
            }
            //获取规则集
            Map<Long, Object> ruleGroupMap = getRuleGroupMapById(ruleAccountTemplate.getTemplateId());
            if (Check.isNull(ruleGroupMap)) {
                log.error("获取规则集失败");
                return;
            }
            //获取指标内容
            JSONObject indicators = getRuleIndicator();
            if (Check.isNull(indicators)) {
                log.error("获取指标内容失败");
                return;
            }
            matchAlarmRules(ruleGroupMap, indicators, ruleAccountTemplate);
        } catch (Exception e) {
            log.error("预警触发异常", e);
        }
    }

    @Override
    public void checkRules() {
        try {
            //查询账户绑定过的规则模板
            Map<String, Object> map = new HashMap<>();
            //0状态为启动
            map.put("template_status", 0);
            List<RuleAccountTemplate> ruleAccountTemplates = ruleAccountTemplateMapper.selectByMap(map);
            if (Check.isNull(ruleAccountTemplates)) {
                log.error("查询账户绑定过的规则模板失败");
                return;
            }
            //获取规则集
            Map<Long, Object> ruleGroupMap = getRuleGroupMap();
            if (Check.isNull(ruleGroupMap)) {
                log.error("获取规则集失败");
                return;
            }
            //获取指标内容
            JSONObject indicators = getRuleIndicator();
            if (Check.isNull(indicators)) {
                log.error("获取指标内容失败");
                return;
            }

            ruleAccountTemplates.forEach(templates -> executorService.submit(() -> {
                matchAlarmRules(ruleGroupMap, indicators, templates);
            }));
        } catch (Exception e) {
            log.error("预警触发异常", e);
        }
    }

    /**
     * 匹配预警规则
     *
     * @param ruleGroups 规则数据集
     * @param templates  规则模板
     * @param indicators 指标码
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private void matchAlarmRules(Map<Long, Object> ruleGroupMap, JSONObject indicators, RuleAccountTemplate templates) {
        userObj = new JSONObject();
        boolean isCopy = false;
        Map<Long, Object> map = (Map<Long, Object>) ruleGroupMap.get(templates.getTemplateId());
        Integer type = (Integer) map.get("type");
        JSONObject matchData = null;
        //媒体类型 1,3：头条2：快手
        if (NoEn.NO1.valueInt() == type || NoEn.NO3.valueInt() == type) {
            //查询头条匹配数据
            matchData = getRuleData(templates.getAccountId(), "TT");
        } else {
            matchData = getRuleData(templates.getAccountId(), "KS");
        }
        if (Check.isNull(matchData) || matchData.isEmpty()) {
            log.warn("获取匹配数据失败");
            return;
        }
        //常规规则+阈值
        List<RuleGroup> notCopyGroups = (List<RuleGroup>) map.get("notCopy");
        JSONObject notCopythreshold = getThreshold(templates.getAccountId(), isCopy);
        //可复制规则+阈值
        List<RuleGroup> isCopyGroups = (List<RuleGroup>) map.get("isCopy");
        JSONObject isCopythreshold = new JSONObject();
        if (!Check.isNull(isCopyGroups)) {
            isCopy = true;
            isCopythreshold = getThreshold(templates.getAccountId(), isCopy);
        }
        log.info("已获取账户（{}）的数据，开始匹配规则", templates.getAccountId());
        UserAllocation userAllocations = userAllocationMapper.getUserAllocation(templates.getAccountId());
        String accountName = "";
        if (!Check.isNull(userAllocations)) {
            accountName = userAllocations.getAuthName();
        }
        try {
            if (!Check.isNull(notCopyGroups) && !Check.isNull(notCopythreshold)) {
                checkRuleGroups(notCopyGroups, notCopythreshold, indicators, matchData, type, false, accountName);
            }
            if (isCopy && !Check.isNull(isCopythreshold)) {
                checkRuleGroups(isCopyGroups, isCopythreshold, indicators, matchData, type, isCopy, accountName);
            }

        } catch (Exception e) {
            log.error("匹配规则逻辑异常", e);
        }
    }

    /**
     * 规则匹配逻辑
     *
     * @param ruleGroups   规则组集
     * @param thresholdObj 阈值数据
     * @param indicators   指标数据
     * @param matchData    匹配数据
     * @param type         媒体类型 1：头条2：快手
     * @param isCopy       是否复制规则
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private void checkRuleGroups(List<RuleGroup> ruleGroups, JSONObject thresholdObj, JSONObject indicators, JSONObject matchData, Integer type, boolean isCopy, String accountName) {
        for (RuleGroup ruleGroup : ruleGroups) {
            JSONArray complianceList = new JSONArray();
            List<RuleBase> ruleBaseList = ruleGroup.getRuleBaseList();
            if (Check.isNull(ruleBaseList)) {
                continue;
            }
            //存储触发预警的数据
            List<AlarmRuleRecord> recordList = new ArrayList<>();
            //是否有后续操作
            Integer operation = ruleGroup.getOperation();
            if (!Check.isNull(operation) && operation == 1) {
                //有后续操作
                checkRuleGroupsForOperation(isCopy, thresholdObj, ruleGroup, indicators, matchData, complianceList, type, ruleBaseList, accountName);
            } else {
                //复制逻辑
                if (isCopy) {
                    JSONObject batchNo = thresholdObj.getJSONObject(ruleGroup.getId().toString());
                    for (Map.Entry<String, Object> entry : batchNo.entrySet()) {
                        JSONObject thresholdJn = (JSONObject) entry.getValue();
                        if (!Check.isNull(thresholdJn)) {
                            CombinationRule(ruleGroup, thresholdJn, indicators, matchData, complianceList, type, recordList);
                        }
                    }
                } else {
                    boolean isGroup = "group".equals(ruleGroup.getRuleType());
                    //匹配组合规则
                    if (isGroup) {
                        CombinationRule(ruleGroup, thresholdObj, indicators, matchData, complianceList, type, recordList);
                        //匹配单规则
                    } else {
                        RuleBase ruleBase = ruleBaseList.get(0);
                        if (Check.isNull(ruleBase)) {
                            continue;
                        }
                        //指标对象
                        JSONObject indicator = indicators.getJSONObject(ruleBase.getIndicatorCode());
                        //阈值对象
                        JSONObject thresholdJson = thresholdObj.getJSONObject(ruleBase.getId().toString());
                        if (Check.isNull(thresholdJson) || thresholdJson.isEmpty()) {
                            continue;
                        }
                        //阈值
                        String threshold = thresholdJson.getString("threshold");
                        //阈值为空时，或者阈值为“unlimited”（不限），不执行该规则
                        if (Check.isNull(threshold) || threshold.contains("unlimited")) {
                            continue;
                        }
                        //维度数据
                        JSONArray dimensionData = matchData.getJSONArray(ruleBase.getRuleDimension());
                        if (!Check.isNull(dimensionData)) {
                            for (int i = 0; i < dimensionData.size(); i++) {
                                JSONObject obj = dimensionData.getJSONObject(i);
                                String value = obj.getString(ruleBase.getIndicatorCode());
                                if (MatchLogic.matchCondition(indicator, ruleBase.getRuleCondition(), threshold, value, obj, ruleBase.getIndicatorCode())) {
                                    alarmRuleRecord(obj, ruleGroup, ruleBase, indicator, type, threshold, recordList);
                                    complianceList.add(obj);
                                }
                            }
                        }
                    }
                }
                if (!Check.isNull(complianceList)) {
                    getAndSendMessag(complianceList, ruleGroup, accountName, type, false);
                    Thread thread = new Thread() {
                        @Override
                        public void run() {
                            alarmRuleRecordService.saveBatch(recordList);
                        }
                    };
                    thread.start();
                }
            }
        }
    }

    /**
     * B 含操作的规则匹配逻辑
     */
    private void checkRuleGroupsForOperation(boolean isCopy, JSONObject thresholdObj, RuleGroup ruleGroup, JSONObject indicators, JSONObject matchData, JSONArray complianceList, Integer type, List<RuleBase> ruleBaseList, String accountName) {
        //存储触发预警的数据
        List<AlarmRuleRecord> recordList = new ArrayList<>();
        String indicatorCode = "";
        //复制逻辑
        if (isCopy) {
            JSONObject batchNo = thresholdObj.getJSONObject(ruleGroup.getId().toString());
            for (Map.Entry<String, Object> entry : batchNo.entrySet()) {
                JSONObject thresholdJn = (JSONObject) entry.getValue();
                if (!Check.isNull(thresholdJn)) {
                    CombinationRule(ruleGroup, thresholdJn, indicators, matchData, complianceList, type, recordList);
                }
            }
        } else {
            boolean isGroup = "group".equals(ruleGroup.getRuleType());
            //匹配组合规则
            if (isGroup) {
                CombinationRule(ruleGroup, thresholdObj, indicators, matchData, complianceList, type, recordList);
                //匹配单规则
            } else {
                RuleBase ruleBase = ruleBaseList.get(0);
                if (Check.isNull(ruleBase)) {
                    return;
                }
                indicatorCode = ruleBase.getIndicatorCode();
                //比较指标
                String cindicatorCode = ruleBase.getCindicatorCode();
                //指标对象
                JSONObject indicator = indicators.getJSONObject(indicatorCode);
                //阈值对象
                JSONObject thresholdJson = thresholdObj.getJSONObject(ruleBase.getId().toString());
                if (Check.isNull(thresholdJson) || thresholdJson.isEmpty()) {
                    return;
                }
                //阈值
                String threshold = thresholdJson.getString("threshold");
                //阈值为空时，或者阈值为“unlimited”（不限），不执行该规则
                if (Check.isNull(threshold) || threshold.contains("unlimited")) {
                    return;
                }
                //维度数据
                JSONArray dimensionData = matchData.getJSONArray(ruleBase.getRuleDimension());
                if (!Check.isNull(dimensionData)) {
                    for (int i = 0; i < dimensionData.size(); i++) {
                        JSONObject obj = dimensionData.getJSONObject(i);
                        String value = obj.getString(indicatorCode);
                        String othreshold = "";
                        if (threshold.contains("~")) {
                            othreshold = threshold;
                        } else {
                            othreshold = getThresholdForOperation(obj, ruleBase, threshold, type);
                        }
                        if (MatchLogic.matchCondition(indicator, ruleBase.getRuleCondition(), othreshold, value, obj, ruleBase.getIndicatorCode())) {
                            alarmRuleRecord(obj, ruleGroup, ruleBase, indicator, type, othreshold, recordList);
                            obj.put("thresholdJson", thresholdJson);
                            complianceList.add(obj);
                        }
                    }
                }
            }
        }
        if (!Check.isNull(complianceList)) {
            performOperation(ruleGroup, complianceList, type, accountName);
            Thread thread = new Thread() {
                @Override
                public void run() {
                    alarmRuleRecordService.saveBatch(recordList);
                }
            };
            thread.start();
        }
    }

    /**
     * 获取操作阈值
     */
    private String getThresholdForOperation(JSONObject obj, RuleBase ruleBase, String threshold, Integer type) {
        String othreshold = "";
        Long accountId = obj.getLong("accountId");
        //比较指标
        String cindicatorCode = ruleBase.getCindicatorCode();
        String indicatorCode = ruleBase.getIndicatorCode();
        if (type == 1 || type == 3) {
            //头条
            Long planId = obj.getLong("planId");
            ByteDanceAdvertisePlan byteDancePlan = byteDanceAdvertisePlanService.getById(planId);
            if ("bid".equals(cindicatorCode)) {
                BigDecimal bid = byteDancePlan.getBid();
                if ("PRICING_OCPC".equals(byteDancePlan.getPricing()) || "PRICING_OCPM".equals(byteDancePlan.getPricing())) {
                    bid = byteDancePlan.getCpaBid();
                }
                //转化成本
                if ("convertCost".equals(indicatorCode)) {
                    //倍数计算， 转化成本 高于or低于当前倍数时，预警
                    othreshold = bid.multiply(new BigDecimal(threshold)).toString();
                }
            } else {
                othreshold = threshold;
            }
            if ("dayBudget".equals(cindicatorCode)) {
                if ("cost".equals(indicatorCode)) {
                    //倍数计算， 转化成本 高于or低于当前倍数时，预警
                    threshold = new BigDecimal(String.valueOf(byteDancePlan.getBudget())).multiply(new BigDecimal(threshold)).toString();
                }
            } else {
                othreshold = threshold;
            }
        } else {
            //快手
            Long unitId = obj.getLong("unitId");
            KuaiShouGroup kuaiShouGroup = kuaiShouGroupService.selectGroupByUnitId(accountId, unitId);
            if ("bid".equals(cindicatorCode)) {
                Long bid = kuaiShouGroup.getBid();
                if (kuaiShouGroup.getBidType() == 10) {
                    bid = kuaiShouGroup.getCpaBid();
                }
                //转化成本
                if ("convertCost".equals(indicatorCode)) {
                    //倍数计算， 转化成本 高于or低于当前倍数时，预警
                    othreshold = new BigDecimal(String.valueOf(bid)).multiply(new BigDecimal(threshold)).toString();
                }
            } else {
                othreshold = threshold;
            }
            if ("dayBudget".equals(cindicatorCode)) {
                if ("charge".equals(indicatorCode)) {
                    //倍数计算， 转化成本 高于or低于当前倍数时，预警
                    othreshold = new BigDecimal(String.valueOf(kuaiShouGroup.getDayBudget())).multiply(new BigDecimal(threshold)).toString();
                }
            } else {
                othreshold = threshold;
            }
        }
        return othreshold;
    }

    /**
     * 执行操作
     */
    private void performOperation(RuleGroup ruleGroup, JSONArray complianceList, Integer type, String accountName) {
        String operationType = ruleGroup.getOperationType();
        String operationValue = ruleGroup.getOperationValue();
        String operate = ruleGroup.getOperate();
        String ruleType = ruleGroup.getRuleType();
        Long accountId = complianceList.getJSONObject(0).getLong("accountId");
        CtopOauthToken token = tokenService.getTokenByAccountId(accountId);
        //已操作成功的list
        JSONArray alreadyOperatedList = new JSONArray();
        for (int i = 0; i < complianceList.size(); i++) {
            JSONObject obj = complianceList.getJSONObject(i);
            AlarmRuleOperationRecord recordEntity = null;
            JSONObject thresholdJson = obj.getJSONObject("thresholdJson");
            if (Check.isNull(thresholdJson) || thresholdJson.isEmpty()) {
                continue;
            }
            //后续 操作阈值
            String operationThreshold = thresholdJson.getString("operationThreshold");
            //峰值
            String maxValue = Check.isNull(thresholdJson.getString("maxValue")) ? "-1" : thresholdJson.getString("maxValue");
            String minValue = Check.isNull(thresholdJson.getString("minValue")) ? "-1" : thresholdJson.getString("minValue");

            if (type == 1 || type == 3) {
                Long planId = obj.getLong("planId");//头条
                if (Check.isNull(planId)) {
                    continue;
                }
                ByteDanceAdvertisePlan byteDancePlan = byteDanceAdvertisePlanService.getById(planId);
                if (Check.isNull(byteDancePlan)) {
                    continue;
                }
                //修改出价
                if ("BID".equals(operate)) {
                    BigDecimal oldBid = byteDancePlan.getBid();
                    if ("PRICING_OCPC".equals(byteDancePlan.getPricing()) || "PRICING_OCPM".equals(byteDancePlan.getPricing())) {
                        oldBid = byteDancePlan.getCpaBid();
                    }
                    if (("up".equals(operationType) && oldBid.compareTo(new BigDecimal(maxValue)) == 0) || ("down".equals(operationType) && oldBid.compareTo(new BigDecimal(minValue)) == 0)) {
                        continue;
                    }
                    Long newBid = calculatedValue(oldBid, operationType, operationValue, operationThreshold, maxValue, minValue);
                    //TODO
                    String code = externalLinkService.editBytedancePlanCpaBidOrBudget(accountId, planId.toString(), new BigDecimal(newBid.toString()), null, null);
                    String message = "success";
                    if (code.equals("")) {
                        alreadyOperatedList.add(obj);
                    } else {
                        message = "fail";
                        log.error("(头条)规则引擎修改出价失败,accountId={},unitId={}", accountId, planId);
                    }
                    recordEntity = getOperationRecordEntity(accountId, ruleGroup, maxValue, minValue, operate, null, planId, operationThreshold, oldBid.toString(), newBid.toString(), message, "头条");
                    //修改预算
                } else if ("BUDGET".equals(operate)) {
                    BigDecimal oldBudget = byteDancePlan.getBudget();
                    if (("up".equals(operationType) && oldBudget.compareTo(new BigDecimal(maxValue)) == 0) || ("down".equals(operationType) && oldBudget.compareTo(new BigDecimal(minValue)) == 0)) {
                        continue;
                    }
                    Long newBudget = calculatedValue(oldBudget, operationType, operationValue, operationThreshold, maxValue, minValue);
                    //TODO
                    String code = externalLinkService.editBytedancePlanCpaBidOrBudget(accountId, planId.toString(), null, null, newBudget);
                    String message = "success";
                    if (code.equals("")) {
                        alreadyOperatedList.add(obj);
                    } else {
                        message = "fail";
                        log.error("(头条)规则引擎修改预算失败,accountId={},unitId={}", accountId, planId);
                    }
                    recordEntity = getOperationRecordEntity(accountId, ruleGroup, maxValue, minValue, operate, null, planId, operationThreshold, oldBudget.toString(), newBudget.toString(), message, "头条");
                    //修改投放时间
                } else if ("TIME".equals(operate) && !byteDancePlan.getScheduleTime().equals(operationThreshold)) {
                    //TODO
                    String code = externalLinkService.editBytedancePlanCpaBidOrBudget(accountId, planId.toString(), null, operationThreshold, null);
                    String message = "success";
                    if (code.equals("")) {
                        alreadyOperatedList.add(obj);
                    } else {
                        message = "fail";
                        log.error("规则引擎修改投放时间段失败,accountId={},unitId={}", accountId, planId);
                    }
                    recordEntity = getOperationRecordEntity(accountId, ruleGroup, maxValue, minValue, operate, null, planId, operationThreshold, byteDancePlan.getScheduleTime(), operationThreshold, message, "头条");
                }

            } else {
                Long unitId = obj.getLong("unitId");//快手
                if (Check.isNull(unitId)) {
                    continue;
                }
                KuaiShouGroup kuaiShouGroup = kuaiShouGroupService.selectGroupByUnitId(accountId, unitId);
                if (Check.isNull(kuaiShouGroup)) {
                    continue;
                }
                //修改出价
                if ("BID".equals(operate)) {
                    Integer bidType = kuaiShouGroup.getBidType();
                    Long oldBid = Check.isNull(kuaiShouGroup.getBid()) ? 0L : kuaiShouGroup.getBid();
                    if (bidType == 10) {
                        oldBid = kuaiShouGroup.getCpaBid();
                    }
                    if (("up".equals(operationType) && oldBid - Long.valueOf(maxValue) == 0) || ("down".equals(operationType) && oldBid - Long.valueOf(minValue) == 0)) {
                        continue;
                    }
                    Long newBid = calculatedValue(new BigDecimal(oldBid.toString()), operationType, operationValue, operationThreshold, maxValue, minValue);
                    JSONObject unitJson = new JSONObject();
                    unitJson.put("advertiser_id", accountId);
                    unitJson.put("unit_id", unitId);
                    String msg = "规则引擎修改bid";
                    if (bidType == 2) {
                        unitJson.put("bid", newBid);
                    } else if (bidType == 10) {
                        unitJson.put("cpa_bid", newBid);
                        msg = "规则引擎修改cpa_bid";
                    }
                    JSONObject jsonObject = kuaiShouUpdateService.updateUnit(token.getAccessToken(), unitJson);
                    Integer code = jsonObject.getInteger("code");
                    String message = "success";
                    if (code == 0) {
                        alreadyOperatedList.add(obj);
                        //添加改价记录
                        KuaiShouGroupBid groupBid = new KuaiShouGroupBid();
                        groupBid.setAccountId(accountId);
                        groupBid.setUnitId(unitId);
                        groupBid.setBid(newBid);
                        groupBid.setUpdateBid(oldBid);
                        groupBid.setUserId(msg);
                        groupBidMapper.insert(groupBid);
                        kuaiShouGroupService.getGroupByUnitId(token.getAccessToken(), accountId, unitId);
                    } else {
                        message = "fail，" + jsonObject.getString("message");
                        log.error("(快手)规则引擎修改出价失败,accountId={},unitId={},{}", accountId, unitId, jsonObject.getString("message"));
                    }
                    recordEntity = getOperationRecordEntity(accountId, ruleGroup, maxValue, minValue, operate, unitId, null, operationThreshold, oldBid.toString(), newBid.toString(), message, "快手");
                    //修改预算
                } else if ("BUDGET".equals(operate)) {
                    Long oldBudget = Check.isNull(kuaiShouGroup.getDayBudget()) ? 0L : kuaiShouGroup.getDayBudget();
                    if (("up".equals(operationType) && oldBudget - Long.valueOf(maxValue) == 0) || ("down".equals(operationType) && oldBudget - Long.valueOf(minValue) == 0)) {
                        continue;
                    }
                    Long newBudget = calculatedValue(new BigDecimal(oldBudget.toString()), operationType, operationValue, operationThreshold, maxValue, minValue);
                    Map<String, Object> updateMap = kuaiShouUpdateService.updateUnitDayBudget(token.getAccessToken(), accountId, unitId, newBudget, "规则引擎修改预算");
                    String message = "success";
                    if ((Integer) updateMap.get("code") == 0) {
                        alreadyOperatedList.add(obj);
                        kuaiShouGroupService.getGroupByUnitId(token.getAccessToken(), accountId, unitId);
                    } else {
                        message = "fail，" + updateMap.get("message");
                        log.error("(快手)规则引擎修改预算失败,accountId={},unitId={},{}", accountId, unitId, updateMap.get("message"));
                    }
                    recordEntity = getOperationRecordEntity(accountId, ruleGroup, maxValue, minValue, operate, unitId, null, operationThreshold, oldBudget.toString(), newBudget.toString(), message, "快手");
                    //修改投放时间
                } else if ("TIME".equals(operate)) {
                    String newScheduleTime = getNewScheduleTime(operationThreshold);
                    JSONObject unitJson = new JSONObject();
                    unitJson.put("advertiser_id", accountId);
                    unitJson.put("unit_id", unitId);
                    unitJson.put("schedule_time", newScheduleTime);
                    JSONObject jsonObject = kuaiShouUpdateService.updateUnit(token.getAccessToken(), unitJson);
                    Integer code = jsonObject.getInteger("code");
                    String message = "success";
                    if (code == 0) {
                        alreadyOperatedList.add(obj);
                        kuaiShouGroupService.getGroupByUnitId(token.getAccessToken(), accountId, unitId);
                    } else {
                        message = "fail，" + jsonObject.getString("message");
                        log.error("(快手)规则引擎修改投放时间段失败,accountId={},unitId={},{}", accountId, unitId, jsonObject.getString("message"));
                    }
                    recordEntity = getOperationRecordEntity(accountId, ruleGroup, maxValue, minValue, operate, unitId, null, newScheduleTime, kuaiShouGroup.getScheduleTime(), operationThreshold, message, "快手");
                }
            }
            if (!Check.isNull(recordEntity)) {
                ruleOperationRecordMapper.insert(recordEntity);
            }
        }
        if (alreadyOperatedList.size() > 0) {
            getAndSendMessag(alreadyOperatedList, ruleGroup, accountName, type, true);
        }
    }

    /**
     * 获取新的投放时间
     */
    private static String getNewScheduleTime(String operationThreshold) {
        String ScheduleTime = "";
        String WeekStr = DateUtils.getWhatDay(new Date().getTime());
        Integer weekInt = -1;
        switch (WeekStr) {
            case "星期一":
                weekInt = 0;
                break;
            case "星期二":
                weekInt = 1;
                break;
            case "星期三":
                weekInt = 2;
                break;
            case "星期四":
                weekInt = 3;
                break;
            case "星期五":
                weekInt = 4;
                break;
            case "星期六":
                weekInt = 5;
                break;
            default:
                weekInt = 6;
                break;
        }
        //获取当前小时
        Integer nowHour = -1;
        try {
            nowHour = DateUtils.getNowHour();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < weekInt * 24 + nowHour; i++) {
            buf.append("1");
        }
        Integer invalidTime = Integer.valueOf(operationThreshold);
        for (int i = 0; i < invalidTime; i++) {
            buf.append("0");
        }
        for (int i = 0; i < 7 * 24 - weekInt * 24 - nowHour - invalidTime; i++) {
            buf.append("1");
        }
        return buf.toString();
    }

    /**
     * 整合操作记录实体类
     */
    private AlarmRuleOperationRecord getOperationRecordEntity(Long accountId, RuleGroup ruleGroup, String maxValue, String minValue, String indicatorCode, Long unitId, Long planId, String operationThreshold, String oldValue, String newValue, String msg, String mediaType) {
        AlarmRuleOperationRecord record = new AlarmRuleOperationRecord();
        record.setAccountId(accountId);
        record.setOperationType(ruleGroup.getOperationType());
        record.setOperationValue(ruleGroup.getOperationValue());
        record.setMaxValue(maxValue);
        record.setMinValue(minValue);
        record.setUnitId(unitId);
        record.setPlanId(planId);
        record.setGroupId(ruleGroup.getId());
        record.setIndicatorCode(indicatorCode);
        record.setBeforeValue(oldValue);
        record.setAfterValue(newValue);
        record.setOperationThreshold(operationThreshold);
        record.setMediaType(mediaType);
        record.setMessage(msg);
        record.setValueTime(DateUtils.formatDate(new Date()));
        return record;
    }

    /**
     * 计算待调整数值
     * <p>
     * 广告组bid_type为CPC和eCPC时：不得低于0.2元，不得高于100元，单位：厘；广告组bid_type为OCPC时：行为出价不得低于1元；激活出价不得低于5元（白名单用户不得低于2元），单位：厘
     *
     * @param goalValue          目标值
     * @param operationType      操作类型：up-提高,down-降低,to-调整至
     * @param operationValue     操作内容：price-金额，percent-百分数
     * @param operationThreshold 操作阈值
     * @param fmaxValue          目标值的峰值
     * @param fminValue          目标值的最低值
     * @return
     * @throws
     */
    private static Long calculatedValue(BigDecimal goal, String operationType, String operationValue, String operationThreshold, String fmaxValue, String fminValue) {
        BigDecimal threshold = new BigDecimal(operationThreshold);
        BigDecimal maxValue = new BigDecimal(fmaxValue);
        BigDecimal minValue = new BigDecimal(fminValue);
        BigDecimal value = null;
        //操作金额
        if ("price".equals(operationValue)) {
            if ("up".equals(operationType)) {
                value = goal.add(threshold);
            } else if ("down".equals(operationType)) {
                value = goal.subtract(threshold);
            } else if ("to".equals(operationType)) {
                value = threshold;
            }
        } else {
            //操作百分数
            if ("up".equals(operationType)) {
                value = goal.add(goal.multiply(threshold));
            } else if ("down".equals(operationType)) {
                value = goal.subtract(goal.multiply(threshold));
            } else if ("to".equals(operationType)) {
                value = goal.multiply(threshold);
            }
        }
        if (value.compareTo(maxValue) == 1) {
            value = maxValue;
        }
        if (value.compareTo(minValue) == -1) {
            value = minValue;
        }
        return value.longValue();
    }

    /**
     * 添加预警规则记录
     *
     * @param
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private void alarmRuleRecord(JSONObject obj, RuleGroup ruleGroup, RuleBase ruleBase, JSONObject
            indicator, Integer mediaType, String threshold, List<AlarmRuleRecord> recordList) {
        try {
            Long planId = obj.getLong("planId");
            Long unidId = null;
            Long creativeId = null;
            String mediaTypeName = "快手";
            if ((NoEn.NO1.valueInt() == mediaType || NoEn.NO3.valueInt() == mediaType)) {
                mediaTypeName = "头条";
                creativeId = obj.getLong("creativeId");
            } else {
                unidId = obj.getLong("unitId");
            }
            AlarmRuleRecord alarmRuleRecord = new AlarmRuleRecord();
            alarmRuleRecord.setBaseId(ruleBase.getId());
            alarmRuleRecord.setAccountId(obj.getLong("accountId"));
            alarmRuleRecord.setPlanId(planId);
            alarmRuleRecord.setUnitId(unidId);
            alarmRuleRecord.setCreativeId(creativeId);
            alarmRuleRecord.setGroupId(ruleGroup.getId());
            alarmRuleRecord.setIndicatorCode(indicator.getString("code"));
            alarmRuleRecord.setIndicatorName(indicator.getString("name"));
            alarmRuleRecord.setGroupName(ruleGroup.getGroupName());
            alarmRuleRecord.setThreshold(threshold);
            alarmRuleRecord.setRuleCondition(ConditEnum.getEnumByCode(ruleBase.getRuleCondition()).getName());
            alarmRuleRecord.setDataValue(obj.getString(ruleBase.getIndicatorCode()));
            alarmRuleRecord.setValueTime(obj.getDate("createTime"));
            alarmRuleRecord.setOperate("PAUSE".equals(ruleGroup.getOperate()) ? "关停" : "仅发送");
            alarmRuleRecord.setMediaType(mediaTypeName);
            recordList.add(alarmRuleRecord);
        } catch (Exception e) {
            log.error("insert ctop_alarm_rule_record error ", e);
        }
    }

    /**
     * 组合规则匹配逻辑
     *
     * @param ruleGroup    规则组对象
     * @param ruleBaseList 单条规则数据集
     * @param thresholdObj 阈值数据
     * @param indicators   指标数据
     * @param matchData    匹配数据
     * @param type         媒体类型 1：头条2：快手
     * @param accountName  账户名称
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private void CombinationRule(RuleGroup ruleGroup, JSONObject thresholdObj, JSONObject indicators, JSONObject
            matchData, JSONArray complianceList, Integer type, List<AlarmRuleRecord> recordList) {
        List<RuleBase> ruleBaseList = ruleGroup.getRuleBaseList();
        //符合规则的数据集
        JSONArray groupDatas = new JSONArray();
        JSONArray targetDatas = new JSONArray();
        JSONArray planDatas = new JSONArray();
        JSONArray creativeDatas = new JSONArray();
        JSONArray accountDatas = new JSONArray();
        for (RuleBase ruleBase : ruleBaseList) {
            //指标对象
            JSONObject indicator = indicators.getJSONObject(ruleBase.getIndicatorCode());
            //阈值对象
            JSONObject thresholdJson = thresholdObj.getJSONObject(ruleBase.getId().toString());
            if (Check.isNull(thresholdJson) || thresholdJson.isEmpty()) {
                continue;
            }
            //阈值
            String threshold = thresholdJson.getString("threshold");
            //阈值为空时，或者阈值为“unlimited”（不限），不执行该规则
            if (Check.isNull(threshold) || threshold.contains("unlimited")) {
                if (!Check.isNull(accountDatas)) {
                    accountDatas = null;
                }
                if (!Check.isNull(planDatas)) {
                    planDatas = null;
                }
                if (!Check.isNull(creativeDatas)) {
                    creativeDatas = null;
                }
                if (!Check.isNull(targetDatas)) {
                    targetDatas = null;
                }
                if (!Check.isNull(groupDatas)) {
                    groupDatas = null;
                }
                break;
            }
            //获取维度数据
            String ruleDimension = ruleBase.getRuleDimension();
            JSONArray dimensionData = matchData.getJSONArray(ruleDimension);
            if (Check.isNull(dimensionData)) {
                if (!Check.isNull(accountDatas)) {
                    accountDatas = null;
                }
                if (!Check.isNull(planDatas)) {
                    planDatas = null;
                }
                if (!Check.isNull(creativeDatas)) {
                    creativeDatas = null;
                }
                if (!Check.isNull(targetDatas)) {
                    targetDatas = null;
                }
                if (!Check.isNull(groupDatas)) {
                    groupDatas = null;
                }
                break;
            }
            //账户维度数据
            if (ACCOUNT.equals(ruleDimension)) {
                JSONObject accountEntity = dimensionData.getJSONObject(0);
                if (MatchLogic.matchCondition(indicator, ruleBase.getRuleCondition(), threshold, accountEntity.getString(ruleBase.getIndicatorCode()), accountEntity, ruleBase.getIndicatorCode())) {
                    if (Check.isNull(accountDatas)) {
                        alarmRuleRecord(accountEntity, ruleGroup, ruleBase, indicator, type, threshold, recordList);
                        accountDatas.add(accountEntity);
                    }
                } else {
                    if (!Check.isNull(accountDatas)) {
                        accountDatas = null;
                    }
                    if (!Check.isNull(planDatas)) {
                        planDatas = null;
                    }
                    if (!Check.isNull(creativeDatas)) {
                        creativeDatas = null;
                    }
                    if (!Check.isNull(targetDatas)) {
                        targetDatas = null;
                    }
                    if (!Check.isNull(groupDatas)) {
                        groupDatas = null;
                    }
                    break;
                }
            } else if (PLAN.equals(ruleDimension)) {
                planDatas = getOKData(planDatas, dimensionData, ruleBase, indicator, thresholdJson, ruleGroup, type, recordList);
                if (Check.isNull(planDatas)) {
                    if (!Check.isNull(accountDatas)) {
                        accountDatas = null;
                    }
                    if (!Check.isNull(planDatas)) {
                        planDatas = null;
                    }
                    if (!Check.isNull(creativeDatas)) {
                        creativeDatas = null;
                    }
                    if (!Check.isNull(targetDatas)) {
                        targetDatas = null;
                    }
                    if (!Check.isNull(groupDatas)) {
                        groupDatas = null;
                    }
                    break;
                }
            } else if (CREATIVE.equals(ruleDimension)) {
                creativeDatas = getOKData(creativeDatas, dimensionData, ruleBase, indicator, thresholdJson, ruleGroup, type, recordList);
                if (Check.isNull(creativeDatas)) {
                    if (!Check.isNull(accountDatas)) {
                        accountDatas = null;
                    }
                    if (!Check.isNull(planDatas)) {
                        planDatas = null;
                    }
                    if (!Check.isNull(creativeDatas)) {
                        creativeDatas = null;
                    }
                    if (!Check.isNull(targetDatas)) {
                        targetDatas = null;
                    }
                    if (!Check.isNull(groupDatas)) {
                        groupDatas = null;
                    }
                    break;
                }
            } else if ("target".equals(ruleDimension)) {
                targetDatas = getOKData(targetDatas, dimensionData, ruleBase, indicator, thresholdJson, ruleGroup, type, recordList);
                if (Check.isNull(targetDatas)) {
                    if (!Check.isNull(accountDatas)) {
                        accountDatas = null;
                    }
                    if (!Check.isNull(planDatas)) {
                        planDatas = null;
                    }
                    if (!Check.isNull(creativeDatas)) {
                        creativeDatas = null;
                    }
                    if (!Check.isNull(targetDatas)) {
                        targetDatas = null;
                    }
                    if (!Check.isNull(groupDatas)) {
                        groupDatas = null;
                    }
                    break;
                }
            } else if (UNIT.equals(ruleDimension)) {
                groupDatas = getOKData(groupDatas, dimensionData, ruleBase, indicator, thresholdJson, ruleGroup, type, recordList);
                if (Check.isNull(groupDatas)) {
                    if (!Check.isNull(accountDatas)) {
                        accountDatas = null;
                    }
                    if (!Check.isNull(planDatas)) {
                        planDatas = null;
                    }
                    if (!Check.isNull(creativeDatas)) {
                        creativeDatas = null;
                    }
                    if (!Check.isNull(targetDatas)) {
                        targetDatas = null;
                    }
                    if (!Check.isNull(groupDatas)) {
                        groupDatas = null;
                    }
                    break;
                }
            }
        }
        //获取达标可发送的数据
        JSONArray sendData = null;
        if (NoEn.NO1.valueInt() == type || NoEn.NO3.valueInt() == type) {
            sendData = MatchLogic.getSendData(accountDatas, planDatas, creativeDatas, targetDatas);
        } else {
            sendData = MatchLogic.getKuaiShouSendData(accountDatas, groupDatas, planDatas, creativeDatas, targetDatas);
        }
        if (!Check.isNull(sendData)) {
            complianceList.addAll(sendData);
        }
    }

    /**
     * @param ruleDatas     达标数据集
     * @param dimensionData 匹配数据
     * @param ruleBase      规则
     * @param type          数据类型
     * @param threshold     阈值
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private JSONArray getOKData(JSONArray ruleDatas, JSONArray dimensionData, RuleBase ruleBase, JSONObject
            indicator, JSONObject thresholdJson, RuleGroup ruleGroup, Integer type, List<AlarmRuleRecord> recordList) {
        String threshold = thresholdJson.getString("threshold");
        if (!Check.isNull(ruleDatas)) {
            JSONArray okData = new JSONArray();
            for (int i = 0; i < ruleDatas.size(); i++) {
                JSONObject obj = ruleDatas.getJSONObject(i);
                String value = obj.getString(ruleBase.getIndicatorCode());
                String othreshold = "";
                if (threshold.contains("~")) {
                    othreshold = threshold;
                } else {
                    othreshold = getThresholdForOperation(obj, ruleBase, threshold, type);
                }
                if (MatchLogic.matchCondition(indicator, ruleBase.getRuleCondition(), othreshold, value, obj, ruleBase.getIndicatorCode())) {
                    alarmRuleRecord(obj, ruleGroup, ruleBase, indicator, type, othreshold, recordList);
                    if (groupThresholdFromIndicator(thresholdJson, ruleGroup.getOperate())) {
                        obj.put("thresholdJson", thresholdJson);
                    }
                    okData.add(obj);
                }
            }
            return okData;
        }
        for (int i = 0; i < dimensionData.size(); i++) {
            JSONObject obj = dimensionData.getJSONObject(i);
            String value = obj.getString(ruleBase.getIndicatorCode());
            String othreshold = "";
            if (threshold.contains("~")) {
                othreshold = threshold;
            } else {
                othreshold = getThresholdForOperation(obj, ruleBase, threshold, type);
            }
            if (MatchLogic.matchCondition(indicator, ruleBase.getRuleCondition(), othreshold, value, obj, ruleBase.getIndicatorCode())) {
                alarmRuleRecord(obj, ruleGroup, ruleBase, indicator, type, othreshold, recordList);
                if (groupThresholdFromIndicator(thresholdJson, ruleGroup.getOperate())) {
                    obj.put("thresholdJson", thresholdJson);
                }
                ruleDatas.add(obj);
            }
        }
        return ruleDatas;
    }

    private boolean groupThresholdFromIndicator(JSONObject thresholdJson, String operate) {
        if ("SEND".equals(operate) || "PAUSE".equals(operate)) {
            return false;
        }
        RuleBase base = ruleBaseMapper.selectById(thresholdJson.getLong("ruleId"));
        String code = base.getIndicatorCode();
        if ("BID".equals(operate)) {
            return "charge".equals(code) || "cost".equals(code);
        } else if ("BUDGET".equals(operate)) {
            return "convertCost".equals(code);
        } else if ("TIME".equals(operate)) {
            return "scheduleTime".equals(code);
        }
        return false;
    }

    /**
     * @param complianceList 触发规则数据
     * @param ruleGroup      触发的规则组
     * @param accountName    账户名称
     * @param mediaType      媒体类型
     * @param isOperation    是否有后续操作（仅做发送内容变更）
     * @throws
     */
    private void getAndSendMessag(JSONArray complianceList, RuleGroup ruleGroup, String accountName, Integer mediaType, boolean isOperation) {
        Long accountId = complianceList.getJSONObject(0).getLong("accountId");
        if (Check.isNull(accountId)) {
            return;
        }
        JSONObject user = getUserByAccountId(accountId);
        if (Check.isNull(user)) {
            return;
        }
        StringJoiner creativeJoiner = new StringJoiner("，");
        StringJoiner unitJoiner = new StringJoiner("，");
        StringJoiner planJoiner = new StringJoiner("，");
        StringJoiner isNullCreativeJoiner = new StringJoiner("，");
        StringJoiner isNullPlanJoiner = new StringJoiner("，");
        for (int i = 0; i < complianceList.size(); i++) {
            JSONObject obj = complianceList.getJSONObject(i);
            boolean isNull = obj.getBoolean("isNull");
            if (isNull) {
                if (!Check.isNull(obj.getString("creativeId"))) {
                    //字节小于1800时
                    if (isNullCreativeJoiner.toString().getBytes().length <= 1800) {
                        isNullCreativeJoiner.add(obj.getString("creativeId"));
                    } else {
                        String message = MatchLogic.getMessage(ruleGroup.getGroupName(), accountId, accountName, CREATIVE, isNullCreativeJoiner.toString(), isNull);
                        sendMsg(message, ruleGroup, user, CREATIVE, isNullCreativeJoiner.toString(), accountId, mediaType, isOperation);
                        isNullCreativeJoiner = new StringJoiner("，");
                        isNullCreativeJoiner.add(obj.getString("creativeId"));
                    }
                } else if (!Check.isNull(obj.getLong("planId"))) {
                    if (isNullPlanJoiner.toString().getBytes().length <= 1800) {
                        isNullPlanJoiner.add(obj.getString("planId"));
                    } else {
                        String msg = MatchLogic.getMessage(ruleGroup.getGroupName(), accountId, accountName, PLAN, isNullPlanJoiner.toString(), isNull);
                        sendMsg(msg, ruleGroup, user, PLAN, isNullPlanJoiner.toString(), accountId, mediaType, isOperation);
                        isNullPlanJoiner = new StringJoiner("，");
                        isNullPlanJoiner.add(obj.getString("planId"));
                    }
                } else {
                    String msg = MatchLogic.getMessage(ruleGroup.getGroupName(), accountId, accountName, null, null, isNull);
                    sendMsg(msg, ruleGroup, user, ACCOUNT, null, accountId, mediaType, isOperation);
                }
            } else {
                if (!Check.isNull(obj.getString("creativeId"))) {
                    //字节小于1800时（消息支持最大2048字节）
                    if (creativeJoiner.toString().getBytes().length <= 1800) {
                        creativeJoiner.add(obj.getString("creativeId"));
                    } else {
                        String message = MatchLogic.getMessage(ruleGroup.getGroupName(), accountId, accountName, CREATIVE, creativeJoiner.toString(), isNull);
                        sendMsg(message, ruleGroup, user, CREATIVE, creativeJoiner.toString(), accountId, mediaType, isOperation);
                        creativeJoiner = new StringJoiner("，");
                        creativeJoiner.add(obj.getString("creativeId"));
                    }
                } else if (!Check.isNull(obj.getLong("unitId"))) {
                    if (unitJoiner.toString().getBytes().length <= 1800) {
                        unitJoiner.add(obj.getString("unitId"));
                    } else {
                        String msg = MatchLogic.getMessage(ruleGroup.getGroupName(), accountId, accountName, UNIT, unitJoiner.toString(), isNull);
                        sendMsg(msg, ruleGroup, user, UNIT, unitJoiner.toString(), accountId, mediaType, isOperation);
                        unitJoiner = new StringJoiner("，");
                        unitJoiner.add(obj.getString("unitId"));
                    }
                } else if (!Check.isNull(obj.getLong("planId"))) {
                    if (planJoiner.toString().getBytes().length <= 1800) {
                        planJoiner.add(obj.getString("planId"));
                    } else {
                        String msg = MatchLogic.getMessage(ruleGroup.getGroupName(), accountId, accountName, PLAN, planJoiner.toString(), isNull);
                        sendMsg(msg, ruleGroup, user, PLAN, planJoiner.toString(), accountId, mediaType, isOperation);
                        planJoiner = new StringJoiner("，");
                        planJoiner.add(obj.getString("planId"));
                    }
                } else {
                    String msg = MatchLogic.getMessage(ruleGroup.getGroupName(), accountId, accountName, null, null, isNull);
                    sendMsg(msg, ruleGroup, user, ACCOUNT, null, accountId, mediaType, isOperation);
                }
            }
        }
        if (!Check.isNull(creativeJoiner.toString())) {
            String creativeMsg = MatchLogic.getMessage(ruleGroup.getGroupName(), accountId, accountName, CREATIVE, creativeJoiner.toString(), false);
            sendMsg(creativeMsg, ruleGroup, user, CREATIVE, creativeJoiner.toString(), accountId, mediaType, isOperation);
        }
        if (!Check.isNull(unitJoiner.toString())) {
            String creativeMsg = MatchLogic.getMessage(ruleGroup.getGroupName(), accountId, accountName, UNIT, unitJoiner.toString(), false);
            sendMsg(creativeMsg, ruleGroup, user, UNIT, unitJoiner.toString(), accountId, mediaType, isOperation);
        }
        if (!Check.isNull(planJoiner.toString())) {
            String planMsg = MatchLogic.getMessage(ruleGroup.getGroupName(), accountId, accountName, PLAN, planJoiner.toString(), false);
            sendMsg(planMsg, ruleGroup, user, PLAN, planJoiner.toString(), accountId, mediaType, isOperation);
        }
        if (!Check.isNull(isNullCreativeJoiner.toString())) {
            String isNullCreativeMsg = MatchLogic.getMessage(ruleGroup.getGroupName(), accountId, accountName, CREATIVE, isNullCreativeJoiner.toString(), true);
            sendMsg(isNullCreativeMsg, ruleGroup, user, CREATIVE, isNullCreativeJoiner.toString(), accountId, mediaType, isOperation);
        }
        if (!Check.isNull(isNullPlanJoiner.toString())) {
            String isNullPlanMsg = MatchLogic.getMessage(ruleGroup.getGroupName(), accountId, accountName, PLAN, isNullPlanJoiner.toString(), true);
            sendMsg(isNullPlanMsg, ruleGroup, user, PLAN, isNullPlanJoiner.toString(), accountId, mediaType, isOperation);
        }
    }

    /**
     * 发送消息
     *
     * @param ruleGroup 达标的规则组对象
     * @param obj       达标数据对象
     * @param type      1：头条2：快手
     * @param isNull    查询的值为空
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private void sendMsg(String msg, RuleGroup ruleGroup, JSONObject user, String type, String ids, Long accountId, Integer mediaType, boolean isOperation) {
        try {
            boolean isPause = "PAUSE".equals(ruleGroup.getOperate());
            if (isPause) {
                //type 1：头条2：快手
                if (NoEn.NO1.valueInt() == mediaType || NoEn.NO3.valueInt() == mediaType) {
                    msg = touTiaoshutDown(accountId, ids, type, msg);
                } else {
                    msg = kuaiShoushutDown(accountId, ids, type, user.getString("id"), msg);
                }
                if (msg.getBytes().length >= 2000) {
                    msg = msg.replace("，", ",");
                }
                if (ACCOUNT.equals(type)) {
                    msg += "请您及时查看并调整";
                }
            } else {
                msg += "请您及时查看并调整";
            }
            if (isOperation) {
                msg = msg.replace("请您及时查看并调整", "系统已按照设置调整出价，请您及时查看");
                if ("BUDGET".equals(ruleGroup.getOperate())) {
                    msg = msg.replace("出价", "预算");
                } else if ("TIME".equals(ruleGroup.getOperate())) {
                    msg = msg.replace("出价", "投放时间");
                }
            }
            String sendType = ruleGroup.getSendType();
            if ("SMS".equals(sendType)) {
            } else if ("EMAIL".equals(sendType)) {
                List<String> mailList = new ArrayList<>();
                mailList.add(user.getString("email"));
                SendMailUtil.sendMail(mailList, ruleGroup.getGroupName() + "预警", msg);
            } else if ("TEL".equals(sendType)) {
            } else {
//                sendMessageService.sendMessage("1b3deb8258e84df994f1371a51cfc14a", msg);
                sendMessageService.sendMessage(user.getString("id"), msg);
                Thread.sleep(100);
            }
            if (!Check.isNull(msg)) {
                AlarmEventSend send = new AlarmEventSend();
                send.setUserId(user.getString("realname").concat("_").concat(user.getString("id")));
                send.setAlarmDetail(msg.replace("<br/>", "，"));
                send.setAlarmStatus(NoEn.NO1.valueStr());
                send.setGroupId(ruleGroup.getId());
                send.setAccountId(accountId);
                send.setMediaType((NoEn.NO1.valueInt() == mediaType || NoEn.NO3.valueInt() == mediaType) ? "头条" : "快手");
                send.setGroupName(ruleGroup.getGroupName());
                if (CREATIVE.equals(type)) {
                    send.setRuleDimension(type);
                } else if (PLAN.equals(type)) {
                    send.setRuleDimension(type);
                } else if (UNIT.equals(type)) {
                    send.setRuleDimension(type);
                } else {
                    send.setRuleDimension(ACCOUNT);
                }
                send.setCreateTime(new Date());
                alarmEventSendMapper.insert(send);
            }
        } catch (Exception e) {
            log.error("发送消息异常", e);
        }
    }

    /**
     * 根据accountId获取人员对象
     *
     * @param accountId 账户ID
     * @return com.alibaba.fastjson.JSONObject
     * @throws
     * @author ZHAOXA
     */
    private synchronized JSONObject getUserByAccountId(Long accountId) {
        JSONObject obj = userObj.getJSONObject(accountId.toString());
        if (Check.isNull(obj)) {
            JSONObject user = sysUserMapper.selectUserByAccount(accountId);
            userObj.put(accountId.toString(), user);
            return user;
        }
        return obj;
    }

    /**
     * 关停操作
     *
     * @param
     * @return boolean
     * @throws
     * @author ZHAOXA
     */
    private String kuaiShoushutDown(Long accountId, String ids, String type, String userId, String msg) {
        CtopOauthToken token = tokenService.getTokenByAccountId(accountId);
        Map<String, Object> updateMap = new HashMap<>();
        try {
            if (!Check.isNull(token) && !Check.isNull(ids)) {
                String[] idSplit = ids.split("，");
                for (String id : idSplit) {
                    if (CREATIVE.equals(type)) {
                        updateMap = kuaiShouUpdateService.updateCreativeStatus(token.getAccessToken(), accountId, Long.valueOf(id), NoEn.NO2.valueInt(), userId);
                        if (!(boolean) updateMap.get("success")) {
                            msg = msg.replace(id, id + "(F)");
                        }
                    } else if (UNIT.equals(type)) {
                        updateMap = kuaiShouUpdateService.updateUnitStatus(token.getAccessToken(), accountId, Long.valueOf(id), NoEn.NO2.valueInt(), userId);
                        if (!(boolean) updateMap.get("success")) {
                            msg = msg.replace(id, id + "(F)");
                        }
                    } else if (PLAN.equals(type)) {
                        updateMap = kuaiShouUpdateService.updateCampaignStatus(token.getAccessToken(), accountId, Long.valueOf(id), NoEn.NO2.valueInt(), userId);
                        if (!(boolean) updateMap.get("success")) {
                            msg = msg.replace(id, id + "(F)");
                        }
                    }
                }
                msg += "现已被关停，请您及时查看并调整！";
            } else {
                msg = msg + "(F)获取签证失败！请联系管理员！";
            }
        } catch (NumberFormatException e) {
            log.error("快手关停操作异常", e);
            msg = msg + "(F)操作关停失败！请联系管理员！";
        }
        return msg;
    }

    /**
     * 头条关停
     *
     * @param
     * @return boolean
     * @throws
     * @author ZHAOXA
     */
    private String touTiaoshutDown(Long accountId, String ids, String type, String msg) {
        try {
            Map<String, Object> result = new HashMap<>();
            if (!Check.isNull(ids)) {
                String[] split = ids.split("，");
                if (!Check.isNull(split)) {
                    if (PLAN.equals(type)) {
                        CtopOauthToken token = tokenService.getTokenByAccountId(accountId);
                        for (String id : split) {
                            JSONArray idArray = new JSONArray();
                            idArray.add(id);
                            result = byteDanceAdvertisePlanService.updateAdvertiserPlanStatus(token, idArray, "AD_STATUS_DISABLE");
                            if (Check.isNull(result.get("code")) || (int) result.get("code") != 0) {
                                msg = msg.replace(id, id + "(F)");
                            }
                        }
                    } else if (CREATIVE.equals(type)) {
                        CtopOauthToken creativeToken = tokenService.getOauthTokenByAccountId(accountId + "");
                        for (String id : split) {
                            result = byteDanceCreativeService.advertiserCreativeUpdateStatus(creativeToken, accountId, id, "disable");
                            if (Check.isNull(result.get("code")) || (int) result.get("code") != 0) {
                                msg = msg.replace(id, id + "(F)");
                            }
                        }
                    }
                    msg += "现已被关停，请您及时查看并调整！";
                }
            }
        } catch (Exception e) {
            msg = msg + "(F)操作关停失败！请联系管理员！";
            log.error("头条账户：{}，关停{}失败", accountId, type, e);
        }
        return msg;
    }


    //整理阈值数据
    public JSONObject getThreshold(Long accountId, boolean isCopy) {
        JSONObject obj = new JSONObject();
        try {
            if (isCopy) {
                List<Long> groupList = ruleAccountThresholdMapper.selectGroupIdsByAccountId(accountId);
                if (Check.isNull(groupList)) {
                    return null;
                }
                for (Long groupId : groupList) {
                    JSONObject groupIdObj = new JSONObject();
                    List<String> batchNos = ruleAccountThresholdMapper.selectbatchNosByGroupIdId(accountId, groupId);
                    if (Check.isNull(batchNos)) {
                        return null;
                    }
                    for (String batchNo : batchNos) {
                        JSONObject batchNoObj = new JSONObject();
                        List<RuleAccountThreshold> thresholdList = ruleAccountThresholdMapper.selectCopyThresholdByAccountId(accountId, groupId, batchNo);
                        if (Check.isNull(thresholdList)) {
                            return null;
                        }
                        for (RuleAccountThreshold threshold : thresholdList) {
                            batchNoObj.put(String.valueOf(threshold.getRuleId()), threshold);
                        }
                        groupIdObj.put(batchNo.toString(), batchNoObj);
                    }
                    obj.put(groupId.toString(), groupIdObj);
                }
            } else {
                List<RuleAccountThreshold> thresholdList = ruleAccountThresholdMapper.selectByAccountId(accountId);
                if (Check.isNull(thresholdList)) {
                    return null;
                }
                for (RuleAccountThreshold threshold : thresholdList) {
                    obj.put(String.valueOf(threshold.getRuleId()), threshold);
                }
            }
        } catch (Exception e) {
            log.error("获取账户阈值数据集失败", e);
        }
        return obj;

    }

    //整理指标数据
    public JSONObject getRuleIndicator() {
        JSONObject obj = new JSONObject();
        try {
            List<RuleIndicator> indicators = ruleIndicatorMapper.selectByMap(null);
            if (Check.isNull(indicators)) {
                return null;
            }
            for (RuleIndicator indicator : indicators) {
                obj.put(indicator.getCode(), indicator);
            }
        } catch (Exception e) {
            log.error("获取指标数据集异常", e);
        }
        return obj;
    }

    private Map<Long, Object> getRuleGroupMapById(Long id) {
        try {
            Map<Long, Object> returnMap = new HashMap<>();
            Map<String, Object> parMap = new HashMap<>();
            parMap.put("id", id);
            List<RuleTemplate> ruleTemplates = ruleTemplateMapper.selectByMap(parMap);
            for (RuleTemplate tem : ruleTemplates) {
                Map<String, Object> map = new HashMap<>();
                QueryWrapper<RuleGroup> groupWrapper = new QueryWrapper<>();
                groupWrapper.eq("template_id", tem.getId());
                groupWrapper.eq("is_copy", NoEn.NO1.valueInt());
                List<RuleGroup> copyGroups = ruleGroupMapper.selectList(groupWrapper);
                for (RuleGroup group : copyGroups) {
                    QueryWrapper<RuleBase> baseWrapper = new QueryWrapper<>();
                    baseWrapper.eq("group_id", group.getId());
                    baseWrapper.orderByDesc("is_unlimited");
                    group.setRuleBaseList(ruleBaseMapper.selectList(baseWrapper));
                }
                map.put("isCopy", copyGroups);
                groupWrapper = new QueryWrapper<>();
                groupWrapper.eq("template_id", tem.getId());
                groupWrapper.eq("is_copy", NoEn.NO0.valueInt());
                List<RuleGroup> notCopyGroups = ruleGroupMapper.selectList(groupWrapper);
                for (RuleGroup group : notCopyGroups) {
                    QueryWrapper<RuleBase> baseWrapper = new QueryWrapper<>();
                    baseWrapper.eq("group_id", group.getId());
                    baseWrapper.orderByDesc("is_unlimited");
                    group.setRuleBaseList(ruleBaseMapper.selectList(baseWrapper));
                }
                map.put("notCopy", notCopyGroups);
                map.put("type", tem.getMediaType());
                returnMap.put(tem.getId(), map);
            }
            return returnMap;
        } catch (Exception e) {
            log.error("获取模板规则组集合异常", e);
        }
        return null;
    }

    /**
     * 整理规则模板的数据，根据模板ID获取模板具体规则内容
     *
     * @param
     * @return MAP key为模板ID,value是规则集
     * @throws
     * @author ZHAOXA
     */
    private Map<Long, Object> getRuleGroupMap() {
        try {
            Map<Long, Object> returnMap = new HashMap<>();
            List<RuleTemplate> ruleTemplates = ruleTemplateMapper.selectByMap(null);
            for (RuleTemplate tem : ruleTemplates) {
                Map<String, Object> map = new HashMap<>();
                QueryWrapper<RuleGroup> groupWrapper = new QueryWrapper<>();
                groupWrapper.eq("template_id", tem.getId());
                groupWrapper.eq("is_copy", NoEn.NO1.valueInt());
                List<RuleGroup> copyGroups = ruleGroupMapper.selectList(groupWrapper);
                for (RuleGroup group : copyGroups) {
                    QueryWrapper<RuleBase> baseWrapper = new QueryWrapper<>();
                    baseWrapper.eq("group_id", group.getId());
                    baseWrapper.orderByDesc("is_unlimited");
                    group.setRuleBaseList(ruleBaseMapper.selectList(baseWrapper));
                }
                map.put("isCopy", copyGroups);
                groupWrapper = new QueryWrapper<>();
                groupWrapper.eq("template_id", tem.getId());
                groupWrapper.eq("is_copy", NoEn.NO0.valueInt());
                List<RuleGroup> notCopyGroups = ruleGroupMapper.selectList(groupWrapper);
                for (RuleGroup group : notCopyGroups) {
                    QueryWrapper<RuleBase> baseWrapper = new QueryWrapper<>();
                    baseWrapper.eq("group_id", group.getId());
                    baseWrapper.orderByDesc("is_unlimited");
                    group.setRuleBaseList(ruleBaseMapper.selectList(baseWrapper));
                }
                map.put("notCopy", notCopyGroups);
                map.put("type", tem.getMediaType());
                returnMap.put(tem.getId(), map);
            }
            return returnMap;
        } catch (Exception e) {
            log.error("获取模板规则组集合异常", e);
        }
        return null;
    }


    /**
     * 获取指标数据
     *
     * @param
     * @return java.util.List<org.json.JSONObject>
     * @throws
     * @author ZHAOXA
     */
    private JSONObject getRuleData(Long accountId, String type) {
        try {
            if ("TT".equals(type)) {
                return ruleDataAccountService.getRuleDataByAccountId(accountId);
            }
            if ("KS".equals(type)) {
                return ruleDataAccountKuaishouService.getKuaiShouRuleDataByAccountId(accountId);
            }
        } catch (Exception e) {
            log.error("获取匹配数据集异常", e);
        }
        return null;
    }

    /**
     * 创建规则集
     *
     * @param requestJson
     * @return
     */


    @Override
    public JSONObject createRuleGroup(com.alibaba.fastjson.JSONObject requestJson) {
        JSONObject returnJson = new JSONObject();
        try {
            Long templateId = requestJson.getLong("templateId");
            if (Check.isNull(templateId)) {
                throw new Exception("请选择需要创建的规则模板id");
            }
            RuleTemplate template = ruleTemplateMapper.selectById(templateId);
            if (Check.isNull(template)) {
                throw new Exception("未获取到对应的规则模板详细情");
            }

            JSONArray ruleList = requestJson.getJSONArray("ruleList");
            if (Check.isNull(ruleList)) {
                throw new Exception("请传入具体规则");
            }
            QueryWrapper<RuleAccountTemplate> templateQueryWrapper = new QueryWrapper<>();
            templateQueryWrapper.eq("template_id", templateId);
            List<RuleAccountTemplate> accountList = ruleAccountTemplateService.list(templateQueryWrapper);

            for (int i = 0; i < ruleList.size(); i++) {
                JSONObject groupJson = ruleList.getJSONObject(i);
                JSONArray ruleDetail = groupJson.getJSONArray("ruleDetail");
                if (Check.isNull(ruleDetail)) {
                    throw new Exception("规则组下指标不能为空");
                }

                Integer isCopy = groupJson.getInteger("isCopy");
                String batchId = null;
                if (isCopy == 1) {
                    batchId = UUID.randomUUID().toString().replace("-", "");

                }
                JSONArray ruleIds = new JSONArray();
                JSONArray thresholdIds = new JSONArray();
                for (int j = 0; j < ruleDetail.size(); j++) {
                    JSONObject ruleDetailJson = ruleDetail.getJSONObject(j);
                    RuleBase ruleBase = new RuleBase();
                    ruleBase.setRuleName(ruleDetailJson.getString("ruleName"));
                    ruleBase.setIndicatorCode(ruleDetailJson.getString("indicatorCode"));
                    ruleBase.setRuleCondition(ruleDetailJson.getString("ruleCondition"));
                    ruleBase.setThreshold(ruleDetailJson.getString("threshold"));
                    ruleBase.setVariableType(ruleDetailJson.getInteger("variableType"));
                    ruleBase.setRuleDimension(ruleDetailJson.getString("ruleDimension"));
                    ruleBase.setJudgeFormat(ruleDetailJson.getInteger("judgeFormat"));
                    ruleBase.setIsUnlimited(ruleDetailJson.getInteger("isUnlimited"));
                    int insert = ruleBaseMapper.insert(ruleBase);    // 添加基础规则

                    if (insert > 0) {
                        if (!Check.isNull(accountList)) {
                            for (RuleAccountTemplate accountTemplate : accountList) {
                                Long accountId = accountTemplate.getAccountId();
                                QueryWrapper<RuleAccountThreshold> thresholdQueryWrapper = new QueryWrapper<>();
                                thresholdQueryWrapper.eq("account_id", accountId);
                                thresholdQueryWrapper.eq("rule_id", ruleBase.getId());
                                thresholdQueryWrapper.last("limit 1");
                                RuleAccountThreshold accountThreshold = ruleAccountThresholdMapper.selectOne(thresholdQueryWrapper);
                                if (!Check.isNull(accountThreshold)) {
                                    continue;
                                }
                                RuleAccountThreshold threshold = new RuleAccountThreshold();
                                if (ruleBase.getVariableType() == 0) {
                                    threshold.setThreshold(ruleBase.getThreshold());
                                }
                                threshold.setAccountId(accountId);
                                if (isCopy == 1) {
                                    threshold.setBatchNo(batchId);
                                }
                                threshold.setRuleId(ruleBase.getId());
                                int thresholdInsert = ruleAccountThresholdMapper.insert(threshold);// 默认将新建的规则同步到已应用的账户下
                                if (thresholdInsert > 0) {
                                    thresholdIds.add(threshold.getId());
                                }
                            }
                        }
                        ruleIds.add(ruleBase.getId()); // 获取基本规则集
                    }
                }
                RuleGroup ruleGroup = new RuleGroup();
                ruleGroup.setRuleIds(ruleIds.toJSONString());
                ruleGroup.setRuleType(groupJson.getString("ruleType"));
                ruleGroup.setTemplateId(templateId);
                ruleGroup.setRuleRelationship(groupJson.getString("ruleRelationship"));
                ruleGroup.setGroupName(groupJson.getString("groupName"));
                ruleGroup.setRemark(groupJson.getString("remark"));
                ruleGroup.setIsCopy(groupJson.getInteger("isCopy"));
                ruleGroup.setIsRequired(groupJson.getInteger("isRequired"));
                ruleGroup.setOperate(groupJson.getString("operate"));
                ruleGroup.setSendType(groupJson.getString("sendType"));
                boolean save = ruleGroupService.save(ruleGroup); // 新增规则组
                if (save) {
                    for (int j = 0; j < ruleIds.size(); j++) {
                        Long ruleId = ruleIds.getLong(j);
                        RuleBase updateRuleBase = new RuleBase();
                        updateRuleBase.setId(ruleId);
                        updateRuleBase.setGroupId(ruleGroup.getId());
                        ruleBaseMapper.updateById(updateRuleBase); // 组同步成功之后 基础规则关联到组id
                    }
                    for (int j = 0; j < thresholdIds.size(); j++) {
                        Long threshold = thresholdIds.getLong(j);
                        RuleAccountThreshold updateThreshold = new RuleAccountThreshold();
                        updateThreshold.setId(threshold);
                        updateThreshold.setGroupId(ruleGroup.getId());
                        ruleAccountThresholdMapper.updateById(updateThreshold); // 同步到阈值之后 阈值关联到组id

                    }
                    JSONArray groupIds = JSONArray.parseArray(template.getGroupIds());
                    groupIds.add(ruleGroup.getId());
                    template.setGroupIds(groupIds.toJSONString());
                    ruleTemplateMapper.updateById(template); // 将新增的规则组id 添加到规则模板的规则组id下
                }
            }

            returnJson.put("code", 0);
            returnJson.put("message", "添加成功");
        } catch (Exception e) {
            e.printStackTrace();
            returnJson.put("code", -1);
            returnJson.put("message", e.getMessage());

        }

        return returnJson;
    }


}
