package com.pcloud.book.adnews.biz.impl;

import com.pcloud.book.adnews.biz.AdNewsBiz;
import com.pcloud.book.adnews.check.AdNewsCheck;
import com.pcloud.book.adnews.dao.AdNewsBanDao;
import com.pcloud.book.adnews.dao.AdNewsChooseDao;
import com.pcloud.book.adnews.dao.AdNewsClickRecordDao;
import com.pcloud.book.adnews.dao.AdNewsDao;
import com.pcloud.book.adnews.dao.AdNewsGroupDao;
import com.pcloud.book.adnews.dao.AdNewsGroupRecordDao;
import com.pcloud.book.adnews.dao.AdNewsMessageStatisticDao;
import com.pcloud.book.adnews.dao.AdNewsSetDao;
import com.pcloud.book.adnews.dao.AdNewsWechatChooseDao;
import com.pcloud.book.adnews.dao.AdNewsWechatDao;
import com.pcloud.book.adnews.entity.AdNews;
import com.pcloud.book.adnews.entity.AdNewsBan;
import com.pcloud.book.adnews.entity.AdNewsChoose;
import com.pcloud.book.adnews.entity.AdNewsClickRecord;
import com.pcloud.book.adnews.entity.AdNewsGroup;
import com.pcloud.book.adnews.entity.AdNewsGroupRecord;
import com.pcloud.book.adnews.entity.AdNewsMessageStatistic;
import com.pcloud.book.adnews.entity.AdNewsSet;
import com.pcloud.book.adnews.entity.AdNewsWechat;
import com.pcloud.book.adnews.entity.AdNewsWechatChoose;
import com.pcloud.book.adnews.enums.AdContentFromEnum;
import com.pcloud.book.adnews.enums.AdNewsBanEnum;
import com.pcloud.book.adnews.dto.AdNewsListParam;
import com.pcloud.book.adnews.enums.AdNewsContentTypeEnum;
import com.pcloud.book.adnews.enums.AdNewsSendRuleEnum;
import com.pcloud.book.adnews.vo.AdNewsGroupRecordVO;
import com.pcloud.book.adnews.vo.AdNewsMessageStatisticVO;
import com.pcloud.book.base.exception.BookBizException;
import com.pcloud.book.consumer.wechatgroup.WechatGroupConsr;
import com.pcloud.book.group.biz.WeixinQrcodeBiz;
import com.pcloud.book.group.dao.GroupQrcodeDao;
import com.pcloud.book.group.dto.BookWxQrcodeDTO;
import com.pcloud.book.group.dto.GroupQrcodeFoAdDTO;
import com.pcloud.common.core.aspect.ParamLog;
import com.pcloud.common.page.PageBeanNew;
import com.pcloud.common.page.PageParam;
import com.pcloud.common.utils.DateUtils;
import com.pcloud.common.utils.ListUtils;
import com.pcloud.common.utils.ResponseHandleUtil;
import com.pcloud.common.utils.httpclient.UrlUtils;
import com.pcloud.common.utils.string.StringUtil;
import com.pcloud.facade.quartz.entity.CallBackParam;
import com.pcloud.facade.quartz.entity.ScheduleJob;
import com.pcloud.facade.quartz.service.ScheduleService;
import com.pcloud.labelcenter.label.service.LabelService;
import com.sdk.wxgroup.SendTextMessageVO;
import com.sdk.wxgroup.WxGroupSDK;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author ruansiyuan
 * @Date 2019/7/17 15:02
 **/
@Component("adNewsBiz")
public class AdNewsBizImpl implements AdNewsBiz {

    private static final Logger LOGGER = LoggerFactory.getLogger(AdNewsBizImpl.class);

    @Autowired
    private AdNewsCheck adNewsCheck;
    @Autowired
    private AdNewsSetDao adNewsSetDao;
    @Autowired
    private AdNewsGroupDao adNewsGroupDao;
    @Autowired
    private AdNewsWechatDao adNewsWechatDao;
    @Autowired
    private AdNewsDao adNewsDao;
    @Autowired
    private AdNewsGroupRecordDao adNewsGroupRecordDao;
    @Autowired
    private AdNewsMessageStatisticDao adNewsMessageStatisticDao;
    @Autowired
    private AdNewsClickRecordDao adNewsClickRecordDao;
    @Autowired
    private AdNewsBanDao adNewsBanDao;
    @Autowired
    private ScheduleService scheduleService;
    @Autowired
    private GroupQrcodeDao groupQrcodeDao;
    @Autowired
    private WechatGroupConsr wechatGroupConsr;
    @Autowired
    private AdNewsChooseDao adNewsChooseDao;
    @Autowired
    private AdNewsWechatChooseDao adNewsWechatChooseDao;
    @Autowired
    private WeixinQrcodeBiz weixinQrcodeBiz;
    @Autowired
    private LabelService labelService;

    private static final String AD_MORNING_NEWS_SCHEDULE_PRE = "AD_MORNING_NEWS_SCHEDULE_";

    private static final String AD_EVENING_NEWS_SCHEDULE_PRE = "AD_EVENING_NEWS_SCHEDULE_";

    // 发送消息的长度限制
    private static final Integer SEND_MESSAGE_LENGTH=1000;

