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

import com.alibaba.fastjson.JSON;
import com.pcloud.appcenter.app.dto.AppTypeDto;
import com.pcloud.appcenter.app.service.AppTypeService;
import com.pcloud.book.advertising.biz.AdvertisingSpaceBiz;
import com.pcloud.book.advertising.biz.BmBiz;
import com.pcloud.book.advertising.check.AdvertisingCheck;
import com.pcloud.book.advertising.check.AdvertisingPlanCheck;
import com.pcloud.book.advertising.dao.AdvertisingAdviserPermissionDao;
import com.pcloud.book.advertising.dao.AdvertisingAgentPermissionDao;
import com.pcloud.book.advertising.dao.AdvertisingBrowseRecordDao;
import com.pcloud.book.advertising.dao.AdvertisingClickRecordDao;
import com.pcloud.book.advertising.dao.AdvertisingCpaDao;
import com.pcloud.book.advertising.dao.AdvertisingDetailFileDao;
import com.pcloud.book.advertising.dao.AdvertisingDistributionBookDao;
import com.pcloud.book.advertising.dao.AdvertisingExposureRecordDao;
import com.pcloud.book.advertising.dao.AdvertisingIncomeDailyDao;
import com.pcloud.book.advertising.dao.AdvertisingMasterDao;
import com.pcloud.book.advertising.dao.AdvertisingPilotRecordDao;
import com.pcloud.book.advertising.dao.AdvertisingPlanDao;
import com.pcloud.book.advertising.dao.AdvertisingPlanGroupDao;
import com.pcloud.book.advertising.dao.AdvertisingPutDao;
import com.pcloud.book.advertising.dao.AdvertisingSettlementMethodDao;
import com.pcloud.book.advertising.dao.AdvertisingSpaceDao;
import com.pcloud.book.advertising.dao.AdvertisingWechatMsgDao;
import com.pcloud.book.advertising.dao.BmOptionDao;
import com.pcloud.book.advertising.dto.AdvertisingCpaDTO;
import com.pcloud.book.advertising.dto.AdvertisingDistributionDTO;
import com.pcloud.book.advertising.dto.AdvertisingMasterDTO;
import com.pcloud.book.advertising.dto.AdvertisingSettlementMethodDTO;
import com.pcloud.book.advertising.dto.AdvertisingSpaceDTO;
import com.pcloud.book.advertising.dto.AdvertisingWechatMsgDTO;
import com.pcloud.book.advertising.dto.BookAndChannelDTO;
import com.pcloud.book.advertising.dto.QrcodeAdvertisingSpaceCountDTO;
import com.pcloud.book.advertising.dto.WechatGroupClickUserDTO;
import com.pcloud.book.advertising.dto.WeixinClassifyInfoDTO;
import com.pcloud.book.advertising.entity.AdvertisingAdviserPermission;
import com.pcloud.book.advertising.entity.AdvertisingAgentPermission;
import com.pcloud.book.advertising.entity.AdvertisingBrowseRecord;
import com.pcloud.book.advertising.entity.AdvertisingClickRecord;
import com.pcloud.book.advertising.entity.AdvertisingCpa;
import com.pcloud.book.advertising.entity.AdvertisingDetailFile;
import com.pcloud.book.advertising.entity.AdvertisingDistributionBook;
import com.pcloud.book.advertising.entity.AdvertisingExposureRecord;
import com.pcloud.book.advertising.entity.AdvertisingMaster;
import com.pcloud.book.advertising.entity.AdvertisingPilotRecord;
import com.pcloud.book.advertising.entity.AdvertisingPlan;
import com.pcloud.book.advertising.entity.AdvertisingPlanGroup;
import com.pcloud.book.advertising.entity.AdvertisingPut;
import com.pcloud.book.advertising.entity.AdvertisingSettlementMethod;
import com.pcloud.book.advertising.entity.AdvertisingSpace;
import com.pcloud.book.advertising.entity.AdvertisingWechatMsg;
import com.pcloud.book.advertising.entity.DistributionWechatGroup;
import com.pcloud.book.advertising.enums.AdDetailModeEnum;
import com.pcloud.book.advertising.enums.AdPositionDetailEnum;
import com.pcloud.book.advertising.enums.AdPositionEnum;
import com.pcloud.book.advertising.enums.AdPositionModeEnum;
import com.pcloud.book.advertising.enums.BookJumpTypeEnum;
import com.pcloud.book.advertising.enums.PutTypeEnum;
import com.pcloud.book.advertising.enums.SettlementMethodEnum;
import com.pcloud.book.base.exception.BookBizException;
import com.pcloud.book.book.biz.BookBiz;
import com.pcloud.book.book.dao.BookDao;
import com.pcloud.book.book.dto.BookDto;
import com.pcloud.book.book.entity.BookAdviser;
import com.pcloud.book.book.set.BookSet;
import com.pcloud.book.consumer.common.ExportConsr;
import com.pcloud.book.consumer.convert.ConvertConsr;
import com.pcloud.book.consumer.label.LabelConsr;
import com.pcloud.book.consumer.reader.ReaderConsr;
import com.pcloud.book.consumer.user.AdviserConsr;
import com.pcloud.book.consumer.user.AgentConsr;
import com.pcloud.book.consumer.user.ChannelConsr;
import com.pcloud.book.consumer.wechatgroup.WechatGroupConsr;
import com.pcloud.book.group.biz.WeixinQrcodeBiz;
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.AdGroupQrcodeDTO;
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.GroupQrcodeInfo4Advertising;
import com.pcloud.book.group.dto.GroupQrcodeInfoDTO;
import com.pcloud.book.group.entity.BookGroup;
import com.pcloud.book.group.entity.GroupQrcode;
import com.pcloud.book.group.vo.GroupQrcodeVO;
import com.pcloud.book.util.common.CommonUtils;
import com.pcloud.channelcenter.qrcode.service.QrcodeSceneService;
import com.pcloud.common.constant.UrlConstant;
import com.pcloud.common.core.aspect.ParamLog;
import com.pcloud.common.core.biz.ConvertQueueBiz;
import com.pcloud.common.core.constant.ConvertEnum;
import com.pcloud.common.core.dto.ConvertQueueDto;
import com.pcloud.common.exceptions.BizException;
import com.pcloud.common.page.PageBean;
import com.pcloud.common.page.PageBeanNew;
import com.pcloud.common.page.PageParam;
import com.pcloud.common.utils.DateNewUtils;
import com.pcloud.common.utils.DateUtils;
import com.pcloud.common.utils.ListUtils;
import com.pcloud.common.utils.ResponseHandleUtil;
import com.pcloud.common.utils.aliyun.OssUtils;
import com.pcloud.common.utils.httpclient.UrlUtils;
import com.pcloud.common.utils.string.StringUtil;
import com.pcloud.convert.file.entity.FileUploadInfo;
import com.pcloud.facade.quartz.entity.CallBackParam;
import com.pcloud.facade.quartz.entity.ScheduleJob;
import com.pcloud.facade.quartz.service.ScheduleService;
import com.pcloud.readercenter.wechat.entity.WechatUser;
import com.pcloud.resourcecenter.product.service.ProductService;
import com.pcloud.wechatgroup.message.dto.GroupChatCountDTO;
import com.sdk.wxgroup.SendArticleMessageVO;
import com.sdk.wxgroup.SendPicMessageVO;
import com.sdk.wxgroup.SendTextMessageVO;
import com.sdk.wxgroup.WxGroupSDK;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
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.math.BigDecimal;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.pcloud.book.util.common.ThreadPoolUtils.SEND_MESSAGE_THREAD_POOL;

/**
 * Description 广告位业务逻辑层接口实现类
 * @author PENG
 * @date 2019/2/28
 */
@Component("advertisingSpaceBiz")
public class AdvertisingSpaceBizImpl implements AdvertisingSpaceBiz {
    private static final Logger LOGGER = LoggerFactory.getLogger(AdvertisingSpaceBizImpl.class);

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

    @Autowired
    private AdvertisingSpaceDao advertisingSpaceDao;
    @Autowired
    private AdviserConsr adviserConsr;
    @Autowired
    private AdvertisingDistributionBookDao advertisingDistributionBookDao;
    @Autowired
    private BookBiz bookBiz;
    @Autowired
    private AdvertisingAgentPermissionDao advertisingAgentPermissionDao;
    @Autowired
    private BookDao bookDao;
    @Autowired
    private BookSet bookSet;
    @Autowired
    private AdvertisingAdviserPermissionDao advertisingAdviserPermissionDao;
    @Autowired
    private QrcodeSceneService qrcodeSceneService;
    @Autowired
    private ChannelConsr channelConsr;
    @Autowired
    private AdvertisingExposureRecordDao advertisingExposureRecordDao;
    @Autowired
    private AdvertisingClickRecordDao advertisingClickRecordDao;
    @Autowired
    private AdvertisingMasterDao advertisingMasterDao;
    @Autowired
    private AdvertisingSettlementMethodDao advertisingSettlementMethodDao;
    @Autowired
    private AdvertisingIncomeDailyDao advertisingIncomeDailyDao;
    @Autowired
    private BookGroupClassifyDao bookGroupClassifyDao;
    @Autowired
    private GroupQrcodeDao groupQrcodeDao;
    @Autowired
    private AgentConsr agentConsr;
    @Autowired
    private WechatGroupConsr wechatGroupConsr;
    @Autowired
    private ExportConsr exportConsr;
    @Autowired
    private ProductService productService;
    @Autowired
    private AppTypeService appTypeService;
    @Autowired
    private BookGroupDao bookGroupDao;
    @Autowired
    private ReaderConsr readerConsr;
    @Autowired
    private LabelConsr labelConsr;
    @Autowired
    private ConvertQueueBiz convertQueueBiz;
    @Autowired
    private AdvertisingPlanCheck advertisingPlanCheck;
    @Autowired
    private AdvertisingPlanDao advertisingPlanDao;
    @Autowired
    private AdvertisingPlanGroupDao advertisingPlanGroupDao;
    @Autowired
    private AdvertisingPutDao advertisingPutDao;
    @Autowired
    private ScheduleService scheduleService;
    @Autowired
    private BmBiz bmBiz;
    @Autowired
    private AdvertisingPilotRecordDao advertisingPilotRecordDao;
    @Autowired
    private AdvertisingBrowseRecordDao advertisingBrowseRecordDao;
    @Autowired
    private WeixinQrcodeBiz weixinQrcodeBiz;
    @Autowired
    private AdvertisingDistributionBookDao distributionBookDao;
    @Autowired
    private ConvertConsr convertConsr;
    @Autowired
    private AdvertisingDetailFileDao detailFileDao;
    @Autowired
    private AdvertisingWechatMsgDao advertisingWechatMsgDao;
    @Autowired
    private AdvertisingCheck advertisingCheck;
    @Autowired
    private AdvertisingCpaDao advertisingCpaDao;
    @Autowired
    private BmOptionDao bmOptionDao;



    private static final String ADVERTISING_PUT_PLAN_SCHEDULE_PRE = "ADVERTISING_PUT_PALN_";

    /**
     * 创建广告位
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @ParamLog("创建广告位")
    public Long create(AdvertisingSpace advertisingSpace) throws BizException {
        // 校验参数
        advertisingCheck.checkParamBeforeCreate(advertisingSpace);
        String settlementMethod = advertisingSpace.getSettlementMethod();
        advertisingSpace.setIsOpen(true);
        advertisingSpaceDao.insert(advertisingSpace);
        Long adId = advertisingSpace.getId();
        AdvertisingSettlementMethod method = new AdvertisingSettlementMethod();
        method.setMasterId(advertisingSpace.getMasterId());
        method.setAdId(adId);
        method.setSettlementMethod(settlementMethod);
        method.setPriceEachTime(advertisingSpace.getPriceEachTime());
        advertisingSettlementMethodDao.insert(method);
        return adId;
    }

    /**
     * 新增广告详情文件
     * @param cpaId
     * @param fileList
     */
    private void addAdvertisingDetailFile(Long cpaId, List<AdvertisingDetailFile> fileList) {
        if (ListUtils.isEmpty(fileList)) {
            return;
        }
        fileList.forEach(detailFile -> detailFile.setCpaId(cpaId));
        detailFileDao.insert(fileList);
    }

    /**
     * cpa广告详情参数配置
     * @param advertisingCpa
     * @return
     */
    private AdvertisingCpa equipCPA(AdvertisingCpa advertisingCpa) {
        if (AdDetailModeEnum.AUDIO.code.equals(advertisingCpa.getDetailMode()) || AdDetailModeEnum.VIDEO.code.equals(advertisingCpa.getDetailMode())) {
            List<AdvertisingDetailFile> fileList = advertisingCpa.getAdvertisingDetailFileList();
            if (ListUtils.isEmpty(fileList)) {
                throw new BookBizException(BookBizException.PARAM_IS_ERROR, "请上传文件");
            }
            for (AdvertisingDetailFile detailFile : fileList) {
                String fileId = detailFile.getFileId();
                String fileUrl = detailFile.getFileUrl();
                Long seconds = detailFile.getFilePilotSecond();
                String title = detailFile.getTitle();
                //总时长
                BigDecimal duration = null;
                if (!StringUtil.isEmpty(fileId)) {
                    //获取转码路径
                    FileUploadInfo fileUploadInfo = convertConsr.getByFileId(fileId);
                    if (null == fileUploadInfo) {
                        throw new BookBizException(BookBizException.PARAM_IS_ERROR, "文件路径为空！");
                    }
                    fileUrl = fileUploadInfo.getUrl();
                    duration = fileUploadInfo.getDuration();
                    detailFile.setFileUrl(fileUrl);
                    if (null != duration) {
                        detailFile.setFileDuration(duration);
                    }
                }
               /* if (StringUtil.isEmpty(fileUrl) && !StringUtil.isEmpty(fileId)) {
                    //转码通知
                    sendConvertFileQueue(fileId, 0l);
                    advertisingSpace.setAdDetailFileConvertState(0);
                    return advertisingSpace;
                }*/
                if (null != duration && null != seconds && BigDecimal.valueOf(seconds.doubleValue()).compareTo(duration) >= 0) {
                    throw new BookBizException(BookBizException.PARAM_IS_ERROR, "资源" + title + "时长" + duration + "s,试听/试看时长超过该时长，请重新设置");
                }
                if (!StringUtil.isEmpty(fileUrl)) {//剪切文件
                    detailFile.setFileConvertState(1);
                    if (null == seconds) {//试播秒数为null,播放全部
                        detailFile.setFilePilotUrl(fileUrl);
                        detailFile.setFilePilotSecond(0L);
                    } else if (seconds > 0) {
                        String cutUrl = getCutUrl(fileUrl, title, 0d, seconds.doubleValue(), seconds.doubleValue());
                        detailFile.setFilePilotUrl(cutUrl);
                    }
                }
            }
        }
        return advertisingCpa;
    }

    /**
     * 向文件转换中心发送通知
     */
    public void sendConvertFileQueue(String fileId, Long fromId) throws BizException {
        LOGGER.info("【文件转换(消)】向文件转换中心发送通知,<START>.[fileId]=" + fileId + ",[fromId]=" + fromId);
        if (StringUtil.isEmpty(fileId)) {
            return;
        }
        ConvertQueueDto convertQueueDto = new ConvertQueueDto();
        convertQueueDto.setFileId(fileId);
        convertQueueDto.setFromType(ConvertEnum.BOOK.value);
        convertQueueDto.setFromId(fromId);
        try {
            convertQueueBiz.sendConvertFileQueue(convertQueueDto);
        } catch (Exception e) {
            LOGGER.error("【文件转换(消)】向文件转换中心发送通知.[sendConvertFileQueue]:" + e.getMessage(), e);
        }
    }

    /**
     * 修改广告位
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @ParamLog(value = "修改广告位", isAfterReturn = false)
    public void update(AdvertisingSpace advertisingSpace) throws BizException {
        // 校验参数
        advertisingCheck.checkParamBeforeCreate(advertisingSpace);
        if (null == advertisingSpace.getId()) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        AdvertisingSpace space = advertisingSpaceDao.getById(advertisingSpace.getId());
        if (null == space) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告位不存在或已删除！");
        }
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("adId", advertisingSpace.getId());
        Integer bookNum = (Integer) advertisingDistributionBookDao.getBy(paramMap, "countByAdId");
        // 已投放,不是cpa模式
        if (null != bookNum && bookNum > 0 && !SettlementMethodEnum.CPA.code.equals(advertisingSpace.getSettlementMethod())) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "已投放书刊的广告位不能修改！");
        }
        if (!AdPositionEnum.WECHAT_GROUP_MSG.positionCode.equals(advertisingSpace.getAdPosition()) && null == advertisingSpace.getBookJumpType()) {
            //书刊广告默认跳到链接
            advertisingSpace.setBookJumpType(BookJumpTypeEnum.LINK.value);
        }
        advertisingSpaceDao.update(advertisingSpace);
        // 修改结算方式
        AdvertisingSettlementMethodDTO methodDTO = advertisingSettlementMethodDao.getByMasterIdAndAdId(space.getMasterId(), space.getId());
        AdvertisingSettlementMethod method = new AdvertisingSettlementMethod();
        method.setMasterId(advertisingSpace.getMasterId());
        method.setSettlementMethod(advertisingSpace.getSettlementMethod());
        method.setPriceEachTime(advertisingSpace.getPriceEachTime());
        if (null == methodDTO || null == methodDTO.getId()) {
            advertisingSettlementMethodDao.insert(method);
        } else {
            method.setId(methodDTO.getId());
            advertisingSettlementMethodDao.update(method);
        }
    }

    /**
     * 停用广告位
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @ParamLog(value = "停用广告位", isAfterReturn = false)
    public void stop(AdvertisingSpace advertisingSpace) throws BizException {
        if (null == advertisingSpace.getId()) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        AdvertisingSpace space = advertisingSpaceDao.getById(advertisingSpace.getId());
        if (null == space) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告位不存在或已删除！");
        }
        if (!space.getIsOpen()) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告位已停用！");
        }
        advertisingSpace.setIsOpen(false);
        advertisingSpaceDao.update(advertisingSpace);
    }

    /**
     * 获取广告位信息
     */
    @Override
    @ParamLog(value = "获取广告位信息")
    public AdvertisingSpaceDTO get(Long adId) throws BizException {
        AdvertisingSpaceDTO spaceDTO = advertisingSpaceDao.getDTOById(adId);
        if (null != spaceDTO) {
            AdvertisingMaster master = advertisingMasterDao.getById(spaceDTO.getMasterId());
            if (null != master) {
                spaceDTO.setMasterName(master.getMasterName());
            }
            AdvertisingSettlementMethodDTO methodDTO = advertisingSettlementMethodDao.getByMasterIdAndAdId(spaceDTO.getMasterId(), spaceDTO.getId());
            if (null != methodDTO) {
                spaceDTO.setSettlementMethod(methodDTO.getSettlementMethod());
                spaceDTO.setSettlementMethodName(SettlementMethodEnum.getNameByCode(methodDTO.getSettlementMethod()));
                spaceDTO.setPriceEachTime(methodDTO.getPriceEachTime());
               //CPA方式填充报名表单,多个上传文件
                if (SettlementMethodEnum.CPA.code.equals(methodDTO.getSettlementMethod())) {
                    if (null != spaceDTO.getCpaId()) {
                        fillCPAContent(spaceDTO);
                    }
                }
            }
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("adId", adId);
            Integer bookNum = (Integer) advertisingDistributionBookDao.getBy(paramMap, "countByAdId");
            spaceDTO.setBookNum(bookNum);
            // 填充广告位置名称
            if (AdPositionEnum.WECHAT_GROUP_MSG.positionCode.equals(spaceDTO.getAdPosition())) {
                fillAdpositionName4WechatGroupAd(spaceDTO);
            } else {
                fillAdpositionName4CommonAd(spaceDTO);
            }
        }
        return spaceDTO;
    }

    /**
     * 填充cpa内容
     */
    private AdvertisingSpaceDTO fillCPAContent(AdvertisingSpaceDTO spaceDTO) {
        spaceDTO.setAdvertisingBmOptionList(bmBiz.getByCPAId(spaceDTO.getCpaId()));
        List<AdvertisingDetailFile> fileList = detailFileDao.getByCPAId(spaceDTO.getCpaId());
        if (!ListUtils.isEmpty(fileList)) {
            spaceDTO.setAdvertisingDetailFileList(fileList);
        }
        AdvertisingCpa advertisingCpa = advertisingCpaDao.getById(spaceDTO.getCpaId());
        spaceDTO.setAdDetailMode(advertisingCpa.getDetailMode());
        spaceDTO.setAdDetailCoverPic(advertisingCpa.getCoverPic());
        spaceDTO.setAdDetailWelfarePic(advertisingCpa.getWelfarePic());
        spaceDTO.setAdDetailInfo(advertisingCpa.getDetailInfo());
        spaceDTO.setAdFormMobileCheck(advertisingCpa.getFormMobileCheck());
        spaceDTO.setAdFormRemark(advertisingCpa.getFormRemark());
        spaceDTO.setAdFormButton(advertisingCpa.getFormButton());
        spaceDTO.setAdFormLink(advertisingCpa.getFormLink());
        return spaceDTO;
    }

