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

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.pcloud.appcenter.app.dto.AppDto;
import com.pcloud.book.base.exception.BookBizException;
import com.pcloud.book.consumer.app.AppConsr;
import com.pcloud.book.consumer.channel.QrcodeSceneConsr;
import com.pcloud.book.consumer.resource.ProductConsr;
import com.pcloud.book.consumer.wechatgroup.WechatGroupConsr;
import com.pcloud.book.es.biz.ESNewsBiz;
import com.pcloud.book.es.entity.ESNews;
import com.pcloud.book.group.biz.BookGroupClassifyBiz;
import com.pcloud.book.group.biz.WeixinQrcodeBiz;
import com.pcloud.book.group.dao.AppTouchRecordDao;
import com.pcloud.book.group.dao.BookGroupClassifyDao;
import com.pcloud.book.group.dao.BookGroupDao;
import com.pcloud.book.group.dao.GroupQrcodeDao;
import com.pcloud.book.group.dto.BookGroupDTO;
import com.pcloud.book.group.dto.BookWxQrcodeDTO;
import com.pcloud.book.group.dto.ClassifyDTO;
import com.pcloud.book.group.dto.GroupClassifyQrcodeDTO;
import com.pcloud.book.group.entity.AppTouchRecord;
import com.pcloud.book.group.entity.GroupQrcode;
import com.pcloud.book.group.enums.TouchTypeEnum;
import com.pcloud.book.group.tools.SendWeixinRequestTools;
import com.pcloud.book.push.biz.PushBiz;
import com.pcloud.book.push.check.PushCheck;
import com.pcloud.book.push.dao.*;
import com.pcloud.book.push.dto.*;
import com.pcloud.book.push.entity.*;
import com.pcloud.book.push.enums.ItemTypeEnum;
import com.pcloud.book.push.enums.PushStatusEnum;
import com.pcloud.book.push.enums.PushTypeEnum;
import com.pcloud.book.util.common.ThreadPoolUtils;
import com.pcloud.channelcenter.wechat.dto.AccountSettingDto;
import com.pcloud.common.core.aspect.ParamLog;
import com.pcloud.common.page.PageBeanNew;
import com.pcloud.common.page.PageParam;
import com.pcloud.common.utils.BeanUtils;
import com.pcloud.common.utils.DateUtils;
import com.pcloud.common.utils.ListUtils;
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.resourcecenter.product.dto.ProductDto;
import com.sdk.wxgroup.SendArticleMessageVO;
import com.sdk.wxgroup.SendPicMessageVO;
import com.sdk.wxgroup.SendTextMessageVO;
import com.sdk.wxgroup.WxGroupSDK;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author ruansiyuan
 * @Date 2019/4/17 17:46
 **/
@Component("pushBiz")
public class PushBizImpl implements PushBiz {

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

    @Autowired
    private PushCheck pushCheck;
    @Autowired
    private PushDao pushDao;
    @Autowired
    private PushGroupDao pushGroupDao;
    @Autowired
    private PushItemDao pushItemDao;
    @Autowired
    private ScheduleService scheduleService;
    @Autowired
    private PushGroupRecordDao pushGroupRecordDao;
    @Autowired
    private PushRecordDao pushRecordDao;
    @Autowired
    private GroupQrcodeDao groupQrcodeDao;
    @Autowired
    private AppConsr appConsr;
    @Autowired
    private ProductConsr productConsr;
    @Autowired
    private WechatGroupConsr wechatGroupConsr;
    @Autowired
    private BookGroupClassifyBiz bookGroupClassifyBiz;
    @Autowired
    private QrcodeSceneConsr qrcodeSceneConsr;
    @Autowired
    private BookGroupClassifyDao bookGroupClassifyDao;
    @Autowired
    private AppTouchRecordDao appTouchRecordDao;
    @Autowired
    private MorningEveningNewsDao morningEveningNewsDao;
    @Autowired
    private PushNewsRecordDao pushNewsRecordDao;
    @Autowired
    private ESNewsBiz esNewsBiz;
    @Autowired
    private PushPlanDao pushPlanDao;
    @Autowired
    private BookGroupDao bookGroupDao;
    @Autowired
    private WeixinQrcodeBiz weixinQrcodeBiz;

    private static final String PUSH_SCHEDULE_PRE="BOOK_GROUP_PUSH_";

    private static final String MORNING_NEWS_SCHEDULE="MORNING_NEWS_SCHEDULE";

    private static final String EVENING_NEWS_SCHEDULE="EVENING_NEWS_SCHEDULE";


    @Transactional(rollbackFor = Exception.class)
    @ParamLog("创建群发")
    @Override
    public void createPush(Push push) {
        changeQrcode(push);
        //校验参数
        pushCheck.createPushParamCheck(push);
        //插入群发
        pushDao.insert(push);
        //填充关联模型字段
        fillPushGroupParam(push);
        //插入群发关联表
        pushGroupDao.batchInsert(push.getPushGroups());
        //填充消息项模型
        fillPushItemParam(push);
        //插入群发消息项
        pushItemDao.batchInsert(push.getPushItems());
        //遍历，如果含有超级作者作品，自动上架
        ThreadPoolUtils.SEND_MESSAGE_THREAD_POOL.execute(() -> itemProductAutoOnShelves(push.getPushGroups(), push.getPushItems()));
        //设置定时任务
        pushQuartz(push);
    }

    /**
     * 转换关联关系
     */
    private void changeQrcode(Push push) {
        List<PushGroup> pushGroups = push.getPushGroups();
        List<PushGroup> pushGroupsNew = new ArrayList<>();
        if (!ListUtils.isEmpty(pushGroups)) {
            for (PushGroup pushGroup : pushGroups) {
                //如果只传了classifyId，补充微信二维码id
                if (pushGroup.getClassifyId() != null) {
                    if (pushGroup.getBookGroupQrcodeId() == null) {
                        List<Long> qrcodeIds = groupQrcodeDao.getIdsByClassifyId(pushGroup.getClassifyId());
                        if (!ListUtils.isEmpty(qrcodeIds)) {
                            for (Long qrcodeId : qrcodeIds) {
                                PushGroup pushGroupNew = new PushGroup();
                                BeanUtils.copyProperties(pushGroup, pushGroupNew);
                                pushGroupNew.setBookGroupQrcodeId(qrcodeId);
                                pushGroupsNew.add(pushGroupNew);
                            }
                        }
                    } else {
                        pushGroupsNew.add(pushGroup);
                    }
                }
            }
            if (!ListUtils.isEmpty(pushGroupsNew)){
                push.setPushGroups(pushGroupsNew);
            }
        }
    }

    /**
     * 遍历，如果含有超级作者作品，自动上架
     */
    @ParamLog("遍历，如果含有超级作者作品，自动上架")
    private void itemProductAutoOnShelves(List<PushGroup> pushGroups, List<PushItem> pushItems) {
        if (ListUtils.isEmpty(pushItems)) {
            return;
        }
        if (ListUtils.isEmpty(pushGroups)) {
            return;
        }
        List<Long> allProductIds = pushItems.stream().filter(s -> ItemTypeEnum.APP.value.equals(s.getItemType()) && s.getProductId() != null).map(PushItem::getProductId).collect(Collectors.toList());
        // 过滤判断是否是超级作者
        Map<Long, Boolean> isSuperMap = productConsr.getIsSuperByProductIdList(allProductIds);
        for (PushGroup pushGroup : pushGroups) {
            //通过群id获取对应基本信息
            ClassifyDTO classifyDTO = bookGroupClassifyDao.getById(pushGroup.getClassifyId());
            if (classifyDTO == null) {
                LOGGER.error("未找到群分类信息" + classifyDTO.toString());
                break;
            }
            Long channelId = classifyDTO.getChannelId();
            List<Long> productIds = new ArrayList<>();
            for (PushItem pushItem : pushItems) {
                if (ItemTypeEnum.APP.value.equals(pushItem.getItemType())) {
                    Long productId = pushItem.getProductId();
                    if (productId != null && isSuperMap.get(productId) != null && isSuperMap.get(productId)) {
                        productIds.add(productId);
                    }
                }
            }
            if (channelId != null && !ListUtils.isEmpty(productIds)) {
                productConsr.productAutoOnShelves(channelId, productIds);
            }
        }
    }