    @Value("${wechat.group.link.prefix}")
    private String wechatGroupLinkPrefix;

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("新增早晚报设置")
    @Override
    public Long createAdNewsSet(AdNewsSet adNewsSet) {
        adNewsCheck.createAdNewsSetCheck(adNewsSet);
        //新增之前判断是否有记录
        AdNewsSet adNewsSetOld = adNewsSetDao.getByPartyId(adNewsSet.getCreateUser());
        if (adNewsSetOld != null) {
            throw new BookBizException(BookBizException.ERROR, "不可重复创建！");
        }
        adNewsSetDao.insert(adNewsSet);
        buildAdNewsGroups(adNewsSet, adNewsSet.getCreateUser());
        adNewsGroupDao.batchInsert(adNewsSet.getAdNewsGroups());
        // 判断是否增加自有过公众号,如果没有则将当前数据入库
        if (!ListUtils.isEmpty(adNewsSet.getAdNewsWechats())
                && !adNewsWechatDao.existsAdNewsWechat(adNewsSet.getUpdateUser())) {
            buildAdNewsWechats(adNewsSet);
            adNewsWechatDao.batchInsert(adNewsSet.getAdNewsWechats());
        }
        //创建定时任务
        buildAdNewsSh(adNewsSet);
        return adNewsSet.getId();
    }

    /**
     * 构建自有公众号对象
     *
     * @param adNewsSet
     */
    private void buildAdNewsWechats(AdNewsSet adNewsSet) {
        for (AdNewsWechat adNewsWechat : adNewsSet.getAdNewsWechats()) {
            adNewsWechat.setAdviserId(adNewsSet.getUpdateUser());
        }
    }

    @ParamLog("创建编辑端早晚报定时任务")
    private void buildAdNewsSh(AdNewsSet adNewsSet) {
        if (adNewsSet.getHasMorningOpen()) {
            //早报定时任务
            createAdNewsSch(adNewsSet.getId(), adNewsSet.getMorningTime(), AD_MORNING_NEWS_SCHEDULE_PRE + adNewsSet.getId());
        }
        if (adNewsSet.getHasEveningOpen()) {
            //晚报定是任务
            createAdNewsSch(adNewsSet.getId(), adNewsSet.getEveningTime(), AD_EVENING_NEWS_SCHEDULE_PRE + adNewsSet.getId());
        }
    }