    /**
     * 投放书刊
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @ParamLog(value = "投放书刊", isAfterReturn = false)
    public void distributeBook(AdvertisingDistributionBook book) throws BizException {
        if (null == book.getAdId() || ListUtils.isEmpty(book.getBooks()) || null == book.getIsBookGroup()) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        AdvertisingSpace advertisingSpace = advertisingSpaceDao.getById(book.getAdId());
        if (null == advertisingSpace || !advertisingSpace.getIsOpen()) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告位不存在或已关闭！");
        }
        AdvertisingSpace space = new AdvertisingSpace();
        space.setId(book.getAdId());
        advertisingSpaceDao.update(space);
        boolean paramError = book.getBooks().stream().anyMatch(bookAdviser -> null == bookAdviser.getBookId()
                || null == bookAdviser.getChannelId() || null == bookAdviser.getAdviserId());
        if (paramError) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        // 校验书刊是否已投广告位
        Map<String, Object> paramMap = new HashMap<>();
        if (book.getIsBookGroup()) {
            // 社群书
            for (BookAdviser bookAdviser : book.getBooks()) {
                paramMap.put("adPositionDetail", advertisingSpace.getAdPositionDetail());
                paramMap.put("bookId", bookAdviser.getBookId());
                paramMap.put("channelId", bookAdviser.getChannelId());
                Integer existCount = (Integer) advertisingDistributionBookDao.getBy(paramMap, "checkBookGroupAdExist");
                if (null != existCount && existCount > 0) {
                    BookDto bookDto = bookBiz.getBaseById(bookAdviser.getBookId());
                    throw new BookBizException(BookBizException.PARAM_IS_ERROR, StringUtil.addBracket(bookDto.getBookName()) + "等社群书"
                            + AdPositionDetailEnum.getNameByCode(advertisingSpace.getAdPositionDetail()) + "已投放广告位！");
                }
            }
        } else {
            // 非社群书
            paramMap.put("adPositionDetail", advertisingSpace.getAdPositionDetail());
            paramMap.put("adPositionMode", advertisingSpace.getAdPositionMode());
            for (BookAdviser bookAdviser : book.getBooks()) {
                paramMap.put("bookId", bookAdviser.getBookId());
                paramMap.put("channelId", bookAdviser.getChannelId());
                Integer existCount = (Integer) advertisingDistributionBookDao.getBy(paramMap, "checkBookAdExist");
                if (null != existCount && existCount > 0) {
                    BookDto bookDto = bookBiz.getBaseById(bookAdviser.getBookId());
                    throw new BookBizException(BookBizException.PARAM_IS_ERROR, StringUtil.addBracket(bookDto.getBookName()) + "等书已投放该广告位！");
                }
            }
        }
        //关闭广告位的出版社
        List<Long> closeAgentIds = this.getCloseAgentId();
        List<AdvertisingDistributionBook> list = new ArrayList<>();
        for (BookAdviser bookAdviser : book.getBooks()) {
            AdvertisingDistributionBook distributionBook = new AdvertisingDistributionBook();
            distributionBook.setAdId(book.getAdId());
            distributionBook.setBookId(bookAdviser.getBookId());
            distributionBook.setChannelId(bookAdviser.getChannelId());
            distributionBook.setAdviserId(bookAdviser.getAdviserId());
            Long agentId = adviserConsr.getAgentIdByAdviser(bookAdviser.getAdviserId());
            distributionBook.setAgentId(agentId);
            distributionBook.setIsBookGroup(book.getIsBookGroup());
            if (!ListUtils.isEmpty(closeAgentIds) && closeAgentIds.contains(agentId)){
                BookDto bookDto = bookBiz.getBaseById(bookAdviser.getBookId());
                throw new BookBizException(BookBizException.PARAM_IS_ERROR, StringUtil.addBracket(bookDto.getBookName()) + "等书所属出版社未开放广告投放权限！");
            }
            list.add(distributionBook);
        }
        advertisingDistributionBookDao.batchInsert(list);
    }

    /**
     * 投放微信群
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @ParamLog(value = "投放微信群", isAfterReturn = false)
    public void distributeWechatGroup(AdvertisingDistributionBook book, Boolean isPlan) throws BizException {
        List<DistributionWechatGroup> bookGroups = book.getGroups();
        if (null == book.getAdId() || ListUtils.isEmpty(bookGroups)) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        AdvertisingSpace advertisingSpace = advertisingSpaceDao.getById(book.getAdId());
        if (null == advertisingSpace) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告位不存在！");
        }
        if (!AdPositionEnum.WECHAT_GROUP_MSG.positionCode.equals(advertisingSpace.getAdPosition())) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "该广告位不能投放到微信群！");
        }
        AdvertisingSpace space = new AdvertisingSpace();
        space.setId(book.getAdId());
        advertisingSpaceDao.update(space);
        boolean paramError = bookGroups.stream().anyMatch(group -> null == group.getBookId()
                || null == group.getChannelId() || null == group.getAdviserId() || null == group.getClassifyId() || null == group.getQrcodeId());
        if (paramError) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        List<AdvertisingDistributionBook> list = new ArrayList<>();
        Map<Long, Long> agentIdMap = new HashMap<>();
        //过滤无机器人的群
        for (int i = 0; i < bookGroups.size() && null != bookGroups; i++) {
            DistributionWechatGroup wechatGroup = bookGroups.get(i);
            if (null != wechatGroup) {
                //机器人不存在，不投放广告
                GroupQrcode groupQrcode = groupQrcodeDao.getById(wechatGroup.getQrcodeId());
                if (null != groupQrcode) {//取群里活跃的小号发送20191216
                    String altId = wechatGroupConsr.getRobotIdByGroupId(groupQrcode.getWeixinGroupId());
                    if (StringUtil.isEmpty(altId)) {
                        LOGGER.info("机器人不在微信群，广告投放失败" + wechatGroup);
                        bookGroups.remove(wechatGroup);
                    }
                }
            }
        }
        if (ListUtils.isEmpty(bookGroups)){
            LOGGER.info("微信群为空，广告投放失败");
            return;
        }
        for (DistributionWechatGroup group : bookGroups) {
            AdvertisingDistributionBook distributionBook = new AdvertisingDistributionBook();
            distributionBook.setAdId(book.getAdId());
            distributionBook.setBookId(group.getBookId());
            distributionBook.setChannelId(group.getChannelId());
            distributionBook.setAdviserId(group.getAdviserId());
            distributionBook.setClassifyId(group.getClassifyId());
            distributionBook.setQrcodeId(group.getQrcodeId());
            Long agentId;
            if (!agentIdMap.containsKey(group.getAdviserId())) {
                agentId = adviserConsr.getAgentIdByAdviser(group.getAdviserId());
                agentIdMap.put(group.getAdviserId(), agentId);
            } else {
                agentId = agentIdMap.get(group.getAdviserId());
            }
            distributionBook.setAgentId(agentId);
            distributionBook.setIsBookGroup(true);
            list.add(distributionBook);
        }
        advertisingDistributionBookDao.batchInsert(list);
        //发送微信群消息
        SEND_MESSAGE_THREAD_POOL.execute(() -> {
            LOGGER.info("广告位投放微信群" + book);
            sendWeixinAdMessage(advertisingSpace, bookGroups);
        });
        // 根据微信群人数埋点
        List<Long> qrcodeIds = bookGroups.stream().map(DistributionWechatGroup::getQrcodeId).collect(Collectors.toList());
        Map<Long, GroupQrcodeInfoDTO> map = groupQrcodeDao.listQrcodeInfoByIds(qrcodeIds);
        if (!MapUtils.isEmpty(map)) {
            List<AdvertisingExposureRecord> recordList = new ArrayList<>();
            for (DistributionWechatGroup group : bookGroups) {
                Long qrcodeId = group.getQrcodeId();
                if (map.containsKey(qrcodeId)) {
                    GroupQrcodeInfoDTO dto = map.get(qrcodeId);
                    Integer userNumber = dto.getUserNumber();
                    AdvertisingExposureRecord record = new AdvertisingExposureRecord();
                    record.setAdId(book.getAdId());
                    record.setBookId(group.getBookId());
                    record.setIsBookGroup(true);
                    record.setAdviserId(group.getAdviserId());
                    record.setAgentId(agentIdMap.get(group.getAdviserId()));
                    record.setChannelId(group.getChannelId());
                    record.setFromType("WECHAT_GROUP");
                    record.setFromId(qrcodeId);
                    record.setCount(userNumber.longValue());
                    recordList.add(record);
                }
            }
            if (!ListUtils.isEmpty(recordList)) {
                advertisingExposureRecordDao.insert(recordList);
            }
        }
    }

    /**
     * 发送微信群消息
     * @param advertisingSpace
     * @param groups
     */
    private void sendWeixinAdMessage(AdvertisingSpace advertisingSpace, List<DistributionWechatGroup> groups){
        //结算方式
        AdvertisingSettlementMethodDTO methodDTO = advertisingSettlementMethodDao.getByMasterIdAndAdId(advertisingSpace.getMasterId(), advertisingSpace.getId());
        for (DistributionWechatGroup group : groups) {
            // 获取群信息
            GroupQrcode groupQrcode = groupQrcodeDao.getById(group.getQrcodeId());
            String groupId = groupQrcode.getWeixinGroupId();
            // 获取机器人微信号
            String altId = wechatGroupConsr.getRobotIdByGroupId(groupId);
            if (StringUtil.isEmpty(altId)) {
                continue;
            }
            String positionMode = advertisingSpace.getAdPositionMode();
            String adTitle = advertisingSpace.getAdTitle();
            String adPic = advertisingSpace.getAdPic();
            Long adId = advertisingSpace.getId();
            if (!StringUtil.isEmpty(positionMode) && (!StringUtil.isEmpty(adTitle) || !StringUtil.isEmpty(adPic))) {//旧流程
                sendMessage(adTitle, adId, adPic, advertisingSpace.getAdLink(), advertisingSpace.getAdSlogan(), positionMode, methodDTO.getSettlementMethod(), group, groupId, altId, false);
            } else {//配置多条信息
                List<AdvertisingWechatMsg> msgList = advertisingWechatMsgDao.getByAdId(advertisingSpace.getId());
                if (ListUtils.isEmpty(msgList)) {
                    return;
                }
                for (AdvertisingWechatMsg wechatMsg : msgList) {
                    sendMessage(wechatMsg.getAdTitle(), adId, wechatMsg.getAdPic(), wechatMsg.getAdLink(), wechatMsg.getAdSlogan(), wechatMsg.getAdPositionMode(), methodDTO.getSettlementMethod(), group, groupId, altId, true);
                }
            }
        }
    }

    /**
     * 微信群发送消息
     * @param adTitle
     * @param adId
     * @param adPic
     * @param advertisingLink
     * @param slogan
     * @param positionMode
     * @param settlementMethod
     * @param group
     * @param groupId
     * @param altId
     */
    private void sendMessage(String adTitle, Long adId, String adPic, String advertisingLink, String slogan, String positionMode,
                             String settlementMethod, DistributionWechatGroup group, String groupId, String altId, Boolean isNew) {
        ClassifyDTO classifyDTO = bookGroupClassifyDao.getById(group.getClassifyId());
        if (AdPositionModeEnum.TEXT_AND_LINK.modeCode.equals(positionMode)) {
            //多文字+多链接
            List<String> linkList = getLinkList(adTitle, isNew);//提取文本链接
            if (!ListUtils.isEmpty(linkList)) {
                if (isNew){
                    for (String link : linkList) {
                        String hex = link.replaceAll(UrlConstant.OWN_SHORT_URL, "");
                        String originLink = UrlUtils.getUrl4Own(hex);
                        String adLink = jointLink(originLink, classifyDTO.getBookGroupId(), group.getClassifyId(), group.getQrcodeId(), adId, settlementMethod);
                        adLink = UrlUtils.getShortUrl4Own(wechatGroupLinkPrefix + "/link?url=" + URLEncoder.encode(adLink));
                        adTitle = adTitle.replace(link, adLink);
                    }
                }else {
                    //例如：[色]123<link>http://www.baidu.com</link>1111111[闭嘴]
                    adTitle = adTitle.replaceAll("</link><link>", "</link> <link>");//连续两个链接之间要有分隔
                    for (String link : linkList) {
                        String originLink = link.replaceAll("<link>", "").replace("</link>", "");
                        String adLink = jointLink(originLink, classifyDTO.getBookGroupId(), group.getClassifyId(), group.getQrcodeId(), adId, settlementMethod);
                        adLink = UrlUtils.getShortUrl4Own(wechatGroupLinkPrefix + "/link?url=" + URLEncoder.encode(adLink));
                        adTitle = adTitle.replace(link, adLink);
                    }
                }
            }
            SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
            sendTextMessageVO.setWxGroupId(groupId);
            sendTextMessageVO.setAltId(altId);
            sendTextMessageVO.setContent(adTitle);
            sendTextMessageVO.setIp(findIp(groupId));
            LOGGER.info("发送微信消息-文字链接" + sendTextMessageVO.toString());
            // TODO sdk.message
                wechatGroupConsr.sendMessage(JSON.toJSONString(sendTextMessageVO));
        } else if (AdPositionModeEnum.NEWS_SHARE.modeCode.equals(positionMode)) {
            String adLink = jointLink(advertisingLink, classifyDTO.getBookGroupId(), group.getClassifyId(), group.getQrcodeId(), adId, settlementMethod);
            SendArticleMessageVO sendArticleMessageVO = new SendArticleMessageVO();
            sendArticleMessageVO.setAltId(altId);
            sendArticleMessageVO.setTitle(adTitle);
            sendArticleMessageVO.setDescription(slogan);
            sendArticleMessageVO.setWxGroupId(groupId);
            sendArticleMessageVO.setLinkUrl(adLink);
            sendArticleMessageVO.setPicUrl(adPic);
            sendArticleMessageVO.setIp(findIp(groupId));
            LOGGER.info("发送微信消息-图文分享" + sendArticleMessageVO.toString());
            // TODO sdk.message
                wechatGroupConsr.sendMessage(JSON.toJSONString(sendArticleMessageVO));
        } else if (AdPositionModeEnum.JUST_PIC.modeCode.equals(positionMode)) {
            SendPicMessageVO sendPicMessageVO = new SendPicMessageVO();
            sendPicMessageVO.setAltId(altId);
            sendPicMessageVO.setWxGroupId(groupId);
            sendPicMessageVO.setPicUrl(adPic);
            sendPicMessageVO.setIp(findIp(groupId));
            LOGGER.info("发送微信消息-纯图片" + sendPicMessageVO.toString());
            // TODO sdk.message
                wechatGroupConsr.sendMessage(JSON.toJSONString(sendPicMessageVO));
        }
        try {
            Thread.sleep(5000);
            LOGGER.info("发送微信消息-线程休眠5秒");
        } catch (InterruptedException e) {
            LOGGER.error("线程休眠失败" + e.getMessage());
        }
    }

    /**
     * 提取文本中链接
     * @param title
     * @return
     */
    private List<String> getLinkList(String title, Boolean isNew) {
        List<String> list = new ArrayList<>();
        if (isNew) {
            String regex = "(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
            Matcher matcher = Pattern.compile(regex, Pattern.DOTALL).matcher(title);
            while (matcher.find()) {
                String url = matcher.group();
                if (url.startsWith(UrlConstant.OWN_SHORT_URL)) {
                    list.add(matcher.group());
                }
            }
        } else {
            Pattern pa = Pattern.compile("<link>.*?</link>");
            Matcher ma = pa.matcher(title);
            while (ma.find()) {
                list.add(ma.group());
            }
        }
        return list;
    }

    /**
     * 链接拼加参数
     */
    private String jointLink(String originLink, Long bookGroupId, Long classifyId, Long qrcodeId, Long adId, String settlementMethod) {
        String adLink = "";
        if (!StringUtil.isEmpty(originLink)) {
            if (originLink.contains("?")) {
                adLink = originLink + "&";
            } else {
                adLink = originLink + "?";
            }
            adLink = adLink + "book_group_id=" + bookGroupId + "&classify_id=" + classifyId + "&qrcode_id=" + qrcodeId
                    + "&ad_id=" + adId + "&settlement_method=" + settlementMethod;
        }
        return adLink;
    }

    /**
     * 获取出版社书刊权限
     */
    @Override
    @ParamLog("获取出版社书刊权限")
    public Boolean getAgentPermission(Long agentId) throws BizException {
        AdvertisingAgentPermission permission = advertisingAgentPermissionDao.getByAgentId(agentId);
        return null == permission ? true : permission.getIsOpen();
    }

    /**
     * 设置出版社书刊权限
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @ParamLog(value = "设置出版社书刊权限", isAfterReturn = false)
    public void setAgentPermission(AdvertisingAgentPermission agentPermission) throws BizException {
        if (null == agentPermission.getAgentId() || null == agentPermission.getIsOpen()) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        AdvertisingAgentPermission permission = advertisingAgentPermissionDao.getByAgentId(agentPermission.getAgentId());
        if (null == permission) {
            advertisingAgentPermissionDao.insert(agentPermission);
        } else {
            agentPermission.setId(permission.getId());
            advertisingAgentPermissionDao.update(agentPermission);
        }
    }

    /**
     * 设置编辑书刊权限
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @ParamLog(value = "设置编辑书刊权限", isAfterReturn = false)
    public void setAdviserPermission(AdvertisingAdviserPermission adviserPermission, Long adviserId) throws BizException {
        if (null == adviserPermission.getAdviserId() || null == adviserPermission.getIsOpen()
                || (!adviserPermission.getIsBatchOperation() && (null == adviserPermission.getBookId() || null == adviserPermission.getChannelId()))) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("adviserId", adviserId);
        if (!adviserPermission.getIsBatchOperation()) {
            paramMap.put("bookId", adviserPermission.getBookId());
            paramMap.put("channelId", adviserPermission.getChannelId());
            AdvertisingAdviserPermission permission = (AdvertisingAdviserPermission) advertisingAdviserPermissionDao.getBy(paramMap, "getByAdviserId");
            if (null == permission) {
                advertisingAdviserPermissionDao.insert(adviserPermission);
            } else {
                adviserPermission.setId(permission.getId());
                advertisingAdviserPermissionDao.update(adviserPermission);
            }
        } else {
            if (!ListUtils.isEmpty(adviserPermission.getBookAndChannelDTOS())) {
                //一个一个更新
                for (BookAndChannelDTO bookAndChannelDTO : adviserPermission.getBookAndChannelDTOS()) {
                    if (bookAndChannelDTO != null && bookAndChannelDTO.getBookId() != null && bookAndChannelDTO.getChannelId() != null) {
                        paramMap.put("bookId", bookAndChannelDTO.getBookId());
                        paramMap.put("channelId", bookAndChannelDTO.getChannelId());
                        AdvertisingAdviserPermission permission = (AdvertisingAdviserPermission) advertisingAdviserPermissionDao.getBy(paramMap, "getByAdviserId");
                        if (null == permission) {
                            adviserPermission.setBookId(bookAndChannelDTO.getBookId());
                            adviserPermission.setChannelId(bookAndChannelDTO.getChannelId());
                            adviserPermission.setAdviserId(adviserId);
                            advertisingAdviserPermissionDao.insert(adviserPermission);
                        } else {
                            adviserPermission.setId(permission.getId());
                            adviserPermission.setChannelId(null);
                            adviserPermission.setBookId(null);
                            adviserPermission.setAdviserId(null);
                            advertisingAdviserPermissionDao.update(adviserPermission);
                        }
                    }
                }
            } else {
                // 删除编辑所有数据
                advertisingAdviserPermissionDao.deleteByAdviserId(adviserPermission.getAdviserId());
                List<BookDto> bookList = bookDao.listBookIdAndChannelId4Adviser(paramMap);
                if (!ListUtils.isEmpty(bookList)) {
                    List<AdvertisingAdviserPermission> list = new ArrayList<>();
                    for (BookDto bookDto : bookList) {
                        AdvertisingAdviserPermission permission = new AdvertisingAdviserPermission();
                        permission.setAdviserId(adviserPermission.getAdviserId());
                        permission.setIsOpen(adviserPermission.getIsOpen());
                        permission.setBookId(bookDto.getBookId());
                        permission.setChannelId(bookDto.getChannelId());
                        list.add(permission);
                    }
                    advertisingAdviserPermissionDao.batchInsert(list);
                }
            }
        }
    }

    /**
     * 编辑端获取书刊列表
     */
    @Override
    public PageBean listBook4Adviser(Map<String, Object> paramMap, PageParam pageParam, Long adviserId) throws BizException {
        Long agentId = adviserConsr.getAgentIdByAdviser(adviserId);
        paramMap.put("agentId", agentId);
        PageBean pageBean = bookDao.listPage(pageParam, paramMap, "listBook4Adviser");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        // 填充渠道基础信息
        bookSet.setChannelInfoList(pageBean.getRecordList());
        return pageBean;
    }