    /**
     * 设置任务
     * @param push
     */
    @ParamLog("设置任务")
    private void pushQuartz(Push push) {
        Integer pushType = push.getPushType();
        if (PushTypeEnum.NOW.value.equals(pushType)) {
            sendGroupMessage(push.getId());
        } else if (PushTypeEnum.ONE.value.equals(pushType)) {
            pushOne(push);
        } else if (PushTypeEnum.DAY.value.equals(pushType)) {
            pushDay(push);
        } else if (PushTypeEnum.WEEK.value.equals(pushType)) {
            pushWeek(push);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("发送群发消息")
    @Override
    public void sendGroupMessage(Long pushId) {
        Push push=pushDao.getById(pushId);
        if (push==null){
            return;
        }
        //判断时间范围
        if (PushTypeEnum.WEEK.value.equals(push.getPushType()) || PushTypeEnum.DAY.value.equals(push.getPushType())) {
            Date startTime = push.getStartTime();
            Date endTime = push.getEndTime();
            Date dateNow = new Date();
            if (startTime != null && dateNow.before(startTime)) {
                return;
            }
            if (endTime != null && dateNow.after(endTime)) {
                return;
            }
        }
        //查询关联群
        List<PushGroup> pushGroupList=pushGroupDao.getListByPushId(pushId);
        if (ListUtils.isEmpty(pushGroupList)){
            return;
        }
        //查询消息项
        List<PushItem> pushItemList = pushItemDao.getListByPushId(pushId);
        if (ListUtils.isEmpty(pushItemList)){
            return;
        }
        //群发消息记录
        PushRecord pushRecord = new PushRecord();
        BeanUtils.copyProperties(push, pushRecord);
        pushRecord.setPushId(pushId);
        try {
            pushRecord.setPushStatus(PushStatusEnum.PUSHING.value);
            //新增群发记录
            pushRecordDao.insert(pushRecord);
            //新增细化到群的记录并发送消息
            addPushGroupRecordsAndSend(pushGroupList, pushRecord, pushItemList);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), "群发消息失败！"+e);
            pushRecordDao.updateStateById(pushRecord.getId(), PushStatusEnum.FAIL.value);
            pushGroupRecordDao.updateStateByPushRecordId(pushRecord.getId(), PushStatusEnum.FAIL.value);
        }
    }

    /**
     * 新增群发记录，细化到群
     * @param pushGroupList
     * @param pushRecord
     */
    private void addPushGroupRecordsAndSend(List<PushGroup> pushGroupList, PushRecord pushRecord, List<PushItem> pushItemList) {
        //群发消息记录，细化到群
        List<PushGroupRecord> pushGroupRecords = new ArrayList<>();
        for (PushGroup pushGroup : pushGroupList) {
            PushGroupRecord pushGroupRecord = new PushGroupRecord();
            BeanUtils.copyProperties(pushGroup, pushGroupRecord);
            pushGroupRecord.setPushRecordId(pushRecord.getId());
            pushGroupRecord.setPushStatus(pushRecord.getPushStatus());
            pushGroupRecords.add(pushGroupRecord);
            pushGroupRecordDao.insert(pushGroupRecord);
            //遍历发送消息
            for (PushItem pushItem : pushItemList) {
                sendWechatMessage(pushGroup, pushItem, pushGroupRecord.getId());
            }
        }
    }

    /**
     * 发消息
     */
    @ParamLog("发消息")
    private void sendWechatMessage(PushGroup pushGroup, PushItem pushItem, Long pushGroupRecordId) {
        //获取群信息
        GroupQrcode groupQrcode = groupQrcodeDao.getById(pushGroup.getBookGroupQrcodeId());
        if (groupQrcode == null) {
            LOGGER.error("未找到群信息！");
            return;
        }
        String groupId = groupQrcode.getWeixinGroupId();
        ClassifyDTO classifyDTO = bookGroupClassifyDao.getById(groupQrcode.getClassifyId());
        if (classifyDTO == null) {
            LOGGER.error("未找到分类信息！");
            return;
        }
        String otherUrl = "book_group_id=" + classifyDTO.getBookGroupId() + "&classify_id=" + classifyDTO.getId() + "&qrcode_id=" + pushGroup.getBookGroupQrcodeId();
        //获取机器人微信号
        String altId = wechatGroupConsr.getRobotIdByGroupId(groupId);
        if (groupQrcode != null && !StringUtil.isEmpty(groupId) && !StringUtil.isEmpty(altId)) {
            Integer itemType = pushItem.getItemType();
            if (ItemTypeEnum.TEXT.value.equals(itemType)) {
                SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
                sendTextMessageVO.setGroupId(groupId);
                sendTextMessageVO.setAltId(altId);
                sendTextMessageVO.setContent(pushItem.getTextContent());
                sendTextMessageVO.setPushGroupRecordId(pushGroupRecordId);
                sendTextMessageVO.setIp(findIp(groupId));
                WxGroupSDK.sendTextMessage(sendTextMessageVO);
            }
            if (ItemTypeEnum.LINK.value.equals(itemType)) {
                SendArticleMessageVO sendArticleMessageVO = new SendArticleMessageVO();
                sendArticleMessageVO.setAltId(altId);
                sendArticleMessageVO.setDescription(pushItem.getLinkDescription());
                sendArticleMessageVO.setGroupId(groupId);
                String url = pushItem.getLinkUrl();
                if (url.contains("?")) {
                    url = url + "&" + otherUrl;
                } else {
                    url = url + "?" + otherUrl;
                }
                sendArticleMessageVO.setLinkUrl(url);
                sendArticleMessageVO.setPicUrl(pushItem.getLinkImageUrl());
                sendArticleMessageVO.setTitle(pushItem.getLinkTitle());
                sendArticleMessageVO.setPushGroupRecordId(pushGroupRecordId);
                sendArticleMessageVO.setIp(findIp(groupId));
                WxGroupSDK.sendArticleMessage(sendArticleMessageVO);
            }
            if (ItemTypeEnum.APP.value.equals(itemType)) {
                //通过群id获取对应基本信息
                GroupClassifyQrcodeDTO classifyQrcodeInfo = bookGroupClassifyBiz.getClassifyQrcodeInfo(groupId);
                if (classifyQrcodeInfo == null) {
                    return;
                }
                AccountSettingDto accountSettingDto = qrcodeSceneConsr.getWechatInfo(classifyQrcodeInfo.getChannelId());
                if (pushItem.getAppId() != null) {
                    AppDto appDto = appConsr.getBaseById(pushItem.getAppId());

                    if (appDto != null) {
                        SendArticleMessageVO sendArticleMessageVO = new SendArticleMessageVO();
                        sendArticleMessageVO.setAltId(altId);
                        sendArticleMessageVO.setDescription(appDto.getTypeName());
                        sendArticleMessageVO.setGroupId(groupId);
                        // 处理链接地址
                        String endUrl = pushItem.getAppUrl() + "&" + otherUrl;
                        String linkUrl = SendWeixinRequestTools.splitUrl(accountSettingDto, endUrl);
                        sendArticleMessageVO.setLinkUrl(linkUrl);
                        sendArticleMessageVO.setPicUrl(appDto.getSquareImg());
                        sendArticleMessageVO.setTitle(appDto.getTitle());
                        sendArticleMessageVO.setPushGroupRecordId(pushGroupRecordId);
                        sendArticleMessageVO.setIp(findIp(groupId));
                        WxGroupSDK.sendArticleMessage(sendArticleMessageVO);
                        //新增群发应用作品触发记录
                        addPushAppTouchRecord(pushItem, groupId, classifyDTO.getId(), classifyDTO.getBookGroupId());
                    }
                }
                if (pushItem.getProductId() != null) {
                    ProductDto productDto = productConsr.getProBaseById(pushItem.getProductId());
                    if (productDto != null) {
                        SendArticleMessageVO sendArticleMessageVO = new SendArticleMessageVO();
                        sendArticleMessageVO.setAltId(altId);
                        if (productDto.getProductTypeDto() != null) {
                            sendArticleMessageVO.setDescription(productDto.getProductTypeDto().getTypeName());
                        }
                        sendArticleMessageVO.setGroupId(groupId);
                        // 处理链接地址
                        String endUrl = pushItem.getProductUrl() + "&" + otherUrl;
                        String linkUrl = SendWeixinRequestTools.splitUrl(accountSettingDto, endUrl);
                        sendArticleMessageVO.setLinkUrl(linkUrl);
                        sendArticleMessageVO.setPicUrl(productDto.getCoverImg());
                        sendArticleMessageVO.setTitle(productDto.getProductName());
                        sendArticleMessageVO.setPushGroupRecordId(pushGroupRecordId);
                        sendArticleMessageVO.setIp(findIp(groupId));
                        WxGroupSDK.sendArticleMessage(sendArticleMessageVO);
                        //新增群发应用作品触发记录
                        addPushAppTouchRecord(pushItem, groupId, classifyDTO.getId(), classifyDTO.getBookGroupId());
                    }
                }
            }
            if (ItemTypeEnum.IMAGE.value.equals(itemType)) {
                SendPicMessageVO sendPicMessageVO = new SendPicMessageVO();
                sendPicMessageVO.setAltId(altId);
                sendPicMessageVO.setGroupId(groupId);
                sendPicMessageVO.setPicUrl(pushItem.getImageUrl());
                sendPicMessageVO.setPushGroupRecordId(pushGroupRecordId);
                sendPicMessageVO.setIp(findIp(groupId));
                WxGroupSDK.sendPicMessage(sendPicMessageVO);
            }
        }
    }