    @ParamLog("创建编辑早晚报定时任务")
    private void createAdNewsSch(Long adNewsSetId, String time, String jobName) {
        String cron;
        try {
            int hour = Integer.parseInt(time.substring(0, 2));
            int minute = Integer.parseInt(time.substring(3, 5));
            int second = Integer.parseInt(time.substring(6, 8));
            cron = second + " " + minute + " " + hour + " * * ?";
        } catch (Exception e) {
            throw new BookBizException(BookBizException.ERROR, "时间格式错误");
        }
        ScheduleJob scheduleJob = new ScheduleJob();
        scheduleJob.setJobGroup("book");
        scheduleJob.setJobName(jobName);
        scheduleJob.setCronExpression(cron);
        CallBackParam callBackParam = new CallBackParam();
        callBackParam.setMethodName("sendAdNews");
        callBackParam.setBeanName("adNewsService");
        Map<String, Object> paramMap = new HashMap<>();
        //定时任务调用时用到的参数
        paramMap.put("adNewsSetId", adNewsSetId);
        callBackParam.setParamMap(paramMap);
        Map<String, Object> scheduleMap = new HashMap<>();
        scheduleMap.put("scheduleJob", scheduleJob);
        scheduleMap.put("callBackParam", callBackParam);
        try {
            scheduleService.addCronJob(scheduleMap);
        } catch (Exception e) {
            LOGGER.error("设置定时任务失败" + e.getMessage(), e);
            throw new BookBizException(BookBizException.ERROR, "定时任务设置失败");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("修改早晚报设置")
    @Override
    public void updateAdNewsSet(AdNewsSet adNewsSet) {
        adNewsCheck.createAdNewsSetCheck(adNewsSet);
        Long adNewsSetId = adNewsSet.getId();
        AdNewsSet adNewsSetOld = adNewsSetDao.getById(adNewsSetId);
        if (adNewsSetOld == null) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "未找到该设置！");
        }
        //更新设置
        adNewsSetDao.update(adNewsSet);
        //删除之前旧的关联关系
        adNewsGroupDao.deleteByAdNewsSetId(adNewsSetId);
        //重新设置关联
        buildAdNewsGroups(adNewsSet, adNewsSet.getUpdateUser());
        adNewsGroupDao.batchInsert(adNewsSet.getAdNewsGroups());
        // 判断是否增加自有过公众号,如果没有则将当前数据入库
        if (!ListUtils.isEmpty(adNewsSet.getAdNewsWechats())
                && !adNewsWechatDao.existsAdNewsWechat(adNewsSet.getUpdateUser())) {
            buildAdNewsWechats(adNewsSet);
            adNewsWechatDao.batchInsert(adNewsSet.getAdNewsWechats());
        }
        //删除旧的定时任务
        deleteAdNewsSch(AD_MORNING_NEWS_SCHEDULE_PRE + adNewsSet.getId());
        deleteAdNewsSch(AD_EVENING_NEWS_SCHEDULE_PRE + adNewsSet.getId());
        //设置新的定时任务
        buildAdNewsSh(adNewsSet);
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("发送编辑端早晚报")
    @Override
    public void sendAdNews(Long adNewsSetId) {

        AdNewsSet adNewsSet = adNewsSetDao.getById(adNewsSetId);
        if (adNewsSet == null) {
            return;
        }
        // 查询关联
        List<AdNewsGroup> adNewsGroups = adNewsGroupDao.getListByAdNewsSetId(adNewsSetId);
        if (ListUtils.isEmpty(adNewsGroups)) {
            return;
        }
        List<Long> classifyIds = adNewsGroups.stream().filter(s -> s.getClassifyId() != null).map(AdNewsGroup::getClassifyId).collect(Collectors.toList());
        //查询关联的群
        List<GroupQrcodeFoAdDTO> groupQrcodeFoAdDTOS = groupQrcodeDao.GroupQrcodeFoAdDTOSByClassifyIds(classifyIds);
        if (ListUtils.isEmpty(groupQrcodeFoAdDTOS)) {
            LOGGER.info("没有关联任何群，无法发送早晚报！");
            return;
        }

        // 需要发送的早晚报内容
        List<String> contents = new ArrayList<>();

        // 需要入库的发送记录
        List<AdNewsGroupRecord> adNewsGroupRecords = new ArrayList<>();
        // 需要入库的消息统计信息
        List<AdNewsMessageStatistic> adNewsMessageStatistics = new ArrayList<>();

        // for循环内的临时变量
        AdNewsGroupRecord adNewsGroupRecord;
        AdNewsMessageStatistic adNewsMessageStatistic;

        if (adNewsSet.getContentFrom() == AdContentFromEnum.WECHAT.key) {
            // 填充要发送的早晚报
            setPushAdNewsList(adNewsSet,groupQrcodeFoAdDTOS);

            // 构建早晚报发送记录对象
            for (GroupQrcodeFoAdDTO groupQrcodeFoAdDTO : groupQrcodeFoAdDTOS) {
                if (ListUtils.isEmpty(groupQrcodeFoAdDTO.getAdNewsList())) {
                    LOGGER.info("BookGroupId:"+ groupQrcodeFoAdDTO.getBookGroupId() + " 没有可供发送的早晚报！");
                    continue;
                }

                this.replaceUnescaped(groupQrcodeFoAdDTO.getAdNewsList());

                for (AdNews adNews : groupQrcodeFoAdDTO.getAdNewsList()) {
                    adNewsGroupRecord = new AdNewsGroupRecord();
                    adNewsGroupRecord.setAdNewsId(adNews.getId());
                    adNewsGroupRecord.setAdNewsSetId(adNewsSetId);
                    adNewsGroupRecord.setBookGroupId(groupQrcodeFoAdDTO.getBookGroupId());
                    adNewsGroupRecord.setClassifyId(groupQrcodeFoAdDTO.getClassifyId());
                    adNewsGroupRecord.setQrcodeId(groupQrcodeFoAdDTO.getQrcodeId());
                    adNewsGroupRecord.setCreateUser(adNewsSet.getCreateUser());
                    adNewsGroupRecord.setUpdateUser(adNewsSet.getCreateUser());
                    adNewsGroupRecord.setContentFrom(adNews.getContentFrom());
                    adNewsGroupRecord.setInsertMark(groupQrcodeFoAdDTOS.indexOf(groupQrcodeFoAdDTO));
                    adNewsGroupRecords.add(adNewsGroupRecord);
                }
                // 统计记录
                adNewsMessageStatistic = new AdNewsMessageStatistic();
                adNewsMessageStatistic.setAdNewsSetId(adNewsSetId);
                adNewsMessageStatistic.setBookGroupId(groupQrcodeFoAdDTO.getBookGroupId());
                adNewsMessageStatistic.setClassifyId(groupQrcodeFoAdDTO.getClassifyId());
                adNewsMessageStatistic.setQrcodeId(groupQrcodeFoAdDTO.getQrcodeId());
                adNewsMessageStatistic.setCreateUser(adNewsSet.getCreateUser());
                adNewsMessageStatistic.setMessageCount(groupQrcodeFoAdDTO.getAdNewsList().size());
                adNewsMessageStatistic.setInsertMark(groupQrcodeFoAdDTOS.indexOf(groupQrcodeFoAdDTO));
                adNewsMessageStatistics.add(adNewsMessageStatistic);
            }

            // 因为拼短链接需要主键，所以需要先入库拿到主键后然后拼接Url，最后再发送微信消息；
            // 记录入库
            int flag = this.insertRecord(adNewsGroupRecords, adNewsMessageStatistics);
            if(flag == 0){ return; }

            // 拼接中转链接
            String transferUrl;
            AdNews adNews;
            // 遍历微信群
            for (GroupQrcodeFoAdDTO groupQrcodeFoAdDTO : groupQrcodeFoAdDTOS) {
                // 遍历记录详情
                for (AdNewsGroupRecord item : adNewsGroupRecords) {
                    // 获取对应当前群的记录详情
                    if (groupQrcodeFoAdDTO.getQrcodeId() == item.getQrcodeId()) {
                        // 将对应当前群里的需要发送的新闻链接拼接完成
                        adNews = groupQrcodeFoAdDTO.getAdNewsList().stream().filter(x -> x.getId() == item.getAdNewsId()).findFirst().get();
                        transferUrl = wechatGroupLinkPrefix
                                .concat("/link")
                                .concat("?message_statistic_id=").concat(item.getMessageStatisticId().toString())
                                .concat("&group_record_id=").concat(item.getId().toString())
                                .concat("&url=").concat(URLEncoder.encode(adNews.getUrl()));
                        adNews.setShortUrl(UrlUtils.getShortUrl4Own(transferUrl));
                    }
                }
                // 将多条需要发送的早晚报拼接成小于字数限制一组的消息
                contents = this.concatContent(groupQrcodeFoAdDTO.getAdNewsList());
                // 将对应群的消息发送出去
                this.sendAdNewsToWechatGroup(adNewsSet, new ArrayList<GroupQrcodeFoAdDTO>() {{
                    add(groupQrcodeFoAdDTO);
                }}, contents);
            }

        } else if (adNewsSet.getContentFrom() == AdContentFromEnum.CUSTOM.key) {

            // 获取发送早报还是晚报
            String customContent = this.getMorningOrEveningContent(adNewsSet);

            if (StringUtil.isEmpty(customContent)) {
                LOGGER.info(AdNewsContentTypeEnum.getDescriptionByKey(adNewsSet.getContentType()) + "内容为空，无法发送");
                return;
            }

            for (GroupQrcodeFoAdDTO groupQrcodeFoAdDTO : groupQrcodeFoAdDTOS) {
                adNewsGroupRecord = new AdNewsGroupRecord();
                adNewsGroupRecord.setAdNewsSetId(adNewsSetId);
                adNewsGroupRecord.setBookGroupId(groupQrcodeFoAdDTO.getBookGroupId());
                adNewsGroupRecord.setClassifyId(groupQrcodeFoAdDTO.getClassifyId());
                adNewsGroupRecord.setQrcodeId(groupQrcodeFoAdDTO.getQrcodeId());
                adNewsGroupRecord.setCreateUser(adNewsSet.getCreateUser());
                adNewsGroupRecord.setUpdateUser(adNewsSet.getCreateUser());
                adNewsGroupRecord.setCustomContent(customContent);
                adNewsGroupRecord.setInsertMark(groupQrcodeFoAdDTOS.indexOf(groupQrcodeFoAdDTO));
                adNewsGroupRecord.setContentType(adNewsSet.getContentType());
                adNewsGroupRecord.setContentFrom(adNewsSet.getContentFrom());
                adNewsGroupRecords.add(adNewsGroupRecord);
                // 统计记录
                adNewsMessageStatistic = new AdNewsMessageStatistic();
                adNewsMessageStatistic.setAdNewsSetId(adNewsSetId);
                adNewsMessageStatistic.setBookGroupId(groupQrcodeFoAdDTO.getBookGroupId());
                adNewsMessageStatistic.setClassifyId(groupQrcodeFoAdDTO.getClassifyId());
                adNewsMessageStatistic.setQrcodeId(groupQrcodeFoAdDTO.getQrcodeId());
                adNewsMessageStatistic.setCreateUser(adNewsSet.getCreateUser());
                adNewsMessageStatistic.setMessageCount(1);
                adNewsMessageStatistic.setInsertMark(groupQrcodeFoAdDTOS.indexOf(groupQrcodeFoAdDTO));
                adNewsMessageStatistics.add(adNewsMessageStatistic);
            }
            // 发送记录入库
            this.insertRecord(adNewsGroupRecords, adNewsMessageStatistics);

            contents.add(customContent);
            // 通过机器人小号发送消息到微信群，自定义的消息无法记录点击，所以可以一起发（其实里面也是遍历微信群然后发送消息）
            this.sendAdNewsToWechatGroup(adNewsSet, groupQrcodeFoAdDTOS, contents);
        }
    }

    // 设置群标签名称
    private void setGroupLabelName(List<GroupQrcodeFoAdDTO> groupQrcodeFoAdDTOS) {
        List<Long> labelIds = new ArrayList<>();
        for (GroupQrcodeFoAdDTO item : groupQrcodeFoAdDTOS) {
            if (item.getProLabelId() != null) {
                labelIds.add(item.getProLabelId());
            }
            if (item.getDepLabelId() != null) {
                labelIds.add(item.getDepLabelId());
            }
            if (item.getPurLabelId() != null) {
                labelIds.add(item.getPurLabelId());
            }
        }

        Map<Long, String> labelMap = new HashMap<>();
        if (!ListUtils.isEmpty(labelIds)) {
            labelMap = ResponseHandleUtil.parseMap(labelService.getLabelName(labelIds), Long.class, String.class);
        }
        if (!MapUtils.isEmpty(labelMap)) {
            for (GroupQrcodeFoAdDTO item : groupQrcodeFoAdDTOS) {
                if (item.getProLabelId() != null) {
                    item.setProLabelName(labelMap.get(item.getProLabelId()));
                }
                if (item.getDepLabelId() != null) {
                    item.setDepLabelName(labelMap.get(item.getDepLabelId()));
                }
                if (item.getPurLabelId() != null) {
                    item.setPurLabelName(labelMap.get(item.getPurLabelId()));
                }
            }
        }
    }

    /**
     * 根据规则获取推送的新闻
     *
     * 规则：优先推送自有公众号的内容，如果自有公众号的内容不够，再从公共库里进行挑选；
     *      公共库需要用群的标签匹配新闻的标题，如果匹配不到再进行公众号的名称匹配，如果还是没有，最后则直接推送最新入库的数据
     *
     * @param adNewsSet
     * @param groupQrcodeFoAdDTOS
     * @return
     */
    private void setPushAdNewsList(AdNewsSet adNewsSet,List<GroupQrcodeFoAdDTO> groupQrcodeFoAdDTOS) {
        // 获取自有公众号的新闻
        List<AdNews> adNewsList = adNewsDao.getNewsToSendBySetIdAndAdviser4Own(adNewsSet.getId(), adNewsSet.getCreateUser(), adNewsSet.getSendCount());
        if(ListUtils.isEmpty(adNewsList)){
            adNewsList = new ArrayList<>();
        }

        // 初始化 groupQrcodeFoAdDTOS.adNewsList
        for (GroupQrcodeFoAdDTO groupQrcodeFoAdDTO : groupQrcodeFoAdDTOS)
        {
            groupQrcodeFoAdDTO.setAdNewsList(adNewsList.stream().collect(Collectors.toList()));
        }

        // 如果自有公众号的数据条数少于要发送的条数，则用系统推荐公众号的补齐剩余的条数
        if(adNewsList.size() < adNewsSet.getSendCount()){
            List<AdNews> _adNewsList = new ArrayList<>();

            if(AdNewsSendRuleEnum.MATCH_LABEL.key == adNewsSet.getSendRule()){
                // 根据群批量查找社群码的标签
                setGroupLabelName(groupQrcodeFoAdDTOS);
                // 标签去重进行匹配新闻
                List<Long> bookGroupIds = groupQrcodeFoAdDTOS.stream().map(x -> x.getBookGroupId()).distinct().collect(Collectors.toList());

                // 三天前
                String beginTime = DateUtils.formatDate(DateUtils.subDays(3), DateUtils.DATE_FORMAT_DATEONLY);

                for (GroupQrcodeFoAdDTO groupQrcodeFoAdDTO : groupQrcodeFoAdDTOS){
                    _adNewsList = adNewsDao.getNewsByLabelName(adNewsSet.getId(), adNewsSet.getCreateUser(), beginTime, groupQrcodeFoAdDTO.getProLabelName(),groupQrcodeFoAdDTO.getDepLabelName(),groupQrcodeFoAdDTO.getPurLabelName(),adNewsSet.getSendCount() - groupQrcodeFoAdDTO.getAdNewsList().size());
                    if(!ListUtils.isEmpty(_adNewsList)){
                        groupQrcodeFoAdDTO.getAdNewsList().addAll(_adNewsList);
                    }

                    // 如果筛选新闻条数还不够，继续再公众号匹配
                    if(groupQrcodeFoAdDTO.getAdNewsList().size() < adNewsSet.getSendCount()) {
                        _adNewsList = adNewsDao.getNews4WechatByLabelName(adNewsSet.getId(), adNewsSet.getCreateUser(), beginTime, groupQrcodeFoAdDTO.getProLabelName(),groupQrcodeFoAdDTO.getDepLabelName(),groupQrcodeFoAdDTO.getPurLabelName(),adNewsSet.getSendCount() - groupQrcodeFoAdDTO.getAdNewsList().size());
                        if(!ListUtils.isEmpty(_adNewsList)){
                            groupQrcodeFoAdDTO.getAdNewsList().addAll(_adNewsList);
                        }
                    }

                    // 继续筛选新闻条数不够的群，根据时间排序补齐剩余
                    if(groupQrcodeFoAdDTO.getAdNewsList().size() < adNewsSet.getSendCount()) {
                        _adNewsList = adNewsDao.getNewsToSendBySetIdAndAdviser(adNewsSet.getId(), adNewsSet.getCreateUser(), adNewsSet.getSendCount() - groupQrcodeFoAdDTO.getAdNewsList().size());
                        if (!ListUtils.isEmpty(_adNewsList)) {
                            groupQrcodeFoAdDTO.getAdNewsList().addAll(_adNewsList);
                        }
                    }
                }
            }else {
                // 直接用最新数据补齐剩余条数
                _adNewsList = adNewsDao.getNewsToSendBySetIdAndAdviser(adNewsSet.getId(), adNewsSet.getCreateUser(), adNewsSet.getSendCount() - adNewsList.size());
                if (!ListUtils.isEmpty(_adNewsList)) {
                    for (GroupQrcodeFoAdDTO groupQrcodeFoAdDTO : groupQrcodeFoAdDTOS)
                    {
                        groupQrcodeFoAdDTO.getAdNewsList().addAll(_adNewsList);
                    }
                }
            }
        }
    }

    /**
     * 替换转义符
     * @param adNewsList
     */
    private void replaceUnescaped(List<AdNews> adNewsList) {
        for (AdNews adNews : adNewsList) {
            if (!StringUtil.isEmpty(adNews.getTitle())) {
                adNews.setTitle(adNews.getTitle()
                        .replaceAll("&amp;quot;", "\"")
                        .replaceAll("&nbsp;", " "));
            }
        }
    }

    /**
     * 替换转义符
     * @param adNewsGroupRecordVOS
     */
    private void replaceUnescaped4Record(List<AdNewsGroupRecordVO> adNewsGroupRecordVOS) {
        for (AdNewsGroupRecordVO adNewsGroupRecordVO : adNewsGroupRecordVOS) {
            if (!StringUtil.isEmpty(adNewsGroupRecordVO.getTitle())) {
                adNewsGroupRecordVO.setTitle(adNewsGroupRecordVO.getTitle()
                        .replaceAll("&amp;quot;", "\"")
                        .replaceAll("&nbsp;", " "));
            }
        }
    }

    /**
     * 将发送记录入库
     *
     * @param adNewsGroupRecords
     * @param adNewsMessageStatistics
     */
    private int insertRecord(List<AdNewsGroupRecord> adNewsGroupRecords, List<AdNewsMessageStatistic> adNewsMessageStatistics) {
        if(ListUtils.isEmpty(adNewsGroupRecords) || ListUtils.isEmpty(adNewsMessageStatistics)){
            LOGGER.info("没有可入库的数据，没有可供发送的早晚报内容");
            return 0;
        }
        adNewsMessageStatisticDao.batchInsert(adNewsMessageStatistics);
        // 将主键赋值给从表外键；(不会mybatis的主从表批量插入，所以只能这么做)
        for (AdNewsMessageStatistic item : adNewsMessageStatistics) {
            adNewsGroupRecords.stream().filter(x -> x.getInsertMark() == item.getInsertMark()).forEach(x -> x.setMessageStatisticId(item.getId()));
        }
        adNewsGroupRecordDao.batchInsert(adNewsGroupRecords);
        return 1;
    }

    /**
     * 通过机器人小号发送消息到微信群
     *
     * @param adNewsSet
     * @param groupQrcodeFoAdDTOS
     * @param contents
     */
    private void sendAdNewsToWechatGroup(AdNewsSet adNewsSet, List<GroupQrcodeFoAdDTO> groupQrcodeFoAdDTOS, List<String> contents) {
        String startContent = "";
        String endContent = "";
        if (adNewsSet.getHasStartContent()) {
            startContent = adNewsSet.getStartContent();
        }
        if (adNewsSet.getHasEndContent()) {
            endContent = adNewsSet.getEndContent();
        }
        //循环发送
        for (GroupQrcodeFoAdDTO groupQrcodeFoAdDTO : groupQrcodeFoAdDTOS) {
            String weixinGroupId = groupQrcodeFoAdDTO.getWeixinGroupId();
            String robotId = wechatGroupConsr.getRobotIdByGroupId(weixinGroupId);
            if (StringUtil.isEmpty(robotId)) {
                LOGGER.info("发送编辑端早晚报未找到小号" + weixinGroupId);
                continue;
            }

            // 如果正文内容的长度小于1，说明文字少于限制数，那么就可以合并发送
            if(contents.size()==1){
                String temp = contents.get(0);
                if (!StringUtil.isEmpty(startContent)) { temp = startContent.concat("\n\n").concat(temp); }
                if (!StringUtil.isEmpty(endContent)) { temp = temp.concat("\n").concat(endContent); }

                // 合并后的字数长度小于限制则发送
                if(temp.length() <= SEND_MESSAGE_LENGTH){
                    // 直接发送合并后的消息
                    sendAdNewsText(temp, robotId, weixinGroupId);
                    continue;
                }
            }

            if (!StringUtil.isEmpty(startContent)) {
                //发送开场语
                sendAdNewsText(startContent, robotId, weixinGroupId);
            }
            //发送中间信息
            for (String text : contents) {
                //发送结束语
                sendAdNewsText(text, robotId, weixinGroupId);
            }
            if (!StringUtil.isEmpty(endContent)) {
                //发送结束语
                sendAdNewsText(endContent, robotId, weixinGroupId);
            }
        }
    }

    /**
     * 发送早报还是晚报
     *
     * @param adNewsSet
     * @return
     */
    private String getMorningOrEveningContent(AdNewsSet adNewsSet) {
        Date date = new Date();
        String dateOnly = DateUtils.formatDate(date, DateUtils.DATE_FORMAT_DATEONLY);
        Date morningDateTime = DateUtils.StringToDateTime(dateOnly.concat(" ").concat(adNewsSet.getMorningTime()));
        Date eveningDateTime = DateUtils.StringToDateTime(dateOnly.concat(" ").concat(adNewsSet.getEveningTime()));
        Long morningDiff = date.getTime() - morningDateTime.getTime();
        Long eveningDiff = date.getTime() - eveningDateTime.getTime();
        // 判断正负太麻烦，直接判断绝对值，哪个时间差小就发哪个
        adNewsSet.setContentType(Math.abs(morningDiff) <= Math.abs(eveningDiff) ? 0 : 1);
        return adNewsSet.getContentType() == 0 ? adNewsSet.getMorningContent() : adNewsSet.getEveningContent();
    }

    /**
     * 将内容拼接为 SEND_MESSAGE_LENGTH 字符一条数据项的数组【微信目前单次消息发送字符长度存在字数限制】
     * 拼接内容大致为
     * 1.xxxxxxxxxxx 2.xxxxxxxxxx 3.xxxxxxxx   【假设此条数据加下一条数据【4.xxx】 就大于限制数，那就先将之前的存一条到数组】
     * 4.xxxxx 5.xxxxxxx   【多余限制数的字符再重新拼接，以此循环下去】
     *
     * @param adNewsList
     * @return
     */
    private List<String> concatContent(List<AdNews> adNewsList) {
        List<String> contents = new ArrayList<>();
        String content = "";
        int i = 1;
        for (AdNews adNews : adNewsList) {
            String temp = content + i + "." + adNews.getTitle() + adNews.getShortUrl() + "\n";
            if (temp.length() >= SEND_MESSAGE_LENGTH) {
                // 如果本次拼接后的长度大于限制数，那么将之前拼接的内容存入数组
                contents.add(content);
                // 重置 content 的内容
                content = i + "." + adNews.getTitle() + adNews.getShortUrl() + "\n";
            } else {
                content = temp;
            }
            if (i == adNewsList.size()) {
                contents.add(content);
            }
            i = i + 1;
        }
        return contents;
    }

    @ParamLog("获取编辑早晚报设置")
    @Override
    public AdNewsSet getAdNewsSet(Long partyId) {
        AdNewsSet adNewsSet = adNewsSetDao.getByPartyId(partyId);
        if (adNewsSet != null) {
            // 获取勾选的社群
            List<AdNewsGroup> adNewsGroups = adNewsGroupDao.getListByAdNewsSetId(adNewsSet.getId());
            adNewsSet.setAdNewsGroups(adNewsGroups);
            if (!ListUtils.isEmpty(adNewsGroups)) {
                List<Long> classifyIds = adNewsGroups.stream().filter(s -> s.getClassifyId() != null).map(AdNewsGroup::getClassifyId).collect(Collectors.toList());
                adNewsSet.setClassifyIds(classifyIds);
            }

            // 自有公众号
            List<AdNewsWechat> adNewsWechats = adNewsWechatDao.getListByAdviserId(adNewsSet.getUpdateUser());
            adNewsSet.setAdNewsWechats(ListUtils.isEmpty(adNewsWechats) ? new ArrayList<>() : adNewsWechats);
        }
        return adNewsSet;
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("编辑添加选择的早晚报素材")
    @Override
    public void createAdNewsChooseBatch(List<Long> adNewsIds, Long partyId) {
        List<AdNewsChoose> adNewsChooses = new ArrayList<>();
        for (Long adNewsId : adNewsIds) {
            AdNewsChoose adNewsChoose = new AdNewsChoose();
            adNewsChoose.setAdNewsId(adNewsId);
            adNewsChoose.setAdviserId(partyId);
            adNewsChoose.setCreateUser(partyId);
            adNewsChoose.setUpdateUser(partyId);
            adNewsChooses.add(adNewsChoose);
        }
        //校验重复添加
        Integer count = adNewsChooseDao.getCountByAdNewsIdsAndPartyId(adNewsIds, partyId);
        if (count > 0) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "请勿重复添加！");
        }
        adNewsChooseDao.batchInsert(adNewsChooses);
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("编辑移除选择的早晚报素材")
    @Override
    public void deleteAdNewsChoose(Long adNewsId, Long partyId) {
        adNewsChooseDao.deleteAdNewsChooseById(adNewsId, partyId);
    }

    @ParamLog("获取素材库列表")
    @Override
    public PageBeanNew<AdNews> getAdNewsList(AdNewsListParam adNewsListParam) {
        PageParam pageParam = new PageParam(adNewsListParam.getCurrentPage(), adNewsListParam.getNumPerPage());
        Map<String, Object> map = new HashMap<>();
        map.put("partyId", adNewsListParam.getPartyId());
        map.put("title", adNewsListParam.getTitle());
        map.put("beginTime", adNewsListParam.getBeginTime());
        map.put("endTime", adNewsListParam.getEndTime());
        map.put("hasBan", adNewsListParam.getHasBan());
        map.put("hasUsed", adNewsListParam.getHasUsed());
        map.put("adNewsWechatIds", adNewsListParam.getAdNewsWechatIds());
        map.put("contentFrom", adNewsListParam.getContentFrom());
        PageBeanNew<AdNews> pageBeanNew = adNewsDao.listPageNew(pageParam, map, "getAdNewsList");
        this.replaceUnescaped(pageBeanNew.getRecordList());
        return pageBeanNew;
    }

    @ParamLog("获取编辑已选取的素材库列表")
    @Override
    public PageBeanNew<AdNews> getAdNewsChooseList(Long partyId, String title, Boolean hasUsed, Integer currentPage, Integer numPerPage) {
        PageParam pageParam = new PageParam(currentPage, numPerPage);
        Map<String, Object> map = new HashMap<>();
        map.put("title", title);
        map.put("partyId", partyId);
        map.put("hasUsed", hasUsed);
        PageBeanNew<AdNews> page = adNewsDao.listPageNew(pageParam, map, "getAdNewsChooseList");
        return page;
    }

    @ParamLog("禁用/允许推送")
    @Override
    public void saveAdNewsBan(Long partyId, Long adNewsId, Integer hasBan) {
        if (AdNewsBanEnum.BAN.key == hasBan) {
            AdNewsBan model = new AdNewsBan();
            model.setAdviserId(partyId);
            model.setAdNewsId(adNewsId);
            adNewsBanDao.insert(model);
        } else {
            adNewsBanDao.deleteByAdNewsId(partyId, adNewsId);
        }
    }

    @ParamLog("发送消息")
    private void sendAdNewsText(String content, String robotId, String weixinGroupId) {
        SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
        sendTextMessageVO.setContent(content);
        sendTextMessageVO.setAltId(robotId);
        sendTextMessageVO.setWxGroupId(weixinGroupId);
        sendTextMessageVO.setIp(findIp(weixinGroupId));
        WxGroupSDK.sendTextMessage(sendTextMessageVO);
    }

    @ParamLog("删除创建编辑早晚报定时任务")
    private void deleteAdNewsSch(String jobName) {
        try {
            scheduleService.deleteJob(jobName, "book");
        } catch (Exception e) {
            LOGGER.error("删除创建编辑早晚报定时任务失败");
        }
    }

    @ParamLog("构建早晚报关联")
    private void buildAdNewsGroups(AdNewsSet adNewsSet, Long partyId) {
        Long adNewsSetId = adNewsSet.getId();
        for (AdNewsGroup adNewsGroup : adNewsSet.getAdNewsGroups()) {
            adNewsGroup.setAdNewsSetId(adNewsSetId);
            adNewsGroup.setCreateUser(partyId);
            adNewsGroup.setUpdateUser(partyId);
        }
    }

    @ParamLog("获取ip")
    private String findIp(String wechatGroupId) {
        Map<String, BookWxQrcodeDTO> groupVersion = weixinQrcodeBiz.getGroupVersion(Collections.singletonList(wechatGroupId));
        String ip = Optional.ofNullable(groupVersion.get(wechatGroupId)).orElse(new BookWxQrcodeDTO()).getWechatGroupIp();
        return ip;
    }

    @ParamLog("增加公众号反馈")
    @Override
    public Long addAdNewsWechat(AdNewsWechat adNewsWechat) {
        this.checkAdNewsWechat(adNewsWechat);
        return adNewsWechatDao.insert(adNewsWechat);
    }

    private void checkAdNewsWechat(AdNewsWechat adNewsWechat) {
        if (adNewsWechat.getAdviserId() == null) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "编辑id不能为空！");
        }
        if (StringUtil.isEmpty(adNewsWechat.getWechatName())) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "公众号不能为空！");
        }
        if (adNewsWechatDao.checkRepeat(adNewsWechat.getWechatName(), adNewsWechat.getAdviserId())) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "该公众号已经存在！");
        }
    }

    @Override
    @ParamLog("获取编辑自有公众号")
    public PageBeanNew<AdNewsWechat> getAdNewsWechatListForAdviser(Long adviserId, Integer currentPage, Integer numPerPage) {
        PageParam pageParam = new PageParam(currentPage, numPerPage);
        Map<String, Object> map = new HashMap<>();
        map.put("adviserId", adviserId);
        return adNewsWechatDao.listPageNew(pageParam, map, "getAdNewsWechatListForAdviser");
    }

    @ParamLog("获取公众号列表")
    @Override
    public PageBeanNew<AdNewsWechat> getAdNewsWechatList(Integer currentPage, Integer numPerPage) {
        PageParam pageParam = new PageParam(currentPage, numPerPage);
        Map<String, Object> map = new HashMap<>();
        PageBeanNew<AdNewsWechat> pageBeanNew = adNewsWechatDao.listPageNew(pageParam, map, "getAdNewsWechatList");
        return pageBeanNew;
    }

    @ParamLog("获取编辑已选取的公众号列表")
    @Override
    public PageBeanNew<AdNewsWechat> getAdNewsWechatChooseList(Long partyId, Integer currentPage, Integer numPerPage) {
        PageParam pageParam = new PageParam(currentPage, numPerPage);
        Map<String, Object> map = new HashMap<>();
        map.put("partyId", partyId);
        PageBeanNew<AdNewsWechat> pageBeanNew = adNewsWechatDao.listPageNew(pageParam, map, "getAdNewsWechatChooseList");
        return pageBeanNew;
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("编辑添加选择的公众号")
    @Override
    public void createAdNewsWechatChooseBatch(List<Long> adNewsWechatIds, Long partyId) {
        List<AdNewsWechatChoose> adNewsWechatChooses = new ArrayList<>();
        for (Long adNewsWechatId : adNewsWechatIds) {
            AdNewsWechatChoose adNewsWechatChoose = new AdNewsWechatChoose();
            adNewsWechatChoose.setAdNewsWechatId(adNewsWechatId);
            adNewsWechatChoose.setAdviserId(partyId);
            adNewsWechatChoose.setCreateUser(partyId);
            adNewsWechatChoose.setUpdateUser(partyId);
            adNewsWechatChooses.add(adNewsWechatChoose);
        }
        // 移除编辑之前的选择
        adNewsWechatChooseDao.deleteAdNewsWechatChooseByPartyId(partyId);
        if (!ListUtils.isEmpty(adNewsWechatChooses)) {
            // 重新建立关系
            adNewsWechatChooseDao.batchInsert(adNewsWechatChooses);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("编辑移除选择的公众号")
    @Override
    public void deleteAdNewsWechatChoose(Long adNewsWechatId, Long partyId) {
        adNewsWechatChooseDao.deleteAdNewsWechatChooseById(adNewsWechatId, partyId);
    }

    @Override
    @ParamLog("获取早晚报发送记录")
    public PageBeanNew<AdNewsMessageStatisticVO> getAdNewsMessageStatistic(Long partyId, String title, String beginTime, String endTime, Long proLabelId, Long depLabelId, Long purLabelId, Integer currentPage, Integer numPerPage) {

        PageParam pageParam = new PageParam(currentPage, numPerPage);
        Map<String, Object> map = new HashMap<>();
        map.put("partyId", partyId);
        map.put("title", title);
        map.put("beginTime", beginTime);
        map.put("endTime", endTime);
        map.put("proLabelId", proLabelId);
        map.put("depLabelId", depLabelId);
        map.put("purLabelId", purLabelId);
        PageBeanNew<AdNewsMessageStatisticVO> pageBeanNew = adNewsMessageStatisticDao.listPageNew(pageParam, map, "getAdNewsMessageStatistic");

        if (pageBeanNew == null) {
            return new PageBeanNew<>(currentPage, numPerPage, new ArrayList<>());
        }

        // 遍历填充标签
        List<AdNewsMessageStatisticVO> adNewsMessageStatisticVOS = pageBeanNew.getRecordList();
        if (!ListUtils.isEmpty((adNewsMessageStatisticVOS))) {
            List<Long> labelIds = new ArrayList<>();
            for (AdNewsMessageStatisticVO item : adNewsMessageStatisticVOS) {
                if (item.getProLabelId() != null) {
                    labelIds.add(item.getProLabelId());
                }
                if (item.getDepLabelId() != null) {
                    labelIds.add(item.getDepLabelId());
                }
                if (item.getPurLabelId() != null) {
                    labelIds.add(item.getPurLabelId());
                }
            }

            Map<Long, String> labelMap = new HashMap<>();
            if (!ListUtils.isEmpty(labelIds)) {
                labelMap = ResponseHandleUtil.parseMap(labelService.getLabelName(labelIds), Long.class, String.class);
            }
            if (!MapUtils.isEmpty(labelMap)) {
                for (AdNewsMessageStatisticVO item : adNewsMessageStatisticVOS) {
                    if (item.getProLabelId() != null) {
                        item.setProLabelName(labelMap.get(item.getProLabelId()));
                    }

                    if (item.getDepLabelId() != null) {
                        item.setDepLabelName(labelMap.get(item.getDepLabelId()));
                    }
                    if (item.getPurLabelId() != null) {
                        item.setPurLabelName(labelMap.get(item.getPurLabelId()));
                    }
                }
            }
        }

        return pageBeanNew;
    }

    @Override
    @ParamLog("查询早晚报发送详情")
    public List<AdNewsGroupRecordVO> getAdNewsGroupRecord(Long partyId, Long messageStatisticId) {
        List<AdNewsGroupRecordVO> adNewsGroupRecordVOS= adNewsGroupRecordDao.getAdNewsGroupRecord(partyId, messageStatisticId);
        this.replaceUnescaped4Record(adNewsGroupRecordVOS);
        return adNewsGroupRecordVOS;
    }

    @Override
    @ParamLog("记录用户点击量")
    public Long addAdNewsClickRecord(AdNewsClickRecord adNewsClickRecord) {
        return adNewsClickRecordDao.insert(adNewsClickRecord);
    }
}
























