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

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.pcloud.appcenter.app.dto.AppDto;
import com.pcloud.book.base.exception.BookBizException;
import com.pcloud.book.book.biz.BookKeywordWarehouseBiz;
import com.pcloud.book.book.dao.BookDao;
import com.pcloud.book.book.dao.SearchRecordDao;
import com.pcloud.book.book.dto.BookDto;
import com.pcloud.book.book.entity.SearchRecord;
import com.pcloud.book.consumer.app.AppConsr;
import com.pcloud.book.consumer.channel.QrcodeSceneConsr;
import com.pcloud.book.consumer.content.ResourceConsr;
import com.pcloud.book.consumer.live.LiveCons;
import com.pcloud.book.consumer.resource.ProductConsr;
import com.pcloud.book.consumer.user.AdviserConsr;
import com.pcloud.book.consumer.user.ChannelConsr;
import com.pcloud.book.consumer.wechatgroup.WechatGroupConsr;
import com.pcloud.book.group.biz.BookGroupAppBiz;
import com.pcloud.book.group.biz.BookGroupBiz;
import com.pcloud.book.group.biz.BookGroupClassifyBiz;
import com.pcloud.book.group.biz.GroupQrcodeBiz;
import com.pcloud.book.group.biz.WeixinQrcodeBiz;
import com.pcloud.book.group.dao.AppTouchRecordDao;
import com.pcloud.book.group.dao.BookGroupCipherUserDao;
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.dao.WxUserWechatRelevanceDao;
import com.pcloud.book.group.dto.BookGroupDTO;
import com.pcloud.book.group.dto.BookWxQrcodeDTO;
import com.pcloud.book.group.dto.GroupClassifyQrcodeDTO;
import com.pcloud.book.group.entity.AppTouchRecord;
import com.pcloud.book.group.entity.BookGroup;
import com.pcloud.book.group.entity.BookGroupCipherUser;
import com.pcloud.book.group.entity.BookGroupClassify;
import com.pcloud.book.group.entity.BookGroupServe;
import com.pcloud.book.group.entity.GroupQrcode;
import com.pcloud.book.group.enums.AppAndProductTypeEnum;
import com.pcloud.book.group.enums.JoinGroupTypeEnum;
import com.pcloud.book.group.enums.QrcodeStatusEnum;
import com.pcloud.book.group.enums.TouchTypeEnum;
import com.pcloud.book.group.enums.UpdateStatusEnum;
import com.pcloud.book.group.tools.SendWeixinRequestTools;
import com.pcloud.book.group.vo.ClassifyNameVO;
import com.pcloud.book.group.vo.ClassifyVO;
import com.pcloud.book.group.vo.ListClassifyVO;
import com.pcloud.book.guide.dto.PcloudGuideDelayDto;
import com.pcloud.book.guide.dto.WakeUpInfoDto;
import com.pcloud.book.guide.mapper.PcloudRobotSilenceMapper;
import com.pcloud.book.keywords.biz.BookKeywordBiz;
import com.pcloud.book.keywords.dao.*;
import com.pcloud.book.keywords.dto.KeywordDTO;
import com.pcloud.book.keywords.dto.KeywordStatisticsDTO;
import com.pcloud.book.keywords.dto.ReplyKeywordDTO;
import com.pcloud.book.keywords.dto.ServiceResourceDTO;
import com.pcloud.book.keywords.entity.*;
import com.pcloud.book.keywords.enums.MessageTypeEnum;
import com.pcloud.book.keywords.enums.ReplyTypeEnum;
import com.pcloud.book.keywords.vo.DeleteKeywordVO;
import com.pcloud.book.keywords.vo.KeywordVO;
import com.pcloud.book.keywords.vo.ListKeywordParam;
import com.pcloud.book.keywords.vo.ListKeywordVO;
import com.pcloud.book.keywords.vo.QrWeixinParam;
import com.pcloud.book.keywords.vo.SetKeywordVO;
import com.pcloud.book.keywords.vo.SetRankVO;
import com.pcloud.book.keywords.vo.UpdateKeywordVO;
import com.pcloud.book.pcloudkeyword.biz.PcloudRobotBiz;
import com.pcloud.book.pcloudkeyword.dao.PcloudKeywordClassifyDao;
import com.pcloud.book.pcloudkeyword.dao.PcloudKeywordDao;
import com.pcloud.book.pcloudkeyword.dao.PcloudKeywordNameDao;
import com.pcloud.book.pcloudkeyword.dao.PcloudKeywordReplyDao;
import com.pcloud.book.pcloudkeyword.dao.PcloudNotKeywordDao;
import com.pcloud.book.pcloudkeyword.dao.PcloudRobotClassifyDao;
import com.pcloud.book.pcloudkeyword.dao.PcloudRobotDao;
import com.pcloud.book.pcloudkeyword.entity.PcloudKeyword;
import com.pcloud.book.pcloudkeyword.entity.PcloudKeywordClassify;
import com.pcloud.book.pcloudkeyword.entity.PcloudKeywordName;
import com.pcloud.book.pcloudkeyword.entity.PcloudKeywordReply;
import com.pcloud.book.pcloudkeyword.entity.PcloudNotKeyword;
import com.pcloud.book.pcloudkeyword.entity.PcloudRobot;
import com.pcloud.book.pcloudkeyword.entity.PcloudRobotClassify;
import com.pcloud.book.pcloudkeyword.enums.KeywordTypeEnum;
import com.pcloud.book.pcloudkeyword.enums.MethodEnum;
import com.pcloud.book.pcloudkeyword.enums.RelevanceTypeEnum;
import com.pcloud.book.pcloudkeyword.set.PcloudRobotSet;
import com.pcloud.book.personalstage.biz.PersonalStageBiz;
import com.pcloud.book.personalstage.biz.PersonalStageJumpBiz;
import com.pcloud.book.personalstage.enums.JumpTypeEnum;
import com.pcloud.book.reading.biz.ReadingActivityBiz;
import com.pcloud.book.util.common.ThreadPoolUtils;
import com.pcloud.channelcenter.base.constants.ChannelEnum;
import com.pcloud.channelcenter.qrcode.service.QrcodeSceneService;
import com.pcloud.channelcenter.qrcode.vo.MessageBookVO;
import com.pcloud.channelcenter.qrcode.vo.QrWeixinParamVO;
import com.pcloud.channelcenter.wechat.dto.AccountSettingDto;
import com.pcloud.common.constant.CacheConstant;
import com.pcloud.common.constant.UrlConstant;
import com.pcloud.common.core.aspect.ParamLog;
import com.pcloud.common.page.PageBeanNew;
import com.pcloud.common.page.PageParam;
import com.pcloud.common.utils.DateUtils;
import com.pcloud.common.utils.ListUtils;
import com.pcloud.common.utils.ResponseHandleUtil;
import com.pcloud.common.utils.bean.ResponesUtils;
import com.pcloud.common.utils.cache.redis.JedisClusterUtils;
import com.pcloud.common.utils.httpclient.UrlUtils;
import com.pcloud.common.utils.string.StringUtil;
import com.pcloud.contentcenter.resource.dto.ResourceDTO;
import com.pcloud.liveapp.live.dto.CourseListDto;
import com.pcloud.resourcecenter.product.dto.ProductDto;
import com.pcloud.resourcecenter.product.dto.ProductTypeDto;
import com.pcloud.wechatgroup.group.dto.RobotReplyDTO;
import com.pcloud.wechatgroup.message.dto.SendTextDTO;
import com.sdk.wxgroup.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
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 org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.pcloud.book.guide.constant.PcloudGuideRedisConstant.PCLOUD_GUIDE_SUFFIX;
import static com.pcloud.book.guide.constant.PcloudGuideRedisConstant.PCLOUD_ROBOT_WAKE_UP_SUFFIX;

/**
 * @author lily
 * @date 2019/4/25 10:47
 */
@Slf4j
@Component("bookKeywordBiz")
public class BookKeywordBizImpl implements BookKeywordBiz {
    @Autowired
    private KeywordDao keywordDao;
    @Autowired
    private BookGroupDao bookGroupDao;
    @Autowired
    private BookKeywordDao bookKeywordDao;
    @Autowired
    private BookGroupClassifyBiz bookGroupClassifyBiz;
    @Autowired
    private QrcodeSceneConsr qrcodeSceneConsr;
    @Autowired
    private WechatGroupConsr wechatGroupConsr;
    @Autowired
    private BookKeywordRecordDao bookKeywordRecordDao;
    @Autowired
    private ProductConsr productConsr;
    @Autowired
    private AppConsr appConsr;
    @Autowired
    private AppTouchRecordDao appTouchRecordDao;
    @Autowired
    private GroupQrcodeDao groupQrcodeDao;
    @Resource
    private BookKeywordWarehouseBiz bookKeywordWarehouseBiz;
    @Autowired
    private BookGroupClassifyDao bookGroupClassifyDao;
    @Autowired
    private QrcodeSceneService qrcodeSceneService;
    @Autowired
    private BookGroupBiz bookGroupBiz;
    @Autowired
    private GroupQrcodeBiz groupQrcodeBiz;
    @Autowired
    private LiveCons liveCons;
    @Autowired
    private BookGroupCipherUserDao bookGroupCipherUserDao;
    @Autowired
    private WeixinQrcodeBiz weixinQrcodeBiz;
    @Autowired
    private BookGroupAppBiz bookGroupAppBiz;
    @Autowired
    private WxUserWechatRelevanceDao wxUserWechatRelevanceDao;
    @Autowired
    private SelfRobotKeywordDao selfRobotKeywordDao;
    @Autowired
    private ResourceConsr resourceConsr;
    @Value("${system.env}")
    private String envStr;
    @Autowired
    private SelfRobotKeywordReplyDao selfRobotKeywordReplyDao;
    @Autowired
    private BookDao bookDao;
    @Autowired
    private SelfRobotKeywordReplyTemplateDao selfRobotKeywordReplyTemplateDao;
    @Autowired
    private ChannelConsr channelConsr;
    @Autowired
    private SelfRobotBookRecordDao selfRobotBookRecordDao;
    @Autowired
    private PcloudKeywordNameDao pcloudKeywordNameDao;

    @Autowired
    private ReadingActivityBiz readingActivityBiz;
    @Autowired
    private NotKeywordTemplateDao notKeywordTemplateDao;
    @Autowired
    private NotKeywordItemDao notKeywordItemDao;
    @Autowired
    private PcloudKeywordDao pcloudKeywordDao;
    @Autowired
    private PcloudKeywordReplyDao pcloudKeywordReplyDao;
    @Autowired
    private PcloudNotKeywordDao pcloudNotKeywordDao;
    @Autowired
    private PcloudKeywordClassifyDao pcloudKeywordClassifyDao;
    @Autowired
    private PcloudRobotDao pcloudRobotDao;
    @Autowired
    private PcloudRobotSilenceMapper pcloudRobotSilenceMapper;

    @Value("${wechat.group.link.prefix}")
    private String wechatGroupLinkPrefix;
    @Autowired
    private PcloudRobotBiz pcloudRobotBiz;
    @Autowired
    private PcloudRobotClassifyDao pcloudRobotClassifyDao;
    @Autowired
    private SearchRecordDao searchRecordDao;
    @Autowired
    private AdviserConsr adviserConsr;
    @Autowired
    private PcloudRobotSet pcloudRobotSet;
    @Autowired
    private PersonalStageBiz personalStageBiz;
    @Autowired
    private PersonalStageJumpBiz personalStageJumpBiz;

    /**
     * 字符串切割长度
     */
    private static final Integer LE = 1000;

    private static final String REPLY_GUIDE_CACHE = "BOOK:replyGuideCache";

    private static final String REPLY_ERROR_SHORT_BOOK_NAME_CACHE = "BOOK:replyErrorShortBookNameCache";