    /**
     * 新增群发应用作品触发记录
     */
    private void addPushAppTouchRecord(PushItem pushItem, String weixinGroupId, Long classifyId, Long bookGroupId) {
        AppTouchRecord appTouchRecord = new AppTouchRecord();
        if (pushItem.getAppId() != null) {
            appTouchRecord.setServeId(pushItem.getAppId());
            appTouchRecord.setServeType("APP");
        }
        if (pushItem.getProductId() != null) {
            appTouchRecord.setServeId(pushItem.getProductId());
            appTouchRecord.setServeType("PRODUCT");
        }
        appTouchRecord.setTouchType(TouchTypeEnum.PUSH.value);
        appTouchRecord.setBookGroupId(bookGroupId);
        appTouchRecord.setClassifyId(classifyId);
        GroupQrcode groupQrcode = groupQrcodeDao.getGroupQrcodeByGroupId(weixinGroupId);
        if (groupQrcode != null) {
            appTouchRecord.setQrcodeId(groupQrcode.getId());
        }
        appTouchRecord.setWeixinGroupId(weixinGroupId);
        appTouchRecordDao.insert(appTouchRecord);
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("更新群发")
    @Override
    public void updatePush(Push push) {
        //校验参数
        pushCheck.updatePushParamCheck(push);
        Push pushOld = pushDao.getById(push.getId());
        if (pushOld == null) {
            throw new BookBizException(BookBizException.ERROR, "未找到该群发！");
        }
        pushDao.update(push);
        dealUpdatePushGroup(push);
        dealUpdatePushItem(push);
        //判断是否需要删除群发定时任务
        if (isNeedupdatepushSchedule(pushOld, push)) {
            //删除之前的群发定时任务，重新设置群发
            deletePushSchedule(push.getId());
            pushQuartz(push);
        }
    }

    @ParamLog("处理更新关联表")
    private void dealUpdatePushGroup(Push push) {
        List<PushGroup> pushGroupsOld = pushGroupDao.getListByPushId(push.getId());
        List<Long> pushGroupIdToDelete = new ArrayList<>();
        if (!ListUtils.isEmpty(pushGroupsOld)) {
            pushGroupIdToDelete = pushGroupsOld.stream().map(PushGroup::getId).collect(Collectors.toList());
        }
        List<PushGroup> pushGroups = push.getPushGroups();
        List<PushGroup> pushGroupsToAdd = new ArrayList<>();
        List<Long> pushGroupIdToUpdate = new ArrayList<>();
        for (PushGroup pushGroup : pushGroups) {
            if (pushGroup.getId() != null) {
                //更新
                pushGroup.setUpdateUser(push.getUpdateUser());
                pushGroupDao.update(pushGroup);
                pushGroupIdToUpdate.add(pushGroup.getId());
            } else {
                //新增
                pushGroupsToAdd.add(pushGroup);
            }
        }
        if (!ListUtils.isEmpty(pushGroupIdToUpdate)) {
            pushGroupIdToDelete.removeAll(pushGroupIdToUpdate);
        }
        if (!ListUtils.isEmpty(pushGroupsToAdd)) {
            //填充关联模型字段
            fillPushGroupParam(push);
            pushGroupDao.batchInsert(pushGroupsToAdd);
        }
        if (!ListUtils.isEmpty(pushGroupIdToDelete)) {
            pushGroupDao.deleteByIds(pushGroupIdToDelete, push.getUpdateUser());
        }
    }

    @ParamLog("处理更新关联表")
    private void dealUpdatePushItem(Push push) {
        List<PushItem> pushItemsOld = pushItemDao.getListByPushId(push.getId());
        List<Long> pushItemIdsToDelete = new ArrayList<>();
        if (!ListUtils.isEmpty(pushItemsOld)) {
            pushItemIdsToDelete = pushItemsOld.stream().map(PushItem::getId).collect(Collectors.toList());
        }
        List<PushItem> pushItems = push.getPushItems();
        List<PushItem> pushItemsToAdd = new ArrayList<>();
        List<Long> pushItemIdToUpdate = new ArrayList<>();
        for (PushItem pushItem : pushItems) {
            if (pushItem.getId() != null) {
                //更新
                pushItem.setUpdateUser(push.getUpdateUser());
                pushItemDao.update(pushItem);
                pushItemIdToUpdate.add(pushItem.getId());
            } else {
                //新增
                pushItemsToAdd.add(pushItem);
            }
        }
        if (!ListUtils.isEmpty(pushItemIdToUpdate)) {
            pushItemIdsToDelete.removeAll(pushItemIdToUpdate);
        }
        if (!ListUtils.isEmpty(pushItemsToAdd)) {
            //填充关联模型字段
            fillPushItemParam(push);
            pushItemDao.batchInsert(pushItemsToAdd);
        }
        if (!ListUtils.isEmpty(pushItemIdsToDelete)) {
            pushItemDao.deleteByIds(pushItemIdsToDelete, push.getUpdateUser());
        }
    }

    /**
     * 删除群发定时任务
     */
    @ParamLog("删除群发定时任务")
    private void deletePushSchedule(Long pushId) {
        try {
            scheduleService.deleteJob(PUSH_SCHEDULE_PRE + pushId, "book");
        } catch (Exception e) {
            LOGGER.error("删除定时任务失败");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("删除群发")
    @Override
    public void deletePush(Long pushId, Long partyId) {
        if (pushId == null) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "群发id不能为空！");
        }
        Push pushOld = pushDao.getById(pushId);
        if (pushOld == null) {
            throw new BookBizException(BookBizException.ERROR, "未找到该群发！");
        }
        //删除群发
        pushDao.deleteByPushId(pushId,partyId);
        //删除群发关联
        pushGroupDao.deleteByPushId(pushId,partyId);
        //删除群发消息项
        pushItemDao.deleteByPushId(pushId,partyId);
        //删除定时任务
        deletePushSchedule(pushId);
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("批量新增群发关联")
    @Override
    public void createPushGroupBatch(List<PushGroup> pushGroups, Long partyId) {
        pushCheck.createPushGroupBatchParamCheck(pushGroups);
        Long pushId = pushGroups.get(0).getPushId();
        Push push = pushDao.getByPushId(pushId);
        if (push == null) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "没有该群发或已被删除！");
        }
        for (PushGroup pushGroup : pushGroups) {
            pushGroup.setCreateUser(partyId);
            pushGroup.setUpdateUser(partyId);
        }
        pushGroupDao.batchInsert(pushGroups);
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("批量删除群发关联")
    @Override
    public void deletePushGroupBatch(List<Long> pushGroupIds, Long partyId) {
        if (ListUtils.isEmpty(pushGroupIds)) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "群发关联id集合不能为空！");
        }
        pushGroupDao.deleteByIds(pushGroupIds, partyId);
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("新增群发消息项")
    @Override
    public void createPushItem(PushItem pushItem) {
        pushCheck.createPushItemParamCheck(pushItem);
        Integer count = pushItemDao.getCountByPushId(pushItem.getPushId());
        if (count >= 4) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "群发消息数量不能超过4条！");
        }
        Integer maxSeqNum = pushItemDao.getMaxSeqNumByPushId(pushItem.getPushId());
        pushItem.setSeqNum(maxSeqNum+1);
        pushItemDao.insert(pushItem);
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("删除群发消息项")
    @Override
    public void deletePushItem(Long pushItemId, Long partyId) {
        if (pushItemId == null) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "群发项id不能为空！");
        }
        pushItemDao.deleteByPushItemId(pushItemId,partyId);
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("群发项排序")
    @Override
    public void sortPushItems(List<Long> pushItemIds, Long partyId) {
        if (ListUtils.isEmpty(pushItemIds)){
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "群发项id集合不能为空！");
        }
        pushItemDao.sortPushItems(pushItemIds,partyId);
    }

    @ParamLog("分页获取群发记录")
    @Override
    public PageBeanNew<PushDTO> getTimingPushList( Long partyId, Integer currentPage, Integer numPerPage) {
        PageParam pageParam = new PageParam(currentPage, numPerPage);
        Map<String, Object> map = new HashMap<>();
        map.put("partyId", partyId);
        PageBeanNew<PushDTO> pageBeanNew = pushDao.listPageNew(pageParam, map, "getTimingPushList");
        List<PushDTO> pushDTOS = pageBeanNew.getRecordList();
        fillPushDTOList(pushDTOS);
        return pageBeanNew;
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("更新群发状态为成功")
    @Override
    public void updatePushStateSuccess(Integer pushGroupRecordId) {
        PushGroupRecord pushGroupRecord = pushGroupRecordDao.getById(pushGroupRecordId);
        if (pushGroupRecord != null) {
            //更新细化到群的状态
            pushGroupRecordDao.updateStateById(pushGroupRecordId, PushStatusEnum.SUCCESS.value);
            Long pushRecordId = pushGroupRecord.getPushRecordId();
            //查询群发关联表
            List<PushGroupRecord> pushGroupRecords = pushGroupRecordDao.getListByPushRecordId(pushRecordId);
            if (!ListUtils.isEmpty(pushGroupRecords)) {
                Integer successCount = 0;
                for (PushGroupRecord pushGroupRecordIn : pushGroupRecords) {
                    if (PushStatusEnum.SUCCESS.value.equals(pushGroupRecordIn.getPushStatus())) {
                        successCount = successCount + 1;
                    }
                }
                if (successCount == pushGroupRecords.size()) {
                    //如果全部成功才成功，更新群发状态
                    pushRecordDao.updateStateById(pushRecordId, PushStatusEnum.SUCCESS.value);
                }
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("修改群发时间")
    @Override
    public void updatePushTime(Push push) {
        if (push.getId()==null){
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "群发id不能为空！");
        }
        if (push.getPushType() == null) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "群发类型不能为空！");
        }
        pushCheck.checkPushTime(push);
        Push pushOld = pushDao.getById(push.getId());
        if (pushOld == null) {
            throw new BookBizException(BookBizException.ERROR, "未找到该群发！");
        }
        //修改数据库
        pushDao.updatePushTime(push);
        //判断是否需要删除群发定时任务
        if (isNeedupdatepushSchedule(pushOld, push)) {
            //删除之前的群发定时任务，重新设置群发
            deletePushSchedule(push.getId());
            pushQuartz(push);
        }

    }

    @ParamLog("获取群发关联集合")
    @Override
    public PageBeanNew<PushGroupDTO> getPushGroupList(Long pushId, Boolean isRecord, Integer currentPage, Integer numPerPage) {
        PageParam pageParam = new PageParam(currentPage, numPerPage);
        Map<String, Object> map = new HashMap<>();
        map.put("pushId",pushId);
        map.put("isRecord",isRecord);
        PageBeanNew<PushGroupDTO> pageBeanNew = pushGroupDao.listPageNew(pageParam, map, "getPushGroupList");
        return pageBeanNew;
    }

    @ParamLog("获取群发记录集合")
    @Override
    public PageBeanNew<PushRecordDTO> getPushRecordList(Long partyId, Integer currentPage, Integer numPerPage) {
        PageParam pageParam = new PageParam(currentPage, numPerPage);
        Map<String, Object> map = new HashMap<>();
        map.put("partyId",partyId);
        PageBeanNew<PushRecordDTO> pageBeanNew = pushRecordDao.listPageNew(pageParam, map, "getPushRecordList");
        List<PushRecordDTO> pushRecordDTOS = pageBeanNew.getRecordList();
        fillPushRecordDTOList(pushRecordDTOS);
        return pageBeanNew;
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("新增早晚报")
    @Override
    public Long createMorningEveningNews(MorningEveningNews morningEveningNews) {
        MorningEveningNews morningEveningNewsOld = morningEveningNewsDao.getByPartyId(morningEveningNews.getCreateUser());
        if (morningEveningNewsOld != null) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "已有记录，请勿重复添加！");
        }
        //参数校验
        pushCheck.createMorningEveningNewsParamCheck(morningEveningNews);
        //插入
        morningEveningNewsDao.insert(morningEveningNews);
        if (morningEveningNews.getHasMorningOpen()) {
            //早报定时任务
            addNewsSch(morningEveningNews.getMorningTime(), MORNING_NEWS_SCHEDULE, morningEveningNews.getCreateUser());
        }
        if (morningEveningNews.getHasEveningOpen()) {
            //晚报定是任务
            addNewsSch(morningEveningNews.getEveningTime(), EVENING_NEWS_SCHEDULE, morningEveningNews.getCreateUser());
        }
        return morningEveningNews.getId();
    }

    @ParamLog("新增早晚报定时任务")
    private void addNewsSch(String time, String jobName, Long partyId) {
        //创建早晚报定时任务
        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("sendMorningEveningNews");
        callBackParam.setBeanName("pushService");
        Map<String, Object> paramMap = new HashMap<>();
        //定时任务调用时用到的参数
        paramMap.put("partyId", partyId);
        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 updateMorningEveningNews(MorningEveningNews morningEveningNews) {
        //参数校验
        pushCheck.createMorningEveningNewsParamCheck(morningEveningNews);
        MorningEveningNews morningEveningNewsOld = morningEveningNewsDao.getById(morningEveningNews.getId());
        if (morningEveningNewsOld == null) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "未找到该数据！");
        }
        //修改
        morningEveningNewsDao.update(morningEveningNews);
        //删除之前的早晚报定时任务
        deleteNewsSch(MORNING_NEWS_SCHEDULE);
        deleteNewsSch(EVENING_NEWS_SCHEDULE);
        if (morningEveningNews.getHasMorningOpen()) {
            //早报定时任务
            addNewsSch(morningEveningNews.getMorningTime(), MORNING_NEWS_SCHEDULE, morningEveningNewsOld.getCreateUser());
        }
        if (morningEveningNews.getHasEveningOpen()) {
            //晚报定时任务
            addNewsSch(morningEveningNews.getEveningTime(), EVENING_NEWS_SCHEDULE, morningEveningNewsOld.getCreateUser());
        }

    }

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

    @ParamLog("获取早晚报")
    @Override
    public MorningEveningNews getMorningEveningNews(Long partyId) {
        return morningEveningNewsDao.getByPartyId(partyId);
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("发送早晚报")
    @Override
    public void sendMorningEveningNews(Long partyId) {
        //查询要发送的早晚报基本信息
        MorningEveningNews morningEveningNews = morningEveningNewsDao.getByPartyId(partyId);
        if (morningEveningNews == null) {
            return;
        }
        Integer top = morningEveningNews.getSendCount();
        //查询群人数>10的群
        List<GroupQrcode> groupQrcodes = groupQrcodeDao.getListByUserCount(10);
        if (ListUtils.isEmpty(groupQrcodes)) {
            return;
        }
        List<String> wechatGroupIds = groupQrcodes.stream().map(GroupQrcode::getWeixinGroupId).collect(Collectors.toList());
        List<Long> classifyIds = groupQrcodes.stream().map(GroupQrcode::getClassifyId).collect(Collectors.toList());
        List<ClassifyDTO> classifyDTOS = bookGroupClassifyDao.getNameWithBookNameByIds(classifyIds);
        Map<Long, String> classifyNameMap = new HashMap<>();
        for (ClassifyDTO classifyDTO : classifyDTOS) {
            classifyNameMap.put(classifyDTO.getId(), classifyDTO.getClassify());
        }
        //查询发送记录
        List<PushNewsRecord> pushNewsRecords = pushNewsRecordDao.getListByGroupIds(wechatGroupIds);
        Map<String, List<PushNewsRecord>> pushNewsMap = pushNewsRecords.stream().collect(Collectors.groupingBy(PushNewsRecord::getWeixinGroupId));
        Map<String, String> classifyNameToWeixinIdMap = new HashMap<>();
        for (GroupQrcode groupQrcode : groupQrcodes) {
            classifyNameToWeixinIdMap.put(groupQrcode.getWeixinGroupId(), classifyNameMap.get(groupQrcode.getClassifyId()));
        }
        //内部接口批量获取小号id
        Map<String, String> weixinIdToRobotMap = wechatGroupConsr.getSendDailyRobotByGroupIds(wechatGroupIds);
        List<List<String>> weixinIdsList = weixinIdsList(weixinIdToRobotMap);
        String startContent = null;
        String endContent = null;
        if (morningEveningNews.getHasStartContent() && !StringUtil.isEmpty(morningEveningNews.getStartContent())) {
            startContent = morningEveningNews.getStartContent();
        }
        if (morningEveningNews.getHasEndContent() && !StringUtil.isEmpty(morningEveningNews.getEndContent())) {
            endContent = morningEveningNews.getEndContent();
        }
        if (!ListUtils.isEmpty(weixinIdsList)) {
            for (List<String> weixinIds : weixinIdsList) {
                if (!ListUtils.isEmpty(weixinIds)) {
                    //发送一组小号不一样的群
                    sendNewsGroup(weixinIds, pushNewsMap, classifyNameToWeixinIdMap, weixinIdToRobotMap, top, startContent, endContent);
                    LOGGER.info("一组消息发送完成" + weixinIds.toString());
                    try {
                        LOGGER.info("一组消息发完之后线程等2秒之后再发下一组");
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            LOGGER.info("所有消息发送完毕！");
        }
    }

    /**
     * 发送一组群消息
     */
    private void sendNewsGroup(List<String> weixinIds, Map<String, List<PushNewsRecord>> pushNewsMap,
                               Map<String, String> classifyNameToWeixinIdMap, Map<String, String> weixinIdToRobotMap,
                               Integer top, String startContent, String endContent) {
        if (!ListUtils.isEmpty(weixinIds)) {
            for (String weixinGroupId : weixinIds) {
                List<PushNewsRecord> pushNewsRecordList = pushNewsMap.get(weixinGroupId);
                List<Long> hasPushNewsIds = new ArrayList<>();
                if (!ListUtils.isEmpty(pushNewsRecordList)) {
                    hasPushNewsIds = pushNewsRecordList.stream().filter(s -> s.getNewsId() != null).map(PushNewsRecord::getNewsId).collect(Collectors.toList());
                }
                //按照群排除已经发送的新闻
                String classify = classifyNameToWeixinIdMap.get(weixinGroupId);
                List<ESNews> news = esNewsBiz.getNews(classify, hasPushNewsIds, top);
                if (!ListUtils.isEmpty(news)) {
                    //发送早晚报
                    String robotId = weixinIdToRobotMap.get(weixinGroupId);
                    if (!StringUtil.isEmpty(startContent)) {
                        //发送开场语
                        sendText(startContent, robotId, weixinGroupId);
                    }
                    //发送中间信息
                    sendNews(news, robotId, weixinGroupId);
                    if (!StringUtil.isEmpty(endContent)) {
                        //发送结束语
                        sendText(endContent, robotId, weixinGroupId);
                    }
                    List<PushNewsRecord> pushNewsRecordsToAdd = new ArrayList<>();
                    for (ESNews esNews : news) {
                        PushNewsRecord pushNewsRecord = new PushNewsRecord();
                        pushNewsRecord.setNewsId(new Long(esNews.getId()));
                        pushNewsRecord.setWeixinGroupId(weixinGroupId);
                        pushNewsRecordsToAdd.add(pushNewsRecord);
                    }
                    //存发送记录
                    pushNewsRecordDao.batchInsert(pushNewsRecordsToAdd);
                }
            }
        }
    }

    /**
     * 转换为一组一组，每组小号不一样，同时发
     */
    private List<List<String>> weixinIdsList(Map<String, String> weixinIdToRobotMap) {
        List<String> robotsIds = new ArrayList<>(weixinIdToRobotMap.values()).stream().distinct().collect(Collectors.toList());
        List<String> weixinIds = new ArrayList<>(weixinIdToRobotMap.keySet());
        Map<String, List<String>> map = new HashMap<>();
        for (String robotId : robotsIds) {
            for (String weixinId : weixinIds) {
                if (weixinIdToRobotMap.get(weixinId).equals(robotId)) {
                    List<String> oldOne = map.get(robotId);
                    if (oldOne == null) {
                        List<String> newOne = new ArrayList<>();
                        newOne.add(weixinId);
                        map.put(robotId, newOne);
                    } else {
                        oldOne.add(weixinId);
                        map.put(robotId, oldOne);
                    }
                }
            }
        }
        List<List<String>> result = new ArrayList<>();
        List<String> allWeixinIds = new ArrayList<>();
        allWeixinIds.addAll(weixinIds);
        while (!ListUtils.isEmpty(allWeixinIds)) {
            List<String> oneGroup = new ArrayList<>();
            for (String robotId : robotsIds) {
                List<String> weixinIdsForOne = map.get(robotId);
                Iterator<String> iterator = weixinIdsForOne.iterator();
                while (iterator.hasNext()) {
                    oneGroup.add(iterator.next());
                    iterator.remove();
                    break;
                }
            }
            result.add(oneGroup);
            allWeixinIds.removeAll(oneGroup);
        }
        LOGGER.info("转换List<List<String>>结果" + result.toString());
        return result;
    }

    /**
     * 发送消息
     */
    private void sendNews(List<ESNews> esNewsList, String robotId, String weixinGroupId) {
        if (!ListUtils.isEmpty(esNewsList)) {
            List<String> contents = new ArrayList<>();
            String content = "";
            int i = 1;
            for (ESNews esNews : esNewsList) {
                String contentL = content + i + "." + esNews.getTitle() + esNews.getShortUrl() + "\n";
                if (contentL.length() >= 300) {
                    contents.add(content);
                    content = i + "." + esNews.getTitle() + esNews.getShortUrl() + "\n";
                } else {
                    content = contentL;
                }
                if (i == esNewsList.size()) {
                    contents.add(content);
                }
                i = i + 1;
            }
            if (!ListUtils.isEmpty(contents)) {
                for (String contentToSend : contents) {
                    sendText(contentToSend, robotId, weixinGroupId);
                }
            }
        }
    }

    /**
     * 发送文本消息
     */
    private void sendText(String content, String robotId, String weixinGroupId) {
        SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
        sendTextMessageVO.setContent(content);
        sendTextMessageVO.setAltId(robotId);
        sendTextMessageVO.setGroupId(weixinGroupId);
        sendTextMessageVO.setIp(findIp(weixinGroupId));
        WxGroupSDK.sendTextMessage(sendTextMessageVO);
        LOGGER.info("发送早晚报" + sendTextMessageVO.toString());
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("新增计划式群发")
    @Override
    public void createPushPlan(PushPlanDTO pushPlanDTO) {
        List<Long> classifyIds = pushPlanDTO.getClassifyIds();
        List<PushGroup> pushGroups = buildPushGroups(classifyIds);
        //新增计划
        PushPlan pushPlan = new PushPlan();
        Long partyId=pushPlanDTO.getCreateUser();
        pushPlan.setCreateUser(partyId);
        pushPlan.setUpdateUser(partyId);
        pushPlanDao.insert(pushPlan);
        Long pushPlanId = pushPlan.getId();
        List<PushPlanItemDTO> pushPlanItemDTOS=pushPlanDTO.getPushPlanItemDTOList();
        for (PushPlanItemDTO pushPlanItemDTO : pushPlanItemDTOS) {
            Push push = new Push();
            //填充push基本信息
            BeanUtils.copyProperties(pushPlanItemDTO, push);
            push.setPushPlanId(pushPlanId);
            push.setCreateUser(partyId);
            push.setUpdateUser(partyId);
            push.setPushGroups(pushGroups);
            push.setPushItems(pushPlanItemDTO.getPushItems());
            //创建群发
            createPush(push);
        }

    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("修改计划式群发")
    @Override
    public void updatePushPlan(PushPlanDTO pushPlanDTO) {
        Long planId = pushPlanDTO.getPushPlanId();
        List<Push> pushes = pushDao.getByPushPlanId(planId);
        if (ListUtils.isEmpty(pushes)) {
            return;
        }
        List<Long> classifyIds = pushPlanDTO.getClassifyIds();
        List<PushGroup> pushGroups = buildPushGroups(classifyIds);
        List<Long> oldPushIds = pushes.stream().map(Push::getId).collect(Collectors.toList());
        List<Long> pushIds=new ArrayList<>();
        Long partyId = pushPlanDTO.getUpdateUser();
        PushPlan pushPlan = new PushPlan();
        pushPlan.setId(pushPlanDTO.getPushPlanId());
        pushPlan.setUpdateUser(partyId);
        pushPlanDao.update(pushPlan);
        List<PushPlanItemDTO> pushPlanItemDTOList = pushPlanDTO.getPushPlanItemDTOList();
        for (PushPlanItemDTO pushPlanItemDTO : pushPlanItemDTOList) {
            Push push = new Push();
            BeanUtils.copyProperties(pushPlanItemDTO, push);
            Long pushId=pushPlanItemDTO.getPushId();
            pushIds.add(pushId);
            push.setId(pushId);
            push.setCreateUser(partyId);
            push.setUpdateUser(partyId);
            List<PushGroup> list = new ArrayList<>();
            list.addAll(pushGroups);
            List<PushGroup> pushGroupsOld = pushGroupDao.getListByPushId(pushId);
            if (!ListUtils.isEmpty(pushGroupsOld)) {
                for (PushGroup pushGroup : list) {
                    for (PushGroup pushGroupIn : pushGroupsOld) {
                        if (pushGroup.getBookGroupQrcodeId().equals(pushGroupIn.getBookGroupQrcodeId())
                                && pushGroup.getClassifyId().equals(pushGroupIn.getClassifyId())) {
                            pushGroup.setId(pushGroupIn.getId());
                        }
                    }
                }
            }
            push.setPushGroups(list);
            push.setPushItems(pushPlanItemDTO.getPushItems());
            //没有id是新增
            if (pushId != null) {
                updatePush(push);
            } else {
                push.setPushPlanId(planId);
                createPush(push);
            }
        }
        //删除没有修改的
        for (Long oldPushId : oldPushIds) {
            if (!pushIds.contains(oldPushId)) {
                deletePush(oldPushId, partyId);
            }
        }
    }

    @ParamLog("获取计划式群发")
    @Override
    public PushPlanDTO getPushPlan(Long pushPlanId) {
        PushPlanDTO pushPlanDTO = new PushPlanDTO();
        PushPlan pushPlan = pushPlanDao.getById(pushPlanId);
        pushPlanDTO.setPushPlanId(pushPlan.getId());
        BeanUtils.copyProperties(pushPlan, pushPlanDTO);
        List<Push> pushList = pushDao.getByPushPlanId(pushPlanId);
        if (!ListUtils.isEmpty(pushList)){
            List<PushPlanItemDTO> pushPlanItemDTOList = new ArrayList<>();
            List<Long> pushIds = new ArrayList<>();
            for (Push push : pushList) {
                PushPlanItemDTO pushPlanItemDTO = new PushPlanItemDTO();
                BeanUtils.copyProperties(push, pushPlanItemDTO);
                pushPlanItemDTO.setPushId(push.getId());
                pushIds.add(push.getId());
                pushPlanItemDTOList.add(pushPlanItemDTO);
            }
            List<PushItem> pushItemList = pushItemDao.getListByPushIds(pushIds);
            fillItemAPInfo(pushItemList);
            Map<Long, List<PushItem>> itemMap = pushItemList.stream().collect(Collectors.groupingBy(PushItem::getPushId));
            for (PushPlanItemDTO pushPlanItemDTO : pushPlanItemDTOList) {
                pushPlanItemDTO.setPushItems(itemMap.get(pushPlanItemDTO.getPushId()));
            }
            pushPlanDTO.setPushPlanItemDTOList(pushPlanItemDTOList);
            List<PlanClassifyDTO> planClassifyDTOS = pushGroupDao.getPlanClassifyListByPlanId(pushPlanId);
            if (!ListUtils.isEmpty(planClassifyDTOS)) {
                pushPlanDTO.setClassifyIds(planClassifyDTOS.stream().filter(s -> s.getClassifyId() != null).map(PlanClassifyDTO::getClassifyId).collect(Collectors.toList()));
            }
        }
        return pushPlanDTO;
    }

    /**
     * 填充应用信息
     */
    private void fillItemAPInfo(List<PushItem> pushItemList) {
        if (ListUtils.isEmpty(pushItemList)) {
            return;
        }
        List<Long> appIds = pushItemList.stream().filter(s -> ItemTypeEnum.APP.value.equals(s.getItemType()) && s.getAppId() != null).map(PushItem::getAppId).collect(Collectors.toList());
        List<Long> productIds = pushItemList.stream().filter(s -> ItemTypeEnum.APP.value.equals(s.getItemType()) && s.getProductId() != null).map(PushItem::getProductId).collect(Collectors.toList());
        Map<Long, AppDto> appDtoMap = null;
        Map<Long, ProductDto> productDtoMap = null;
        if (!appIds.isEmpty()) {
            appDtoMap = appConsr.mapByIds(appIds);
        }
        if (!productIds.isEmpty()) {
            productDtoMap = productConsr.getProBasesByIds(productIds);
        }
        for (PushItem pushItem : pushItemList) {
            if (ItemTypeEnum.APP.value.equals(pushItem.getItemType())) {
                Long appId = pushItem.getAppId();
                Long productId = pushItem.getProductId();
                String apName = null;
                String apTypeName = null;
                String apImage = null;
                if (appId != null) {
                    AppDto appDto = appDtoMap.get(appId);
                    apName = appDto.getTitle();
                    apTypeName = appDto.getTypeName();
                    apImage = appDto.getSquareImg();
                }
                if (productId != null) {
                    ProductDto productDto = productDtoMap.get(productId);
                    apName = productDto.getProductName();
                    if (productDto.getProductTypeDto() != null) {
                        apTypeName = productDto.getProductTypeDto().getTypeName();
                    }
                    apImage = productDto.getCoverImg();
                }
                pushItem.setApTypeName(apTypeName);
                pushItem.setApName(apName);
                pushItem.setApImage(apImage);
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("删除计划式群发")
    @Override
    public void deletePushPlan(Long pushPlanId, Long partyId) {
        PushPlan pushPlan = pushPlanDao.getById(pushPlanId);
        if (pushPlan == null) {
            return;
        }
        List<Push> pushList = pushDao.getByPushPlanId(pushPlanId);
        if (ListUtils.isEmpty(pushList)) {
            return;
        }
        //删除计划
        pushPlanDao.deleteById(pushPlanId);
        //循环删除群发
        for (Push push : pushList) {
            deletePush(push.getId(), partyId);
        }
    }

    @ParamLog("获取计划式群发列表")
    @Override
    public PageBeanNew<PushPlanDTO> getPushPlanList(String keyword, String day, Integer weekDay, Integer currentPage, Integer numPerPage, Long partyId) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("keyword", keyword);
        paramMap.put("day", day);
        paramMap.put("weekDay", weekDay);
        paramMap.put("partyId", partyId);
        PageParam pageParam = new PageParam(currentPage, numPerPage);
        PageBeanNew<PushPlanDTO> pageBeanNew = pushPlanDao.listPageNew(pageParam, paramMap, "getPushPlanList");
        List<PushPlanDTO> pushPlanDTOS = pageBeanNew.getRecordList();
        if (!ListUtils.isEmpty(pushPlanDTOS)) {
            List<Long> pushPlanIds = pushPlanDTOS.stream().map(PushPlanDTO::getPushPlanId).collect(Collectors.toList());
            List<Push> pushList = pushDao.getByPushPlanIds(pushPlanIds);
            List<PlanClassifyDTO> planClassifyDTOS = pushGroupDao.getPlanClassifyListByPlanIds(pushPlanIds);
            List<Long> bookGroupIds=planClassifyDTOS.stream().filter(s->s.getBookGroupId()!=null).map(PlanClassifyDTO::getBookGroupId).distinct().collect(Collectors.toList());
            if (!ListUtils.isEmpty(bookGroupIds)) {
                List<BookGroupDTO> bookGroupDTOS = bookGroupDao.getBookBaseInfoByIds(bookGroupIds);
                if (!ListUtils.isEmpty(bookGroupDTOS)){
                    Map<Long,BookGroupDTO> bookGroupDTOMap=new HashMap<>();
                    for (BookGroupDTO bookGroupDTO:bookGroupDTOS){
                        bookGroupDTOMap.put(bookGroupDTO.getId(),bookGroupDTO);
                    }
                    for (PlanClassifyDTO planClassifyDTO : planClassifyDTOS) {
                        BookGroupDTO bookGroupDTO = bookGroupDTOMap.get(planClassifyDTO.getBookGroupId());
                        if (bookGroupDTO!=null){
                            planClassifyDTO.setGroupQrcodeName(bookGroupDTO.getGroupQrcodeName());
                            planClassifyDTO.setBookName(bookGroupDTO.getBookName());
                        }
                    }
                }
            }
            Map<Long,List<PlanClassifyDTO>> planClassifyMap=planClassifyDTOS.stream().collect(Collectors.groupingBy(PlanClassifyDTO::getPushPlanId));
            if (!ListUtils.isEmpty(pushList)) {
                List<Long> pushIds = pushList.stream().map(Push::getId).collect(Collectors.toList());
                Map<Long, List<Push>> pushMap=pushList.stream().collect(Collectors.groupingBy(Push::getPushPlanId));
                List<PushItem> pushItemList = pushItemDao.getListByPushIds(pushIds);
                fillItemAPInfo(pushItemList);
                Map<Long, List<PushItem>> pushItemMap = pushItemList.stream().collect(Collectors.groupingBy(PushItem::getPushId));
                for (PushPlanDTO pushPlanDTO : pushPlanDTOS) {
                    Long pushPlanId=pushPlanDTO.getPushPlanId();
                    List<Push> pushes=pushMap.get(pushPlanId);
                    if (ListUtils.isEmpty(pushes)){
                        continue;
                    }
                    List<PushPlanItemDTO> pushPlanItemDTOList=new ArrayList<>();
                    for (Push push:pushes){
                        PushPlanItemDTO pushPlanItemDTO=new PushPlanItemDTO();
                        List<PushItem> pushItems = pushItemMap.get(push.getId());
                        pushPlanItemDTO.setPushId(push.getId());
                        BeanUtils.copyProperties(push,pushPlanItemDTO);
                        pushPlanItemDTO.setPushItems(pushItems);
                        pushPlanItemDTOList.add(pushPlanItemDTO);
                    }
                    pushPlanDTO.setPushPlanItemDTOList(pushPlanItemDTOList);
                    pushPlanDTO.setPlanClassifyDTOList(planClassifyMap.get(pushPlanId));
                    if (!ListUtils.isEmpty(planClassifyMap.get(pushPlanId))) {
                        List<Long> classifyIds = planClassifyMap.get(pushPlanId).stream().filter(s -> s.getClassifyId() != null).map(PlanClassifyDTO::getClassifyId).collect(Collectors.toList());
                        pushPlanDTO.setClassifyIds(classifyIds);
                    }
                }
            }
        }
        return pageBeanNew;
    }

    @ParamLog("获取计划日历")
    @Override
    public List<String> getPushPlanCalendar(Long partyId) {
        List<String> list = new ArrayList<>();
        List<Push> pushList = pushDao.getPlanPushListPartyId(partyId);
        if (ListUtils.isEmpty(pushList)) {
            return list;
        }
        for (Push push : pushList) {
            Integer pushType = push.getPushType();
            String pushTime = push.getPushTime();
            Date startTime = push.getStartTime();
            Date endTime = push.getEndTime();
            Integer weekDay = push.getWeekDay();
            Date startDay = DateUtils.getYearStart(new Date());
            Date endDay = DateUtils.getYearEnd(new Date());
            if (startTime != null) {
                startDay = startTime;
            }
            if (endTime != null) {
                endDay = endTime;
            }
            //如果是单次发送，直接截取时间
            if (PushTypeEnum.ONE.value.equals(pushType)) {
                if (pushTime.length()>=10){
                    list.add(pushTime.substring(0,10));
                }
            }
            //如果是按照天设置
            if (PushTypeEnum.DAY.value.equals(pushType)) {
                for (Date i = startDay; !i.after(endDay); i = DateUtils.addDay(i, 1)) {
                    list.add(DateUtils.formatDate(i,"yyyy-MM-dd"));
                }
            }
            //如果是按照周设置
            if (PushTypeEnum.WEEK.value.equals(pushType)) {
                if (weekDay == null) {
                    break;
                }
                if (weekDay == 7) {
                    weekDay = 1;
                } else {
                    weekDay = weekDay + 1;
                }
                for (Date i = startDay; !i.after(endDay); i = DateUtils.addDay(i, 1)) {
                    if (weekDay == DateUtils.getWeekIndex(i)) {
                        list.add(DateUtils.formatDate(i, "yyyy-MM-dd"));
                    }
                }
            }
        }
        if (!ListUtils.isEmpty(list)){
            list = list.stream().distinct().sorted().collect(Collectors.toList());
        }
        return list;
    }

    /**
     * 构建关联表
     */
    private List<PushGroup> buildPushGroups(List<Long> classifyIds){
        List<PushGroup> pushGroups = new ArrayList<>();
        for (Long classifyId : classifyIds) {
            ClassifyDTO classifyDTO = bookGroupClassifyDao.getById(classifyId);
            List<Long> qrCodeIds = groupQrcodeDao.getIdsByClassifyId(classifyId);
            if (ListUtils.isEmpty(qrCodeIds)) {
                continue;
            }
            for (Long qrCodeId : qrCodeIds) {
                PushGroup pushGroup = new PushGroup();
                pushGroup.setBookId(classifyDTO.getBookId());
                pushGroup.setClassifyId(classifyId);
                pushGroup.setBookGroupQrcodeId(qrCodeId);
                pushGroups.add(pushGroup);
            }
        }
        return pushGroups;
    }
    /**
     * 填充群发记录
     *
     * @param pushRecordDTOS
     */
    private void fillPushRecordDTOList(List<PushRecordDTO> pushRecordDTOS) {
        if (ListUtils.isEmpty(pushRecordDTOS)) {
            return;
        }
        List<Long> pushIds = pushRecordDTOS.stream().map(PushRecordDTO::getPushId).distinct().collect(Collectors.toList());
        List<PushItem> pushItems = pushItemDao.getAllListByPushIds(pushIds);
        Map<Long, List<PushItem>> pushItemMap = new HashMap<>();
        List<Long> appIds = new ArrayList<>();
        List<Long> productIds = new ArrayList<>();
        Map<Long, AppDto> appDtoMap = new HashMap<>();
        Map<Long, ProductDto> productDtoMap = new HashMap<>();
        fillSomeMap(pushItems, pushItemMap, appIds, productIds);
        if (!ListUtils.isEmpty(appIds)) {
            appDtoMap = appConsr.mapByIds(appIds);
        }
        if (!ListUtils.isEmpty(productIds)) {
            productDtoMap = productConsr.getProBasesByIds(productIds);
        }

        for (PushRecordDTO pushRecordDTO : pushRecordDTOS) {
            List<PushItem> pushItemsForOne = pushItemMap.get(pushRecordDTO.getPushId());
            if (!ListUtils.isEmpty(pushItemsForOne)) {
                pushRecordDTO.setPushItems(pushItemsForOne);
                pushRecordDTO.setItemCount(pushItemsForOne.size());
                pushRecordDTO.setItemTypes(fillAppItemAndGetItemTypes(pushItemsForOne,appDtoMap,productDtoMap));
            }
        }
    }

    /**
     *
     * @param pushItems
     * @param appDtoMap
     * @param productDtoMap
     * @return
     */
    private List<String> fillAppItemAndGetItemTypes(List<PushItem> pushItems, Map<Long, AppDto> appDtoMap, Map<Long, ProductDto> productDtoMap) {
        List<String> itemTypes = new ArrayList<>();
        if (ListUtils.isEmpty(pushItems)){
            return itemTypes;
        }
        for (PushItem pushItem : pushItems) {
            if (ItemTypeEnum.TEXT.value.equals(pushItem.getItemType())) {
                if (!itemTypes.contains(changeToItemTypeName(ItemTypeEnum.TEXT.value))) {
                    itemTypes.add(changeToItemTypeName(ItemTypeEnum.TEXT.value));
                }
            }
            if (ItemTypeEnum.LINK.value.equals(pushItem.getItemType())) {
                if (!itemTypes.contains(changeToItemTypeName(ItemTypeEnum.LINK.value))) {
                    itemTypes.add(changeToItemTypeName(ItemTypeEnum.LINK.value));
                }
            }
            if (ItemTypeEnum.APP.value.equals(pushItem.getItemType())) {
                if (!itemTypes.contains(changeToItemTypeName(ItemTypeEnum.APP.value))) {
                    itemTypes.add(changeToItemTypeName(ItemTypeEnum.APP.value));
                }
                if (pushItem.getAppId() != null) {
                    //应用
                    if (appDtoMap != null) {
                        AppDto appDto = appDtoMap.get(pushItem.getAppId());
                        if (appDto != null) {
                            pushItem.setApName(appDto.getTitle());
                            pushItem.setApTypeName(appDto.getTypeName());
                            pushItem.setApImage(appDto.getSquareImg());
                        }
                    }

                } else if (pushItem.getProductId() != null) {
                    //作品
                    if (productDtoMap != null) {
                        ProductDto productDto = productDtoMap.get(pushItem.getProductId());
                        if (productDto != null) {
                            pushItem.setApName(productDto.getProductName());
                            if (productDto.getProductTypeDto() != null) {
                                pushItem.setApTypeName(productDto.getProductTypeDto().getTypeName());
                            }
                            pushItem.setApImage(productDto.getCoverImg());
                        }
                    }
                }
            }
            if (ItemTypeEnum.IMAGE.value.equals(pushItem.getItemType())) {
                if (!itemTypes.contains(changeToItemTypeName(ItemTypeEnum.IMAGE.value))) {
                    itemTypes.add(changeToItemTypeName(ItemTypeEnum.IMAGE.value));
                }
            }
        }
        return itemTypes;
    }

    /**
     * 是否需要更新群发
     */
    private Boolean isNeedupdatepushSchedule(Push pushOld, Push push) {
        Boolean need = true;
        if (pushOld.getPushType().equals(push.getPushType())) {
            if (pushOld.getPushTime() != null && push.getPushTime() != null) {
                if (pushOld.getPushTime().equals(push.getPushTime())) {
                    if (pushOld.getWeekDay() != null && push.getWeekDay() != null) {
                        if (pushOld.getWeekDay().equals(push.getWeekDay())) {
                            need = false;
                        }
                    } else if (pushOld.getWeekDay() == null && push.getWeekDay() == null) {
                        need = false;
                    }
                }
            } else if (pushOld.getPushTime() == null && push.getPushTime() == null) {
                if (pushOld.getWeekDay() != null && push.getWeekDay() != null) {
                    if (pushOld.getWeekDay().equals(push.getWeekDay())) {
                        need = false;
                    }
                } else if (pushOld.getWeekDay() == null && push.getWeekDay() == null) {
                    need = false;
                }
            }
        }
        return need;
    }

    /**
     * 填充集合相关字段
     */
    @ParamLog("填充集合相关字段")
    private void fillPushDTOList(List<PushDTO> pushDTOS) {
        if (ListUtils.isEmpty(pushDTOS)) {
            return;
        }
        List<Long> pushIds = pushDTOS.stream().map(PushDTO::getId).collect(Collectors.toList());
        List<PushItem> pushItems = pushItemDao.getListByPushIds(pushIds);
        Map<Long, List<PushItem>> pushItemMap = new HashMap<>();
        List<Long> appIds = new ArrayList<>();
        List<Long> productIds = new ArrayList<>();
        Map<Long, AppDto> appDtoMap = new HashMap<>();
        Map<Long, ProductDto> productDtoMap = new HashMap<>();
        fillSomeMap(pushItems, pushItemMap, appIds, productIds);
        if (!ListUtils.isEmpty(appIds)) {
            appDtoMap = appConsr.mapByIds(appIds);
        }
        if (!ListUtils.isEmpty(productIds)) {
            productDtoMap = productConsr.getProBasesByIds(productIds);
        }
        for (PushDTO pushDTO : pushDTOS) {
            List<PushItem> pushItemsForOne = pushItemMap.get(pushDTO.getId());
            if (!ListUtils.isEmpty(pushItemsForOne)) {
                pushDTO.setPushItems(pushItemsForOne);
                pushDTO.setItemCount(pushItemsForOne.size());
                pushDTO.setItemTypes(fillAppItemAndGetItemTypes(pushItemsForOne, appDtoMap, productDtoMap));
            }
        }
    }

    /**
     * 处理一些map等
     *
     * @param pushItems
     * @param pushItemMap
     * @param appIds
     * @param productIds
     */
    private void fillSomeMap(List<PushItem> pushItems, Map<Long, List<PushItem>> pushItemMap, List<Long> appIds, List<Long> productIds) {
        if (pushItems == null || pushItemMap == null || appIds == null || productIds == null) {
            return;
        }
        for (PushItem pushItem : pushItems) {
            Long pushId = pushItem.getPushId();
            List<PushItem> pushItemsForOneOld = pushItemMap.get(pushId);
            if (pushItemsForOneOld != null) {
                pushItemsForOneOld.add(pushItem);
                pushItemMap.put(pushId, pushItemsForOneOld);
            } else {
                List<PushItem> pushItemsNew = new ArrayList<>();
                pushItemsNew.add(pushItem);
                pushItemMap.put(pushId, pushItemsNew);
            }
            if (ItemTypeEnum.APP.value.equals(pushItem.getItemType())) {
                if (pushItem.getAppId() != null) {
                    if (!appIds.contains(pushItem.getAppId())) {
                        appIds.add(pushItem.getAppId());
                    }
                } else if (pushItem.getProductId() != null) {
                    if (!productIds.contains(pushItem.getProductId())) {
                        productIds.add(pushItem.getProductId());
                    }
                }
            }
        }
    }

    /**
     * 转换
     *
     * @param value
     * @return
     */
    private String changeToItemTypeName(Integer value) {
        String name = "";
        if (ItemTypeEnum.TEXT.value.equals(value)) {
            name = "文本";
        }
        if (ItemTypeEnum.LINK.value.equals(value)) {
            name = "链接";
        }
        if (ItemTypeEnum.APP.value.equals(value)) {
            name = "应用";
        }
        if (ItemTypeEnum.IMAGE.value.equals(value)) {
            name = "图片";
        }
        return name;
    }

    /**
     * 每周发送
     */
    @ParamLog("每周发送")
    private void pushWeek(Push push) {
        String cron;
        String pushTime=push.getPushTime();
        try {
            int hour = Integer.parseInt(pushTime.substring(0,2));
            int minute = Integer.parseInt(pushTime.substring(3,5));
            int second = Integer.parseInt(pushTime.substring(6,8));
            String weekS="";
            int weekDay=push.getWeekDay();
            if (weekDay==1){
                weekS="MON";
            }
            if (weekDay==2){
                weekS="TUE";
            }
            if (weekDay==3){
                weekS="WED";
            }
            if (weekDay==4){
                weekS="THU";
            }
            if (weekDay==5){
                weekS="FRI";
            }
            if (weekDay==6){
                weekS="SAT";
            }
            if (weekDay==7){
                weekS="SUN";
            }
            cron = second + " " + minute + " " + hour + " ? * "+weekS;
        } catch (Exception e) {
            throw new BookBizException(BookBizException.ERROR, "时间格式错误");
        }
        ScheduleJob scheduleJob = new ScheduleJob();
        scheduleJob.setJobGroup("book");
        scheduleJob.setJobName(PUSH_SCHEDULE_PRE + push.getId());
        scheduleJob.setCronExpression(cron);
        CallBackParam callBackParam = new CallBackParam();
        Map<String, Object> paramMap = new HashMap<>();
        //定时任务调用时用到的参数
        paramMap.put("pushId", push.getId());
        callBackParam.setParamMap(paramMap);
        callBackParam.setMethodName("sendGroupMessage");
        callBackParam.setBeanName("pushService");
        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, "定时任务设置失败");
        }
    }

    /**
     * 每天发送
     */
    @ParamLog("每天发送")
    private void pushDay(Push push) {
        String cron;
        String pushTime=push.getPushTime();
        try {
            int hour = Integer.parseInt(pushTime.substring(0,2));
            int minute = Integer.parseInt(pushTime.substring(3,5));
            int second = Integer.parseInt(pushTime.substring(6,8));
            cron = second + " " + minute + " " + hour + " * * ?";
        } catch (Exception e) {
            throw new BookBizException(BookBizException.ERROR, "时间格式错误");
        }
        ScheduleJob scheduleJob = new ScheduleJob();
        scheduleJob.setJobGroup("book");
        scheduleJob.setJobName(PUSH_SCHEDULE_PRE + push.getId());
        scheduleJob.setCronExpression(cron);
        CallBackParam callBackParam = new CallBackParam();
        Map<String, Object> paramMap = new HashMap<>();
        //定时任务调用时用到的参数
        paramMap.put("pushId", push.getId());
        callBackParam.setParamMap(paramMap);
        callBackParam.setMethodName("sendGroupMessage");
        callBackParam.setBeanName("pushService");
        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, "定时任务设置失败");
        }
    }

    /**
     * 单次发送
     */
    @ParamLog("单次发送")
    private void pushOne(Push push) {
        Long second = (DateUtils.StringToDateTime(push.getPushTime()).getTime() - System.currentTimeMillis()) / 1000;
        ScheduleJob scheduleJob = new ScheduleJob();
        scheduleJob.setJobGroup("book");
        scheduleJob.setStartTimeFormat("ss");
        scheduleJob.setStartTime(second.intValue());
        scheduleJob.setRepeatCount(0);
        scheduleJob.setIntervalTime(0);
        scheduleJob.setIntervalTimeFormat("dd");
        scheduleJob.setJobName( PUSH_SCHEDULE_PRE + push.getId());
        CallBackParam callBackParam = new CallBackParam();
        Map<String, Object> paramMap = new HashMap<>();
        //定时任务调用时用到的参数
        paramMap.put("pushId", push.getId());
        callBackParam.setParamMap(paramMap);
        callBackParam.setMethodName("sendGroupMessage");
        callBackParam.setBeanName("pushService");
        Map<String, Object> scheduleMap = new HashMap<>();
        scheduleMap.put("scheduleJob", scheduleJob);
        scheduleMap.put("callBackParam", callBackParam);
        try {
            scheduleService.addSimpleJob(scheduleMap);
        } catch (Exception e) {
            LOGGER.error("设置定时任务失败" + e.getMessage(), e);
            throw new BookBizException(BookBizException.ERROR, "定时任务设置失败");
        }
    }


    /**
     * 填充消息项模型
     */
    @ParamLog("填充消息项模型")
    private void fillPushItemParam(Push push) {
        int i=0;
        for (PushItem pushItem : push.getPushItems()) {
            pushItem.setPushId(push.getId());
            pushItem.setSeqNum(++i);
            pushItem.setCreateUser(push.getCreateUser());
            pushItem.setUpdateUser(push.getCreateUser());
        }
    }

    /**
     * 设置群发关联表字段
     */
    @ParamLog("设置群发关联表字段")
    private void fillPushGroupParam(Push push) {
        for (PushGroup pushGroup : push.getPushGroups()) {
            pushGroup.setCreateUser(push.getCreateUser());
            pushGroup.setUpdateUser(push.getCreateUser());
            pushGroup.setPushId(push.getId());
        }
    }

    @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;
    }
}