    /**
     * 平台端获取某编辑书刊列表
     */
    @Override
    public PageBean listBook4Platform(Map<String, Object> paramMap, PageParam pageParam, Long adviserId) throws BizException {
        PageBean pageBean = bookDao.listPage(pageParam, paramMap, "listBook4Platform");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        return pageBean;
    }

    /**
     * 平台端获取社群书微信群列表
     */
    @Override
    public PageBean listGroupQrcode4Platform(Map<String, Object> paramMap, PageParam pageParam) throws BizException {
        PageBean pageBean = bookGroupClassifyDao.listPage(pageParam, paramMap, "listGroupQrcode4Platform");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        List<Object> objs = pageBean.getRecordList();
        Object objectlist = (Object) objs;
        List<GroupQrcodeVO> groupQrcodeVOList = (List<GroupQrcodeVO>) objectlist;
        List<Long> labelIds = new ArrayList<>();
        List<Long> proLabels = groupQrcodeVOList.stream().filter(s -> s.getProLabelId() != null).map(GroupQrcodeVO::getProLabelId).distinct().collect(Collectors.toList());
        List<Long> depLabels = groupQrcodeVOList.stream().filter(s -> s.getDepLabelId() != null).map(GroupQrcodeVO::getDepLabelId).distinct().collect(Collectors.toList());
        List<Long> purLabels = groupQrcodeVOList.stream().filter(s -> s.getPurLabelId() != null).map(GroupQrcodeVO::getPurLabelId).distinct().collect(Collectors.toList());
        List<Long> qrcodeIds = groupQrcodeVOList.stream().filter(s -> s.getGroupQrcodeId() != null).map(GroupQrcodeVO::getGroupQrcodeId).distinct().collect(Collectors.toList());
        Map<Long, Integer> weekActiveMap = wechatGroupConsr.get7DayActiveUserCountByQrcode(qrcodeIds);
        if (!ListUtils.isEmpty(proLabels)) {
            labelIds.addAll(proLabels);
        }
        if (!ListUtils.isEmpty(depLabels)) {
            labelIds.addAll(depLabels);
        }
        if (!ListUtils.isEmpty(purLabels)) {
            labelIds.addAll(purLabels);
        }
        if (!ListUtils.isEmpty(labelIds)) {
            Map<Long, String> labelMap = labelConsr.getLabelName(labelIds);
            if (!MapUtils.isEmpty(labelMap)) {
                for (Object object : objs) {
                    GroupQrcodeVO qrcodeVO = (GroupQrcodeVO) object;
                    if (null != qrcodeVO.getProLabelId() && labelMap.containsKey(qrcodeVO.getProLabelId())) {
                        qrcodeVO.setProLabelName(labelMap.get(qrcodeVO.getProLabelId()));
                    }
                    if (null != qrcodeVO.getDepLabelId() && labelMap.containsKey(qrcodeVO.getDepLabelId())) {
                        qrcodeVO.setDepLabelName(labelMap.get(qrcodeVO.getDepLabelId()));
                    }
                    if (null != qrcodeVO.getPurLabelId() && labelMap.containsKey(qrcodeVO.getPurLabelId())) {
                        qrcodeVO.setPurLabelName(labelMap.get(qrcodeVO.getPurLabelId()));
                    }
                }
            }
        }
        for (Object object : objs) {
            GroupQrcodeVO qrcodeVO = (GroupQrcodeVO) object;
            //填充7天活跃人数
            qrcodeVO.setWeekActiveCount(weekActiveMap.get(qrcodeVO.getGroupQrcodeId()));
        }
        return pageBean;
    }

    /**
     * 获取广告位投放书刊列表
     */
    @Override
    public PageBean distributionBookList(Long adId, String name, PageParam pageParam) throws BizException {
        AdvertisingSpace space = advertisingSpaceDao.getById(adId);
        if (null == space) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告位不存在或已删除！");
        }
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("adId", adId);
        paramMap.put("name", StringUtil.isEmpty(name) ? null : name);
        PageBean pageBean = bookDao.listPage(pageParam, paramMap, "listBook4AdvertisingSpace");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        // 设置编辑名称
        bookSet.setAdviserName4BookDto(pageBean.getRecordList());
        // 设置出版名称
        bookSet.setAgentName4BookDto(pageBean.getRecordList());
        for (Object object : pageBean.getRecordList()) {
            BookDto bookDto = (BookDto) object;
            // 曝光量
            Long exposureNum = getExposureNumByAdId(adId, bookDto.getBookId(), bookDto.getAdviserId(), bookDto.getChannelId(), null);
            bookDto.setExposureNum(exposureNum);
            // 点击量
            Long clickNum = getClickNumByAdId(adId, bookDto.getBookId(), bookDto.getAdviserId(), bookDto.getChannelId(), null);
            bookDto.setClickNum(clickNum);
            BigDecimal clickRate = (null == bookDto.getExposureNum() || bookDto.getExposureNum().equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(bookDto.getClickNum().doubleValue() / bookDto.getExposureNum()).setScale(4, BigDecimal.ROUND_HALF_UP);
            bookDto.setClickRate(clickRate);
            BigDecimal income = getIncomeByAdId(adId, bookDto.getBookId(), bookDto.getChannelId(), null);
            bookDto.setTotalIncome(income);
            //报名量报名率
            Long registerNum = bmBiz.getRegisterCountByAdId(adId,null,bookDto.getBookId(),bookDto.getAdviserId(),bookDto.getChannelId());
            BigDecimal registerRate = (null == clickNum || clickNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            bookDto.setRegisterNum(registerNum);
            bookDto.setRegisterRate(registerRate);
        }
        return pageBean;
    }

    /**
     * 获取广告位投放微信群列表
     */
    @Override
    public PageBean distributionWechatGroupList(Long adId, String name, PageParam pageParam) throws BizException {
        AdvertisingSpace space = advertisingSpaceDao.getById(adId);
        if (null == space) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告位不存在或已删除！");
        }
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("adId", adId);
        paramMap.put("name", StringUtil.isEmpty(name) ? null : name);
        PageBean pageBean = advertisingDistributionBookDao.listPage(pageParam, paramMap, "distributionWechatGroupList");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        List<Long> adviserIds = new ArrayList<>();
        List<Long> agentIds = new ArrayList<>();
        pageBean.getRecordList().forEach(obj -> {
            adviserIds.add(((AdGroupQrcodeDTO) obj).getAdviserId());
            agentIds.add(((AdGroupQrcodeDTO) obj).getAgentId());
        });
        Map<Long, String> adviserNameMap = adviserConsr.getNames(new ArrayList<>(new HashSet<>(adviserIds)));
        Map<Long, String> agentNameMap = agentConsr.getNames(new ArrayList<>(new HashSet<>(agentIds)));
        for (Object object : pageBean.getRecordList()) {
            AdGroupQrcodeDTO qrcodeDTO = (AdGroupQrcodeDTO) object;
            if (!MapUtils.isEmpty(adviserNameMap) && adviserNameMap.containsKey(qrcodeDTO.getAdviserId())) {
                qrcodeDTO.setAdviserName(adviserNameMap.get(qrcodeDTO.getAdviserId()));
            }
            if (!MapUtils.isEmpty(agentNameMap) && agentNameMap.containsKey(qrcodeDTO.getAgentId())) {
                qrcodeDTO.setAgentName(agentNameMap.get(qrcodeDTO.getAgentId()));
            }
            // 曝光量
            Long exposureNum = getExposureNumByGroupQrcodeId(qrcodeDTO.getGroupQrcodeId(), adId, null);
            qrcodeDTO.setExposureNum(exposureNum);
            // 点击量
            Long clickNum = getClickNumByGroupQrcodeId(qrcodeDTO.getGroupQrcodeId(), adId, null);
            qrcodeDTO.setClickNum(clickNum);
            BigDecimal clickRate = (null == qrcodeDTO.getExposureNum() || qrcodeDTO.getExposureNum().equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(qrcodeDTO.getClickNum().doubleValue() / qrcodeDTO.getExposureNum()).setScale(4, BigDecimal.ROUND_HALF_UP);
            qrcodeDTO.setClickRate(clickRate);
            BigDecimal income = getIncomeByGroupQrcodeId(qrcodeDTO.getGroupQrcodeId(), adId, null);
            qrcodeDTO.setTotalIncome(income);
            // 点击读者量
            Long clickUserNum = getClickUserNumByGroupQrcodeId(qrcodeDTO.getGroupQrcodeId(), adId, null);
            qrcodeDTO.setClickUserNum(clickUserNum);
            //报名量报名率
            Long registerNum = bmBiz.getRegisterCountByAdIdQrcodeId(adId,null,qrcodeDTO.getGroupQrcodeId());
            BigDecimal registerRate = (null == clickNum || clickNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            qrcodeDTO.setRegisterNum(registerNum);
            qrcodeDTO.setRegisterRate(registerRate);
        }
        return pageBean;
    }

    /**
     * 获取广告位投放书刊列表
     */
    @Override
    public PageBean advertisingBookList(String name, PageParam pageParam) throws BizException {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("name", StringUtil.isEmpty(name) ? null : name);
        PageBean pageBean = bookDao.listPage(pageParam, paramMap, "advertisingBookList");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        // 设置编辑名称
        bookSet.setAdviserName4BookDto(pageBean.getRecordList());
        // 设置出版名称
        bookSet.setAgentName4BookDto(pageBean.getRecordList());
        for (Object object : pageBean.getRecordList()) {
            BookDto bookDto = (BookDto) object;
            // 曝光量
            Long exposureNum = getExposureNumByBookId(bookDto.getBookId(), bookDto.getAdviserId(), bookDto.getChannelId(), null, null);
            bookDto.setExposureNum(exposureNum);
            // 点击量
            Long clickNum = getClickNumByBookId(bookDto.getBookId(), bookDto.getAdviserId(), bookDto.getChannelId(), null, null);
            bookDto.setClickNum(clickNum);
            BigDecimal clickRate = (null == bookDto.getExposureNum() || bookDto.getExposureNum().equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(bookDto.getClickNum().doubleValue() / bookDto.getExposureNum()).setScale(4, BigDecimal.ROUND_HALF_UP);
            bookDto.setClickRate(clickRate);
            BigDecimal income = getIncomeByBookId(bookDto.getBookId(), bookDto.getChannelId());
            bookDto.setTotalIncome(income);
            // 点击读者量
            Long clickUserNum = advertisingClickRecordDao.getClickUserNumByBookId(null, bookDto.getBookId(), bookDto.getAdviserId(), bookDto.getChannelId(), null);
            bookDto.setClickUserNum(clickUserNum);
            //报名量报名率
            Long registerNum = bmBiz.getRegisterCountByBookId(bookDto.getBookId(), bookDto.getAdviserId(), bookDto.getChannelId(), null, null);
            BigDecimal registerRate = (null == clickNum || clickNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            bookDto.setRegisterNum(registerNum);
            bookDto.setRegisterRate(registerRate);
            //完课量 完课率
            Long pilotFinishUserNum = advertisingPilotRecordDao.getPilotUserNumByBookId(bookDto.getBookId(), bookDto.getAdviserId(), bookDto.getChannelId(), null, null);
            BigDecimal pilotFinishRate = (null == clickUserNum || clickUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(pilotFinishUserNum.doubleValue() / clickUserNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            bookDto.setPilotFinishUserNum(pilotFinishUserNum);
            bookDto.setPilotFinishRate(pilotFinishRate);
            //行为数据（平均浏览时长 跳出量 跳出率）
            Long browseTimes = advertisingBrowseRecordDao.getBrowseTimesByBook(bookDto.getBookId(), bookDto.getAdviserId(), bookDto.getChannelId(), null, null);
            Long browseUserNum = advertisingBrowseRecordDao.getBrowseUserCountByBook(bookDto.getBookId(), bookDto.getAdviserId(), bookDto.getChannelId(), null, null);
            BigDecimal avgBrowseTimes = (browseUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(browseTimes.doubleValue() / browseUserNum / 1000).setScale(2, BigDecimal.ROUND_HALF_UP);
            bookDto.setAvgBrowseTimes(avgBrowseTimes);
            Long outNum = 0L;
            BigDecimal outRate = BigDecimal.ZERO;
            if (null != clickNum && clickNum.compareTo(registerNum) > 0) {
                outNum = clickNum - registerNum;
                outRate = (clickNum.equals(0L)) ? BigDecimal.ZERO :
                        new BigDecimal(outNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            }
            bookDto.setOutNum(outNum);
            bookDto.setOutRate(outRate);
        }
        return pageBean;
    }

    /**
     * 获取广告位投放微信群列表
     */
    @Override
    public PageBean advertisingWechatGroupList(String name, PageParam pageParam) throws BizException {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("name", StringUtil.isEmpty(name) ? null : name);
        PageBean pageBean = bookGroupClassifyDao.listPage(pageParam, paramMap, "advertisingWechatGroupList");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        Object objs = (Object) pageBean.getRecordList();
        List<AdGroupQrcodeDTO> dtoList = (List<AdGroupQrcodeDTO>) objs;
        List<Long> qrcodeIds = dtoList.stream().filter(s -> s != null).map(AdGroupQrcodeDTO::getGroupQrcodeId).distinct().collect(Collectors.toList());
        if (ListUtils.isEmpty(qrcodeIds)) {
            return new PageBean(pageParam.getPageNum(), pageParam.getNumPerPage(), 0, new ArrayList<>());
        }
        Map<Long, GroupChatCountDTO> chatCountDTOMap = wechatGroupConsr.getChatCountByGroup(qrcodeIds, null);

        for (Object object : pageBean.getRecordList()) {
            AdGroupQrcodeDTO qrcodeDTO = (AdGroupQrcodeDTO) object;
            // 曝光量
            Long exposureNum = getExposureNumByGroupQrcodeId(qrcodeDTO.getGroupQrcodeId(), null, null);
            qrcodeDTO.setExposureNum(exposureNum);
            // 点击量
            Long clickNum = getClickNumByGroupQrcodeId(qrcodeDTO.getGroupQrcodeId(), null, null);
            qrcodeDTO.setClickNum(clickNum);
            BigDecimal clickRate = (null == qrcodeDTO.getExposureNum() || qrcodeDTO.getExposureNum().equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(qrcodeDTO.getClickNum().doubleValue() / qrcodeDTO.getExposureNum()).setScale(4, BigDecimal.ROUND_HALF_UP);
            qrcodeDTO.setClickRate(clickRate);
            BigDecimal income = getIncomeByGroupQrcodeId(qrcodeDTO.getGroupQrcodeId(), null, null);
            qrcodeDTO.setTotalIncome(income);
            // 点击读者量
            Long clickUserNum = getClickUserNumByGroupQrcodeId(qrcodeDTO.getGroupQrcodeId(), null, null);
            qrcodeDTO.setClickUserNum(clickUserNum);
            //报名量报名率
            Long registerNum = bmBiz.getRegisterCountByAdIdQrcodeId(null, null, qrcodeDTO.getGroupQrcodeId());
            BigDecimal registerRate = (null == clickNum || clickNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            qrcodeDTO.setRegisterNum(registerNum);
            qrcodeDTO.setRegisterRate(registerRate);
            //完课量 完课率
            Long pilotFinishUserNum = advertisingPilotRecordDao.getPilotUserNumByQrcode(qrcodeDTO.getGroupQrcodeId(), null, null);
            BigDecimal pilotFinishRate = (null == clickUserNum || clickUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(pilotFinishUserNum.doubleValue() / clickUserNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            qrcodeDTO.setPilotFinishUserNum(pilotFinishUserNum);
            qrcodeDTO.setPilotFinishRate(pilotFinishRate);
            //群活跃度
            if (!MapUtils.isEmpty(chatCountDTOMap)) {
                GroupChatCountDTO chatCountDTO = chatCountDTOMap.get(qrcodeDTO.getGroupQrcodeId());
                if (null != chatCountDTO) {
                    qrcodeDTO.setActiveCount(chatCountDTO.getChatCount());
                } else {
                    qrcodeDTO.setActiveCount(0);
                }
            } else {
                qrcodeDTO.setActiveCount(0);
            }
            //行为数据（平均浏览时长 跳出量 跳出率）
            Long browseTimes = advertisingBrowseRecordDao.getBrowseTimesByQrcode(qrcodeDTO.getGroupQrcodeId(), null, null);
            Long browseUserNum = advertisingBrowseRecordDao.getBrowseUserCountByQrcode(qrcodeDTO.getGroupQrcodeId(), null, null);
            BigDecimal avgBrowseTimes = (browseUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(browseTimes.doubleValue() / browseUserNum / 1000).setScale(2, BigDecimal.ROUND_HALF_UP);
            qrcodeDTO.setAvgBrowseTimes(avgBrowseTimes);
            Long outNum = 0L;
            BigDecimal outRate = BigDecimal.ZERO;
            if (null != clickNum && clickNum.compareTo(registerNum) > 0) {
                outNum = clickNum - registerNum;
                outRate = (clickNum.equals(0L)) ? BigDecimal.ZERO :
                        new BigDecimal(outNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            }
            qrcodeDTO.setOutNum(outNum);
            qrcodeDTO.setOutRate(outRate);
        }
        return pageBean;
    }

    /**
     * 获取广告位投放微信群曝光量前几位
     */
    @Override
    public List<AdGroupQrcodeDTO> advertisingWechatGroupRankTop() throws BizException {
        List<AdGroupQrcodeDTO> list = advertisingExposureRecordDao.advertisingWechatGroupRankTop();
        return list;
    }

    /**
     * 获取广告位曝光量
     */
    private Long getExposureNumByAdId(Long adId, Long bookId, Long adviserId, Long channelId, String statisMonth) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("adId", adId);
        paramMap.put("bookId", bookId);
        paramMap.put("adviserId", adviserId);
        paramMap.put("channelId", channelId);
        paramMap.put("statisMonth", statisMonth);
        Long exposureNum = (Long) advertisingExposureRecordDao.getBy(paramMap, "getExposureNum");
        return null == exposureNum ? 0L : exposureNum;
    }

    /**
     * 获取书刊广告位曝光量
     */
    private Long getExposureNumByBookId(Long bookId, Long adviserId, Long channelId, Long adId, String statisMonth) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("bookId", bookId);
        paramMap.put("adviserId", adviserId);
        paramMap.put("channelId", channelId);
        paramMap.put("adId", adId);
        paramMap.put("statisMonth", statisMonth);
        Long exposureNum = (Long) advertisingExposureRecordDao.getBy(paramMap, "getExposureNumByBookId");
        return null == exposureNum ? 0L : exposureNum;
    }

    /**
     * 获取微信群广告位曝光量
     */
    private Long getExposureNumByGroupQrcodeId(Long qrcodeId, Long adId, String statisMonth) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("qrcodeId", qrcodeId);
        paramMap.put("adId", adId);
        paramMap.put("statisMonth", statisMonth);
        Long exposureNum = (Long) advertisingExposureRecordDao.getBy(paramMap, "getExposureNumByGroupQrcodeId");
        return null == exposureNum ? 0L : exposureNum;
    }

    /**
     * 获取广告位点击量
     */
    private Long getClickNumByAdId(Long adId, Long bookId, Long adviserId, Long channelId, String statisMonth) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("adId", adId);
        paramMap.put("bookId", bookId);
        paramMap.put("adviserId", adviserId);
        paramMap.put("channelId", channelId);
        paramMap.put("statisMonth", statisMonth);
        Long clickNum = (Long) advertisingClickRecordDao.getBy(paramMap, "getClickNum");
        return null == clickNum ? 0L : clickNum;
    }

    /**
     * 获取书刊广告位点击量
     */
    private Long getClickNumByBookId(Long bookId, Long adviserId, Long channelId, Long adId, String statisMonth) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("bookId", bookId);
        paramMap.put("adviserId", adviserId);
        paramMap.put("channelId", channelId);
        paramMap.put("adId", adId);
        paramMap.put("statisMonth", statisMonth);
        Long clickNum = (Long) advertisingClickRecordDao.getBy(paramMap, "getClickNumByBookId");
        return null == clickNum ? 0L : clickNum;
    }

    /**
     * 获取微信群广告位点击量
     */
    private Long getClickNumByGroupQrcodeId(Long qrcodeId, Long adId, String statisMonth) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("qrcodeId", qrcodeId);
        paramMap.put("adId", adId);
        paramMap.put("statisMonth", statisMonth);
        Long clickNum = (Long) advertisingClickRecordDao.getBy(paramMap, "getClickNumByGroupQrcodeId");
        return null == clickNum ? 0L : clickNum;
    }

    /**
     * 获取书刊广告位点击读者量（废弃，读者不走授权，读者信息全为空，没办法聚合，点击量就是读者量）
     */
    private Long getClickUserNumByGroupQrcodeId(Long qrcodeId, Long adId, String statisMonth) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("qrcodeId", qrcodeId);
        paramMap.put("adId", adId);
        paramMap.put("statisMonth", statisMonth);
        Long clickUserNum = (Long) advertisingClickRecordDao.getBy(paramMap, "getClickUserNumByGroupQrcodeId");
        return null == clickUserNum ? 0L : clickUserNum;
    }

    /**
     * 获取书刊广告位曝光量
     */
    private Long getExposureNumByAdId(Long adId) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("adId", adId);
        Long exposureNum = (Long) advertisingExposureRecordDao.getBy(paramMap, "getExposureNum");
        return null == exposureNum ? 0L : exposureNum;
    }

    /**
     * 获取广告主曝光量
     */
    private Long getExposureNumByMasterId(Long masterId) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("masterId", masterId);
        Long exposureNum = (Long) advertisingExposureRecordDao.getBy(paramMap, "getExposureNumByMasterId");
        return null == exposureNum ? 0L : exposureNum;
    }

    /**
     * 获取书刊广告位点击量
     */
    private Long getClickNumByAdId(Long adId) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("adId", adId);
        Long clickNum = (Long) advertisingClickRecordDao.getBy(paramMap, "getClickNum");
        return null == clickNum ? 0L : clickNum;
    }

    /**
     * 获取书刊广告位点击量
     */
    private Long getClickNumByMasterId(Long masterId) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("masterId", masterId);
        Long clickNum = (Long) advertisingClickRecordDao.getBy(paramMap, "getClickNumByMasterId");
        return null == clickNum ? 0L : clickNum;
    }

    /**
     * 获取书刊广告位收益
     */
    private BigDecimal getIncomeByAdId(Long adId) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("adId", adId);
        BigDecimal income = (BigDecimal) advertisingIncomeDailyDao.getBy(paramMap, "getIncomeByAdId");
        return null == income ? BigDecimal.ZERO : income;
    }

    /**
     * 获取书刊广告位收益
     */
    private BigDecimal getIncomeByMasterId(Long masterId) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("masterId", masterId);
        BigDecimal income = (BigDecimal) advertisingIncomeDailyDao.getBy(paramMap, "getIncomeByMasterId");
        return null == income ? BigDecimal.ZERO : income;
    }

    /**
     * 获取书刊广告位收益
     */
    private BigDecimal getIncomeByAdId(Long adId, Long bookId, Long channelId, String statisMonth) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("adId", adId);
        paramMap.put("bookId", bookId);
        paramMap.put("channelId", channelId);
        paramMap.put("statisMonth", statisMonth);
        BigDecimal income = (BigDecimal) advertisingIncomeDailyDao.getBy(paramMap, "getIncomeByAdId");
        return null == income ? BigDecimal.ZERO : income;
    }