    @Override
    @ParamLog("新增关键词")
    @Transactional(rollbackFor = Exception.class)
    public void insertKeyword(SetKeywordVO setKeywordVO) {
        if (null == setKeywordVO || setKeywordVO.check()) {
            throw new BookBizException(BookBizException.PARAM_IS_NULL, "应用/作品信息为空");
        }
        if (setKeywordVO.getContent() != null && setKeywordVO.getContent().length() > 1000) {
            throw new BookBizException(BookBizException.PARAM_IS_NULL, "回复内容超出限制！");
        }
        if (setKeywordVO.getClassifyId() == null) {
            setKeywordVO.setClassifyId(0L);
        }
        //获取图书相关信息
        BookGroupDTO bookGroupDTO = bookGroupDao.getDTOById(setKeywordVO.getBookGroupId());
        if (bookGroupDTO == null) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "该社群码不存在！");
        }
        //校验关键词
        checkKeyword(setKeywordVO.getKeywords(), setKeywordVO.getClassifyId(), setKeywordVO.getBookGroupId(), null);
        Integer count = bookKeywordDao.getKeywordCount(setKeywordVO.getBookGroupId(), setKeywordVO.getClassifyId());
        if (count >= 200) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "超出关键词数量限制");
        }
        //新增关键词
        Keyword keyword = new Keyword();
        BeanUtils.copyProperties(setKeywordVO, keyword);
        keyword.setCreateUser(setKeywordVO.getUserId());
        keywordDao.insert(keyword);
        //上架商品
        List<Long> productIds = new ArrayList<>();
        if (ReplyTypeEnum.APP.value.equals(keyword.getReplyType()) && "PRODUCT".equalsIgnoreCase(keyword.getServeType())) {
            productIds.add(keyword.getServeId());
        }
        productConsr.productAutoOnShelves(bookGroupDTO.getChannelId(), productIds);
        //新增关键词与分类或者图书的关系
        //按群创建
        if (setKeywordVO.getSetType() == 1) {
            BookKeyword bookKeyword = new BookKeyword();
            BeanUtils.copyProperties(setKeywordVO, bookKeyword);
            bookKeyword.setKeywordId(keyword.getId());
            bookKeyword.setBookId(bookGroupDTO.getBookId());
            bookKeyword.setChannelId(bookGroupDTO.getChannelId());
            //获取最大排序值
            Integer rank = bookKeywordDao.getMaxRank(setKeywordVO.getBookGroupId(), bookKeyword.getClassifyId());
            bookKeyword.setRank(rank + 1);
            bookKeyword.setCreateUser(setKeywordVO.getUserId());
            if (null != setKeywordVO.getAddWarehouse() && 1 == setKeywordVO.getAddWarehouse()) {
                setKeywordVO.setChannelId(bookGroupDTO.getChannelId());
                final Long warehouseId = bookKeywordWarehouseBiz.addBookKeyword(setKeywordVO);
                bookKeyword.setWarehouseId(warehouseId);
                bookKeyword.setIsWarehouse(1);
            }
            if (null == bookKeyword.getIsWarehouse()) {
                bookKeyword.setIsWarehouse(0);
            }
            if (null == bookKeyword.getWarehouseId()) {
                bookKeyword.setWarehouseId(0L);
            }
            bookKeyword.setIsEdit(1);
            bookKeywordDao.insert(bookKeyword);
            bookKeyword.setKeyword(keyword);
            bookGroupAppBiz.addAppKeywordToBookGroupApp(Arrays.asList(bookKeyword));
        } else {
            //按书创建
            List<ClassifyNameVO> classifyNameVOS = bookGroupClassifyDao.listClassifyByRank(setKeywordVO.getBookGroupId());
            List<BookKeyword> bookKeywords = new ArrayList<>();
            //创建书的记录
            BookKeyword bookKeyword = new BookKeyword();
            BeanUtils.copyProperties(setKeywordVO, bookKeyword);
            bookKeyword.setKeywordId(keyword.getId());
            bookKeyword.setBookId(bookGroupDTO.getBookId());
            bookKeyword.setChannelId(bookGroupDTO.getChannelId());
            //获取最大排序值
            Integer rank = bookKeywordDao.getMaxRank(setKeywordVO.getBookGroupId(), bookKeyword.getClassifyId());
            bookKeyword.setRank(rank + 1);
            bookKeyword.setCreateUser(setKeywordVO.getUserId());
            if (null != setKeywordVO.getAddWarehouse() && 1 == setKeywordVO.getAddWarehouse()) {
                setKeywordVO.setChannelId(bookGroupDTO.getChannelId());
                final Long warehouseId = bookKeywordWarehouseBiz.addBookKeyword(setKeywordVO);
                bookKeyword.setWarehouseId(warehouseId);
                bookKeyword.setIsWarehouse(1);
            }
            //创建群的记录
            if (!ListUtils.isEmpty(classifyNameVOS)) {
                classifyNameVOS.forEach(e -> {
                    BookKeyword cKeyword = new BookKeyword();
                    BeanUtils.copyProperties(setKeywordVO, cKeyword);
                    cKeyword.setKeywordId(keyword.getId());
                    cKeyword.setBookId(bookGroupDTO.getBookId());
                    cKeyword.setChannelId(bookGroupDTO.getChannelId());
                    cKeyword.setClassifyId(e.getId());
                    //获取最大排序值
                    Integer rank1 = bookKeywordDao.getMaxRank(setKeywordVO.getBookGroupId(), cKeyword.getClassifyId());
                    cKeyword.setRank(rank1 + 1);
                    cKeyword.setCreateUser(setKeywordVO.getUserId());
                    if (null != setKeywordVO.getAddWarehouse() && 1 == setKeywordVO.getAddWarehouse()) {
                        // 书入库一遍，同样的就不要入库了
//                        setKeywordVO.setChannelId(bookGroupDTO.getChannelId());
//                        final Long warehouseId = bookKeywordWarehouseBiz.addBookKeyword(setKeywordVO);
                        cKeyword.setWarehouseId(bookKeyword.getWarehouseId());
                        cKeyword.setIsWarehouse(1);
                    }
                    if (null == cKeyword.getIsWarehouse()) {
                        cKeyword.setIsWarehouse(0);
                    }
                    if (null == cKeyword.getWarehouseId()) {
                        cKeyword.setWarehouseId(0L);
                    }
                    if (0 == cKeyword.getClassifyId()) {
                        cKeyword.setIsEdit(1);
                    } else {
                        cKeyword.setIsEdit(0);
                    }
                    cKeyword.setKeyword(keyword);
                    bookKeywords.add(cKeyword);
                });
            }
            if (null == bookKeyword.getIsWarehouse()) {
                bookKeyword.setIsWarehouse(0);
            }
            if (null == bookKeyword.getWarehouseId()) {
                bookKeyword.setWarehouseId(0L);
            }
            if (0 == bookKeyword.getClassifyId()) {
                bookKeyword.setIsEdit(1);
            } else {
                bookKeyword.setIsEdit(0);
            }
            bookKeyword.setKeyword(keyword);
            bookKeywords.add(bookKeyword);
            bookKeywordDao.insert(bookKeywords);
            bookGroupAppBiz.addAppKeywordToBookGroupApp(bookKeywords);
        }

    }

    @Override
    @ParamLog("批量新增关键词")
    @Transactional(rollbackFor = {Exception.class})
    public void insertKeywords(List<SetKeywordVO> setKeywordVOs, Long partyId) {
        if (CollectionUtils.isEmpty(setKeywordVOs) || null == partyId) {
            log.info("[insertKeywords] : setKeywordVOs:{}, partyId:{}", setKeywordVOs, partyId);
            return;
        }
        for (SetKeywordVO vo : setKeywordVOs) {
            vo.setUserId(partyId);
            this.insertKeyword(vo);
        }
    }

    @ParamLog("校验关键词是否重复")
    private void checkKeyword(String keywords, Long classifyId, Long bookGroupId, Long keywordId) {
        Boolean isHaveKeyword = bookKeywordDao.checkKeyword(keywords, classifyId, bookGroupId, keywordId);
        if (isHaveKeyword) {
            throw new BookBizException(BookBizException.ERROR, "关键词" + keywords + "已存在！");
        }
    }

    @Override
    @ParamLog("更新关键词")
    @Transactional(rollbackFor = Exception.class)
    public void updateKeyword(UpdateKeywordVO updateKeywordVO, Long partyId) {
        checkKeyword(updateKeywordVO.getKeywords(), updateKeywordVO.getClassifyId(), updateKeywordVO.getBookGroupId(), updateKeywordVO.getKeywordId());
        //获取图书相关信息
        BookGroupDTO bookGroupDTO = bookGroupDao.getDTOById(updateKeywordVO.getBookGroupId());
        if (bookGroupDTO == null) {
            throw new BookBizException(BookBizException.PARAM_IS_ERROR, "该社群码不存在！");
        }
        if (null != updateKeywordVO.getAddWarehouse() && 1 == updateKeywordVO.getAddWarehouse()) {
            SetKeywordVO v = new SetKeywordVO();
            BeanUtils.copyProperties(updateKeywordVO, v);
            if (ChannelEnum.APP.value.equals(updateKeywordVO.getServeType())) {
                final AppDto baseById = appConsr.getBaseById(updateKeywordVO.getServeId());
                if (null == baseById) {
                    throw new BookBizException(BookBizException.ID_NOT_EXIST, "应用不存在");
                }
                v.setTypeCode(baseById.getTypeCode());
            }
            if (ChannelEnum.PRODUCT.value.equals(updateKeywordVO.getServeType())) {
                final ProductDto proBaseById = productConsr.getProBaseById(updateKeywordVO.getServeId());
                if (null == proBaseById) {
                    throw new BookBizException(BookBizException.ID_NOT_EXIST, "作品不存在");
                }
                v.setTypeCode(proBaseById.getProductTypeCode());
            }
            v.setUserId(partyId);
            v.setChannelId(bookGroupDTO.getChannelId());
            final Long warehouseId = this.bookKeywordWarehouseBiz.addBookKeyword(v);
            this.bookKeywordDao.updateIsWarehouse(updateKeywordVO.getBookKeywordId(), warehouseId);
        }
        Keyword keyword = new Keyword();
        BeanUtils.copyProperties(updateKeywordVO, keyword);
        keyword.setId(updateKeywordVO.getKeywordId());
        keyword.setUpdateUser(partyId);
        keywordDao.update(keyword);
        //上架商品
        List<Long> productIds = new ArrayList<>();
        if (ReplyTypeEnum.APP.value.equals(keyword.getReplyType()) && ChannelEnum.PRODUCT.value.equalsIgnoreCase(keyword.getServeType())) {
            productIds.add(keyword.getServeId());
        }
        productConsr.productAutoOnShelves(bookGroupDTO.getChannelId(), productIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @ParamLog("删除关键词")
    public void deleteKeyword(DeleteKeywordVO deleteKeywordVO, Long partyId) {
        if (deleteKeywordVO == null || deleteKeywordVO.getIsEdit() == null || deleteKeywordVO.getKeywordId() == null) {
            throw new BookBizException(BookBizException.ERROR, "缺少必要参数");
        }
        if (deleteKeywordVO.getClassifyId() == null) {
            deleteKeywordVO.setClassifyId(0L);
        }
        if (deleteKeywordVO.getIsEdit() == 1) {
            keywordDao.deleteById(deleteKeywordVO.getKeywordId(), partyId);
            bookKeywordDao.deleteById(deleteKeywordVO.getKeywordId(), partyId);
        } else {
            bookKeywordDao.deleteKeywords(deleteKeywordVO.getKeywordId(), deleteKeywordVO.getBookGroupId(), deleteKeywordVO.getClassifyId(), partyId);
        }

    }

    @Override
    @ParamLog("设置排序值")
    public void setRank(SetRankVO setRankVO, Long partyId) {
        BookKeyword bookKeyword = new BookKeyword();
        bookKeyword.setId(setRankVO.getBookKeywordId());
        bookKeyword.setRank(setRankVO.getRank());
        bookKeyword.setUpdateUser(partyId);
        KeywordVO byBookKeyword = bookKeywordDao.getByBookKeyword(setRankVO.getBookKeywordId());
        if (byBookKeyword != null && (byBookKeyword.getClassifyId() == null || byBookKeyword.getClassifyId() == 0)) {
            Long bookGroupId = byBookKeyword.getBookGroupId();
            Long keywordId = byBookKeyword.getKeywordId();
            bookKeywordDao.updateRank(bookGroupId, setRankVO.getRank(), partyId, keywordId);
        } else {
            bookKeywordDao.setRank(bookKeyword);
        }
    }

    @Override
    @ParamLog(value = "获取关键词列表", isAfterReturn = false)
    public PageBeanNew<ListKeywordVO> listKeywordsByClassify(ListKeywordParam listKeywordParam) {
        PageParam pageParam = new PageParam(listKeywordParam.getCurrentPage(), listKeywordParam.getNumPerPage());
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("keywords", listKeywordParam.getKeywords());
        paramMap.put("classifyId", listKeywordParam.getClassifyId());
        paramMap.put("bookGroupId", listKeywordParam.getBookGroupId());
        PageBeanNew<ListKeywordVO> keywords = bookKeywordDao.listPageNew(pageParam, paramMap, "listKeywordsByClassify");
        if (keywords == null) {
            return new PageBeanNew<>(listKeywordParam.getCurrentPage(), listKeywordParam.getNumPerPage(), new ArrayList<>());
        }
        final List<Long> appIds = Lists.newArrayList();
        final List<Long> productIds = Lists.newArrayList();
        for (ListKeywordVO vo : keywords.getRecordList()) {
            if ("APP".equalsIgnoreCase(vo.getServeType())) {
                appIds.add(vo.getServeId());
            }
            if ("PRODUCT".equalsIgnoreCase(vo.getServeType())) {
                productIds.add(vo.getServeId());
            }
            if (vo.getIsEdit() == null) {
                vo.setIsEdit(1);
            }
        }
        Map<Long, AppDto> app = Maps.newHashMap();
        if (!CollectionUtils.isEmpty(appIds)) {
            app = appConsr.mapBaseByIds(appIds);
        }
        Map<Long, ProductDto> proBasesByIds = Maps.newHashMap();
        if (!CollectionUtils.isEmpty(productIds)) {
            proBasesByIds = productConsr.getProBasesByIds(productIds);
        }
        final BookGroupDTO dtoById = bookGroupDao.getDTOById(listKeywordParam.getBookGroupId());
        for (ListKeywordVO vo : keywords.getRecordList()) {
            if ("APP".equalsIgnoreCase(vo.getServeType())) {
                final AppDto appDto = app.get(vo.getServeId());
                if (null == appDto) {
                    continue;
                }
                vo.setChannelId(appDto.getChannelId());
                vo.setTypeCode(appDto.getTypeCode());
            }
            if ("PRODUCT".equalsIgnoreCase(vo.getServeType())) {
                final ProductDto productDto = proBasesByIds.get(vo.getServeId());
                if (null == productDto || null == productDto.getProductTypeDto()) {
                    continue;
                }
                vo.setChannelId(dtoById.getChannelId());
                vo.setTypeCode(productDto.getProductTypeDto().getTypeCode());
            }
        }
        return keywords;
    }

    @Override
    @ParamLog("根据关键词标识获取关键词基本信息")
    public KeywordVO getByBookKeyword(Long bookKeywordId) {
        KeywordVO keywordVO = bookKeywordDao.getByBookKeyword(bookKeywordId);
        return keywordVO;
    }

    @Override
    @ParamLog("获取5个关键词（改成10个了,20190911周磊改成不限制了，但是开发要求必须要加限制，所以先限制40个）")
    public List<KeywordDTO> listFiveKeyword(Long classifyId, Long bookGroupId) {
        //获取是否单独设置关键词
        List<KeywordDTO> keywords = bookKeywordDao.listFiveKeyword(classifyId, bookGroupId);
        if (ListUtils.isEmpty(keywords)) {
            keywords = bookKeywordDao.listFiveKeyword(0L, bookGroupId);
        }
        return keywords;
    }

    @Override
    @ParamLog("关键词消息回复")
    public void sendKeywordMessage(SendTextDTO sendTextDTO) {
        if (Objects.isNull(sendTextDTO)) {
            return;
        }
        if (SendMessageTypeEnum.GROUP.getCode().equals(sendTextDTO.getCode())) {
            this.sendKeywordMessageToGroup(sendTextDTO);
            return;
        }
        //判断是否是平台端配置的小号,如果是，走平台端
        PcloudRobot pcloudRobot = pcloudRobotDao.getByWxId(sendTextDTO.getWxId());
        if (pcloudRobot!=null){
            //如果是全局退订关键词
            if(sendTextDTO.getTextContent().equals(pcloudRobotBiz.getTdKeyword())) {
                pcloudRobotBiz.handlePcloudTdReply(sendTextDTO);
                return;
            }
            // 读者发送书名
            if (!StringUtil.isEmpty(sendTextDTO.getTextContent())
                    &&sendTextDTO.getTextContent().length()>1
                    &&sendTextDTO.getTextContent().startsWith("#")){
                sendSearchBook(sendTextDTO);
                return;
            }
            if (personalStageBiz.isPersonalStageUser(sendTextDTO.getWxId())){
                personalStageBiz.userSendPersonalStage(sendTextDTO.getWechatUserId(), sendTextDTO.getIp(), sendTextDTO.getCode(), sendTextDTO.getWxId(), sendTextDTO.getTextContent());
                return;
            }
            sendPcloudKeyword(sendTextDTO,pcloudRobot);
            return;
        }
        if (SendMessageTypeEnum.SELF.getCode().equals(sendTextDTO.getCode())) {
            dealBookGroupCipher(sendTextDTO);
            //判断是否为某个群分类名称
            inviteToGroupByKeyword(sendTextDTO);
            //智能机器人新版个人号回复
            dealSelfShortBookName(sendTextDTO);
            //回复关键词
            replySelfKeyword(sendTextDTO);
        }
    }

    @ParamLog("发送搜索书籍")
    private void sendSearchBook(SendTextDTO sendTextDTO) {
        String bookName=sendTextDTO.getTextContent().substring(1);
        BookDto bookDto=bookDao.getAdviserBookByName(bookName);
        String userWxId = sendTextDTO.getWechatUserId();
        String ip = sendTextDTO.getIp();
        Integer code = sendTextDTO.getCode();
        String robotId = sendTextDTO.getWxId();
        if (bookDto==null){
            SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
            sendTextMessageVO.setContent("如果这里没有你需要的，小睿深表歉意，小睿还在不断丰富阅读内容，提升阅读体验，一旦发现好东西就会快马加鞭分享给你的！");
            sendTextMessageVO.setAltId(robotId);
            sendTextMessageVO.setWxGroupId(userWxId);
            sendTextMessageVO.setIp(ip);
            sendTextMessageVO.setCode(code);
            WxGroupSDK.sendTextMessage(sendTextMessageVO);
            String h5link = wechatGroupLinkPrefix +"/dialog/search?wxId=" + userWxId +"&robotWxId=" + robotId;
            String linkUrl = UrlUtils.getShortUrl4Own(h5link);
            linkUrl = "★" + linkUrl + "★";
            sendTextMessageVO.setContent("详情点击进入："+linkUrl);
            WxGroupSDK.sendTextMessage(sendTextMessageVO);
        }else {
            SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
            sendTextMessageVO.setAltId(robotId);
            sendTextMessageVO.setWxGroupId(userWxId);
            sendTextMessageVO.setIp(ip);
            sendTextMessageVO.setCode(code);
            String h5link = wechatGroupLinkPrefix +"/dialog/resource?bookId="+bookDto.getBookId()+"&adviserId="+bookDto.getAdviserId()+"&channelId="+bookDto.getChannelId()+"&wxId=" + userWxId +"&robotWxId=" + robotId;
            String linkUrl = UrlUtils.getShortUrl4Own(h5link);
            linkUrl = "★" + linkUrl + "★";
            sendTextMessageVO.setContent("详情点击进入："+linkUrl);
            WxGroupSDK.sendTextMessage(sendTextMessageVO);
            //插入触发记录
            SearchRecord searchRecord=new SearchRecord();
            searchRecord.setContent(bookName);
            searchRecord.setBookId(bookDto.getBookId());
            searchRecord.setWxId(userWxId);
            Long agentId=adviserConsr.getAgentIdByAdviser(bookDto.getAdviserId());
            searchRecord.setAgentId(agentId);
            searchRecordDao.insert(searchRecord);
        }
    }

    @ParamLog("小睿根据书名推送匹配的资源")
    private boolean replySelfKeywordTemplate(SendTextDTO sendTextDTO) {
        try {
            String content = sendTextDTO.getTextContent();
            String redisKey = "replySelfKeywordTemplate:"+sendTextDTO.getWxId()+":"+sendTextDTO.getWechatUserId();
            List<SelfRobotKeywordReplyTemplate> replyTemplateList = selfRobotKeywordReplyTemplateDao.getList();
            // 解析出《》的书名
            Pattern pattern = Pattern.compile("《(.*?)》");
            Matcher matcher = pattern.matcher(content);
            // 如果匹配不到，在匹配出版社
            if (!matcher.find() || matcher.group(1).trim().isEmpty()) {
                // 解析出版社 出版社:
                int index = content.indexOf("出版社：");
                // 再匹配不到，直接返回，并走接下来的逻辑
                if(index == -1) {
                    index = content.indexOf("出版社:");
                    if(index == -1)
                        return true;
                }
                String agentName = content.substring(index + 4);
                // 如果出版社名称为空，则直接return走非关键逻辑
                if(StringUtil.isEmpty(agentName)){ return true; }
                // 从redis取出书名
                String bookName = JedisClusterUtils.get(redisKey);
                // 删掉书名
                JedisClusterUtils.del(redisKey);
                if(StringUtil.isNotNull(bookName)){
                    // 将书的信息录入
                    SelfRobotBookRecord selfRobotBookRecord = new SelfRobotBookRecord();
                    selfRobotBookRecord.setWxUserId(sendTextDTO.getWechatUserId());
                    selfRobotBookRecord.setAltId(sendTextDTO.getWxId());
                    selfRobotBookRecord.setBookName(bookName);
                    selfRobotBookRecord.setStatus(2);
                    selfRobotBookRecord.setAgentName(agentName);
                    selfRobotBookRecordDao.insert(selfRobotBookRecord);
                }
                Optional<SelfRobotKeywordReplyTemplate> replyTemplate = replyTemplateList.stream().filter(x -> x.getStatus().equals(2)).findFirst();
                sendText(sendTextDTO, replyTemplate.get().getReplyContentTemplate());
                return false;
            }

            String bookName = matcher.group(1).trim();
            // 根据书名查询到 bookIds
            List<Long> bookIds = bookDao.getBookIdsByBookName(bookName);
            if(ListUtils.isEmpty(bookIds)){
                // 将书名写入redis暂存
                JedisClusterUtils.set(redisKey, bookName);
                // 如果bookIds没有值，则回复模板状态3
                // 呃，小睿刚才仔细的找了下，发现书刊的配套资源我这边还没有呢，要不等小睿看完整理好后再给你吧
                Optional<SelfRobotKeywordReplyTemplate> replyTemplate = replyTemplateList.stream().filter(x -> x.getStatus().equals(3)).findFirst();
                sendText(sendTextDTO, replyTemplate.get().getReplyContentTemplate().replace("${bookName}",bookName));
                return false;
            } else {
                // 根据bookIds查询资源数；
                Integer totalCount = channelConsr.getMessageCountByBookIds(bookIds);
                if(totalCount > 0){
                    // 信息录入
                    SelfRobotBookRecord selfRobotBookRecord = new SelfRobotBookRecord();
                    selfRobotBookRecord.setWxUserId(sendTextDTO.getWechatUserId());
                    selfRobotBookRecord.setAltId(sendTextDTO.getWxId());
                    selfRobotBookRecord.setBookName(bookName);
                    selfRobotBookRecord.setStatus(3);
                    selfRobotBookRecordDao.insert(selfRobotBookRecord);
                    // 如果有书有资源，则转换链接，并推送链接给用户
                    Map<String,String> paramMap = new HashMap<>();
                    paramMap.put("wxId",sendTextDTO.getWechatUserId());
                    paramMap.put("robotWxId",sendTextDTO.getWxId());
                    paramMap.put("bookName",URLEncoder.encode(bookName));
                    Optional<SelfRobotKeywordReplyTemplate> replyTemplate = replyTemplateList.stream().filter(x -> x.getStatus().equals(4)).findFirst();
                    sendText(sendTextDTO,appendUrlParams(replyTemplate.get().getReplyContentTemplate(),paramMap));
                } else{
                    // 信息录入
                    SelfRobotBookRecord selfRobotBookRecord = new SelfRobotBookRecord();
                    selfRobotBookRecord.setWxUserId(sendTextDTO.getWechatUserId());
                    selfRobotBookRecord.setAltId(sendTextDTO.getWxId());
                    selfRobotBookRecord.setBookName(bookName);
                    selfRobotBookRecord.setStatus(1);
                    selfRobotBookRecordDao.insert(selfRobotBookRecord);
                    // 如果有书但没有资源，则回复模板状态1
                    Optional<SelfRobotKeywordReplyTemplate> replyTemplate = replyTemplateList.stream().filter(x -> x.getStatus().equals(1)).findFirst();
                    sendText(sendTextDTO, replyTemplate.get().getReplyContentTemplate().replace("${bookName}",bookName));
                }
                return false;
            }
        } catch (Exception e) {
            log.info("小睿收集出版社信息异常；异常信息：" + e.getMessage());
            return true;
        }
    }

    @ParamLog("发送平台端小号的")
    private void sendPcloudKeyword(SendTextDTO sendTextDTO,PcloudRobot pcloudRobot) {
        String userWxId = sendTextDTO.getWechatUserId();
        String ip = sendTextDTO.getIp();
        Integer code = sendTextDTO.getCode();
        String robotId = sendTextDTO.getWxId();
        Long robotClassifyId=pcloudRobot.getRobotType().longValue();
        //判断人工客服服务状态
        Integer serviceState = wechatGroupConsr.getServiceStateByRobotUser(robotId, userWxId);
        if (serviceState == 1){
            log.info("人工客服服务中，userWxId="+userWxId+"+robotId+"+robotId);
            return;
        }
        PcloudRobotClassify robotClassify = pcloudRobotClassifyDao.getById(robotClassifyId);
        if (robotClassify==null||robotClassify.getKeywordClassifyId()==null){
            return;
        }
        Long pcloudClassifyId=robotClassify.getKeywordClassifyId();
        //查询该小号对应小号分类的关键词分类
        PcloudKeywordClassify pcloudKeywordClassify = pcloudKeywordClassifyDao.getById(pcloudClassifyId);
        if (null == pcloudKeywordClassify || !pcloudKeywordClassify.getOpen()){//分类未启用
            log.info("查询小号关键词分类为空或未启用sendTextDTO="+sendTextDTO.toString()+"pcloudRobot="+pcloudRobot.toString());
            return;
        }
        String content=sendTextDTO.getTextContent();
        //根据关键词分类和关键词查询关键词
        //精准
        PcloudKeyword pcloudKeyword=pcloudKeywordDao.getByClassifyIdAndName(KeywordTypeEnum.ACCURATE.value,pcloudClassifyId,content);
        if (pcloudKeyword==null){
            // 正向模糊
            pcloudKeyword=pcloudKeywordDao.getByClassifyIdAndName(KeywordTypeEnum.LIKE.value,pcloudClassifyId,content);
            // 反向模糊
            if (Objects.isNull(pcloudKeyword)) {
                List<PcloudKeyword> pcloudKeywords = pcloudKeywordDao.listAllBlurryKeyword(pcloudClassifyId);
                if (!CollectionUtils.isEmpty(pcloudKeywords)) {
                    Map<Long, PcloudKeyword> collect = pcloudKeywords.stream().collect(Collectors.toMap(PcloudKeyword::getId, p -> p));
                    List<PcloudKeywordName> names = pcloudKeywordNameDao.getByPcloudKeywordIds(pcloudKeywords.stream().map(PcloudKeyword::getId).collect(Collectors.toList()));
                    for (PcloudKeywordName name : names) {
                        if (!StringUtils.isEmpty(name.getName()) && content.contains(name.getName())) {
                            pcloudKeyword = collect.get(name.getPcloudKeywordId());
                            break;
                        }
                    }
                }
            }
        }
        if (pcloudKeyword!=null){
            //走关键词
            log.info("查询平台端关键词"+pcloudKeyword.toString()+"id="+pcloudKeyword.getId());
            //3min不回复同一关键词
            String key ="BOOK:PCLOUD_KEYWORD:" + userWxId + "-" +robotId + "-" + pcloudKeyword.getId();
            String redisContent = JedisClusterUtils.getJson(key, String.class);
            if (!StringUtil.isEmpty(redisContent)){
                return;
            }else {
                JedisClusterUtils.setJson(key, userWxId, 3*60);
            }
            List<PcloudKeywordReply> replies = pcloudKeywordReplyDao.getByRelevance(RelevanceTypeEnum.KEYWORD.value, pcloudKeyword.getId());
            if (MethodEnum.RANDOM.value.equals(pcloudKeyword.getMethod())){
                int a= (int) Math.floor(Math.random()*replies.size());
                replies=Arrays.asList(replies.get(a));
            }
            for (PcloudKeywordReply reply:replies){
                sendPcloudKeywordReply(reply, robotId, userWxId, ip, code);
            }
            //有关联技能,发送H5页面链接,取第一个技能
            if (!StringUtil.isEmpty(pcloudKeyword.getSkillIds())){
                List<Long> skillIds = pcloudKeyword.getSkillList(pcloudKeyword.getSkillIds());
                String h5link = wechatGroupLinkPrefix +"/dialog?wxId=" + userWxId +"&robotWxId=" + robotId +"&skillId="+skillIds.get(0);
                String linkUrl = UrlUtils.getShortUrl4Own(h5link);
                linkUrl = "★" + linkUrl + "★";
                SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
                sendTextMessageVO.setContent(linkUrl);
                sendTextMessageVO.setAltId(robotId);
                sendTextMessageVO.setWxGroupId(userWxId);
                sendTextMessageVO.setIp(ip);
                sendTextMessageVO.setCode(code);
                WxGroupSDK.sendTextMessage(sendTextMessageVO);
            }

        }else {
            //非关键词响应
            sendPcloudNotKeyWord(userWxId,ip,code,robotId,pcloudClassifyId, MessageTypeEnum.TEXT.value);
        }

    }


    @ParamLog("平台端个人号关键词回复")
    public void sendPcloudKeywordReply(PcloudKeywordReply reply, String robotId, String userWxId, String ip, Integer code){
        Integer type = reply.getType();
        if (ReplyTypeEnum.TEXT.value.equals(type)) {
            SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
            sendTextMessageVO.setContent(reply.getContent());
            sendTextMessageVO.setAltId(robotId);
            sendTextMessageVO.setWxGroupId(userWxId);
            sendTextMessageVO.setIp(ip);
            sendTextMessageVO.setCode(code);
            WxGroupSDK.sendTextMessage(sendTextMessageVO);
        }
        if (ReplyTypeEnum.IMAGE.value.equals(type)) {
            SendPicMessageVO sendPicMessageVO = new SendPicMessageVO();
            sendPicMessageVO.setWxGroupId(userWxId);
            sendPicMessageVO.setAltId(robotId);
            sendPicMessageVO.setPicUrl(reply.getPicUrl());
            sendPicMessageVO.setIp(ip);
            sendPicMessageVO.setCode(code);
            WxGroupSDK.sendPicMessage(sendPicMessageVO);
        }
        if (ReplyTypeEnum.APP.value.equals(type)) {
            pcloudRobotSet.sendAppOrProductReply(reply.getServeType(), reply.getServeId(),
                    reply.getLinkUrl(), robotId,userWxId, ip, code);
        }
        if (ReplyTypeEnum.RESOURCE.value.equals(type)) {
            SendFileVO sendFileVO = new SendFileVO();
            Map<Long, ResourceDTO> map = resourceConsr.mapByIds(Arrays.asList(reply.getResourceId()));
            ResourceDTO resourceDTO = map.get(reply.getResourceId());
            if (resourceDTO != null) {
                sendFileVO.setFileUrl(resourceDTO.getFileUrl());
                String fileName = resourceDTO.getResourceName();
                String fileType = resourceDTO.getFileType();
                log.info("fileName="+fileName+"+fileType+"+fileType);
                if (fileName.contains(fileType)){//去掉后缀
                    fileName = fileName.substring(0, fileName.indexOf(fileType)-1);
                }
                log.info("fileName="+fileName);
                sendFileVO.setFileName(fileName);
            }
            sendFileVO.setIp(ip);
            sendFileVO.setAltId(robotId);
            sendFileVO.setWxId(userWxId);
            WxGroupSDK.sendFile(sendFileVO);
        }
        if (ReplyTypeEnum.LINK.value.equals(type)) {
            SendArticleMessageVO sendArticleMessageVO = new SendArticleMessageVO();
            sendArticleMessageVO.setCode(code);
            sendArticleMessageVO.setAltId(robotId);
            sendArticleMessageVO.setDescription(reply.getDescription());
            sendArticleMessageVO.setWxGroupId(userWxId);
            sendArticleMessageVO.setLinkUrl(reply.getLinkUrl());
            sendArticleMessageVO.setPicUrl(reply.getPicUrl());
            sendArticleMessageVO.setTitle(reply.getContent());
            sendArticleMessageVO.setIp(ip);
            WxGroupSDK.sendArticleMessage(sendArticleMessageVO);
        }
    }

    @ParamLog("回复新版关键词")
    private void replySelfKeyword(SendTextDTO sendTextDTO) {
        String content = sendTextDTO.getTextContent();
        BookGroupCipherUser bookGroupCipherUser = bookGroupCipherUserDao.getByWxUserId(sendTextDTO.getWechatUserId());
        if (bookGroupCipherUser == null || StringUtil.isEmpty(bookGroupCipherUser.getShortBookName())) {
            return;
        }
        SelfRobotKeyword selfRobotKeyword = selfRobotKeywordDao.getByBookGroupIdAndKeyword(bookGroupCipherUser.getBookGroupId(), content);
        if (selfRobotKeyword != null) {
            sendSelfKeyword(selfRobotKeyword, sendTextDTO);
        } else {
            BookGroup bookGroup = bookGroupDao.getByShortBookName(content);
            Boolean isActivityKeyword = readingActivityBiz.isKeyWord(sendTextDTO);
            if (bookGroup == null && !isActivityKeyword) {
                //既不是关键词也不是识别码
                String altIdAndWxId = sendTextDTO.getWxId() + "_" + sendTextDTO.getWechatUserId();
                Integer send = JedisClusterUtils.hgetJson2Class(REPLY_GUIDE_CACHE, altIdAndWxId, Integer.class);
                if (send == null) {
                    sendText(sendTextDTO, "请按以上指引回复准确的内容以便获取对应服务，谢谢！");
                    JedisClusterUtils.hset2Json(REPLY_GUIDE_CACHE, altIdAndWxId, 1);
                    //1小时缓存
                    JedisClusterUtils.expire(REPLY_GUIDE_CACHE, 60 * 60 * 1);
                }
            }
        }
    }

    @ParamLog("发送非关键词回复模板")
    private void sendNotKeywords(SendTextDTO sendTextDTO) {
        NotKeywordTemplate template=notKeywordTemplateDao.getRandomTemplate();
        if (template==null){
            return;
        }
        List<NotKeywordItem> list = notKeywordItemDao.getListByTemplateIds(Arrays.asList(template.getId()));
        if (ListUtils.isEmpty(list)){
            return;
        }
        String userWxId = sendTextDTO.getWechatUserId();
        String ip = sendTextDTO.getIp();
        Integer code = sendTextDTO.getCode();
        String robotId = sendTextDTO.getWxId();
        for (NotKeywordItem notKeywordItem:list){
            Integer type = notKeywordItem.getType();
            if (ReplyTypeEnum.TEXT.value.equals(type)) {
                SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
                sendTextMessageVO.setContent(notKeywordItem.getContent());
                sendTextMessageVO.setAltId(robotId);
                sendTextMessageVO.setWxGroupId(userWxId);
                sendTextMessageVO.setIp(ip);
                sendTextMessageVO.setCode(code);
                WxGroupSDK.sendTextMessage(sendTextMessageVO);
            }
            if (ReplyTypeEnum.RESOURCE.value.equals(type)) {
                SendFileVO sendFileVO = new SendFileVO();
                Map<Long, ResourceDTO> map = resourceConsr.mapByIds(Arrays.asList(notKeywordItem.getResourceId()));
                ResourceDTO resourceDTO = map.get(notKeywordItem.getResourceId());
                if (resourceDTO != null) {
                    sendFileVO.setFileUrl(resourceDTO.getFileUrl());
                    String fileName = resourceDTO.getResourceName();
                    String fileType = resourceDTO.getFileType();
                    log.info("fileName="+fileName+"+fileType+"+fileType);
                    if (fileName.contains(fileType)){//去掉后缀
                        fileName = fileName.substring(0, fileName.indexOf(fileType)-1);
                    }
                    log.info("fileName="+fileName);
                    sendFileVO.setFileName(fileName);
                }
                sendFileVO.setIp(ip);
                sendFileVO.setAltId(robotId);
                sendFileVO.setWxId(userWxId);
                WxGroupSDK.sendFile(sendFileVO);
            }
        }
    }

    @ParamLog("推送新版关键词回复消息")
    private void sendSelfKeyword(SelfRobotKeyword selfRobotKeyword, SendTextDTO sendTextDTO) {
        String userWxId = sendTextDTO.getWechatUserId();
        String ip = sendTextDTO.getIp();
        Integer code = sendTextDTO.getCode();
        String robotId = sendTextDTO.getWxId();
        List<SelfRobotKeywordReply> replyList = selfRobotKeywordReplyDao.getListByKeywordId(selfRobotKeyword.getId());
        if (ListUtils.isEmpty(replyList)){
            return;
        }
        for (SelfRobotKeywordReply keywordReply : replyList){
            Integer type = keywordReply.getType();
            if (ReplyTypeEnum.TEXT.value.equals(type)) {
                SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
                Map<String,String> paramMap = new HashMap<>();
                paramMap.put("wxId",userWxId);
                paramMap.put("robotWxId",robotId);
                sendTextMessageVO.setContent(appendUrlParams(keywordReply.getContent(),paramMap));
                sendTextMessageVO.setAltId(robotId);
                sendTextMessageVO.setWxGroupId(userWxId);
                sendTextMessageVO.setIp(ip);
                sendTextMessageVO.setCode(code);
                WxGroupSDK.sendTextMessage(sendTextMessageVO);
            }
            if (ReplyTypeEnum.IMAGE.value.equals(type)) {
                SendPicMessageVO sendPicMessageVO = new SendPicMessageVO();
                sendPicMessageVO.setWxGroupId(userWxId);
                sendPicMessageVO.setAltId(robotId);
                sendPicMessageVO.setPicUrl(keywordReply.getPicUrl());
                sendPicMessageVO.setIp(ip);
                sendPicMessageVO.setCode(code);
                WxGroupSDK.sendPicMessage(sendPicMessageVO);
            }
            if (ReplyTypeEnum.LINK.value.equals(type)) {
               /* SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
                String content;
                if (!StringUtil.isEmpty(selfRobotKeyword.getGuide())){
                    content = selfRobotKeyword.getGuide() + "，点击这里进入：" + keywordReply.getLinkUrl();
                }else {
                    content = "点击这里进入：" + keywordReply.getLinkUrl();
                }
                sendTextMessageVO.setContent(content);
                sendTextMessageVO.setAltId(robotId);
                sendTextMessageVO.setWxGroupId(userWxId);
                sendTextMessageVO.setIp(ip);
                sendTextMessageVO.setCode(code);
                WxGroupSDK.sendTextMessage(sendTextMessageVO);*/

                SendArticleMessageVO sendArticleMessageVO = new SendArticleMessageVO();
                sendArticleMessageVO.setCode(code);
                sendArticleMessageVO.setAltId(robotId);
                sendArticleMessageVO.setDescription(keywordReply.getDescription());
                sendArticleMessageVO.setWxGroupId(userWxId);
                sendArticleMessageVO.setLinkUrl(keywordReply.getLinkUrl());
                sendArticleMessageVO.setPicUrl(keywordReply.getPicUrl());
                sendArticleMessageVO.setTitle(keywordReply.getContent());
                sendArticleMessageVO.setIp(ip);
                WxGroupSDK.sendArticleMessage(sendArticleMessageVO);
            }
            if (ReplyTypeEnum.APP.value.equals(type)) {
                BookGroupDTO bookGroupDTO = bookGroupDao.getDTOById(selfRobotKeyword.getBookGroupId());
                AccountSettingDto accountSettingDto = qrcodeSceneConsr.getWechatInfo(bookGroupDTO.getChannelId());
                if (AppAndProductTypeEnum.APP.value.equals(keywordReply.getServeType())) {
                    AppDto appDto = appConsr.getBaseById(keywordReply.getServeId());
                    if (appDto != null) {
                        accountSettingDto = qrcodeSceneConsr.getWechatInfo(appDto.getChannelId());
                    }
                }
                // 处理链接地址
                String endUrl = keywordReply.getLinkUrl() + "&book_group_id=" + selfRobotKeyword.getBookGroupId() + "&wxId=" + userWxId + "&robotWxId=" + robotId;
                String linkUrl = SendWeixinRequestTools.splitUrl(accountSettingDto, endUrl);
                String resultUrl = UrlUtils.getShortUrl4Own(linkUrl);
               /* SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
                String content;
                if (!StringUtil.isEmpty(selfRobotKeyword.getGuide())){
                    content = selfRobotKeyword.getGuide() + "，点击这里进入：" + resultUrl;
                }else {
                    content = "点击这里进入：" + resultUrl;
                }
                sendTextMessageVO.setContent(content);
                sendTextMessageVO.setAltId(robotId);
                sendTextMessageVO.setWxGroupId(userWxId);
                sendTextMessageVO.setIp(ip);
                sendTextMessageVO.setCode(code);
                WxGroupSDK.sendTextMessage(sendTextMessageVO);*/

                SendArticleMessageVO sendArticleMessageVO = new SendArticleMessageVO();
                sendArticleMessageVO.setCode(code);
                sendArticleMessageVO.setAltId(robotId);
                sendArticleMessageVO.setDescription(keywordReply.getDescription());
                sendArticleMessageVO.setWxGroupId(userWxId);
                sendArticleMessageVO.setLinkUrl(resultUrl);
                sendArticleMessageVO.setPicUrl(keywordReply.getPicUrl());
                sendArticleMessageVO.setTitle(keywordReply.getContent());
                sendArticleMessageVO.setIp(ip);
                WxGroupSDK.sendArticleMessage(sendArticleMessageVO);
            }
            if (ReplyTypeEnum.RESOURCE.value.equals(type)) {
                SendFileVO sendFileVO = new SendFileVO();
                Map<Long, ResourceDTO> map = resourceConsr.mapByIds(Arrays.asList(keywordReply.getResourceId()));
                ResourceDTO resourceDTO = map.get(keywordReply.getResourceId());
                if (resourceDTO != null) {
                    sendFileVO.setFileUrl(resourceDTO.getFileUrl());
                    String fileName = resourceDTO.getResourceName();
                    String fileType = resourceDTO.getFileType();
                    log.info("fileName="+fileName+"+fileType+"+fileType);
                    if (fileName.contains(fileType)){//去掉后缀
                        fileName = fileName.substring(0, fileName.indexOf(fileType)-1);
                    }
                    log.info("fileName="+fileName);
                    sendFileVO.setFileName(fileName);
                }
                sendFileVO.setIp(ip);
                sendFileVO.setAltId(robotId);
                sendFileVO.setWxId(userWxId);
                WxGroupSDK.sendFile(sendFileVO);
            }
        }
    }

    /**
     * 关键词 补充wxid
     */
    public String appendUrlParams(String content, Map<String,String> map) {
        String suffix = appendParams(map);
        Pattern pattern = Pattern.compile("(https?)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]");
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            String group = matcher.group();
            if (StringUtil.isEmpty(group)) {
                continue;
            }
            // 短链接
            if (group.startsWith("https://s.5rs.me/") && group.length() == 24) {
                String hex = group.replaceAll(UrlConstant.OWN_SHORT_URL, "");
                String originLink = UrlUtils.getUrl4Own(hex);

                if (!StringUtil.isEmpty(originLink) && originLink.contains("?")) {
                    content = content.replace(group, UrlUtils.getShortUrl(originLink + "&" + suffix));
                } else if (!StringUtil.isEmpty(originLink) && !originLink.contains("?")) {
                    content = content.replace(group, UrlUtils.getShortUrl(originLink + "?" + suffix));
                }
            }

            if (group.contains("?")) {
                content = content.replace(group, UrlUtils.getShortUrl(group + "&" + suffix));
            }else {
                content = content.replace(group, UrlUtils.getShortUrl(group + "?" + suffix));
            }
        }
        return content;
    }

    @ParamLog("非关键词响应")
    @Override
    public void sendPcloudNotKeyWord(String userWxId, String ip, Integer code, String robotId,Long pcloudClassifyId, Integer userMessageType) {
        //判断人工客服服务状态
        Integer serviceState = wechatGroupConsr.getServiceStateByRobotUser(robotId, userWxId);
        if (serviceState == 1){
            log.info("人工客服服务中，userWxId="+userWxId+"+robotId+"+robotId);
            return;
        }
        //1min不重复回复
        String key ="BOOK:PCLOUD_NOT_KEYWORD:"+ userWxId +"-" + robotId +"-" + pcloudClassifyId;
        String redisContent = JedisClusterUtils.getJson(key, String.class);
        if (!StringUtil.isEmpty(redisContent)){
            return;
        }else {
            JedisClusterUtils.setJson(key, userWxId,  60);
        }
        //走非关键词
        List<PcloudNotKeyword> notKeywords = pcloudNotKeywordDao.getPcloudNotKeywordListByMessageType(pcloudClassifyId,userMessageType);
        if (!ListUtils.isEmpty(notKeywords)){
            for (PcloudNotKeyword pcloudNotKeyword : notKeywords){
                List<PcloudKeywordReply> replies = pcloudKeywordReplyDao.getByRelevance(RelevanceTypeEnum.NOT_KEYWORD.value, pcloudNotKeyword.getId());
                if (ListUtils.isEmpty(replies)){
                    continue;
                }
                if (MethodEnum.RANDOM.value.equals(pcloudNotKeyword.getMethod())){
                    replies = Arrays.asList(replies.get(new Random().nextInt(replies.size())));
                }
                for (PcloudKeywordReply reply:replies){
                    sendPcloudKeywordReply(reply, robotId, userWxId, ip, code);
                }
            }
        }
    }

    private String appendParams(Map<String, String> map) {
        Set<String> keys = map.keySet();
        StringBuilder sb = new StringBuilder();
        for (String key : keys) {
            sb.append("&").append(key).append("=").append(map.get(key));
        }
        return sb.toString().replaceFirst("&", "");
    }

    @ParamLog("获取的关键词是否为社群书的简称")
    private void dealSelfShortBookName(SendTextDTO sendTextDTO) {
        String content = sendTextDTO.getTextContent();
        BookGroup bookGroup = bookGroupDao.getByShortBookName(content);
        if (bookGroup != null) {
            addBookGroupCipherUser(sendTextDTO, null, content, bookGroup.getId());
            dealBySelfRobot(sendTextDTO, bookGroup.getId(), bookGroup.getChannelId());
        } else {
            //判断之前是否有关联，如果没有，提示输入正确的识别码
            Long bookGroupId = bookGroupCipherUserDao.getBookGroupIdByWxUserId(sendTextDTO.getWechatUserId());
            if (bookGroupId == null) {
                String altIdAndWxId = sendTextDTO.getWxId() + "_" + sendTextDTO.getWechatUserId();
                Integer send = JedisClusterUtils.hgetJson2Class(REPLY_ERROR_SHORT_BOOK_NAME_CACHE, altIdAndWxId, Integer.class);
                if (send == null) {
                    sendText(sendTextDTO, "发送错误，请核对后重新发送。");
                    JedisClusterUtils.hset2Json(REPLY_ERROR_SHORT_BOOK_NAME_CACHE, altIdAndWxId, 1);
                    //1小时缓存
                    JedisClusterUtils.expire(altIdAndWxId, 60 * 60 * 1);
                }
            }
        }
    }

    @ParamLog("处理智能机器人个人号回复")
    private void dealBySelfRobot(SendTextDTO sendTextDTO, Long bookGroupId, Long channelId) {
        String text = "";
        List<SelfRobotKeyword> selfRobotKeywords = selfRobotKeywordDao.getListByBookGroupId(bookGroupId);
        if (!ListUtils.isEmpty(selfRobotKeywords)) {
            text = text + "识别码收到!我猜您有以下愿望：\n";
            Integer size = selfRobotKeywords.size();
            AccountSettingDto accountSettingDto = qrcodeSceneConsr.getWechatInfo(channelId);

            for (int i = 0; i < size; i++) {
                SelfRobotKeyword selfRobotKeyword = selfRobotKeywords.get(i);
                String toAddIn;
//                if (!StringUtil.isEmpty(selfRobotKeyword.getGuide())){//改为多个引导语了1002267
//                    toAddIn = selfRobotKeyword.getGuide() + "，请回复“" + selfRobotKeyword.getKeyword() + "”\n";
//                }else {
                    toAddIn = "请回复“" + selfRobotKeyword.getKeyword() + "”\n";
//                }
                //最后一个
                if (i + 1 == size) {
                    // 拼接反馈链接
                    String feedbackUrl = "*common/C" + channelId + "/A0/feedback/suggest?bookGroupId="+bookGroupId+"&wxId=" + sendTextDTO.getWechatUserId();
                    String url = SendWeixinRequestTools.splitUrl(accountSettingDto, feedbackUrl);
                    toAddIn = toAddIn + "回复对应数字序号给我，我将助你达成所愿，提高读书效率。\n如有其他问题反馈，请点击链接：\n"
                            + UrlUtils.getShortUrl4Own(url);
                }
                String contents = text + toAddIn;
                if (contents.length() > LE) {
                    sendText(sendTextDTO, text);
                    text = toAddIn;
                } else {
                    text = contents;
                }
            }
        } else {
            text = text + "抱歉，本图书配套服务正在快马加鞭地赶来。服务到达时，会微信通知您。";
        }
        sendText(sendTextDTO, text);
    }

    @ParamLog("获取的关键词是否为社群书的暗号")
    private void dealBookGroupCipher(SendTextDTO sendTextDTO) {
        String cipher = sendTextDTO.getTextContent();
        //判断是否为加了随机码的暗号
        if (cipher.length() > 7) {
            cipher = cipher.substring(0, 7);
        }
        BookGroup bookGroup = bookGroupDao.getByBookGroupCipher(cipher);
        if (bookGroup != null) {
            try {
                addBookGroupCipherUser(sendTextDTO, cipher,null, bookGroup.getId());
                updateWxUserWechatRelevance(sendTextDTO);
                dealByBookGroup(sendTextDTO, bookGroup.getId(), false);
            } catch (Exception e) {
                log.error("拉群出错" + e.getMessage());
            }
        }
    }

    @ParamLog("更新系统微信用户和社群书微信用户关联表")
    private void updateWxUserWechatRelevance(SendTextDTO sendTextDTO) {
        if (sendTextDTO.getTextContent().length() != 15) {
            return;
        }
        String randomCode = sendTextDTO.getTextContent().substring(8);
        //更新
        wxUserWechatRelevanceDao.updateWxIdByRandomCode(randomCode, sendTextDTO.getWechatUserId());
    }

    @ParamLog("增加暗号对应书记录")
    private void addBookGroupCipherUser(SendTextDTO sendTextDTO, String cipher, String shortBookName, Long bookGroupId) {
        BookGroupCipherUser bookGroupCipherUser = new BookGroupCipherUser();
        bookGroupCipherUser.setBookGroupCipher(cipher);
        bookGroupCipherUser.setShortBookName(shortBookName);
        bookGroupCipherUser.setBookGroupId(bookGroupId);
        bookGroupCipherUser.setWxUserId(sendTextDTO.getWechatUserId());
        bookGroupCipherUser.setAltId(sendTextDTO.getWxId());
        //新增暗号对应记录
        bookGroupCipherUserDao.insert(bookGroupCipherUser);
    }

    @ParamLog("按照书处理暗号回复，推送资源")
    public void dealByBookGroup(SendTextDTO sendTextDTO, Long bookGroupId, Boolean isPushUpdate) {
        BookGroupDTO bookGroupDTO = bookGroupDao.getBookBaseInfoById(bookGroupId);
        String text = "";
        if (isPushUpdate != null && isPushUpdate) {
            text = text + "有更新！\n";
        }
        Boolean isInviteGroup = bookGroupDTO.getIsInviteGroup();
        // 获取配置的资源服务
        List<BookGroupServe> bookGroupServeList = bookGroupBiz.getBookGroupServeList(bookGroupId);
        String bookName = bookGroupDTO.getBookName();
        if (!ListUtils.isEmpty(bookGroupServeList)) {
            if (!StringUtil.isEmpty(bookName)) {
                text = text + "《" + bookName + "》" + "配有以下资源服务，点击链接立即获取：\n";
            }
            int i = 1;
            for (BookGroupServe bookGroupServe : bookGroupServeList) {
                if (i == 1 || i == 2) {
                    text = text + "🔥";
                }
                String toAdd = i + "." + bookGroupServe.getServeName() + "\n" + bookGroupServe.getShortUrl() + "\n";
                String content = text + toAdd;
                if (content.length() > LE) {
                    sendText(sendTextDTO, text);
                    text = toAdd;
                } else {
                    text = content;
                }
                i = i + 1;
            }
        } else {
            text = text + "《" + bookName + "》" + "还没有配置资源服务，敬请期待。\n";
        }
        //邀请进群
        if (isInviteGroup != null && isInviteGroup) {
            //获取社群码下的分类
            List<ListClassifyVO> listClassifyVOS = bookGroupClassifyBiz.listAllClassify(bookGroupId);
            if (!ListUtils.isEmpty(listClassifyVOS)) {
                String toAdd = "======================\n";
                String content = text + toAdd;
                if (content.length() > LE) {
                    sendText(sendTextDTO, text);
                    text = toAdd;
                } else {
                    text = content;
                }
                if (listClassifyVOS.size() == 1) {
                    ListClassifyVO listClassifyVO = listClassifyVOS.get(0);
                    //如果只有一个分类
                    //发送欢迎语
                    String toAdds = "☕本书还配有交流群“" + listClassifyVO.getClassify() + "”，" + listClassifyVO.getClassifyIntroduce() + "，点击下方邀请链接，即可进群";
                    String contents = text + toAdds;
                    if (contents.length() > LE) {
                        sendText(sendTextDTO, text);
                        text = toAdds;
                    } else {
                        text = contents;
                    }
                    sendText(sendTextDTO, text);
                    dealGroupInvite(sendTextDTO, listClassifyVO.getId(), listClassifyVO.getChangeNumber());
                } else {
                    String toAdds = "☕本书还配有以下交流群，选择你想加入的微信群，回复群名称，我会拉你入群！\n";
                    String inContents = text + toAdds;
                    if (inContents.length() > LE) {
                        sendText(sendTextDTO, text);
                        text = toAdds;
                    } else {
                        text = inContents;
                    }
                    for (ListClassifyVO listClassifyVO : listClassifyVOS) {
                        String toAddIn = "☑" + listClassifyVO.getClassify() + "：" + listClassifyVO.getClassifyIntroduce() + "\n";
                        String contents = text + toAddIn;
                        if (contents.length() > LE) {
                            sendText(sendTextDTO, text);
                            text = toAddIn;
                        } else {
                            text = contents;
                        }
                    }
                    sendText(sendTextDTO, text);
                }
                return;
            }
        }
        //发送配置的资源服务
        sendText(sendTextDTO, text);
    }

    @ParamLog("发送收到暗号后的本书介绍")
    private void sendText(SendTextDTO sendTextDTO, String content) {
        if (StringUtil.isEmpty(content)) {
            return;
        }
        String userWxId = sendTextDTO.getWechatUserId();
        String ip = sendTextDTO.getIp();
        Integer code = sendTextDTO.getCode();
        String robotId = sendTextDTO.getWxId();
        SendTextMessageVO vo = new SendTextMessageVO();
        vo.setContent(content);
        vo.setCode(code);
        vo.setWxId(robotId);
        vo.setAltId(robotId);
        vo.setWxGroupId(userWxId);
        vo.setIp(ip);
        WxGroupSDK.sendTextMessage(vo);
        log.info("发送收到暗号后的本书介绍 : {}", vo);
    }

    @ParamLog("分类群发送邀请入群链接")
    private void dealGroupInvite(SendTextDTO sendTextDTO, Long classifyId, Integer changeNumber) {
        //获取分类基本信息
        ClassifyVO classify = bookGroupClassifyDao.getClassify(classifyId);
        if (classify == null) {
            throw new BookBizException(BookBizException.PARAM_IS_NULL, "分类不存在");
        }
        //获取第三套群，先判断这个小号是不是属于这个群的，如果不是，则切下一套
        String weixinGroupId = bookGroupClassifyBiz.getSelfGroup(classifyId, changeNumber, sendTextDTO.getWxId());
        if (StringUtil.isEmpty(weixinGroupId)) {
            throw new BookBizException(BookBizException.ID_NOT_EXIST, "群不存在classifyId=" + classifyId + ",changeNumber=" + changeNumber + ", wxId=" + sendTextDTO.getWxId());
        }
        sendAndCheckInviteGroup(sendTextDTO,weixinGroupId);
    }

    @ParamLog("判断冒泡和发送冒泡和发入群链接")
    private void sendAndCheckInviteGroup(SendTextDTO sendTextDTO, String wxGroupId) {
        if (checkLastAcTimeSendMsg(wxGroupId)) {
            //需要冒泡，冒泡之后隔3秒发
            ThreadPoolUtils.SEND_MAOPAO_THREAD_POOL.execute(() -> {
                sendMaoPao(sendTextDTO, wxGroupId);
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    log.error("需要冒泡，冒泡之后隔3秒发出错！sendTextDTO=" + sendTextDTO.toString() + "wxGroupId=" + wxGroupId);
                }
                sendInviteToGroup(sendTextDTO, wxGroupId);
            });
        } else {
            //如果不需要冒泡，就直接发
            sendInviteToGroup(sendTextDTO, wxGroupId);
        }
    }

    @ParamLog("发送入群邀请链接之前先拿到群里最新的消息，如果不是48小时之内，就主动发一条消息")
    private Boolean checkLastAcTimeSendMsg(String wxGroupId) {
        Boolean flag = false;
        List<String> wechatGroupIds = new ArrayList<>();
        wechatGroupIds.add(wxGroupId);
        Map<String, Date> map = wechatGroupConsr.getChatLastTimeByGroup(wechatGroupIds);
        Date lastTime = map.get(wxGroupId);
        if (lastTime == null || (new Date().getTime() - lastTime.getTime()) > 48 * 60 * 60 * 1000) {
            flag = true;
        }
        return flag;
    }

    @ParamLog("发送冒泡消息")
    private void sendMaoPao(SendTextDTO sendTextDTO, String wxGroupId){
        SendTextMessageVO sendTextMessageVO = new SendTextMessageVO();
        sendTextMessageVO.setContent("我来冒个泡");
        sendTextMessageVO.setAltId(sendTextDTO.getWxId());
        sendTextMessageVO.setWxGroupId(wxGroupId);
        sendTextMessageVO.setIp(findIp(wxGroupId));
        WxGroupSDK.sendTextMessage(sendTextMessageVO);
        log.info("发送入群邀请链接之前发冒泡消息："+sendTextMessageVO.toString());
    }

    @ParamLog("发送进群链接")
    private void sendInviteToGroup(SendTextDTO sendTextDTO, String wxGroupId){
        SendGroupInviteVO sendGroupInviteVO = new SendGroupInviteVO();
        sendGroupInviteVO.setAltId(sendTextDTO.getWxId());
        sendGroupInviteVO.setWxId(sendTextDTO.getWechatUserId());
        sendGroupInviteVO.setWxGroupId(wxGroupId);
        sendGroupInviteVO.setIp(sendTextDTO.getIp());
        WxGroupSDK.sendGroupInvite(sendGroupInviteVO);
        log.info("[同意加好友发送欢迎语] 发送进群链接 sendGroupInviteVO：{}", sendGroupInviteVO);
    }

    @ParamLog("根据关键词判断关键词是否为1V1社群群名称")
    private void inviteToGroupByKeyword(SendTextDTO sendTextDTO) {
        String userWxId = sendTextDTO.getWechatUserId();
        //获取这个人最近一次触发的关联社群码
        BookGroupCipherUser bookGroupCipherUser = bookGroupCipherUserDao.getByWxUserId(userWxId);
        if (bookGroupCipherUser == null || StringUtil.isEmpty(bookGroupCipherUser.getBookGroupCipher())) {
            return;
        }
        List<BookGroupClassify> bookGroupClassifyList = bookGroupClassifyDao.getListByBookGroupId(bookGroupCipherUser.getBookGroupId());
        if (!ListUtils.isEmpty(bookGroupClassifyList)) {
            for (BookGroupClassify bookGroupClassify : bookGroupClassifyList) {
                if (bookGroupClassify.getClassify().equals(sendTextDTO.getTextContent())) {
                    //获取第三套群，先判断这个小号是不是属于这个群的，如果不是，则切下一套
                    String weixinGroupId = bookGroupClassifyBiz.getSelfGroup(bookGroupClassify.getId(), bookGroupClassify.getChangeNumber(), sendTextDTO.getWxId());
                    if (StringUtil.isEmpty(weixinGroupId)) {
                        throw new BookBizException(BookBizException.ID_NOT_EXIST, "群不存在classifyId=" + bookGroupClassify.getId() + ",changeNumber=" + bookGroupClassify.getChangeNumber() + ", wxId=" + sendTextDTO.getWxId());
                    }
                    sendAndCheckInviteGroup(sendTextDTO, weixinGroupId);
                }
            }
        }
    }

    private boolean insertBookKeywordRecord(String weixinGroupId, String userWxId, ReplyKeywordDTO replyKeywordDTO, GroupClassifyQrcodeDTO dto, String redisContent) {
        if (redisContent != null) {
            insertBookKeywordRecord(dto, replyKeywordDTO.getKeywordId(), userWxId, weixinGroupId, false);
            log.info("[关键词消息回复] redisContent:{} insertBookKeywordRecord return", redisContent);
            return true;
        } else {
            // 60s不回复同一关键词 20190916降低消息量方案之一
            JedisClusterUtils.setJson("BOOK:KEYWORD:" + weixinGroupId + "-" + replyKeywordDTO.getKeywordId(), replyKeywordDTO.getKeywordId(), 60);
            insertBookKeywordRecord(dto, replyKeywordDTO.getKeywordId(), userWxId, weixinGroupId, true);
        }
        return false;
    }

    /**
     * 原有的推送群关键词改为推送个人关键词
     */
    @ParamLog("群关键词消息回复")
    private void sendKeywordMessageToGroup(SendTextDTO sendTextDTO) {
        if (Objects.isNull(sendTextDTO)) {
            return;
        }
        final String content = sendTextDTO.getTextContent().trim();
        final String weixinGroupId = sendTextDTO.getWechatGroupId();
        String robotId = sendTextDTO.getWxId();
        //通过群id获取对应基本信息
        GroupClassifyQrcodeDTO classifyQrcodeInfo = bookGroupClassifyBiz.getClassifyQrcodeInfo(weixinGroupId);
        if (classifyQrcodeInfo == null) {
            log.info("[关键词消息回复] classifyQrcodeInfo is null");
            return;
        }
        //简单过滤非关键词的词
        if (StringUtil.isEmpty(content) || content.length() > 20) {
            return;
        }
        List<ReplyKeywordDTO> keywordDTOS = Lists.newArrayList();
        final boolean equals = "群主，来个抽奖".equals(content);
        if (equals) {
            ReplyKeywordDTO replyKeywordDTO = keywordDao.getByKeyword("群主，来个抽奖");
            keywordDTOS.add(replyKeywordDTO);
        } else {
            //获取匹配关键词 需求（1001903）by 20191023
            List<ReplyKeywordDTO> replyKeywordDTO = bookKeywordDao.listAllKeywordByClassifyIdAndBookGroupId(classifyQrcodeInfo.getClassifyId(), classifyQrcodeInfo.getBookGroupId());
            keywordDTOS.addAll(replyKeywordDTO);
        }
        if (CollectionUtils.isEmpty(keywordDTOS)) {
            log.info("[关键词消息回复] replyKeywordDTO is null");
            return;
        }
        // 去除被发送内容完整包含的关键词
        List<ReplyKeywordDTO> collect = keywordDTOS.stream().filter(x -> content.contains(x.getKeywords())).collect(Collectors.toList());
        if (ListUtils.isEmpty(collect)){
            log.info("[关键词消息回复] replyKeywordDTO is null");
            return;
        }
        //包含图片或文本的关键词回复
        List<ReplyKeywordDTO> ImgKeyword = keywordDTOS.stream()
                .filter(p -> (ReplyTypeEnum.IMAGE.value.equals(p.getReplyType()) || ReplyTypeEnum.TEXT.value.equals(p.getReplyType()))).collect(Collectors.toList());
        //不包含图片和文字且18点到9点 则合并关键词详情，其他情况走之前的模式
        if (CollectionUtils.isEmpty(ImgKeyword) && !DateUtils.isInBetweenTimes("09:00:00", "18:00:00")) {
            sendKeyWordAtPeak(collect, keywordDTOS, classifyQrcodeInfo, sendTextDTO);
        } else {
            // 组装发送关键词
            for (ReplyKeywordDTO dto : collect) {
                sendKeyword(sendTextDTO, dto, classifyQrcodeInfo);
            }
        }
    }

    @ParamLog("高峰期关键词发送")
    public void sendKeyWordAtPeak(List<ReplyKeywordDTO> replyKeyWords, List<ReplyKeywordDTO> keywordsAll, GroupClassifyQrcodeDTO classifyQrcodeInfo, SendTextDTO sendTextDTO) {
        final String ip = sendTextDTO.getIp();
        final Integer code = sendTextDTO.getCode();
        final String weixinGroupId = sendTextDTO.getWechatGroupId();
        final String userWxId = sendTextDTO.getWechatUserId();
        String robotId = sendTextDTO.getWxId();
        if (StringUtil.isBlank(robotId)) {
            log.info("[关键词消息回复] robotId is null :{}, robotId:{},weixinGroupId:{}", robotId, weixinGroupId);
            robotId = wechatGroupConsr.getRobotIdByGroupId(weixinGroupId);
        }
        //10min内不回复关键词
        String redisContent = JedisClusterUtils.getJson("BOOK:KEYWORD_PEAK:" + weixinGroupId, String.class);
        if (!StringUtil.isEmpty(redisContent)) {
            insertBookKeywordRecord(classifyQrcodeInfo, replyKeyWords.get(0).getKeywordId(), userWxId, weixinGroupId, false);
            return;
        } else {
            JedisClusterUtils.setJson("BOOK:KEYWORD_PEAK:" + weixinGroupId, weixinGroupId, 10 * 60);
            insertBookKeywordRecord(classifyQrcodeInfo, replyKeyWords.get(0).getKeywordId(), userWxId, weixinGroupId, true);
        }
        AccountSettingDto accountSettingDto = qrcodeSceneConsr.getWechatInfo(classifyQrcodeInfo.getChannelId());
        StringBuilder sb = new StringBuilder();
        //目标关键词回复+文案
        combineKeyWord(replyKeyWords, classifyQrcodeInfo, accountSettingDto, sb);
        if (keywordsAll.size()>replyKeyWords.size()){
            sb.append("--------------------------------\n为避免晚间群消息干扰，10分钟内只响应一次关键词，请直接点击下面的链接，获取本群其他资源服务：\n\n");
            //剩余关键词回复
            for (ReplyKeywordDTO replyKeywordDTO : keywordsAll) {
                List<ReplyKeywordDTO> collect = replyKeyWords.stream().filter(x -> replyKeywordDTO.getKeywords().equals(x.getKeywords())).collect(Collectors.toList());
                if (!ListUtils.isEmpty(collect)){
                    continue;
                }
                List<ReplyKeywordDTO> addKeyWordList = new ArrayList<>();
                addKeyWordList.add(replyKeywordDTO);
                StringBuilder toAdd = combineKeyWord(addKeyWordList, classifyQrcodeInfo, accountSettingDto, new StringBuilder());
                StringBuilder content = sb.append(toAdd);
                if (content.length() > LE) {
                    replyKeywordDTO.setReplyType(ReplyTypeEnum.TEXT.value);
                    replyKeywordDTO.setContent(sb.toString());
                    SendWeixinRequestTools.sendKeywordMessage(replyKeywordDTO, robotId, weixinGroupId, ip, code);
                    sb = toAdd;
                } else {
                    sb = content;
                }
            }
        }
        ReplyKeywordDTO keywordDTO = replyKeyWords.get(0);
        keywordDTO.setReplyType(ReplyTypeEnum.TEXT.value);
        keywordDTO.setContent(sb.toString());
        SendWeixinRequestTools.sendKeywordMessage(keywordDTO, robotId, weixinGroupId, ip, code);

        for (ReplyKeywordDTO replyKeywordDTO : replyKeyWords) {
            //新增关键词触发记录
            addKeywordAppTouchRecord(replyKeywordDTO, weixinGroupId, classifyQrcodeInfo.getClassifyId(), classifyQrcodeInfo.getBookGroupId());
            //第一推送关键词消息埋点
            String keywordsKey = "BOOK:FIRSTKEYWORD:" + DateUtils.getShortDateStr() + "-" + weixinGroupId;
            Boolean isSend = JedisClusterUtils.getJson(keywordsKey, Boolean.class);
            if (isSend != null && isSend) {
                return;
            }
            JedisClusterUtils.setJson(keywordsKey, true);
            JedisClusterUtils.expire(keywordsKey, 86400);
            RobotReplyDTO robotReplyDTO = new RobotReplyDTO();
            robotReplyDTO.setKeyWord(sendTextDTO.getTextContent().trim());
            robotReplyDTO.setWxGroupId(weixinGroupId);
            robotReplyDTO.setWxUserId(userWxId);
            wechatGroupConsr.addFirstRobotReplyRecord(robotReplyDTO);
        }
    }

    /**
     *合并关键词回复
     */
    private StringBuilder combineKeyWord(List<ReplyKeywordDTO> keywordsAll, GroupClassifyQrcodeDTO classifyQrcodeInfo, AccountSettingDto accountSettingDto,StringBuilder sb) {
        for (ReplyKeywordDTO keyword : keywordsAll) {
            if (ReplyTypeEnum.LINK.value.equals(keyword.getReplyType()) || ReplyTypeEnum.APP.value.equals(keyword.getReplyType())) {
                String linkUrl = SendWeixinRequestTools.splitUrlNew(accountSettingDto, keyword.getLinkUrl(), classifyQrcodeInfo.getBookGroupId(), classifyQrcodeInfo.getClassifyId(), classifyQrcodeInfo.getGroupQrcodeId());
                sb.append(" ").append(keyword.getContent()).append("\n->").append(UrlUtils.getShortUrl4Own(linkUrl)).append("\n").append("\n");
            }
        }
        return sb;
    }

    /**
     * 发送关键词
     */
    private void sendKeyword(SendTextDTO sendTextDTO, ReplyKeywordDTO replyKeywordDTO, GroupClassifyQrcodeDTO classifyQrcodeInfo) {
        final String content = sendTextDTO.getTextContent().trim();
        final String weixinGroupId = sendTextDTO.getWechatGroupId();
        final String userWxId = sendTextDTO.getWechatUserId();
        final String ip = sendTextDTO.getIp();
        final Integer code = sendTextDTO.getCode();
        String robotId = sendTextDTO.getWxId();
        log.info("[关键词回复原始数据] ： sendKeywordMessage replyKeywordDTO ：{}, robotId:{}, weixinGroupId:{}", replyKeywordDTO, robotId, weixinGroupId);
        // 处理链接地址
        final boolean isApp = ReplyTypeEnum.APP.value.equals(replyKeywordDTO.getReplyType());
        final boolean isLink = ReplyTypeEnum.LINK.value.equals(replyKeywordDTO.getReplyType());
        if (isApp || isLink) {
            AccountSettingDto accountSettingDto = qrcodeSceneConsr.getWechatInfo(classifyQrcodeInfo.getChannelId());
            String linkUrl = SendWeixinRequestTools.splitUrlNew(accountSettingDto, replyKeywordDTO.getLinkUrl(), classifyQrcodeInfo.getBookGroupId(), classifyQrcodeInfo.getClassifyId(), classifyQrcodeInfo.getGroupQrcodeId());
            String shortUrl4Own = UrlUtils.getShortUrl4Own(linkUrl);
            if (replyKeywordDTO.getLinkUrl().contains("live/C")) {
                // 获取直播课信息
                List<CourseListDto> list4BroadcastReminder = liveCons.getList4BroadcastReminder(replyKeywordDTO.getServeId(), replyKeywordDTO.getServeType());
                replyKeywordDTO.setReplyType(ReplyTypeEnum.TEXT.value);
                StringBuilder sb = new StringBuilder("【");
                sb.append(CollectionUtils.isEmpty(list4BroadcastReminder) ? replyKeywordDTO.getDescription() : list4BroadcastReminder.get(0).getTableTitle())
                        .append("】近期将开播的课程\n");
                if (!CollectionUtils.isEmpty(list4BroadcastReminder)) {
                    for (CourseListDto listDto : list4BroadcastReminder) {
                        sb.append("第").append(listDto.getSequenceNum()).append("课：").append(listDto.getTitle()).append("\n");
                    }
                }
                sb.append("查看更多课程表链接：").append(shortUrl4Own);
                replyKeywordDTO.setContent(sb.toString());
            }
            replyKeywordDTO.setLinkUrl(linkUrl);
            replyKeywordDTO.setShortLinkUrl(shortUrl4Own);
        }
        String redisContent = JedisClusterUtils.getJson("BOOK:KEYWORD:" + weixinGroupId + "-" + replyKeywordDTO.getKeywordId(), String.class);
        // 同一群10秒内不回复同一关键词，60s不回复同一关键词 20190916降低消息量方案之一
        if (insertBookKeywordRecord(weixinGroupId, userWxId, replyKeywordDTO, classifyQrcodeInfo, redisContent)) {
            return;
        }
        //获取推送消息机器人
        // 20190704改为收发一体
        if (StringUtil.isBlank(robotId)) {
            log.info("[关键词消息回复] robotId is null content:{}, robotId:{},weixinGroupId:{}", content, robotId, weixinGroupId);
            robotId = wechatGroupConsr.getRobotIdByGroupId(weixinGroupId);
        }
        log.info("[关键词回复发送数据] ： sendKeywordMessage replyKeywordDTO ：{}, robotId:{}, weixinGroupId:{}", replyKeywordDTO, robotId, weixinGroupId);
        SendWeixinRequestTools.sendKeywordMessage(replyKeywordDTO, robotId, weixinGroupId, ip, code);
        //新增关键词触发记录
        addKeywordAppTouchRecord(replyKeywordDTO, weixinGroupId, classifyQrcodeInfo.getClassifyId(), classifyQrcodeInfo.getBookGroupId());
        //第一推送关键词消息埋点
        String keywordsKey = "BOOK:FIRSTKEYWORD:" + DateUtils.getShortDateStr() + "-" + weixinGroupId;
        Boolean isSend = JedisClusterUtils.getJson(keywordsKey, Boolean.class);
        if (isSend != null && isSend) {
            return;
        }
        JedisClusterUtils.setJson(keywordsKey, true);
        JedisClusterUtils.expire(keywordsKey, 86400);
        RobotReplyDTO robotReplyDTO = new RobotReplyDTO();
        robotReplyDTO.setKeyWord(content);
        robotReplyDTO.setWxGroupId(weixinGroupId);
        robotReplyDTO.setWxUserId(userWxId);
        wechatGroupConsr.addFirstRobotReplyRecord(robotReplyDTO);
    }

    /**
     * 更新群人数
     */
    @Override
    public void updateGroupPeopleCount(String wxGroupId, Integer memberCount, GroupClassifyQrcodeDTO classifyQrcodeInfo) {
        Integer changeNumber = classifyQrcodeInfo.getChangeNumber();
        Long weixinQrcodeId = classifyQrcodeInfo.getWeixinQrcodeId();
        Long groupQrcodeId = classifyQrcodeInfo.getGroupQrcodeId();
        log.info("[更新群人数] updateGroupPeopleCount wxGroupId:{} memberCount:{} changeNumber：{} weixinQrcodeId：{} groupQrcodeId：{}", wxGroupId, memberCount, changeNumber, weixinQrcodeId, groupQrcodeId);
        if (Objects.isNull(memberCount) || StringUtil.isBlank(wxGroupId) || memberCount <= 0) {
            return;
        }
        String key = CacheConstant.BOOK + "groupPeopleCount:";
        String groupPeopleCount = JedisClusterUtils.hget(key, wxGroupId);
        log.info("[更新群人数] updateGroupPeopleCount groupPeopleCount:{}", groupPeopleCount);
        // 如果没有缓存或者有缓存但是人数与缓存不一致，代表有更新，则更新数据库
        final boolean update = StringUtil.isBlank(groupPeopleCount) || StringUtil.isNotBlank(groupPeopleCount) && !groupPeopleCount.equals(memberCount + "");
        if (update) {
            JedisClusterUtils.hset(key, wxGroupId, memberCount + "");
            // 将二维码改为已满群BookKeywordBizImpl
            Integer updatState = Optional.ofNullable(weixinQrcodeBiz.getGroupVersion(Collections.singletonList(wxGroupId))).map(x -> x.get(wxGroupId)).map(BookWxQrcodeDTO::getUpdateState).orElse(-1);
            if (memberCount >= changeNumber) {
                groupQrcodeDao.changeQrcodeState(groupQrcodeId, QrcodeStatusEnum.OVER_NUMBER.value);
                log.info("[更新群人数] 触发更新大于等于状态 wxGroupId:{} memberCount:{} changeNumber：{} weixinQrcodeId：{} groupQrcodeId：{}", wxGroupId, memberCount, changeNumber, weixinQrcodeId, groupQrcodeId);
            } else {
                // 二维码过期的数据不处理
                if (!UpdateStatusEnum.FIAL.value.equals(updatState)) {
                    groupQrcodeDao.changeQrcodeStateForNotOver(groupQrcodeId, QrcodeStatusEnum.ON_USE.value);
                    log.info("[更新群人数] 触发更新小于状态 wxGroupId:{} memberCount:{} changeNumber：{} weixinQrcodeId：{} groupQrcodeId：{}", wxGroupId, memberCount, changeNumber, weixinQrcodeId, groupQrcodeId);
                }
            }
            // 当群人数超过限制，或者群二维码更新状态为失败时更新群状态为满群
            boolean overstaff = memberCount >= (JoinGroupTypeEnum.GROUP_QRCODE.getCode().equals(classifyQrcodeInfo.getJoinGroupType()) ? 100 : 500);
            if (overstaff || UpdateStatusEnum.FIAL.value.equals(updatState)) {
                groupQrcodeBiz.changeToOverNumber(weixinQrcodeId, groupQrcodeId);
                log.info("[更新群人数] 触发更新满群状态 wxGroupId:{} memberCount:{} changeNumber：{} weixinQrcodeId：{} groupQrcodeId：{}", wxGroupId, memberCount, changeNumber, weixinQrcodeId, groupQrcodeId);
                groupQrcodeDao.changeQrcodeState(groupQrcodeId, QrcodeStatusEnum.OVER_NUMBER.value);
            } else {
                groupQrcodeBiz.changeToNotOverNumber(weixinQrcodeId, groupQrcodeId);
                log.info("[更新群人数] 触发更新未满群状态 wxGroupId:{} memberCount:{} changeNumber：{} weixinQrcodeId：{} groupQrcodeId：{}", wxGroupId, memberCount, changeNumber, weixinQrcodeId, groupQrcodeId);
            }
            groupQrcodeBiz.updateGroupCount(wxGroupId, memberCount);
            log.info("[成功更新群人数] updateGroupPeopleCount wxGroupId:{} memberCount:{}", wxGroupId, memberCount);
        }
    }

    /**
     * 修改群名称
     */
    @Override
    public void checkGroupName(String weixinGroupId, String groupName, String robotId, String ip) {
        log.info("[checkGroupName] weixinGroupId:{} groupName:{} robotId:{} ip:{} groupQrcodeDTO:{}", weixinGroupId, groupQrcodeDao, robotId, ip);
        if (StringUtil.isBlank(weixinGroupId) || StringUtil.isBlank(groupName) || StringUtil.isBlank(robotId) || StringUtil.isBlank(ip)) {
            return;
        }
        String key = CacheConstant.BOOK + "CHECKGROUPNAME:";
        String s = JedisClusterUtils.hget(key, weixinGroupId);
        log.info("[修改群名称] checkGroupName s:{}", s);
        if (StringUtil.isBlank(s)) {
            ChangeNameVO vo = new ChangeNameVO();
            vo.setName(groupName);
            vo.setAltId(robotId);
            vo.setWxGroupId(weixinGroupId);
            vo.setIp(ip);
            WxGroupSDK.changeGroupName(vo);
            // 改过名之后七天不再改名
            JedisClusterUtils.hset(key, weixinGroupId, "1");
            JedisClusterUtils.expire(key, 60 * 60 * 24 * 31);
            log.info("[修改群名称] checkGroupName vo:{}", vo);
        }
    }

    /**
     * 关键词应用触发记录
     */
    private void addKeywordAppTouchRecord(ReplyKeywordDTO replyKeywordDTO, String weixinGroupId, Long classifyId, Long bookGroupId) {
        Keyword keyword = keywordDao.getById(replyKeywordDTO.getKeywordId());
        if (keyword != null && ReplyTypeEnum.APP.value.equals(keyword.getReplyType())) {
            AppTouchRecord appTouchRecord = new AppTouchRecord();
            appTouchRecord.setServeId(keyword.getServeId());
            appTouchRecord.setServeType(keyword.getServeType());
            appTouchRecord.setTouchType(TouchTypeEnum.KEYWORD.value);
            appTouchRecord.setBookGroupId(bookGroupId);
            appTouchRecord.setClassifyId(classifyId);
            GroupQrcode groupQrcode = groupQrcodeDao.getGroupQrcodeByGroupId(weixinGroupId);
            if (groupQrcode != null) {
                appTouchRecord.setQrcodeId(groupQrcode.getId());
            }
            appTouchRecord.setWeixinGroupId(weixinGroupId);
            appTouchRecordDao.insert(appTouchRecord);
        }
    }

    private void insertBookKeywordRecord(GroupClassifyQrcodeDTO classifyQrcodeInfo, Long keywordId, String userWxId, String weixinGroupId, Boolean isSend) {
        BookKeywordRecord bookKeywordRecord = new BookKeywordRecord();
        BeanUtils.copyProperties(classifyQrcodeInfo, bookKeywordRecord);
        bookKeywordRecord.setKeywordId(keywordId);
        bookKeywordRecord.setUserWxId(userWxId);
        bookKeywordRecord.setWeixinGroupId(weixinGroupId);
        bookKeywordRecord.setSend(isSend);
        if (bookKeywordRecord.getGroupQrcodeId() == null) {
            GroupQrcode groupQr = groupQrcodeDao.getGroupQrcodeByGroupId(weixinGroupId);
            if (groupQr != null) {
                bookKeywordRecord.setGroupQrcodeId(groupQr.getId());
            }
        }
        bookKeywordRecordDao.insert(bookKeywordRecord);
    }

    @Override
    public PageBeanNew<KeywordStatisticsDTO> getKeywordStatistics(Integer currentPage, Integer numPerPage, Long bookGroupId, String weixinGroupId) {
        PageParam pageParam = new PageParam(currentPage, numPerPage);
        Map<String, Object> map = new HashMap<>();
        map.put("bookGroupId", bookGroupId);
        if (!StringUtil.isEmpty(weixinGroupId)) {
            GroupQrcode groupQrcode = groupQrcodeDao.getGroupQrcodeByGroupId(weixinGroupId);
            if (groupQrcode != null) {
                map.put("classifyId", groupQrcode.getClassifyId());
            }
        }
        //分页获取关键词，创建时间，名称信息
        PageBeanNew<KeywordStatisticsDTO> pageBeanNew = bookKeywordDao.listPageNew(pageParam, map, "getKeywordInfoByBookGroupId");
        List<KeywordStatisticsDTO> keywordStatisticsDTOS = pageBeanNew.getRecordList();
        if (ListUtils.isEmpty(keywordStatisticsDTOS)) {
            return pageBeanNew;
        }
        List<Long> keywordIds = keywordStatisticsDTOS.stream().filter(s -> s.getKeywordId() != null).map(KeywordStatisticsDTO::getKeywordId).collect(Collectors.toList());
        //获取关键词累计触发次数，最近一次触发时间
        List<KeywordStatisticsDTO> keywordStatisticsDTOSForOther = bookKeywordRecordDao.getKeywordStatistics(bookGroupId, weixinGroupId, keywordIds);
        Map<Long, KeywordStatisticsDTO> otherMap = new HashMap<>();
        if (!ListUtils.isEmpty(keywordStatisticsDTOSForOther)) {
            for (KeywordStatisticsDTO in : keywordStatisticsDTOSForOther) {
                otherMap.put(in.getKeywordId(), in);
            }
        }
        //获取最近7天的触发次数
        Date startDate = DateUtils.addDay(new Date(), -7);
        List<KeywordStatisticsDTO> keywordStatisticsDTOSForTime = bookKeywordRecordDao.getKeywordStatisticsByTime(bookGroupId, weixinGroupId, keywordIds, startDate);
        Map<Long, Integer> weekTouchCountMap = new HashMap<>();
        if (!ListUtils.isEmpty(keywordStatisticsDTOSForOther)) {
            for (KeywordStatisticsDTO in : keywordStatisticsDTOSForTime) {
                weekTouchCountMap.put(in.getKeywordId(), in.getWeekTouchCount());
            }
        }
        for (KeywordStatisticsDTO keywordStatisticsDTO : keywordStatisticsDTOS) {
            Long keywordId = keywordStatisticsDTO.getKeywordId();
            KeywordStatisticsDTO other = otherMap.get(keywordId);
            if (other == null) {
                //如果记录表里面没有就填充0
                keywordStatisticsDTO.setTouchCount(0);
                keywordStatisticsDTO.setWeekTouchCount(0);
                keywordStatisticsDTO.setAvgDayTouchCount(0D);
            } else {
                //计算，填充
                keywordStatisticsDTO.setTouchCount(other.getTouchCount());
                keywordStatisticsDTO.setLastTouchTime(other.getLastTouchTime());
                keywordStatisticsDTO.setWeekTouchCount(weekTouchCountMap.get(keywordId));
                Date createDate = keywordStatisticsDTO.getKeywordCreateDate();
                int days = (int) (DateUtils.getDayEnd(new Date()).getTime() - DateUtils.getDayEnd(createDate).getTime()) / 1000 / 60 / 60 / 24;
                if (days <= 0) {
                    days = 1;
                }
                Double avgTouchCount = (double) keywordStatisticsDTO.getTouchCount() / (double) days;
                keywordStatisticsDTO.setAvgDayTouchCount(new BigDecimal(avgTouchCount).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            }
        }
        return pageBeanNew;
    }

    @Override
    public Integer getKeywordCount(Long bookGroupId, Long classifyId) {
        if (bookGroupId == null) {
            throw new BookBizException(BookBizException.ERROR, "缺少参数bookGroupId");
        }
        return bookKeywordDao.getKeywordCount(bookGroupId, classifyId);
    }


    @Override
    public List<ServiceResourceDTO> getServiceByWeixinGroup(QrWeixinParam qrWeixinParam, Long classifyId, Long qrcodeId) {
        Map<String, Object> map = new HashMap<>();
        map.put("bookGroupId", qrWeixinParam.getBookGroupId());
        List<ServiceResourceDTO> serviceResourceDTOS = ResponesUtils.list(bookKeywordDao.listBy(map, "getServiceByWeixinGroup"), ServiceResourceDTO.class);
        if (ListUtils.isEmpty(serviceResourceDTOS)) {
            return new ArrayList<>();
        }
        for (ServiceResourceDTO serviceResourceDTO : serviceResourceDTOS) {
            AccountSettingDto accountSettingDto = qrcodeSceneConsr.getWechatInfo(serviceResourceDTO.getChannelId());
            String linkUrl = SendWeixinRequestTools.splitUrlNew(accountSettingDto, serviceResourceDTO.getUrl(), qrWeixinParam.getBookGroupId(), classifyId, qrcodeId);
            serviceResourceDTO.setUrl(linkUrl);
        }
        List<Long> appIds = serviceResourceDTOS.stream().filter(s -> ("APP").equals(s.getTypeCode())).map(ServiceResourceDTO::getFromId).collect(Collectors.toList());
        Map<Long, AppDto> appMap = appConsr.getBaseByIds(appIds);
        if (!MapUtils.isEmpty(appMap)) {
            for (ServiceResourceDTO serviceResourceDTO : serviceResourceDTOS) {
                AppDto appDto = appMap.get(serviceResourceDTO.getFromId());
                if (appDto != null && serviceResourceDTO.getTypeCode().equals("APP")) {
                    serviceResourceDTO.setFromType(appDto.getTypeCode());
                    serviceResourceDTO.setTitle(appDto.getTitle());
                    serviceResourceDTO.setShortName(appDto.getTypeName());
                }
            }
        }
        List<Long> productIds = serviceResourceDTOS.stream().filter(s -> ("PRODUCT").equals(s.getTypeCode())).map(ServiceResourceDTO::getFromId).collect(Collectors.toList());
        Map<Long, ProductDto> productMap = productConsr.getProBasesByIds(productIds);
        if (!MapUtils.isEmpty(productMap)) {
            for (ServiceResourceDTO serviceResourceDTO : serviceResourceDTOS) {
                ProductDto productDto = productMap.get(serviceResourceDTO.getFromId());
                if (productDto != null && serviceResourceDTO.getTypeCode().equals("PRODUCT")) {
                    ProductTypeDto productTypeDto = productDto.getProductTypeDto();
                    if (productTypeDto != null) {
                        serviceResourceDTO.setFromType(productTypeDto.getTypeCode());
                        serviceResourceDTO.setTitle(productDto.getProductName());
                        serviceResourceDTO.setShortName(productTypeDto.getTypeName());
                    }
                }
            }
        }
        if (serviceResourceDTOS != null && !StringUtil.isEmpty(qrWeixinParam.getTypeCode()) && qrWeixinParam.getServeId() != null) {
            serviceResourceDTOS.removeIf(vo -> (qrWeixinParam.getTypeCode().equals(vo.getTypeCode()) && qrWeixinParam.getServeId().equals(vo.getFromId())));
        }
        List<ServiceResourceDTO> uniqueResources = serviceResourceDTOS.stream().collect(
                Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getFromId() + ";" + o.getTypeCode()))), ArrayList::new)
        );
        return uniqueResources;
    }

    @Override
    public List<ServiceResourceDTO> getServiceByChannel(QrWeixinParam qrWeixinParam, Long wechatUserId) {
        QrWeixinParamVO qrWeixinParamVO = new QrWeixinParamVO();
        BeanUtils.copyProperties(qrWeixinParam, qrWeixinParamVO);
        List<MessageBookVO> messageBookVOS = ResponseHandleUtil.parseListResponse(qrcodeSceneService.getQrMessage4Book(qrWeixinParamVO, wechatUserId), MessageBookVO.class);
        return ResponesUtils.list(messageBookVOS, ServiceResourceDTO.class);
    }

    @ParamLog("根据社群码id获取关键词列表")
    @Override
    public List<KeywordDTO> getListByBookGroupId(Long bookGroupId) {
        return bookKeywordDao.getListByBookGroupId(bookGroupId);
    }

    @Override
    public ReplyKeywordDTO getReplyKeyword(String content, String weixinGroupId, String wexinGroupName) {
        ReplyKeywordDTO replyKeywordDTO = null;
        if (StringUtil.isEmpty(weixinGroupId) && StringUtil.isEmpty(wexinGroupName)) {
            return new ReplyKeywordDTO();
        }
        GroupClassifyQrcodeDTO classifyQrcodeInfo = null;
        if (!StringUtil.isEmpty(weixinGroupId)) {
            //通过群id获取对应基本信息
            classifyQrcodeInfo = bookGroupClassifyBiz.getClassifyQrcodeInfo(weixinGroupId);
        }
        if (classifyQrcodeInfo == null && !StringUtil.isEmpty(wexinGroupName)) {
            classifyQrcodeInfo = bookGroupClassifyDao.getClassifyQrcodeInfoByName((wexinGroupName));
        }
        if (classifyQrcodeInfo == null) {
            log.info("[关键词消息回复] classifyQrcodeInfo is null");
            return new ReplyKeywordDTO();
        }
        final boolean equals = "群主，来个抽奖".equals(content);
        if (equals) {
            replyKeywordDTO = keywordDao.getByKeyword("群主，来个抽奖");
        } else {
            //获取匹配关键词
            replyKeywordDTO = bookKeywordDao.getKeywordId(classifyQrcodeInfo.getClassifyId(), classifyQrcodeInfo.getBookGroupId(), content);
        }
        if (replyKeywordDTO == null) {
            return new ReplyKeywordDTO();
        }
        // 处理链接地址
        if (ReplyTypeEnum.APP.value.equals(replyKeywordDTO.getReplyType()) || ReplyTypeEnum.LINK.value.equals(replyKeywordDTO.getReplyType())) {
            AccountSettingDto accountSettingDto = qrcodeSceneConsr.getWechatInfo(classifyQrcodeInfo.getChannelId());
            String linkUrl = SendWeixinRequestTools.splitUrlNew(accountSettingDto, replyKeywordDTO.getLinkUrl(), classifyQrcodeInfo.getBookGroupId(), classifyQrcodeInfo.getClassifyId(), classifyQrcodeInfo.getGroupQrcodeId());
            replyKeywordDTO.setLinkUrl(linkUrl);
            if (!StringUtil.isEmpty(linkUrl)) {
                replyKeywordDTO.setShortLinkUrl(UrlUtils.getShortUrl4Own(linkUrl));
            }
        }
        return replyKeywordDTO;
    }

    @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 silenceProcess(String userWxId, Integer code, String robotId) {
        // 个人号消息 用户发送:wechatCreateUser!=wxId,小号发送wechatCreateUser=wxId
        if (SendMessageTypeEnum.SELF.getCode().equals(code) && !userWxId.equals(robotId)) {
            // 从缓存中获取缄默信息
            if(JedisClusterUtils.hexists(robotId+PCLOUD_GUIDE_SUFFIX,userWxId)){
                PcloudRobot pcloudRobot = pcloudRobotDao.getByWxId(robotId);
                if (pcloudRobot == null) {
                    return;
                }
                Integer duration = pcloudRobotSilenceMapper.getSilenceDurationByRobotWxId(null, pcloudRobot.getRobotType());
                if (duration == null) {
                    duration = 0;
                }
                String hget = JedisClusterUtils.hget(robotId + PCLOUD_GUIDE_SUFFIX, userWxId);
                PcloudGuideDelayDto pcloudGuideDelayDto = JSONObject.parseObject(hget, PcloudGuideDelayDto.class);
                pcloudGuideDelayDto.setSendTime(new Date());
                // duration
                pcloudGuideDelayDto.setDuration(duration);
                JedisClusterUtils.hset(robotId+PCLOUD_GUIDE_SUFFIX,userWxId,JSONObject.toJSONString(pcloudGuideDelayDto));
            }
        }
    }

    /**
     * 机器人自动唤醒配置
     */
    @Override
    public void robotWakeUp(String userWxId, String ip, Integer code, String robotId) {
        // 个人号 用户回复消息
        String userId = userWxId;
        if (SendMessageTypeEnum.SELF.getCode().equals(code) && !userId.equals(robotId)) {
            PcloudRobot byWxId = pcloudRobotDao.getByWxId(robotId);
            if (byWxId != null) {
                // 向缓存中写入用户与小号最后一次聊天时间
                WakeUpInfoDto wakeUpInfoDto = new WakeUpInfoDto();
                wakeUpInfoDto.setIp(ip);
                wakeUpInfoDto.setTime(new Date());
                JedisClusterUtils.hset(robotId + PCLOUD_ROBOT_WAKE_UP_SUFFIX, userId, JSONObject.toJSONString(wakeUpInfoDto));
            }
        }
    }
}