    /**
     * 获取书刊广告位点击量
     */
    private BigDecimal getIncomeByBookId(Long bookId, Long channelId) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("bookId", bookId);
        paramMap.put("channelId", channelId);
        BigDecimal income = (BigDecimal) advertisingIncomeDailyDao.getBy(paramMap, "getIncomeByBookId");
        return null == income ? BigDecimal.ZERO : income;
    }

    /**
     * 获取书刊广告位收益
     */
    private BigDecimal getIncomeByGroupQrcodeId(Long qrcodeId, Long adId, String statisMonth) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("qrcodeId", qrcodeId);
        paramMap.put("adId", adId);
        paramMap.put("statisMonth", statisMonth);
        BigDecimal income = (BigDecimal) advertisingIncomeDailyDao.getBy(paramMap, "getIncomeByGroupQrcodeId");
        return null == income ? BigDecimal.ZERO : income;
    }

    /**
     * 获取广告位列表
     */
    @Override
    public PageBean list(String name, PageParam pageParam, Boolean filterClose, Boolean filterTest, String adPosition, String type) throws BizException {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("name", StringUtil.isEmpty(name) ? null : name);
        paramMap.put("filterClose", filterClose);
        paramMap.put("filterTest", filterTest);
        paramMap.put("adPosition", adPosition);
        paramMap.put("type", type);
        PageBean pageBean = advertisingSpaceDao.listPage(pageParam, paramMap, "list");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        List<Long> wechatAdIds = new ArrayList<>();
        // 填充投放数据
        for (Object object : pageBean.getRecordList()) {
            AdvertisingSpaceDTO spaceDTO = (AdvertisingSpaceDTO) object;
            if (null == spaceDTO) {
                continue;
            }
            AdvertisingMaster master = advertisingMasterDao.getById(spaceDTO.getMasterId());
            if (null != master) {
                spaceDTO.setMasterName(master.getMasterName());
            }
            spaceDTO.setExposureNum(getExposureNumByAdId(spaceDTO.getId()));
            spaceDTO.setClickNum(getClickNumByAdId(spaceDTO.getId()));
            BigDecimal clickRate = (null == spaceDTO.getExposureNum() || spaceDTO.getExposureNum().equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(spaceDTO.getClickNum().doubleValue() / spaceDTO.getExposureNum()).setScale(4, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setClickRate(clickRate);
            BigDecimal income = getIncomeByAdId(spaceDTO.getId());
            spaceDTO.setTotalIncome(income);
            //报名量报名率
            Long registerNum = bmBiz.getRegisterCountByAdId(spaceDTO.getId(), null, null, null, null);
            BigDecimal registerRate = (null == spaceDTO.getClickNum() || spaceDTO.getClickNum().equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / spaceDTO.getClickNum()).setScale(4, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setRegisterNum(registerNum);
            spaceDTO.setRegisterRate(registerRate);

            List<AdvertisingDistributionBook> list = advertisingDistributionBookDao.getByAdId(spaceDTO.getId());
            if (!ListUtils.isEmpty(list)) {
                spaceDTO.setBookNum(list.size());
                Long bookId = list.get(0).getBookId();
                BookDto bookDto = bookBiz.getBaseById(bookId);
                spaceDTO.setBookName(StringUtil.addBracket(bookDto.getBookName()));
            } else {
                spaceDTO.setBookNum(0);
            }
            // 填充广告位置名称
            if (AdPositionEnum.WECHAT_GROUP_MSG.positionCode.equals(spaceDTO.getAdPosition())) {
                fillAdpositionName4WechatGroupAd(spaceDTO);
                wechatAdIds.add(spaceDTO.getId());
            } else {
                fillAdpositionName4CommonAd(spaceDTO);
            }
        }
        if (!ListUtils.isEmpty(wechatAdIds)){
            List<AdvertisingWechatMsg> adms = advertisingWechatMsgDao.getByAdIds(wechatAdIds);
            Map<Long, AdvertisingWechatMsg> wechatAdMap = new HashMap<>();
            for (AdvertisingWechatMsg advertisingWechatMsg : adms) {
                wechatAdMap.put(advertisingWechatMsg.getAdId(), advertisingWechatMsg);
            }
            for (Object object : pageBean.getRecordList()) {
                AdvertisingSpaceDTO spaceDTO = (AdvertisingSpaceDTO) object;
                AdvertisingWechatMsg wechatMsg = wechatAdMap.get(spaceDTO.getId());
                if (wechatMsg!=null){
                    spaceDTO.setAdPic(wechatMsg.getAdPic());
                    spaceDTO.setAdLink(wechatMsg.getAdLink());
                    spaceDTO.setAdSlogan(wechatMsg.getAdSlogan());
                    spaceDTO.setAdTitle(wechatMsg.getAdTitle());
                }
            }
        }
        return pageBean;
    }

    /**
     * 书刊广告位明细
     */
    @Override
    public PageBean advertisingDetail4Book(Long bookId, Long channelId, Long adviserId, String statisMonth, PageParam pageParam) throws BizException {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("bookId", bookId);
        paramMap.put("channelId", channelId);
        paramMap.put("adviserId", adviserId);
        PageBean pageBean = advertisingSpaceDao.listPage(pageParam, paramMap, "advertisingDetail4Book");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        // 填充投放数据
        for (Object object : pageBean.getRecordList()) {
            AdvertisingSpaceDTO spaceDTO = (AdvertisingSpaceDTO) object;
            if (null == spaceDTO) {
                continue;
            }
            AdvertisingMaster master = advertisingMasterDao.getById(spaceDTO.getMasterId());
            if (null != master) {
                spaceDTO.setMasterName(master.getMasterName());
            }
            spaceDTO.setExposureNum(getExposureNumByBookId(bookId, adviserId, channelId, spaceDTO.getId(), statisMonth));
            spaceDTO.setClickNum(getClickNumByBookId(bookId, adviserId, channelId, spaceDTO.getId(), statisMonth));
            BigDecimal clickRate = (null == spaceDTO.getExposureNum() || spaceDTO.getExposureNum().equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(spaceDTO.getClickNum().doubleValue() / spaceDTO.getExposureNum()).setScale(4, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setClickRate(clickRate);
            BigDecimal income = getIncomeByAdId(spaceDTO.getId(), bookId, channelId, statisMonth);
            spaceDTO.setTotalIncome(income);
            // 填充广告位置名称
            spaceDTO.setAdPositionName(AdPositionModeEnum.getNameByCode(spaceDTO.getAdPositionMode()));
            AdvertisingSettlementMethodDTO methodDTO = advertisingSettlementMethodDao.getByMasterIdAndAdId(spaceDTO.getMasterId(), spaceDTO.getId());
            if (null != methodDTO) {
                spaceDTO.setSettlementMethod(methodDTO.getSettlementMethod());
            }
            //报名量报名率
            Long clickNum = spaceDTO.getClickNum();
            Long registerNum = bmBiz.getRegisterCountByBookId(bookId, adviserId, channelId, spaceDTO.getId(), statisMonth);
            BigDecimal registerRate = (null == clickNum || clickNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setRegisterNum(registerNum);
            spaceDTO.setRegisterRate(registerRate);
            // 填充广告位置名称
            fillAdpositionName4CommonAd(spaceDTO);
            // 点击读者量
            Long clickUserNum = advertisingClickRecordDao.getClickUserNumByBookId(spaceDTO.getId(), bookId, adviserId, channelId, statisMonth);
            spaceDTO.setClickUserNum(clickUserNum);
            //完课量 完课率
            Long pilotFinishUserNum = advertisingPilotRecordDao.getPilotUserNumByBookId(bookId, adviserId, channelId, spaceDTO.getId(), statisMonth);
            BigDecimal pilotFinishRate = (null == clickUserNum || clickUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(pilotFinishUserNum.doubleValue() / clickUserNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setPilotFinishUserNum(pilotFinishUserNum);
            spaceDTO.setPilotFinishRate(pilotFinishRate);
            //行为数据（平均浏览时长 跳出量 跳出率）
            Long browseTimes = advertisingBrowseRecordDao.getBrowseTimesByBook(bookId, adviserId, channelId, spaceDTO.getId(), statisMonth);
            Long browseUserNum = advertisingBrowseRecordDao.getBrowseUserCountByBook(bookId, adviserId, channelId, spaceDTO.getId(), statisMonth);
            BigDecimal avgBrowseTimes = (browseUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(browseTimes.doubleValue() / browseUserNum / 1000).setScale(2, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setAvgBrowseTimes(avgBrowseTimes);
            Long outNum = 0L;
            BigDecimal outRate = BigDecimal.ZERO;
            if (null != clickNum && clickNum.compareTo(registerNum) > 0) {
                outNum = clickNum - registerNum;
                outRate = (clickNum.equals(0L)) ? BigDecimal.ZERO :
                        new BigDecimal(outNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            }
            spaceDTO.setOutNum(outNum);
            spaceDTO.setOutRate(outRate);
        }
        return pageBean;
    }

    /**
     * 导出书刊广告位明细
     */
    @Override
    public Map<String, Object> exportAdvertisingDetail4Book(Long bookId, Long channelId, Long adviserId, String statisMonth) throws BizException {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("bookId", bookId);
        paramMap.put("channelId", channelId);
        paramMap.put("adviserId", adviserId);
        List<AdvertisingSpaceDTO> list = advertisingSpaceDao.advertisingDetail4Book(paramMap);
        if (ListUtils.isEmpty(list)) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "暂无数据导出！");
        }
        // 字段名
        String[] rowsName = {"序号", "广告位名称", "广告主", "结算方式", "广告位置", "投放时间", "平均浏览时长", "跳出量", "跳出率", "曝光量", "点击量", "点击率",
                "独立访客数", "留资量", "留资率", "完课量", "完课率", "累计收益"};
        List<Object[]> dataList = new ArrayList<>();
        Object[] objs;
        for (int i = 0; i < list.size(); i++) {
            AdvertisingSpaceDTO spaceDTO = list.get(i);
            objs = new Object[rowsName.length];
            objs[0] = i + 1;
            objs[1] = spaceDTO.getAdName();
            AdvertisingMaster master = advertisingMasterDao.getById(spaceDTO.getMasterId());
            objs[2] = null != master ? master.getMasterName() : "";
            AdvertisingSettlementMethodDTO methodDTO = advertisingSettlementMethodDao.getByMasterIdAndAdId(spaceDTO.getMasterId(), spaceDTO.getId());
            String settlementMethod = "";
            if (null != methodDTO) {
                settlementMethod = SettlementMethodEnum.getNameByCode(methodDTO.getSettlementMethod()) + "(" + methodDTO.getSettlementMethod() + ")";
            }
            objs[3] = settlementMethod;
            fillAdpositionName4CommonAd(spaceDTO);
            objs[4] = spaceDTO.getAdPositionName();
            objs[5] = DateNewUtils.formatDate(spaceDTO.getDistributionTime());
            Long browseTimes = advertisingBrowseRecordDao.getBrowseTimesByBook(bookId, adviserId, channelId, spaceDTO.getId(), statisMonth);
            Long browseUserNum = advertisingBrowseRecordDao.getBrowseUserCountByBook(bookId, adviserId, channelId, spaceDTO.getId(), statisMonth);
            BigDecimal avgBrowseTimes = (browseUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(browseTimes.doubleValue() / browseUserNum / 1000).setScale(2, BigDecimal.ROUND_HALF_UP);
            objs[6] = avgBrowseTimes + "秒";
            Long clickNum = getClickNumByBookId(bookId, adviserId, channelId, spaceDTO.getId(), statisMonth);
            Long registerNum = bmBiz.getRegisterCountByBookId(bookId, adviserId, channelId, spaceDTO.getId(), statisMonth);
            Long outNum = 0L;
            BigDecimal outRate = BigDecimal.ZERO;
            if (null != clickNum && clickNum.compareTo(registerNum) > 0) {
                outNum = clickNum - registerNum;
                outRate = (clickNum.equals(0L)) ? BigDecimal.ZERO :
                        new BigDecimal(outNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            }
            objs[7] = outNum;
            objs[8] = outRate.multiply(new BigDecimal(100)) + "%";
            Long exposureNum = getExposureNumByBookId(bookId, adviserId, channelId, spaceDTO.getId(), statisMonth);
            objs[9] = exposureNum;
            objs[10] = clickNum;
            BigDecimal clickRate = (null == exposureNum || exposureNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(clickNum.doubleValue() / exposureNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            objs[11] = clickRate.multiply(new BigDecimal(100)) + "%";
            Long clickUserNum = advertisingClickRecordDao.getClickUserNumByBookId(spaceDTO.getId(), bookId, adviserId, channelId, statisMonth);
            objs[12] = clickUserNum;
            BigDecimal registerRate = (null == clickNum || clickNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            objs[13] = registerNum;
            objs[14] = registerRate.multiply(new BigDecimal(100)) + "%";
            Long pilotFinishUserNum = advertisingPilotRecordDao.getPilotUserNumByBookId(bookId, adviserId, channelId, spaceDTO.getId(), statisMonth);
            BigDecimal pilotFinishRate = (null == clickUserNum || clickUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(pilotFinishUserNum.doubleValue() / clickUserNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            objs[15] = pilotFinishUserNum;
            objs[16] = pilotFinishRate.multiply(new BigDecimal(100)) + "%";
            BigDecimal income = getIncomeByAdId(spaceDTO.getId(), bookId, channelId, statisMonth);
            objs[17] = income + "元";
            dataList.add(objs);
        }
        BookDto bookDto = bookBiz.getBaseById(bookId);
        String[] dateStr = statisMonth.split("-");
        String fileName = StringUtil.addBracket(bookDto.getBookName()) + dateStr[0] + "年" + Integer.parseInt(dateStr[1]) + "月广告位明细";
        String fileUrl = exportConsr.exportExcel(fileName, rowsName, dataList);
        Map<String, Object> result = new HashMap<>();
        result.put("fileUrl", fileUrl);
        result.put("fileName", fileName);
        return result;
    }

    /**
     * 填充作品应用、社群码页面广告位名称
     */
    private void fillAdpositionName4CommonAd(AdvertisingSpaceDTO spaceDTO) {
        if (StringUtil.isEmpty(spaceDTO.getAdPosition())) {
            return;
        }
        String adPositionName = "";
        String adPositionModeName = AdPositionModeEnum.getNameByCode(spaceDTO.getAdPositionMode());
        if (AdPositionEnum.BOOK_GROUP_PAGE.positionCode.equals(spaceDTO.getAdPosition())) {
            adPositionName = AdPositionDetailEnum.getNameByCode(spaceDTO.getAdPositionDetail()) + ">" + adPositionModeName;
        } else {
            int index = spaceDTO.getAdPositionDetail().indexOf("_");
            String type = spaceDTO.getAdPositionDetail().substring(0, index);
            String typeCode = spaceDTO.getAdPositionDetail().substring(index + 1, spaceDTO.getAdPositionDetail().length());
            if ("APP".equalsIgnoreCase(type)) {
                AppTypeDto appTypeDto = ResponseHandleUtil.parseResponse(appTypeService.getByTypeCode(typeCode), AppTypeDto.class);
                if (null != appTypeDto) {
                    adPositionName = appTypeDto.getTypeName() + ">" + adPositionModeName;
                } else {
                    adPositionName = adPositionModeName;
                }
            } else {
                String productTypeName = ResponseHandleUtil.parseResponse(productService.getProTypeName(typeCode), String.class);
                if (!StringUtil.isEmpty(productTypeName)) {
                    adPositionName = productTypeName + ">" + adPositionModeName;
                } else {
                    adPositionName = adPositionModeName;
                }
            }
        }
        spaceDTO.setAdPositionName(adPositionName);
    }

    /**
     * 填充作品应用、社群码页面广告位名称
     */
    private void fillAdpositionName4WechatGroupAd(AdvertisingSpaceDTO spaceDTO) {
        if (StringUtil.isEmpty(spaceDTO.getAdPosition())) {
            return;
        }
        String adPositionName = AdPositionEnum.getNameByCode(spaceDTO.getAdPosition()) + ">" + AdPositionModeEnum.getNameByCode(spaceDTO.getAdPositionMode());
        spaceDTO.setAdPositionName(adPositionName);
    }

    /**
     * 微信群广告位明细
     */
    @Override
    public PageBean advertisingDetail4WechatGroup(Long qrcodeId, String statisMonth, PageParam pageParam) throws BizException {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("qrcodeId", qrcodeId);
        PageBean pageBean = advertisingSpaceDao.listPage(pageParam, paramMap, "advertisingDetail4WechatGroup");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        // 填充投放数据
        for (Object object : pageBean.getRecordList()) {
            AdvertisingSpaceDTO spaceDTO = (AdvertisingSpaceDTO) object;
            if (null == spaceDTO) {
                continue;
            }
            AdvertisingMaster master = advertisingMasterDao.getById(spaceDTO.getMasterId());
            if (null != master) {
                spaceDTO.setMasterName(master.getMasterName());
            }
            spaceDTO.setExposureNum(getExposureNumByGroupQrcodeId(qrcodeId, spaceDTO.getId(), statisMonth));
            spaceDTO.setClickNum(getClickNumByGroupQrcodeId(qrcodeId, spaceDTO.getId(), statisMonth));
            BigDecimal clickRate = (null == spaceDTO.getExposureNum() || spaceDTO.getExposureNum().equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(spaceDTO.getClickNum().doubleValue() / spaceDTO.getExposureNum()).setScale(4, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setClickRate(clickRate);
            BigDecimal income = getIncomeByGroupQrcodeId(qrcodeId, spaceDTO.getId(), statisMonth);
            spaceDTO.setTotalIncome(income);
            // 点击读者量
            if (!AdPositionModeEnum.JUST_PIC.modeCode.equals(spaceDTO.getAdPositionMode()) && !StringUtil.isEmpty(spaceDTO.getAdLink())) {
                spaceDTO.setClickUserNum(getClickUserNumByGroupQrcodeId(qrcodeId, spaceDTO.getId(), statisMonth));
            } else {
                spaceDTO.setClickUserNum(0L);
            }
            // 填充广告位置名称
            spaceDTO.setAdPositionName(AdPositionModeEnum.getNameByCode(spaceDTO.getAdPositionMode()));
            AdvertisingSettlementMethodDTO methodDTO = advertisingSettlementMethodDao.getByMasterIdAndAdId(spaceDTO.getMasterId(), spaceDTO.getId());
            if (null != methodDTO) {
                spaceDTO.setSettlementMethod(methodDTO.getSettlementMethod());
            }
            //报名量报名率
            Long clickNum = spaceDTO.getClickNum();
            Long registerNum = bmBiz.getRegisterCountByAdIdQrcodeId(spaceDTO.getId(), statisMonth, qrcodeId);
            BigDecimal registerRate = (null == clickNum || clickNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setRegisterNum(registerNum);
            spaceDTO.setRegisterRate(registerRate);
            //完课量 完课率
            Long pilotFinishUserNum = advertisingPilotRecordDao.getPilotUserNumByQrcode(qrcodeId,spaceDTO.getId(),statisMonth);
            Long clickUserNum = spaceDTO.getClickUserNum();
            BigDecimal pilotFinishRate = (null == clickUserNum || clickUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(pilotFinishUserNum.doubleValue() / clickUserNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setPilotFinishUserNum(pilotFinishUserNum);
            spaceDTO.setPilotFinishRate(pilotFinishRate);
            //行为数据（平均浏览时长 跳出量 跳出率）
            Long browseTimes = advertisingBrowseRecordDao.getBrowseTimesByQrcode(qrcodeId, spaceDTO.getId(), statisMonth);
            Long browseUserNum = advertisingBrowseRecordDao.getBrowseUserCountByQrcode(qrcodeId, spaceDTO.getId(), statisMonth);
            BigDecimal avgBrowseTimes = (browseUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(browseTimes.doubleValue() / browseUserNum / 1000).setScale(2, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setAvgBrowseTimes(avgBrowseTimes);
            Long outNum = 0L;
            BigDecimal outRate = BigDecimal.ZERO;
            if (null != clickNum && clickNum.compareTo(registerNum) > 0) {
                outNum = clickNum - registerNum;
                outRate = (clickNum.equals(0L)) ? BigDecimal.ZERO :
                        new BigDecimal(outNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            }
            spaceDTO.setOutNum(outNum);
            spaceDTO.setOutRate(outRate);
        }
        return pageBean;
    }

    /**
     * 导出微信群广告位明细
     */
    @Override
    public Map<String, Object> exportAdvertisingDetail4WechatGroup(Long qrcodeId, String statisMonth) throws BizException {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("qrcodeId", qrcodeId);
        List<AdvertisingSpaceDTO> list = advertisingSpaceDao.advertisingDetail4WechatGroup(paramMap);
        if (ListUtils.isEmpty(list)) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "暂无数据导出！");
        }
        // 字段名
        String[] rowsName = {"序号", "广告位名称", "广告主", "结算方式", "广告位置", "最近投放时间", "已投放次数", "平均浏览时长", "跳出量", "跳出率", "曝光量", "点击量", "点击率",
                "独立访客数", "留资量", "留资率", "完课量", "完课率", "累计收益"};
        List<Object[]> dataList = new ArrayList<>();
        Object[] objs;
        for (int i = 0; i < list.size(); i++) {
            AdvertisingSpaceDTO spaceDTO = list.get(i);
            objs = new Object[rowsName.length];
            objs[0] = i + 1;
            objs[1] = spaceDTO.getAdName();
            AdvertisingMaster master = advertisingMasterDao.getById(spaceDTO.getMasterId());
            objs[2] = null != master ? master.getMasterName() : "";
            AdvertisingSettlementMethodDTO methodDTO = advertisingSettlementMethodDao.getByMasterIdAndAdId(spaceDTO.getMasterId(), spaceDTO.getId());
            String settlementMethod = "";
            if (null != methodDTO) {
                settlementMethod = SettlementMethodEnum.getNameByCode(methodDTO.getSettlementMethod()) + "(" + methodDTO.getSettlementMethod() + ")";
            }
            objs[3] = settlementMethod;
            objs[4] = AdPositionModeEnum.getNameByCode(spaceDTO.getAdPositionMode());
            objs[5] = DateNewUtils.formatDate(spaceDTO.getDistributionTime());
            objs[6] = spaceDTO.getDistributionNum();
            Long browseTimes = advertisingBrowseRecordDao.getBrowseTimesByQrcode(qrcodeId, spaceDTO.getId(), statisMonth);
            Long browseUserNum = advertisingBrowseRecordDao.getBrowseUserCountByQrcode(qrcodeId, spaceDTO.getId(), statisMonth);
            BigDecimal avgBrowseTimes = (browseUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(browseTimes.doubleValue() / browseUserNum / 1000).setScale(2, BigDecimal.ROUND_HALF_UP);
            objs[7] = avgBrowseTimes + "秒";
            Long outNum = 0L;
            BigDecimal outRate = BigDecimal.ZERO;
            Long clickUserNum = 0L;
            if (!AdPositionModeEnum.JUST_PIC.modeCode.equals(spaceDTO.getAdPositionMode()) && !StringUtil.isEmpty(spaceDTO.getAdLink())) {
                clickUserNum = getClickUserNumByGroupQrcodeId(qrcodeId, spaceDTO.getId(), statisMonth);
            }
            Long clickNum = getClickNumByGroupQrcodeId(qrcodeId, spaceDTO.getId(), statisMonth);
            Long registerNum = bmBiz.getRegisterCountByAdIdQrcodeId(spaceDTO.getId(), statisMonth, qrcodeId);
            if (null != clickNum && clickNum.compareTo(registerNum) > 0) {
                outNum = clickNum - registerNum;
                outRate = (clickNum.equals(0L)) ? BigDecimal.ZERO :
                        new BigDecimal(outNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            }
            objs[8] = outNum;
            objs[9] = outRate.multiply(new BigDecimal(100)) + "%";
            Long exposureNum = getExposureNumByGroupQrcodeId(qrcodeId, spaceDTO.getId(), statisMonth);
            objs[10] = exposureNum;
            objs[11] = clickNum;
            BigDecimal clickRate = (null == exposureNum || exposureNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(clickNum.doubleValue() / exposureNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            objs[12] = clickRate.multiply(new BigDecimal(100)) + "%";
            objs[13] = clickUserNum;
            BigDecimal registerRate = (null == clickNum || clickNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            objs[14] = registerNum;
            objs[15] = registerRate.multiply(new BigDecimal(100)) + "%";
            Long pilotFinishUserNum = advertisingPilotRecordDao.getPilotUserNumByQrcode(qrcodeId, spaceDTO.getId(), statisMonth);
            BigDecimal pilotFinishRate = (null == clickUserNum || clickUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(pilotFinishUserNum.doubleValue() / clickUserNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            objs[16] = pilotFinishUserNum;
            objs[17] = pilotFinishRate.multiply(new BigDecimal(100)) + "%";
            BigDecimal income = getIncomeByGroupQrcodeId(qrcodeId, spaceDTO.getId(), statisMonth);
            objs[18] = income + "元";
            dataList.add(objs);
        }
        GroupQrcode groupQrcode = groupQrcodeDao.getById(qrcodeId);
        String[] dateStr = statisMonth.split("-");
        String fileName = (null != groupQrcode ? groupQrcode.getGroupName() : "") + dateStr[0] + "年" + Integer.parseInt(dateStr[1]) + "月广告位明细";
        String fileUrl = exportConsr.exportExcel(fileName, rowsName, dataList);
        Map<String, Object> result = new HashMap<>();
        result.put("fileUrl", fileUrl);
        result.put("fileName", fileName);
        return result;
    }

    /**
     * 微信群广告位点击读者列表
     */
    @Override
    public PageBean clickUserList4AdvertisingWechatGroup(Long qrcodeId, Long adId, String statisMonth, PageParam pageParam) throws BizException {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("qrcodeId", qrcodeId);
        paramMap.put("adId", adId);
        PageBean pageBean = advertisingClickRecordDao.listPage(pageParam, paramMap, "clickUserList4AdvertisingWechatGroup");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        List<Long> wechatUserIds = new ArrayList<>();
        for (Object object : pageBean.getRecordList()) {
            Long wechatUserId = ((WechatGroupClickUserDTO) object).getWechatUserId();
            wechatUserIds.add(wechatUserId);
        }
        Map<Long, WechatUser> wechatUserMap = new HashMap<>();
        if (!ListUtils.isEmpty(wechatUserIds)) {
            wechatUserIds = new ArrayList<>(new HashSet<>(wechatUserIds));
            wechatUserMap = readerConsr.getUserList(wechatUserIds);
        }
        for (Object object : pageBean.getRecordList()) {
            WechatGroupClickUserDTO dto = (WechatGroupClickUserDTO) object;
            if (!MapUtils.isEmpty(wechatUserMap) && wechatUserMap.containsKey(dto.getWechatUserId())) {
                WechatUser wechatUser = wechatUserMap.get(dto.getWechatUserId());
                dto.setWechatUserName(wechatUser.getWechatUserNickname());
                dto.setWechatUserHeadurl(wechatUser.getWechatUserHeadurl());
                dto.setWechatUserSex(wechatUser.getWechatUserSex());
                dto.setWechatUserProvince(wechatUser.getWechatUserProvince());
                dto.setWechatUserCity(wechatUser.getWechatUserCity());
            }
        }
        return pageBean;
    }

    /**
     * 导出微信群广告位点击读者列表
     */
    @Override
    public Map<String, Object> exportClickUserList4AdvertisingWechatGroup(Long qrcodeId, Long adId, String statisMonth) throws BizException {
        AdvertisingSpace space = advertisingSpaceDao.getById(adId);
        if (null == space) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告位不存在或已删除！");
        }
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("qrcodeId", qrcodeId);
        paramMap.put("adId", adId);
        List<WechatGroupClickUserDTO> list = advertisingClickRecordDao.clickUserList4AdvertisingWechatGroup(paramMap);
        if (ListUtils.isEmpty(list)) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "暂无数据导出！");
        }
        List<Long> wechatUserIds = list.stream().filter(dto -> null != dto.getWechatUserId()).map(WechatGroupClickUserDTO::getWechatUserId).collect(Collectors.toList());
        Map<Long, WechatUser> wechatUserMap = new HashMap<>();
        if (!ListUtils.isEmpty(wechatUserIds)) {
            wechatUserIds = new ArrayList<>(new HashSet<>(wechatUserIds));
            wechatUserMap = readerConsr.getUserList(wechatUserIds);
        }
        // 字段名
        String[] rowsName = {"序号", "读者信息", "读者标签", "点击时间"};
        List<Object[]> dataList = new ArrayList<>();
        Object[] objs;
        for (int i = 0; i < list.size(); i++) {
            WechatGroupClickUserDTO dto = list.get(i);
            objs = new Object[rowsName.length];
            objs[0] = i + 1;
            if (!MapUtils.isEmpty(wechatUserMap) && wechatUserMap.containsKey(dto.getWechatUserId())) {
                WechatUser wechatUser = wechatUserMap.get(dto.getWechatUserId());
                objs[1] = null != wechatUser.getWechatUserNickname() ? wechatUser.getWechatUserNickname() : "--";
                if (null == wechatUser.getWechatUserSex() && StringUtil.isEmpty(wechatUser.getWechatUserCity())) {
                    objs[2] = "--";
                } else {
                    String sex = null == wechatUser.getWechatUserSex() ? "--" : (wechatUser.getWechatUserSex().equals(1) ? "男" : (wechatUser.getWechatUserSex().equals(2) ? "女" : "未知"));
                    objs[2] = sex + "，" + (null == wechatUser.getWechatUserCity() ? "--" : wechatUser.getWechatUserCity());
                }
            } else {
                objs[1] = "";
                objs[2] = "";
            }
            objs[3] = DateNewUtils.formatDate(dto.getCreateTime());
            dataList.add(objs);
        }
        GroupQrcode groupQrcode = groupQrcodeDao.getById(qrcodeId);
        String[] dateStr = statisMonth.split("-");
        String fileName;
        if (!StringUtil.isEmpty(statisMonth)) {
            fileName = (null != groupQrcode ? groupQrcode.getGroupName() : "") + dateStr[0] + "年" + Integer.parseInt(dateStr[1]) + "月" + space.getAdName() + "广告位点击明细";
        } else {
            fileName = space.getAdName() + "广告位" + (null != groupQrcode ? groupQrcode.getGroupName() : "") + "点击明细";
        }
        String fileUrl = exportConsr.exportExcel(fileName, rowsName, dataList);
        Map<String, Object> result = new HashMap<>();
        result.put("fileUrl", fileUrl);
        result.put("fileName", fileName);
        return result;
    }

    /**
     * 广告主广告位明细
     */
    @Override
    public PageBean advertisingDetail4Master(Long masterId, String statisMonth, PageParam pageParam) throws BizException {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("masterId", masterId);
        PageBean pageBean = advertisingSpaceDao.listPage(pageParam, paramMap, "advertisingDetail4Master");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        // 填充投放数据
        for (Object object : pageBean.getRecordList()) {
            AdvertisingSpaceDTO spaceDTO = (AdvertisingSpaceDTO) object;
            if (null == spaceDTO) {
                continue;
            }
            Long exposureNum = 0L;
            Long clickNum = 0L;
            BigDecimal income = BigDecimal.ZERO;
            Long registerNum = 0L;
            Long clickUserNum = 0L;
            Long pilotFinishUserNum = 0L;
            if (null != spaceDTO.getBookId()) {
                if (AdPositionEnum.WECHAT_GROUP_MSG.positionCode.equals(spaceDTO.getAdPosition())) {
                    exposureNum = getExposureNumByGroupQrcodeId(spaceDTO.getQrcodeId(), spaceDTO.getId(), statisMonth);
                    clickNum = getClickNumByGroupQrcodeId(spaceDTO.getQrcodeId(), spaceDTO.getId(), statisMonth);
                    income = getIncomeByGroupQrcodeId(spaceDTO.getQrcodeId(), spaceDTO.getId(), statisMonth);
                    registerNum = bmBiz.getRegisterCountByAdIdQrcodeId(spaceDTO.getId(),statisMonth,spaceDTO.getQrcodeId());
                    clickUserNum = advertisingClickRecordDao.getClickUserNumByQrcode(spaceDTO.getQrcodeId(),spaceDTO.getId(),statisMonth);
                    pilotFinishUserNum = advertisingPilotRecordDao.getPilotUserNumByQrcode(spaceDTO.getQrcodeId(),spaceDTO.getId(),statisMonth);
                } else {
                    exposureNum = getExposureNumByAdId(spaceDTO.getId(), spaceDTO.getBookId(), spaceDTO.getAdviserId(), spaceDTO.getChannelId(), statisMonth);
                    clickNum = getClickNumByAdId(spaceDTO.getId(), spaceDTO.getBookId(), spaceDTO.getAdviserId(), spaceDTO.getChannelId(), statisMonth);
                    income = getIncomeByAdId(spaceDTO.getId(), spaceDTO.getBookId(), spaceDTO.getChannelId(), statisMonth);
                    registerNum = bmBiz.getRegisterCountByAdId(spaceDTO.getId(),statisMonth,spaceDTO.getBookId(),spaceDTO.getAdviserId(),spaceDTO.getChannelId());
                    clickUserNum = advertisingClickRecordDao.getClickUserNumByAdId(spaceDTO.getId(), spaceDTO.getBookId(), spaceDTO.getAdviserId(), spaceDTO.getChannelId(), statisMonth);
                    pilotFinishUserNum = advertisingPilotRecordDao.getPilotUserNumByAdId(spaceDTO.getId(), spaceDTO.getBookId(), spaceDTO.getAdviserId(), spaceDTO.getChannelId(), statisMonth);
                }
            }
            BigDecimal clickRate = (null == exposureNum || exposureNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(clickNum.doubleValue() / exposureNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setExposureNum(exposureNum);
            spaceDTO.setClickNum(clickNum);
            spaceDTO.setClickRate(clickRate);
            spaceDTO.setTotalIncome(income);
            //报名量报名率
            BigDecimal registerRate = (null == clickNum || clickNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setRegisterNum(registerNum);
            spaceDTO.setRegisterRate(registerRate);
            //点击人数、完课量完课率
            spaceDTO.setClickUserNum(clickUserNum);
            BigDecimal pilotFinishRate = (null == clickUserNum || clickUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(pilotFinishUserNum.doubleValue() / clickUserNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            spaceDTO.setPilotFinishUserNum(pilotFinishUserNum);
            spaceDTO.setPilotFinishRate(pilotFinishRate);

            if (!StringUtil.isEmpty(spaceDTO.getSettlementMethod())) {
                spaceDTO.setSettlementMethodName(SettlementMethodEnum.getNameByCode(spaceDTO.getSettlementMethod()));
            }
            // 填充广告位置名称
            if (AdPositionEnum.WECHAT_GROUP_MSG.positionCode.equals(spaceDTO.getAdPosition())) {
                fillAdpositionName4WechatGroupAd(spaceDTO);
            } else {
                fillAdpositionName4CommonAd(spaceDTO);
            }
            // 填充投放渠道信息
            if (null != spaceDTO.getBookId()) {
                if (AdPositionEnum.WECHAT_GROUP_MSG.positionCode.equals(spaceDTO.getAdPosition())) {
                    GroupQrcode groupQrcode = groupQrcodeDao.getById(spaceDTO.getQrcodeId());
                    BookDto bookDto = bookBiz.getBaseById(spaceDTO.getBookId());
                    spaceDTO.setGroupQrcodeName(null != groupQrcode ? groupQrcode.getGroupName() : "");
                    spaceDTO.setBookName(StringUtil.addBracket(bookDto.getBookName()));
                    spaceDTO.setIsbn(bookDto.getIsbn());
                } else {
                    BookDto bookDto = bookBiz.getBaseById(spaceDTO.getBookId());
                    spaceDTO.setBookName(StringUtil.addBracket(bookDto.getBookName()));
                    spaceDTO.setIsbn(bookDto.getIsbn());
                    Map<Long, String> agentNameMap = agentConsr.getNames(Arrays.asList(spaceDTO.getAgentId()));
                    if (!MapUtils.isEmpty(agentNameMap) && agentNameMap.containsKey(spaceDTO.getAgentId())) {
                        spaceDTO.setAgentName(agentNameMap.get(spaceDTO.getAgentId()));
                    } else {
                        spaceDTO.setAgentName("");
                    }
                }
            }
        }
        return pageBean;
    }

    /**
     * 导出广告主广告位明细
     */
    @Override
    public Map<String, Object> exportAdvertisingDetail4Master(Long masterId, String statisMonth) throws BizException {
        AdvertisingMaster master = advertisingMasterDao.getById(masterId);
        if (null == master) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告主不存在！");
        }
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("masterId", masterId);
        List<AdvertisingSpaceDTO> list = advertisingSpaceDao.advertisingDetail4Master(paramMap);
        if (ListUtils.isEmpty(list)) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "暂无数据导出！");
        }
        // 字段名
        String[] rowsName = {"序号", "广告位名称", "对方ID", "广告位置", "投放渠道", "投放时间", "曝光量", "点击量", "点击率", "留资量", "留资率", "累计收益"};
        List<Object[]> dataList = new ArrayList<>();
        Object[] objs;
        for (int i = 0; i < list.size(); i++) {
            AdvertisingSpaceDTO spaceDTO = list.get(i);
            objs = new Object[rowsName.length];
            objs[0] = i + 1;
            objs[1] = spaceDTO.getAdName();
            objs[2] = spaceDTO.getSourceId();
            // 填充广告位置名称
            String distributionChannel = "";
            if (AdPositionEnum.WECHAT_GROUP_MSG.positionCode.equals(spaceDTO.getAdPosition())) {
                fillAdpositionName4WechatGroupAd(spaceDTO);
                if (null != spaceDTO.getBookId()) {
                    GroupQrcode groupQrcode = groupQrcodeDao.getById(spaceDTO.getQrcodeId());
                    BookDto bookDto = bookBiz.getBaseById(spaceDTO.getBookId());
                    distributionChannel = (null != groupQrcode ? groupQrcode.getGroupName() : "") + "\r\n社群书" + StringUtil.addBracket(bookDto.getBookName()) + "\r\n书号：" + bookDto.getIsbn();
                }
            } else {
                fillAdpositionName4CommonAd(spaceDTO);
                if (null != spaceDTO.getBookId()) {
                    BookDto bookDto = bookBiz.getBaseById(spaceDTO.getBookId());
                    Map<Long, String> agentNameMap = agentConsr.getNames(Arrays.asList(spaceDTO.getAgentId()));
                    String agentName = "";
                    if (!MapUtils.isEmpty(agentNameMap) && agentNameMap.containsKey(spaceDTO.getAgentId())) {
                        agentName = agentNameMap.get(spaceDTO.getAgentId());
                    }
                    distributionChannel = StringUtil.addBracket(bookDto.getBookName()) + "\r\n出版社：" + agentName + "\r\n书号：" + bookDto.getIsbn();
                }
            }
            objs[3] = spaceDTO.getAdPositionName();
            objs[4] = distributionChannel;
            objs[5] = DateNewUtils.formatDate(spaceDTO.getDistributionTime());
            Long exposureNum = 0L;
            Long clickNum = 0L;
            BigDecimal income = BigDecimal.ZERO;
            Long registerNum = 0L;
            if (null != spaceDTO.getBookId()) {
                if (AdPositionEnum.WECHAT_GROUP_MSG.positionCode.equals(spaceDTO.getAdPosition())) {
                    exposureNum = getExposureNumByGroupQrcodeId(spaceDTO.getQrcodeId(), spaceDTO.getId(), statisMonth);
                    clickNum = getClickNumByGroupQrcodeId(spaceDTO.getQrcodeId(), spaceDTO.getId(), statisMonth);
                    income = getIncomeByGroupQrcodeId(spaceDTO.getQrcodeId(), spaceDTO.getId(), statisMonth);
                    registerNum = bmBiz.getRegisterCountByAdIdQrcodeId(spaceDTO.getId(),statisMonth,spaceDTO.getQrcodeId());
                } else {
                    exposureNum = getExposureNumByAdId(spaceDTO.getId(), spaceDTO.getBookId(), spaceDTO.getAdviserId(), spaceDTO.getChannelId(), statisMonth);
                    clickNum = getClickNumByAdId(spaceDTO.getId(), spaceDTO.getBookId(), spaceDTO.getAdviserId(), spaceDTO.getChannelId(), statisMonth);
                    income = getIncomeByAdId(spaceDTO.getId(), spaceDTO.getBookId(), spaceDTO.getChannelId(), statisMonth);
                    registerNum = bmBiz.getRegisterCountByAdId(spaceDTO.getId(),statisMonth,spaceDTO.getBookId(),spaceDTO.getAdviserId(),spaceDTO.getChannelId());
                }
            }
            objs[6] = exposureNum;
            objs[7] = clickNum;
            BigDecimal clickRate = (null == exposureNum || exposureNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(clickNum.doubleValue() / exposureNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            objs[8] = clickRate.multiply(new BigDecimal(100)) + "%";
            //报名量报名率
            BigDecimal registerRate = (null == clickNum || clickNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            objs[9] = registerNum;
            objs[10] = registerRate.multiply(new BigDecimal(100)) + "%";
            objs[11] = income + "元";
            dataList.add(objs);
        }
        String[] dateStr = statisMonth.split("-");
        String fileName = master.getMasterName() + dateStr[0] + "年" + Integer.parseInt(dateStr[1]) + "月广告位明细";
        String fileUrl = exportConsr.exportExcel(fileName, rowsName, dataList);
        Map<String, Object> result = new HashMap<>();
        result.put("fileUrl", fileUrl);
        result.put("fileName", fileName);
        return result;
    }

    /**
     * 客户端获取书刊配置的广告位信息
     */
    @Override
    @ParamLog("客户端获取书刊配置的广告位信息")
    public List<AdvertisingSpaceDTO> get4Wechat(Long sceneId, Long adviserId, Long channelId, String adPositionDetail, Long bookGroupId) throws BizException {
        Long agentId;
        Long bookId;
        if (null != bookGroupId) {
            BookGroupDTO bookGroupDTO = bookGroupDao.getDTOById(bookGroupId);
            if (null == bookGroupDTO || null == bookGroupDTO.getBookId()) {
                return new ArrayList<>();
            }
            bookId = bookGroupDTO.getBookId();
            agentId = adviserConsr.getAgentIdByAdviser(bookGroupDTO.getCreateUser());
        } else {
            agentId = adviserConsr.getAgentIdByAdviser(adviserId);
            bookId = ResponseHandleUtil.parseResponse(qrcodeSceneService.getBookId4SceneId(sceneId), Long.class);
        }
        if (null == agentId || null == bookId) {
            return new ArrayList<>();
        }
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("bookId", bookId);
        paramMap.put("adviserId", adviserId);
        paramMap.put("channelId", channelId);
        paramMap.put("agentId", agentId);
        Boolean bookIsOpen = (Boolean) advertisingAdviserPermissionDao.getBy(paramMap, "checkBookOpen");
        if (null == bookIsOpen || !bookIsOpen) {
            return new ArrayList<>();
        }
        paramMap.put("adPositionDetail", adPositionDetail);
        List<AdvertisingSpaceDTO> list = advertisingSpaceDao.getDTOByBookId(paramMap);
        if (ListUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        if (!ListUtils.isEmpty(list)) {
            list.forEach(spaceDTO -> spaceDTO.setBookId(bookId));
            for (AdvertisingSpaceDTO spaceDTO : list) {
                //填充结算方式
                AdvertisingSettlementMethodDTO methodDTO = advertisingSettlementMethodDao.getByMasterIdAndAdId(spaceDTO.getMasterId(), spaceDTO.getId());
                if (null != methodDTO) {
                    spaceDTO.setSettlementMethod(methodDTO.getSettlementMethod());
                }
            }
        }
        return list;
    }

    /**
     * 获取社群书配置的广告位信息
     */
    @Override
    @ParamLog("获取社群书配置的广告位信息")
    public AdvertisingSpaceDTO getBookGroupAd4Wechat(Long bookGroupId, String adPositionDetail) throws BizException {
        BookGroupDTO bookGroupDTO = bookGroupDao.getDTOById(bookGroupId);
        if (null == bookGroupDTO) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "该社群书不存在或已删除！");
        }
        Long agentId = adviserConsr.getAgentIdByAdviser(bookGroupDTO.getCreateUser());
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("bookId", bookGroupDTO.getBookId());
        paramMap.put("adviserId", bookGroupDTO.getCreateUser());
        paramMap.put("channelId", bookGroupDTO.getChannelId());
        paramMap.put("agentId", agentId);
        Boolean bookIsOpen = (Boolean) advertisingAdviserPermissionDao.getBy(paramMap, "checkBookOpen");
        if (null == bookIsOpen || !bookIsOpen) {
            return new AdvertisingSpaceDTO();
        }
        paramMap.put("adPositionDetail", adPositionDetail);
        AdvertisingSpaceDTO spaceDTO = (AdvertisingSpaceDTO) advertisingSpaceDao.getBy(paramMap, "getDTOByBookId4BookGroup");
        if (null == spaceDTO) {
            return new AdvertisingSpaceDTO();
        }
        spaceDTO.setBookId(bookGroupDTO.getBookId());
        //填充结算方式
        AdvertisingSettlementMethodDTO methodDTO = advertisingSettlementMethodDao.getByMasterIdAndAdId(spaceDTO.getMasterId(), spaceDTO.getId());
        if (null != methodDTO) {
            spaceDTO.setSettlementMethod(methodDTO.getSettlementMethod());
        }
        return spaceDTO;
    }

    /**
     * 曝光量埋点
     */
    @Override
    @ParamLog(value = "曝光量埋点", isAfterReturn = false)
    public void addExposureTrack(AdvertisingExposureRecord advertisingExposureRecord) throws BizException {
        if (StringUtil.isEmpty(advertisingExposureRecord.getFromType()) || null == advertisingExposureRecord.getFromId()
                || advertisingExposureRecord.getFromId() <= 0L) {
            return;
        }
        AdvertisingSpace space = advertisingSpaceDao.getById(advertisingExposureRecord.getAdId());
        if (null == space) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告位不存在或已删除！");
        }
        Long agentId = channelConsr.getParentId(advertisingExposureRecord.getChannelId());
        advertisingExposureRecord.setAgentId(agentId);
        if (AdPositionEnum.BOOK_GROUP_PAGE.positionCode.equals(space.getAdPosition())) {
            advertisingExposureRecord.setIsBookGroup(true);
        } else {
            advertisingExposureRecord.setIsBookGroup(false);
        }
        advertisingExposureRecord.setCount(1L);
        advertisingExposureRecordDao.insert(advertisingExposureRecord);
    }

    /**
     * 点击量埋点
     */
    @Override
    @ParamLog(value = "点击量埋点", isAfterReturn = false)
    public void addClickTrack(AdvertisingClickRecord advertisingClickRecord) throws BizException {
        if (StringUtil.isEmpty(advertisingClickRecord.getFromType()) || null == advertisingClickRecord.getFromId()
                || advertisingClickRecord.getFromId() <= 0L) {
            return;
        }
        if ("WECHAT_GROUP".equals(advertisingClickRecord.getFromType())) {
            BookGroup bookGroup = bookGroupDao.getById(advertisingClickRecord.getBookGroupId());
            if (null == bookGroup) {
                return;
            }
            advertisingClickRecord.setBookId(bookGroup.getBookId());
            advertisingClickRecord.setChannelId(bookGroup.getChannelId());
            advertisingClickRecord.setAdviserId(bookGroup.getCreateUser());
        }
        AdvertisingSpace space = advertisingSpaceDao.getById(advertisingClickRecord.getAdId());
        if (null == space) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告位不存在或已删除！");
        }
        Long agentId = channelConsr.getParentId(advertisingClickRecord.getChannelId());
        advertisingClickRecord.setAgentId(agentId);
        if (!AdPositionEnum.APP_OR_PRODUCT.positionCode.equals(space.getAdPosition())) {
            advertisingClickRecord.setIsBookGroup(true);
        } else {
            advertisingClickRecord.setIsBookGroup(false);
        }
        advertisingClickRecordDao.insert(advertisingClickRecord);
    }

    /**
     * 添加广告主
     */
    @Override
    @ParamLog(value = "添加广告主", isAfterReturn = false)
    @Transactional(rollbackFor = Exception.class)
    public void createMaster(AdvertisingMaster advertisingMaster) throws BizException {
        if (StringUtil.isEmpty(advertisingMaster.getMasterName()) || ListUtils.isEmpty(advertisingMaster.getSettlementMethodList())) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        boolean paramError = advertisingMaster.getSettlementMethodList().stream().anyMatch(method ->
                StringUtil.isEmpty(method.getSettlementMethod()) || !ArrayUtils.contains(SettlementMethodEnum.SETTLEMENT_METHOD_GATHER, method.getSettlementMethod()));
        if (paramError) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("masterName", advertisingMaster.getMasterName());
        Boolean nameExist = (Boolean) advertisingMasterDao.getBy(paramMap, "checkMasterNameExist");
        if (null != nameExist && nameExist) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告主已存在！");
        }
        advertisingMasterDao.insert(advertisingMaster);
        Long masterId = advertisingMaster.getId();
        advertisingMaster.getSettlementMethodList().forEach(method -> method.setMasterId(masterId));
        advertisingSettlementMethodDao.insert(advertisingMaster.getSettlementMethodList());
    }

    /**
     * 修改广告主
     */
    @Override
    @ParamLog(value = "修改广告主", isAfterReturn = false)
    @Transactional(rollbackFor = Exception.class)
    public void updateMaster(AdvertisingMaster advertisingMaster) throws BizException {
        if (null == advertisingMaster.getId() || StringUtil.isEmpty(advertisingMaster.getMasterName()) || ListUtils.isEmpty(advertisingMaster.getSettlementMethodList())) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        boolean paramError = advertisingMaster.getSettlementMethodList().stream().anyMatch(method ->
                StringUtil.isEmpty(method.getSettlementMethod()) || !ArrayUtils.contains(SettlementMethodEnum.SETTLEMENT_METHOD_GATHER, method.getSettlementMethod()));
        if (paramError) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        Long masterId = advertisingMaster.getId();
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("masterName", advertisingMaster.getMasterName());
        paramMap.put("masterId", masterId);
        Boolean nameExist = (Boolean) advertisingMasterDao.getBy(paramMap, "checkMasterNameExist");
        if (null != nameExist && nameExist) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告主名称不能重复！");
        }
        advertisingMasterDao.update(advertisingMaster);
        // 删除广告主之前配置的结算方式
        advertisingSettlementMethodDao.deleteByMasterId(masterId);
        advertisingMaster.getSettlementMethodList().forEach(method -> method.setMasterId(masterId));
        advertisingSettlementMethodDao.insert(advertisingMaster.getSettlementMethodList());
    }

    /**
     * 获取广告主列表
     */
    @Override
    public PageBean getMasterList(PageParam pageParam, String masterName) throws BizException {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("masterName", masterName);
        PageBean pageBean = advertisingMasterDao.listPage(pageParam, paramMap, "getMasterList");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        for (Object object : pageBean.getRecordList()) {
            AdvertisingMasterDTO masterDTO = (AdvertisingMasterDTO) object;
            List<AdvertisingSettlementMethodDTO> methodDTOList = advertisingSettlementMethodDao.getByMasterId(masterDTO.getId());
            if (!ListUtils.isEmpty(methodDTOList)) {
                methodDTOList.forEach(dto -> dto.setSettlementMethodName(SettlementMethodEnum.getNameByCode(dto.getSettlementMethod())));
            }
            masterDTO.setSettlementMethodList(methodDTOList);
            Long exposureNum = getExposureNumByMasterId(masterDTO.getId());
            masterDTO.setExposureNum(exposureNum);
            Long clickNum = getClickNumByMasterId(masterDTO.getId());
            masterDTO.setClickNum(clickNum);
            BigDecimal clickRate = (null == masterDTO.getExposureNum() || masterDTO.getExposureNum().equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(masterDTO.getClickNum().doubleValue() / masterDTO.getExposureNum()).setScale(4, BigDecimal.ROUND_HALF_UP);
            masterDTO.setClickRate(clickRate);
            BigDecimal income = getIncomeByMasterId(masterDTO.getId());
            masterDTO.setTotalIncome(income);
            List<Long> adIdList = advertisingSpaceDao.getAdIdsByMasterId(masterDTO.getId());
            //报名量报名率
            Long registerNum = bmBiz.getRegisterCountByAdIds(adIdList);
            BigDecimal registerRate = (null == clickNum || clickNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            masterDTO.setRegisterNum(registerNum);
            masterDTO.setRegisterRate(registerRate);
            //点击人数、完课量完课率
            Long clickUserNum = advertisingClickRecordDao.getClickUserNumByMasterId(masterDTO.getId());
            masterDTO.setClickUserNum(clickUserNum);
            Long pilotFinishUserNum = advertisingPilotRecordDao.getPilotUserNumByMasterId(masterDTO.getId());
            BigDecimal pilotFinishRate = (null == clickUserNum || clickUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(pilotFinishUserNum.doubleValue() / clickUserNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            masterDTO.setPilotFinishUserNum(pilotFinishUserNum);
            masterDTO.setPilotFinishRate(pilotFinishRate);
        }
        return pageBean;
    }

    /**
     * 获取所有广告主
     */
    @Override
    public List<AdvertisingMasterDTO> getAllMaster() throws BizException {
        List<AdvertisingMasterDTO> list = advertisingMasterDao.getAllMaster();
        if (!ListUtils.isEmpty(list)) {
            for (AdvertisingMasterDTO masterDTO : list) {
                List<AdvertisingSettlementMethodDTO> methodDTOList = advertisingSettlementMethodDao.getByMasterId(masterDTO.getId());
                if (!ListUtils.isEmpty(methodDTOList)) {
                    methodDTOList.forEach(dto -> dto.setSettlementMethodName(SettlementMethodEnum.getNameByCode(dto.getSettlementMethod())));
                }
                masterDTO.setSettlementMethodList(methodDTOList);
            }
        }
        return list;
    }

    /**
     * 获取广告主信息
     */
    @Override
    public AdvertisingMasterDTO getMasterInfo(Long masterId) throws BizException {
        return advertisingMasterDao.getDTOById(masterId);
    }

    /**
     * 每日凌晨计算昨日广告位收益
     */
    @Override
    @ParamLog(value = "每日凌晨计算昨日广告位收益", isAfterReturn = false)
    public void calculateAdvertisingIncomeYesterday() throws BizException {
        new Thread() {
            @Override
            public void run() {
                Map<String, Object> paramMap = new HashMap<>();
                paramMap.put("createDay", DateNewUtils.getShortDateStr(DateNewUtils.addDay(new Date(), -1)));
                // 插入扫社群码页面和作品/应用广告位的昨日收益数据
                advertisingIncomeDailyDao.insertRecordWithoutWechatGroupMsgAd(paramMap);
                // 插入微信群消息广告位的昨日收益数据
                advertisingIncomeDailyDao.insertWechatGroupMsgAdRecord(paramMap);
            }
        }.start();
    }

    /**
     * 获取概览数据
     */
    @Override
    @ParamLog("获取概览数据")
    public Map<String, Object> overviewStatis() throws BizException {
        Map<String, Object> result = new HashMap<>();
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("statis", "all");
        Long advertisingSpaceNum = (Long) advertisingSpaceDao.getBy(paramMap, "getSpaceNum");
        result.put("advertisingSpaceNum", advertisingSpaceNum);
        Long distributionBookNum = (Long) advertisingDistributionBookDao.getBy(paramMap, "getDistributionBookNum");
        result.put("distributionBookNum", distributionBookNum);
        Long distributionWechatGroupNum = (Long) advertisingDistributionBookDao.getBy(paramMap, "getDistributionWechatGroupNum");
        result.put("distributionWechatGroupNum", distributionWechatGroupNum);
        Long exposureNum = (Long) advertisingExposureRecordDao.getBy(paramMap, "getTotalExposureNum");
        result.put("exposureNum", exposureNum);
        Long clickNum = (Long) advertisingClickRecordDao.getBy(paramMap, "getTotalClickNum");
        result.put("clickNum", clickNum);
        BigDecimal clickRate = (null == exposureNum || exposureNum.equals(0L)) ? BigDecimal.ZERO :
                new BigDecimal(clickNum.doubleValue() / exposureNum).setScale(4, BigDecimal.ROUND_HALF_UP);
        result.put("clickRate", clickRate);
        BigDecimal totalIncome = (BigDecimal) advertisingIncomeDailyDao.getBy(paramMap, "getTotalIncome");
        result.put("totalIncome", null == totalIncome ? BigDecimal.ZERO : totalIncome.setScale(4, BigDecimal.ROUND_HALF_UP));
        return result;
    }

    /**
     * 获取流量趋势
     */
    @Override
    public List<Object> getFlowStatisTrend(Integer day, String startDate, String endDate) throws BizException {
        if (null == day && (StringUtil.isEmpty(startDate) || StringUtil.isEmpty(endDate))) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        Map<String, Object> paramMap = new HashMap<>();
        if (!StringUtil.isEmpty(startDate) && !StringUtil.isEmpty(endDate)) {
            paramMap.put("collectDates", CommonUtils.collectLocalDates(startDate, endDate));
            paramMap.put("startDate", startDate);
            paramMap.put("endDate", endDate);
        } else {
            Date startTime = DateUtils.addDay(new Date(), -day);
            Date endTime = DateUtils.addDay(new Date(), -1);
            paramMap.put("collectDates", CommonUtils.collectLocalDates(startTime, endTime));
            paramMap.put("startDate", DateNewUtils.getShortDateStr(startTime));
            paramMap.put("endDate", DateNewUtils.getShortDateStr(endTime));
        }
        return advertisingExposureRecordDao.listBy(paramMap, "getFlowStatisTrend");
    }

    /**
     * 获取收益趋势
     */
    @Override
    public List<Object> getIncomeStatisTrend(Integer day, String startDate, String endDate) throws BizException {
        if (null == day && (StringUtil.isEmpty(startDate) || StringUtil.isEmpty(endDate))) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "参数有误！");
        }
        Map<String, Object> paramMap = new HashMap<>();
        if (!StringUtil.isEmpty(startDate) && !StringUtil.isEmpty(endDate)) {
            paramMap.put("collectDates", CommonUtils.collectLocalDates(startDate, endDate));
        } else {
            Date startTime = DateUtils.addDay(new Date(), -day);
            Date endTime = DateUtils.addDay(new Date(), -1);
            paramMap.put("collectDates", CommonUtils.collectLocalDates(startTime, endTime));
        }
        return advertisingIncomeDailyDao.listBy(paramMap, "getIncomeStatisTrend");
    }

    /**
     * 获取微信群信息
     */
    @Override
    public AdGroupQrcodeDTO getGroupQrcodeInfo(Long qrcodeId) throws BizException {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("qrcodeId", qrcodeId);
        AdGroupQrcodeDTO dto = (AdGroupQrcodeDTO) groupQrcodeDao.getBy(paramMap, "getGroupQrcodeInfo4Ad");
        if (null == dto) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "微信群不存在或已删除！");
        }
        Long adviserId = dto.getAdviserId();
        String adviserName = adviserConsr.getNameById(adviserId);
        dto.setAdviserName(adviserName);
        Long agentId = adviserConsr.getAgentIdByAdviser(adviserId);
        if (null != agentId) {
            Map<Long, String> agentNameMap = agentConsr.getNames(Arrays.asList(agentId));
            if (!MapUtils.isEmpty(agentNameMap) && agentNameMap.containsKey(agentId)) {
                dto.setAgentName(agentNameMap.get(agentId));
            }
        }
        return dto;
    }

    @Override
    public String getCutUrl(String fileUrl, String fileText, Double startTime, Double endTime, Double duration) {
        String musicUrl;
        try {
            if (startTime.equals(0d) && endTime.equals(0d)) {
                musicUrl = fileUrl;
            } else {
                String realUrl = convertConsr.clipCustomByUrlAndTime(fileText, fileUrl, startTime.toString(), duration.toString());
                musicUrl = realUrl;
            }
        } catch (Exception e) {
            LOGGER.error("音视频剪切失败" + e.getMessage(), e);
            throw new BookBizException(BookBizException.ERROR, "音视频剪切失败");
        }
        return musicUrl;
    }

    @Override
    public List<AdvertisingSpace> getUnConvertFile(String adDetailFileId) {
        return advertisingSpaceDao.getUnConvertFile(adDetailFileId);
    }

    @Override
    public void updateFile(List<AdvertisingSpace> filelist, String fileUrl) {
        if (ListUtils.isEmpty(filelist)) {
            return;
        }
        for (AdvertisingSpace advertisingSpace : filelist) {
            if (null == advertisingSpace) {
                continue;
            }
            Long seconds = advertisingSpace.getAdDetailFilePilotSecond();
            //更新
            AdvertisingSpace space = new AdvertisingSpace();
            space.setAdDetailFileConvertState(1);
            space.setAdDetailFileUrl(fileUrl);
            space.setAdDetailFileId(advertisingSpace.getAdDetailFileId());
            if (null != seconds && seconds > 0) {
                String cutUrl = getCutUrl(fileUrl, advertisingSpace.getAdDetailFileName(), 0d, seconds.doubleValue(), seconds.doubleValue());
                space.setAdDetailFilePilotUrl(cutUrl);
            } else {//播放全部
                space.setAdDetailFilePilotUrl(fileUrl);
            }
            advertisingSpaceDao.updateByFileId(space);
        }
    }

    @Override
    public List<Long> getCloseAgentId() {
        return advertisingAgentPermissionDao.getCloseAgentId();
    }

    @Override
    public Map<Long, QrcodeAdvertisingSpaceCountDTO> mapAdvertisingSpaceNum(List<Long> qrcodeIds) {
        return advertisingDistributionBookDao.mapAdvertisingSpaceNum(qrcodeIds);
    }

    @Override
    public void updateTestStatus(Boolean isTest, Long id) {
        advertisingSpaceDao.updateTestStatus(isTest, id);
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("创建广告位计划")
    @Override
    public Long createAdvertisingPlan(AdvertisingPlan advertisingPlan) {
        advertisingPlanCheck.checkCreateAdvertisingPlan(advertisingPlan);
        //插入计划
        advertisingPlanDao.insert(advertisingPlan);
        buildPutAndGroup(advertisingPlan);
        fillWeixinGroupId(advertisingPlan.getAdvertisingPlanGroups());
        //插入计划广告位
        advertisingPutDao.batchInsert(advertisingPlan.getAdvertisingPuts());
        //插入关联想
        advertisingPlanGroupDao.batchInsert(advertisingPlan.getAdvertisingPlanGroups());
        //设定定时任务
        for (AdvertisingPut advertisingPut : advertisingPlan.getAdvertisingPuts()) {
            createAdvertisingPlanQuartz(advertisingPut);
        }
        return advertisingPlan.getId();
    }

    @ParamLog("填充微信群名称")
    private void fillWeixinGroupId(List<AdvertisingPlanGroup> advertisingPlanGroups) {
        if (ListUtils.isEmpty(advertisingPlanGroups)) {
            return;
        }
        List<Long> qrcodeIds = advertisingPlanGroups.stream().filter(s -> s.getQrcodeId() != null).map(AdvertisingPlanGroup::getQrcodeId).distinct().collect(Collectors.toList());
        if (ListUtils.isEmpty(qrcodeIds)) {
            return;
        }
        Map<Long, GroupQrcodeInfo4Advertising> map = groupQrcodeDao.getWechatGroupInfoMap(qrcodeIds);
        if (map != null) {

        }
        for (AdvertisingPlanGroup advertisingPlanGroup : advertisingPlanGroups) {
            GroupQrcodeInfo4Advertising gr = map.get(advertisingPlanGroup.getQrcodeId());
            if (gr != null) {
                advertisingPlanGroup.setWeixinGroupName(gr.getGroupName());
            }
        }
    }

    @ParamLog("设置定时任务")
    private void createAdvertisingPlanQuartz(AdvertisingPut advertisingPut) {
        Integer putType = advertisingPut.getPutType();
        if (PutTypeEnum.ONE.value.equals(putType)) {
            putOne(advertisingPut);
        } else if (PutTypeEnum.DAY.value.equals(putType)) {
            putDay(advertisingPut);
        } else if (PutTypeEnum.WEEK.value.equals(putType)) {
            putWeek(advertisingPut);
        }
    }

    @ParamLog("按周设置定时任务")
    private void putWeek(AdvertisingPut advertisingPut) {
        String cron;
        String putTime = advertisingPut.getPutTime();
        try {
            int hour = Integer.parseInt(putTime.substring(0, 2));
            int minute = Integer.parseInt(putTime.substring(3, 5));
            int second = Integer.parseInt(putTime.substring(6, 8));
            String weekS = "";
            int weekDay = advertisingPut.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(ADVERTISING_PUT_PLAN_SCHEDULE_PRE + advertisingPut.getId());
        scheduleJob.setCronExpression(cron);
        CallBackParam callBackParam = new CallBackParam();
        Map<String, Object> paramMap = new HashMap<>();
        //定时任务调用时用到的参数
        paramMap.put("advertisingPutId", advertisingPut.getId());
        callBackParam.setParamMap(paramMap);
        callBackParam.setMethodName("sendAdvertisingPlan");
        callBackParam.setBeanName("advertisingSpaceService");
        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 putDay(AdvertisingPut advertisingPut) {
        String cron;
        String putTime = advertisingPut.getPutTime();
        try {
            int hour = Integer.parseInt(putTime.substring(0, 2));
            int minute = Integer.parseInt(putTime.substring(3, 5));
            int second = Integer.parseInt(putTime.substring(6, 8));
            cron = second + " " + minute + " " + hour + " * * ?";
        } catch (Exception e) {
            throw new BookBizException(BookBizException.ERROR, "时间格式错误");
        }
        ScheduleJob scheduleJob = new ScheduleJob();
        scheduleJob.setJobGroup("book");
        scheduleJob.setJobName(ADVERTISING_PUT_PLAN_SCHEDULE_PRE + advertisingPut.getId());
        scheduleJob.setCronExpression(cron);
        CallBackParam callBackParam = new CallBackParam();
        Map<String, Object> paramMap = new HashMap<>();
        //定时任务调用时用到的参数
        paramMap.put("advertisingPutId", advertisingPut.getId());
        callBackParam.setParamMap(paramMap);
        callBackParam.setMethodName("sendAdvertisingPlan");
        callBackParam.setBeanName("advertisingSpaceService");
        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 putOne(AdvertisingPut advertisingPut) {
        Long second = (DateUtils.StringToDateTime(advertisingPut.getPutTime()).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(ADVERTISING_PUT_PLAN_SCHEDULE_PRE + advertisingPut.getId());
        CallBackParam callBackParam = new CallBackParam();
        Map<String, Object> paramMap = new HashMap<>();
        //定时任务调用时用到的参数
        paramMap.put("advertisingPutId", advertisingPut.getId());
        callBackParam.setParamMap(paramMap);
        callBackParam.setMethodName("sendAdvertisingPlan");
        callBackParam.setBeanName("advertisingSpaceService");
        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, "定时任务设置失败");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("修改广告位计划")
    @Override
    public void updateAdvertisingPlan(AdvertisingPlan advertisingPlan) {
        advertisingPlanCheck.checkCreateAdvertisingPlan(advertisingPlan);
        Long planId = advertisingPlan.getId();
        AdvertisingPlan advertisingPlanOld = advertisingPlanDao.getById(planId);
        if (advertisingPlanOld == null) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "没有找到该计划");
        }
        Long partyId = advertisingPlan.getUpdateUser();
        List<AdvertisingPut> oldAdvertisingPuts = advertisingPutDao.getListByPlanId(planId);
        //先删除旧的定时任务
        deleteAdvertisingPlanQuartz(oldAdvertisingPuts);
        //处理计划项
        List<Long> oldAdvertisingPutIds = oldAdvertisingPuts.stream().map(AdvertisingPut::getId).collect(Collectors.toList());
        List<Long> advertisingPutIdsToDelete = oldAdvertisingPutIds;
        for (AdvertisingPut advertisingPut : advertisingPlan.getAdvertisingPuts()) {
            if (advertisingPut.getId() == null) {
                advertisingPut.setCreateUser(partyId);
                advertisingPut.setUpdateUser(partyId);
                advertisingPut.setAdvertisingPlanId(planId);
                //新增
                advertisingPutDao.insert(advertisingPut);
            } else {
                //修改
                advertisingPut.setUpdateUser(partyId);
                advertisingPutDao.update(advertisingPut);
                advertisingPutIdsToDelete.remove(advertisingPut.getId());
            }
        }
        if (!ListUtils.isEmpty(advertisingPutIdsToDelete)) {
            advertisingPutDao.deleteByIds(advertisingPutIdsToDelete, partyId);
        }
        //处理关联关系
        List<AdvertisingPlanGroup> advertisingPlanGroupsOld = advertisingPlanGroupDao.getListByPlanId(planId);
        List<Long> oldAdvertisingPlanGroupIds = advertisingPlanGroupsOld.stream().map(AdvertisingPlanGroup::getId).collect(Collectors.toList());
        List<Long> advertisingPlanGroupIdsToDelete = oldAdvertisingPlanGroupIds;
        //填充群名称
        fillWeixinGroupId(advertisingPlan.getAdvertisingPlanGroups());
        for (AdvertisingPlanGroup advertisingPlanGroup : advertisingPlan.getAdvertisingPlanGroups()) {
            Long planGroupId = advertisingPlanGroup.getId();
            if (planGroupId == null) {
                advertisingPlanGroup.setCreateUser(partyId);
                advertisingPlanGroup.setUpdateUser(partyId);
                advertisingPlanGroup.setAdvertisingPlanId(planId);
                //新增
                advertisingPlanGroupDao.insert(advertisingPlanGroup);
            } else {
                //修改
                advertisingPlanGroup.setUpdateUser(partyId);
                advertisingPlanGroupDao.update(advertisingPlanGroup);
                advertisingPlanGroupIdsToDelete.remove(planGroupId);
            }
        }
        if (!ListUtils.isEmpty(advertisingPlanGroupIdsToDelete)) {
            advertisingPlanGroupDao.deleteByIds(advertisingPlanGroupIdsToDelete, partyId);
        }
        //设置新的定时任务
        for (AdvertisingPut advertisingPut : advertisingPlan.getAdvertisingPuts()) {
            createAdvertisingPlanQuartz(advertisingPut);
        }

    }

    @ParamLog("发送广告位计划")
    @Override
    public void sendAdvertisingPlan(Long advertisingPutId) {
        AdvertisingPut advertisingPut = advertisingPutDao.getById(advertisingPutId);
        if (advertisingPut == null) {
            return;
        }
        //判断时间范围
        if (PutTypeEnum.WEEK.value.equals(advertisingPut.getPutType()) || PutTypeEnum.DAY.value.equals(advertisingPut.getPutType())) {
            Date dateNow = new Date();
            Date startTime = advertisingPut.getStartTime();
            Date endTime = advertisingPut.getEndTime();
            if (startTime != null && dateNow.before(startTime)) {
                return;
            }
            if (endTime != null && dateNow.after(endTime)) {
                return;
            }
        }
        Long planId = advertisingPut.getAdvertisingPlanId();
        Long spaceId = advertisingPut.getAdvertisingSpaceId();

        //查询关联群
        List<AdvertisingPlanGroup> planGroups = advertisingPlanGroupDao.getListByPlanId(planId);
        if (ListUtils.isEmpty(planGroups)) {
            return;
        }
        //要发送的广告
        AdvertisingSpace advertisingSpace = advertisingSpaceDao.getById(spaceId);
        if (advertisingSpace == null) {
            return;
        }
        List<Long> bookGroupIds = planGroups.stream().filter(s -> s.getBookGroupId() != null).map(AdvertisingPlanGroup::getBookGroupId).distinct().collect(Collectors.toList());
        List<BookGroupDTO> bookGroupDTOS = bookGroupDao.getDTOByIds(bookGroupIds);
        Map<Long, BookGroupDTO> map = new HashMap<>();
        for (BookGroupDTO bookGroupDTO : bookGroupDTOS) {
            map.put(bookGroupDTO.getId(), bookGroupDTO);
        }
        AdvertisingDistributionBook adBook = new AdvertisingDistributionBook();
        adBook.setAdId(spaceId);
        List<DistributionWechatGroup> groups = new ArrayList<>();
        for (AdvertisingPlanGroup advertisingPlanGroup : planGroups) {
            DistributionWechatGroup distributionWechatGroup = new DistributionWechatGroup();
            distributionWechatGroup.setBookId(advertisingPlanGroup.getBookId());
            distributionWechatGroup.setClassifyId(advertisingPlanGroup.getClassifyId());
            distributionWechatGroup.setQrcodeId(advertisingPlanGroup.getQrcodeId());
            BookGroupDTO bookGroupDTO = map.get(advertisingPlanGroup.getBookGroupId());
            if (bookGroupDTO != null) {
                distributionWechatGroup.setChannelId(bookGroupDTO.getChannelId());
                distributionWechatGroup.setAdviserId(bookGroupDTO.getCreateUser());
                groups.add(distributionWechatGroup);
            }
        }
        adBook.setGroups(groups);
        //投放微信群
        distributeWechatGroup(adBook, true);
    }

    @Transactional(rollbackFor = Exception.class)
    @ParamLog("删除广告位计划")
    @Override
    public void deleteAdvertisingPlan(Long advertisingPlanId, Long partyId) {
        //删除计划
        advertisingPlanDao.deleteById(advertisingPlanId);
        //删除关联
        advertisingPlanGroupDao.deleteByPlanId(advertisingPlanId, partyId);
        List<AdvertisingPut> oldAdvertisingPuts = advertisingPutDao.getListByPlanId(advertisingPlanId);
        //先删除旧的定时任务
        deleteAdvertisingPlanQuartz(oldAdvertisingPuts);
        List<Long> putIds = oldAdvertisingPuts.stream().map(AdvertisingPut::getId).collect(Collectors.toList());
        //批量删除广告位定时投放
        advertisingPutDao.deleteByIds(putIds, partyId);
    }

    @ParamLog("获取广告位计划")
    @Override
    public AdvertisingPlan getAdvertisingPlan(Long advertisingPlanId) {
        AdvertisingPlan advertisingPlan = advertisingPlanDao.getById(advertisingPlanId);
        if (advertisingPlan == null) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "没有该广告位计划");
        }
        List<AdvertisingPut> advertisingPuts = advertisingPutDao.getListByPlanId(advertisingPlanId);
        List<AdvertisingPlanGroup> advertisingPlanGroups = advertisingPlanGroupDao.getListByPlanId(advertisingPlanId);
        advertisingPlan.setAdvertisingPlanGroups(advertisingPlanGroups);
        List<Long> spaceIds = advertisingPuts.stream().map(AdvertisingPut::getAdvertisingSpaceId).collect(Collectors.toList());
        List<AdvertisingSpace> advertisingSpaces = advertisingSpaceDao.getByIds(spaceIds);
        Map<Long, AdvertisingSpace> map = new HashMap<>();
        for (AdvertisingSpace advertisingSpace : advertisingSpaces) {
            map.put(advertisingSpace.getId(), advertisingSpace);
        }
        //填充广告位
        for (AdvertisingPut advertisingPut : advertisingPuts) {
            Long spaceId = advertisingPut.getAdvertisingSpaceId();
            advertisingPut.setAdvertisingSpace(map.get(spaceId));
        }
        advertisingPlan.setAdvertisingPuts(advertisingPuts);
        return advertisingPlan;
    }

    @ParamLog("获取广告位计划列表")
    @Override
    public PageBeanNew<AdvertisingPlan> getAdvertisingPlanList(String keyword, Long partyId, Integer currentPage, Integer numPerPage) {
        PageParam pageParam = new PageParam(currentPage, numPerPage);
        Map<String, Object> map = new HashMap<>();
        map.put("keyword", keyword);
        map.put("partyId", partyId);
        PageBeanNew<AdvertisingPlan> page = advertisingPlanDao.listPageNew(pageParam, map, "getAdvertisingPlanList");
        List<AdvertisingPlan> advertisingPlans = page.getRecordList();
        if (!ListUtils.isEmpty(advertisingPlans)) {
            //填充一些信息
            List<Long> planIds = advertisingPlans.stream().map(AdvertisingPlan::getId).collect(Collectors.toList());
            List<AdvertisingPut> advertisingPuts = advertisingPutDao.getListByPlanIds(planIds);
            List<Long> spaceIds = advertisingPuts.stream().filter(s -> s.getAdvertisingSpaceId() != null).map(AdvertisingPut::getAdvertisingSpaceId).distinct().collect(Collectors.toList());
            List<AdvertisingSpace> advertisingSpaces = advertisingSpaceDao.getByIds(spaceIds);
            List<Long> wechatAdIds = advertisingSpaces.stream().filter(s -> AdPositionEnum.WECHAT_GROUP_MSG.positionCode.equals(s.getAdPosition())).map(AdvertisingSpace::getId).collect(Collectors.toList());
            List<AdvertisingWechatMsg> adms = advertisingWechatMsgDao.getByAdIds(wechatAdIds);
            Map<Long, AdvertisingWechatMsg> wechatAdMap = new HashMap<>();
            for (AdvertisingWechatMsg advertisingWechatMsg : adms) {
                wechatAdMap.put(advertisingWechatMsg.getAdId(), advertisingWechatMsg);
            }
            Map<Long, AdvertisingSpace> spaceMap = new HashMap<>();
            for (AdvertisingSpace advertisingSpace : advertisingSpaces) {
                spaceMap.put(advertisingSpace.getId(), advertisingSpace);
            }
            //填充广告位
            for (AdvertisingPut advertisingPut : advertisingPuts) {
                Long spaceId = advertisingPut.getAdvertisingSpaceId();
                AdvertisingSpace as = spaceMap.get(spaceId);
                AdvertisingWechatMsg wechatMsg = wechatAdMap.get(spaceId);
                if (wechatMsg!=null){
                    as.setAdPic(wechatMsg.getAdPic());
                    as.setAdLink(wechatMsg.getAdLink());
                    as.setAdSlogan(wechatMsg.getAdSlogan());
                    as.setAdTitle(wechatMsg.getAdTitle());
                }
                advertisingPut.setAdvertisingSpace(as);
            }
            //每个计划对应的广告项集合
            Map<Long, List<AdvertisingPut>> planPutMap = advertisingPuts.stream().collect(Collectors.groupingBy(AdvertisingPut::getAdvertisingPlanId));
            List<AdvertisingPlanGroup> advertisingPlanGroups = advertisingPlanGroupDao.getListByPlanIds(planIds);
            List<Long> qrcodeIds=new ArrayList<>();
            List<Long> classifyIds=new ArrayList<>();
            List<Long> bookGroupIds=new ArrayList<>();
            for (AdvertisingPlanGroup advertisingPlanGroup:advertisingPlanGroups){
                qrcodeIds.add(advertisingPlanGroup.getQrcodeId());
                classifyIds.add(advertisingPlanGroup.getClassifyId());
                bookGroupIds.add(advertisingPlanGroup.getBookGroupId());
            }
            List<ClassifyDTO> classifyDTOS=bookGroupClassifyDao.getNameWithBookNameByIds(classifyIds);
            List<BookGroupDTO> bookGroupDTOS = bookGroupDao.getBookBaseInfoByIds(bookGroupIds);
            List<WeixinClassifyInfoDTO> weixinClassifyInfoDTOS = groupQrcodeDao.getGroupInfoByQrcodeId(qrcodeIds);
            Map<Long, WeixinClassifyInfoDTO> infoMap = new HashMap<>();
            for (WeixinClassifyInfoDTO weixinClassifyInfoDTO : weixinClassifyInfoDTOS) {
                infoMap.put(weixinClassifyInfoDTO.getQrcodeId(), weixinClassifyInfoDTO);
            }
            Map<Long, String> classifyMap=new HashMap<>();
            for (ClassifyDTO classifyDTO:classifyDTOS){
                classifyMap.put(classifyDTO.getId(),classifyDTO.getClassify());
            }
            Map<Long, BookGroupDTO> bookGroupDTOMap=new HashMap<>();
            for (BookGroupDTO bookGroupDTO:bookGroupDTOS){
                bookGroupDTOMap.put(bookGroupDTO.getId(),bookGroupDTO);
            }
            for (AdvertisingPlanGroup advertisingPlanGroup : advertisingPlanGroups) {
                Long qrcodeId = advertisingPlanGroup.getQrcodeId();
                Long classifyId=advertisingPlanGroup.getClassifyId();
                Long bookGroupId=advertisingPlanGroup.getBookGroupId();
                WeixinClassifyInfoDTO weixinClassifyInfoDTO = infoMap.get(qrcodeId);
                if (weixinClassifyInfoDTO == null) {
                    advertisingPlanGroup.setGroupDelete(true);
                }else {
                    advertisingPlanGroup.setGroupDelete(false);
                }
                advertisingPlanGroup.setClassify(classifyMap.get(classifyId));
                BookGroupDTO bookGroupDTO=bookGroupDTOMap.get(bookGroupId);
                if (bookGroupDTO!=null){
                    advertisingPlanGroup.setBookName(bookGroupDTO.getBookName());
                    advertisingPlanGroup.setGroupQrcodeName(bookGroupDTO.getGroupQrcodeName());
                }
            }
            Map<Long, List<AdvertisingPlanGroup>> planGroupMap = advertisingPlanGroups.stream().collect(Collectors.groupingBy(AdvertisingPlanGroup::getAdvertisingPlanId));
            for (AdvertisingPlan advertisingPlan : advertisingPlans) {
                Long planId = advertisingPlan.getId();
                advertisingPlan.setAdvertisingPuts(planPutMap.get(planId));
                advertisingPlan.setAdvertisingPlanGroups(planGroupMap.get(planId));
            }

        }
        return page;
    }

    @Override
    @ParamLog("完课量埋点")
    public void addPilotTrack(AdvertisingPilotRecord advertisingPilotRecord) {
        AdvertisingSpace space = advertisingSpaceDao.getById(advertisingPilotRecord.getAdId());
        if (null == space) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告位不存在或已删除！");
        }
        if ("WECHAT_GROUP".equals(advertisingPilotRecord.getFromType())) {
            BookGroup bookGroup = bookGroupDao.getById(advertisingPilotRecord.getBookGroupId());
            if (null == bookGroup) {
                return;
            }
            advertisingPilotRecord.setBookId(bookGroup.getBookId());
            advertisingPilotRecord.setChannelId(bookGroup.getChannelId());
            advertisingPilotRecord.setAdviserId(bookGroup.getCreateUser());
        } else {
            //投放书刊
            AdvertisingDistributionBook distributionBook = distributionBookDao.getChannelIdByAdIdBookId(advertisingPilotRecord.getAdId(), advertisingPilotRecord.getBookId());
            if (null != distributionBook) {
                advertisingPilotRecord.setChannelId(distributionBook.getChannelId());
                advertisingPilotRecord.setAdviserId(distributionBook.getAdviserId());
            }
        }
        Long agentId = channelConsr.getParentId(advertisingPilotRecord.getChannelId());
        advertisingPilotRecord.setAgentId(agentId);
        if (!AdPositionEnum.APP_OR_PRODUCT.positionCode.equals(space.getAdPosition())) {
            advertisingPilotRecord.setIsBookGroup(true);
        } else {
            advertisingPilotRecord.setIsBookGroup(false);
        }
        advertisingPilotRecordDao.insert(advertisingPilotRecord);
    }

    @Override
    @ParamLog("浏览时长埋点")
    public String addBrowseTrack(AdvertisingBrowseRecord advertisingBrowseRecord) {
        AdvertisingSpace space = advertisingSpaceDao.getById(advertisingBrowseRecord.getAdId());
        if (null == space) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "广告位不存在或已删除！");
        }
        if (StringUtil.isEmpty(advertisingBrowseRecord.getRecordId())) {
            //新增
            if ("WECHAT_GROUP".equals(advertisingBrowseRecord.getFromType())) {
                BookGroup bookGroup = bookGroupDao.getById(advertisingBrowseRecord.getBookGroupId());
                if (null != bookGroup) {
                    advertisingBrowseRecord.setBookId(bookGroup.getBookId());
                    advertisingBrowseRecord.setChannelId(bookGroup.getChannelId());
                    advertisingBrowseRecord.setAdviserId(bookGroup.getCreateUser());
                }
            } else {
                //投放书刊
                AdvertisingDistributionBook distributionBook = distributionBookDao.getChannelIdByAdIdBookId(advertisingBrowseRecord.getAdId(), advertisingBrowseRecord.getBookId());
                if (null != distributionBook) {
                    advertisingBrowseRecord.setChannelId(distributionBook.getChannelId());
                    advertisingBrowseRecord.setAdviserId(distributionBook.getAdviserId());
                }
            }
            String uuid = UUID.randomUUID().toString();
            advertisingBrowseRecord.setRecordId(uuid);
            advertisingBrowseRecordDao.insert(advertisingBrowseRecord);
            return uuid;
        } else {//修改
            advertisingBrowseRecordDao.addBrowseTimesByRecordId(advertisingBrowseRecord.getRecordId(), advertisingBrowseRecord.getBrowseTimes());
            return advertisingBrowseRecord.getRecordId();
        }
    }

    @ParamLog("删除定时任务")
    private void deleteAdvertisingPlanQuartz(List<AdvertisingPut> advertisingPuts) {
        for (AdvertisingPut advertisingPut : advertisingPuts) {
            try {
                scheduleService.deleteJob(ADVERTISING_PUT_PLAN_SCHEDULE_PRE + advertisingPut.getId(), "book");
            } catch (Exception e) {
                LOGGER.error("删除定时任务失败");
            }
        }
    }

    @ParamLog("填充一些参数")
    private void buildPutAndGroup(AdvertisingPlan advertisingPlan) {
        Long planId = advertisingPlan.getId();
        Long partId = advertisingPlan.getCreateUser();
        for (AdvertisingPut advertisingPut : advertisingPlan.getAdvertisingPuts()) {
            advertisingPut.setAdvertisingPlanId(planId);
            advertisingPut.setCreateUser(partId);
            advertisingPut.setUpdateUser(partId);
        }
        for (AdvertisingPlanGroup advertisingPutGroup : advertisingPlan.getAdvertisingPlanGroups()) {
            advertisingPutGroup.setAdvertisingPlanId(planId);
            advertisingPutGroup.setCreateUser(partId);
            advertisingPutGroup.setUpdateUser(partId);
        }
    }

    @Override
    public PageBean advertisingDistributionDetail4WechatGroup(Long qrcodeId, String statisMonth, Long adId, PageParam pageParam) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("qrcodeId", qrcodeId);
        paramMap.put("adId", adId);
        PageBean pageBean = advertisingDistributionBookDao.listPage(pageParam, paramMap, "advertisingDistributionDetail4WechatGroup");
        if (pageBean == null || ListUtils.isEmpty(pageBean.getRecordList())) {
            return new PageBean(0, 0, new ArrayList<>());
        }
        for (Object object : pageBean.getRecordList()) {
            if (null == object) {
                continue;
            }
            AdvertisingDistributionDTO distributionDTO = (AdvertisingDistributionDTO) object;
            Date distributionTime = distributionDTO.getDistributionTime();
            String dateTime = DateUtils.formatDate(distributionTime, DateUtils.DATE_FORMAT_DATETIME);
            String timeOnly = DateUtils.formatDate(distributionTime, "HH:mm:ss");
            String dateOnly = DateUtils.formatDate(distributionTime, DateUtils.DATE_FORMAT_DATEONLY);
            AdvertisingDistributionDTO dto = advertisingPutDao.getPlanByTime(adId, qrcodeId, dateTime, timeOnly, dateOnly);
            distributionDTO.setAdId(adId);
            if (null != dto) {
                distributionDTO.setPutType(dto.getPutType());
                distributionDTO.setPutTime(dto.getPutTime());
                distributionDTO.setStartTime(dto.getStartTime());
                distributionDTO.setEndTime(dto.getEndTime());
                distributionDTO.setWeekDay(dto.getWeekDay());
            } else {
                distributionDTO.setPutType(PutTypeEnum.ONE.value);
                distributionDTO.setPutTime(dateTime);
            }
        }
        return pageBean;
    }

    @Override
    public void updateExposureByQrcode(Long qrcodeId, Integer peopleCounts) {
        advertisingExposureRecordDao.updateExposureByQrcode(qrcodeId, peopleCounts);
    }

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

    @Override
    public void createWechatGroupMsg(List<AdvertisingWechatMsg> advertisingWechatMsgList) {
        Long adId = advertisingWechatMsgList.get(0).getAdId();
        AdvertisingSpaceDTO spaceDTO = advertisingSpaceDao.getDTOById(adId);
        if (null == spaceDTO) {
            throw new BookBizException(BookBizException.ERROR, "广告位不存在");
        }
        AdvertisingSettlementMethodDTO methodDTO = advertisingSettlementMethodDao.getByMasterIdAndAdId(spaceDTO.getMasterId(), adId);
        if (null != methodDTO && SettlementMethodEnum.CPA.code.equals(methodDTO.getSettlementMethod())) {
            for (AdvertisingWechatMsg wechatMsg : advertisingWechatMsgList) {//目前仅限一条消息
                //图文分享必须关联cpa内容
                if (AdPositionModeEnum.NEWS_SHARE.modeCode.equals(wechatMsg.getAdPositionMode()) &&
                        null == wechatMsg.getCpaId()) {
                    throw new BookBizException(BookBizException.ERROR, "缺少cpaId");

                }
                advertisingSpaceDao.updateCPAId(adId, wechatMsg.getCpaId());
            }
        }
        advertisingWechatMsgDao.deleteByAdId(adId);
        advertisingWechatMsgDao.insert(advertisingWechatMsgList);
    }

    @Override
    public Long createCPAContent(AdvertisingCpa advertisingCpa) {
        advertisingCheck.checkCPAParam(advertisingCpa);
        advertisingCpaDao.insert(advertisingCpa);
        Long cpaId = advertisingCpa.getId();
        //配置试听试看参数
        advertisingCpa = equipCPA(advertisingCpa);
        //广告详情,报名表单选项新增
        bmBiz.addOptionList(advertisingCpa.getAdvertisingBmOptionList(), cpaId);
        //音视频保存
        if (!AdDetailModeEnum.IMG_TEXT.code.equals(advertisingCpa.getDetailMode())) {
            addAdvertisingDetailFile(cpaId, advertisingCpa.getAdvertisingDetailFileList());
        }
        //更新预览链接
        String previewLink = wechatGroupLinkPrefix + "/ad?cpa_id=" + cpaId;
        previewLink = UrlUtils.getShortUrl4Own(wechatGroupLinkPrefix + "/link?url=" + URLEncoder.encode(previewLink));
        AdvertisingCpa cpaUpdate = new AdvertisingCpa();
        cpaUpdate.setId(cpaId);
        cpaUpdate.setPreviewLink(previewLink);
        advertisingCpaDao.update(cpaUpdate);
        return cpaId;
    }

    @Override
    public void updateCPAContent(AdvertisingCpa advertisingCpa) {
        advertisingCpa = equipCPA(advertisingCpa);
        Long cpaId = advertisingCpa.getId();
        //广告详情,报名表单选项新增
        bmBiz.addOptionList(advertisingCpa.getAdvertisingBmOptionList(), cpaId);
        //音视频保存
        detailFileDao.deleteByCPAId(cpaId);
        if (!AdDetailModeEnum.IMG_TEXT.code.equals(advertisingCpa.getDetailMode())) {
            addAdvertisingDetailFile(cpaId, advertisingCpa.getAdvertisingDetailFileList());
        }
        advertisingCpaDao.update(advertisingCpa);
    }

    @Override
    public void deleteCPAContent(Long cpaId) {
        List<Long> adIds = advertisingSpaceDao.getAdIdsByCPAId(cpaId);
        if (!ListUtils.isEmpty(adIds)) {
            throw new BookBizException(BookBizException.ERROR, "无法删除，该内容已经投放");
        }
        advertisingCpaDao.deleteById(cpaId);
        detailFileDao.deleteByCPAId(cpaId);
        bmBiz.deleteByCPAId(cpaId);
    }

    @Override
    public AdvertisingCpaDTO getCPAContentById(Long cpaId) {
        AdvertisingCpaDTO cpaDTO = new AdvertisingCpaDTO();
        AdvertisingCpa advertisingCpaDTO = advertisingCpaDao.getById(cpaId);
        if (null == advertisingCpaDTO) {
            throw new BookBizException(BookBizException.ERROR, "CPA内容不存在");
        }
        BeanUtils.copyProperties(advertisingCpaDTO, cpaDTO);
        //填充报名表单,多个上传文件
        cpaDTO.setAdvertisingBmOptionList(bmBiz.getByCPAId(cpaId));
        List<AdvertisingDetailFile> fileList = detailFileDao.getByCPAId(cpaId);
        if (!ListUtils.isEmpty(fileList)) {
            cpaDTO.setAdvertisingDetailFileList(fileList);
        }
        return cpaDTO;
    }

    @Override
    public PageBeanNew<AdvertisingCpaDTO> listPageCPAContent(String title, String detailMode, Integer currentPage, Integer numPerPage) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("name", title);
        paramMap.put("detailMode", detailMode);
        PageBeanNew<AdvertisingCpaDTO> pageBeanNew = advertisingCpaDao.listPageNew(new PageParam(currentPage, numPerPage), paramMap, "listPage");
        if (pageBeanNew == null || ListUtils.isEmpty(pageBeanNew.getRecordList())) {
            return new PageBeanNew<>(currentPage, numPerPage, new ArrayList<>());
        }
        for (AdvertisingCpaDTO cpaDTO : pageBeanNew.getRecordList()) {
            Long cpaId = cpaDTO.getId();
            //曝光量点击量点击率点击人数
            cpaDTO.setExposureNum(advertisingExposureRecordDao.getExposureNumByCPAId(cpaId));
            Long clickNum = advertisingClickRecordDao.getClickNumByCPAId(cpaId);
            cpaDTO.setClickNum(clickNum);
            BigDecimal clickRate = (null == cpaDTO.getExposureNum() || cpaDTO.getExposureNum().equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(clickNum.doubleValue() / cpaDTO.getExposureNum()).setScale(4, BigDecimal.ROUND_HALF_UP);
            cpaDTO.setClickRate(clickRate);
            Long clickUserNum = advertisingClickRecordDao.getClickUserNumByCPAId(cpaId);
            cpaDTO.setClickUserNum(clickUserNum);
            //报名量报名率
            List<Long> adIdList = advertisingSpaceDao.getAdIdsByCPAId(cpaId);
            Long registerNum = bmBiz.getRegisterCountByAdIds(adIdList);
            BigDecimal registerRate = (null == clickNum || clickNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(registerNum.doubleValue() / clickNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            cpaDTO.setRegisterNum(registerNum);
            cpaDTO.setRegisterRate(registerRate);
            //完课量完课率
            Long pilotFinishUserNum = advertisingPilotRecordDao.getPilotUserNumByCPAId(cpaId);
            BigDecimal pilotFinishRate = (null == clickUserNum || clickUserNum.equals(0L)) ? BigDecimal.ZERO :
                    new BigDecimal(pilotFinishUserNum.doubleValue() / clickUserNum).setScale(4, BigDecimal.ROUND_HALF_UP);
            cpaDTO.setPilotFinishUserNum(pilotFinishUserNum);
            cpaDTO.setPilotFinishRate(pilotFinishRate);
        }
        return pageBeanNew;
    }

    @Override
    public PageBeanNew<AdvertisingCpaDTO> listPageCPALink(String title, String detailMode, Integer currentPage, Integer numPerPage) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("name", title);
        paramMap.put("detailMode", detailMode);
        PageBeanNew<AdvertisingCpaDTO> pageBeanNew = advertisingCpaDao.listPageNew(new PageParam(currentPage, numPerPage), paramMap, "listPage");
        if (pageBeanNew == null || ListUtils.isEmpty(pageBeanNew.getRecordList())) {
            return new PageBeanNew<>(currentPage, numPerPage, new ArrayList<>());
        }
        return pageBeanNew;
    }

    @Override
    public List<AdvertisingWechatMsgDTO> getWechatGroupMsgListByAdId(Long adId) {
        List<AdvertisingWechatMsg> msgList = advertisingWechatMsgDao.getByAdId(adId);
        if (ListUtils.isEmpty(msgList)) {
            return new ArrayList<>();
        }
        List<AdvertisingWechatMsgDTO> list = new ArrayList<>();
        for (AdvertisingWechatMsg msg : msgList) {
            AdvertisingWechatMsgDTO msgDTO = new AdvertisingWechatMsgDTO();
            BeanUtils.copyProperties(msg, msgDTO);
            list.add(msgDTO);
        }
        return list;
    }

    @Override
    public void dealOldCPAContent() {
        List<AdvertisingSpace> spaceList = advertisingSpaceDao.getCPASpaceList();
        if (!ListUtils.isEmpty(spaceList)) {
            for (AdvertisingSpace space : spaceList) {
                Long adId = space.getId();
                AdvertisingCpa cpa = new AdvertisingCpa();
                cpa.setDetailMode(space.getAdDetailMode());
                cpa.setDetailName(space.getAdName());
                cpa.setCoverPic(space.getAdDetailCoverPic());
                cpa.setWelfarePic(space.getAdDetailWelfarePic());
                cpa.setDetailInfo(space.getAdDetailInfo());
                cpa.setFormMobileCheck(space.getAdFormMobileCheck());
                cpa.setFormRemark(space.getAdFormRemark());
                cpa.setFormButton(space.getAdFormButton());
                cpa.setFormLink(space.getAdFormLink());
                advertisingCpaDao.insert(cpa);
                Long cpaId = cpa.getId();
                //更新到广告位、报名信息、广告详情
                advertisingSpaceDao.updateCPAId(adId, cpaId);
                bmOptionDao.updateCPAId(adId, cpaId);
                detailFileDao.updateCPAId(adId, cpaId);
            }
        }
        List<Long> linkCpaIds = advertisingCpaDao.getNoPreviewLinkCPAList();
        if (!ListUtils.isEmpty(linkCpaIds)) {
            for (Long cpaId : linkCpaIds) {
                //预览链接
                String previewLink = wechatGroupLinkPrefix + "/ad?cpa_id=" + cpaId;
                previewLink = UrlUtils.getShortUrl4Own(wechatGroupLinkPrefix + "/link?url=" + URLEncoder.encode(previewLink));
                AdvertisingCpa cpaUpdate = new AdvertisingCpa();
                cpaUpdate.setId(cpaId);
                cpaUpdate.setPreviewLink(previewLink);
                advertisingCpaDao.update(cpaUpdate);
            }
        }
    }

}
