package com.ningdatech.pmapi.meeting.manage;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.pmapi.common.util.BizUtils;
import com.ningdatech.pmapi.expert.constant.ExpertAccountStatusEnum;
import com.ningdatech.pmapi.expert.entity.ExpertAvoidCompany;
import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;
import com.ningdatech.pmapi.expert.service.IExpertAvoidCompanyService;
import com.ningdatech.pmapi.expert.service.IExpertIntentionWorkRegionService;
import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService;
import com.ningdatech.pmapi.meeting.builder.ExpertInviteBuilder;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule;
import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.dto.*;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum;
import com.ningdatech.pmapi.meeting.helper.ExpertInviteHelper;
import com.ningdatech.pmapi.meeting.helper.MeetingCallOrMsgHelper;
import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import com.ningdatech.pmapi.meeting.service.IMeetingService;
import com.ningdatech.pmapi.meta.helper.TagCache;
import com.ningdatech.pmapi.meta.model.entity.ExpertDictionary;
import com.ningdatech.pmapi.meta.model.entity.ExpertTag;
import com.ningdatech.pmapi.meta.service.IExpertDictionaryService;
import com.ningdatech.pmapi.meta.service.IExpertTagService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum.*;
import static com.ningdatech.pmapi.meeting.helper.ExpertInviteHelper.getExpertInviteRule;
import static com.ningdatech.pmapi.meeting.helper.ExpertRandomInviteAlgorithm.inviteGroupByCompany;

/**
 * <p>
 * 专家邀请管理
 * </p>
 *
 * @author WendyYang
 * @since 18:05 2022/8/8
 */
@Component
@RequiredArgsConstructor
public class ExpertInviteManage {

    private final IExpertDictionaryService expertDictionaryService;
    private final TagCache tagCache;
    private final IExpertTagService expertTagService;
    private final IExpertIntentionWorkRegionService workRegionService;
    private final IExpertInviteRuleService inviteRuleService;
    private final IMeetingExpertService meetingExpertService;
    private final ExpertInviteHelper expertInviteHelper;
    private final IExpertUserFullInfoService expertUserFullInfoService;
    private final IMeetingService meetingService;
    private final IExpertAvoidCompanyService expertAvoidCompanyService;
    private final MeetingCallOrMsgHelper meetingCallOrMsgHelper;

    @Value("#{randomInviteProperties.recentMeetingCount}")
    private Integer recentMeetingCount;

    @Value("#{randomInviteProperties.recentDays}")
    private Integer recentDays;

    private static final Predicate<Collection<?>> COLL_EMPTY = (coll) -> coll != null && coll.isEmpty();

    private LambdaQueryWrapper<ExpertUserFullInfo> buildBaseExpertQuery() {
        return Wrappers.lambdaQuery(ExpertUserFullInfo.class)
                .select(ExpertUserFullInfo::getUserId,
                        ExpertUserFullInfo::getId,
                        ExpertUserFullInfo::getCompany,
                        ExpertUserFullInfo::getExpertName,
                        ExpertUserFullInfo::getCompanyUniqCode,
                        ExpertUserFullInfo::getPhoneNo)
                .eq(ExpertUserFullInfo::getExpertAccountStatus, ExpertAccountStatusEnum.AVAILABLE.getKey());
    }

    private void buildAvoidCompanyAndBusinessStrip(LambdaQueryWrapper<ExpertUserFullInfo> query, List<String> units, List<String> strips) {
        if (CollUtil.isNotEmpty(units)) {
            String unitStr = BizUtils.inSqlJoin(units);
            query.notExists("select 1 from expert_avoid_company eac where eac.user_id = nd_expert_user_full_info.user_id" +
                    " and company_uniq_code in " + unitStr);
        }
        if (CollUtil.isNotEmpty(strips)) {
            String orgStr = BizUtils.inSqlJoin(strips);
            query.notExists("select 1 from expert_gov_business_strip egbs where egbs.expert_user_id = nd_expert_user_full_info.user_id" +
                    " and business_strip_code in " + orgStr);
        }
    }

    /**
     * 获取一周内被抽中并同意参会的专家ID
     *
     * @param agreeCnt 一周内被抽中并同意参会次数
     * @param sTime    会议开始时间
     * @param days     天数
     * @return java.util.List<java.lang.Long>
     * @author WendyYang
     **/
    private List<Long> listAgreedUserIdByRecentMeetings(int agreeCnt, int days, LocalDateTime sTime) {
        if (agreeCnt == 0 || days == 0) {
            return Collections.emptyList();
        }
        LocalDateTime beginLimit = sTime.minusDays(days);
        return meetingExpertService.listAgreeExpertIdByRecentDaysAndAgreeCount(agreeCnt, beginLimit, sTime);
    }

    /**
     * 增加专家层级限制
     *
     * @param query 查询
     * @param rule  随机邀请规则
     */
    private static void addRegionLimit(LambdaQueryWrapper<ExpertUserFullInfo> query, RandomInviteRuleDTO rule) {
        if (CollUtil.isNotEmpty(rule.getExpertRegionList())) {
            String inSqlParam = rule.getExpertRegionList().stream()
                    .map(w -> "('" + w.getRegionCode() + "'," + w.getRegionLevel() + ")")
                    .collect(Collectors.joining(",", "(", ")"));
            query.apply("(region_code, region_level) in " + inSqlParam);
        }
    }

    /**
     * 获取满足履职意向地的专家ID
     * null -> 表示无需过滤
     *
     * @param rule 抽取规则
     * @return java.util.List<java.lang.Long>
     * @author WendyYang
     **/
    private List<Long> expertIdsByRegion(RandomInviteRuleDTO rule) {
        if (rule.getIntentionRegionCode() == null || rule.getIntentionRegionLevel() == null) {
            return null;
        }
        return workRegionService.userIdsMatchIntentionRegion(rule.getIntentionRegionCode(), rule.getIntentionRegionLevel());
    }


    /**
     * 如果抽取回避单位和专家回避单位一致，同样需要回避该专家
     *
     * @param unitCodeList /
     * @return /
     */
    private List<Long> avoidCompanyExpertIds(List<String> unitCodeList) {
        if (CollUtil.isEmpty(unitCodeList)) {
            return new ArrayList<>();
        }
        LambdaQueryWrapper<ExpertAvoidCompany> query = Wrappers.lambdaQuery(ExpertAvoidCompany.class)
                .select(ExpertAvoidCompany::getUserId)
                .in(ExpertAvoidCompany::getCompanyUniqCode, unitCodeList);
        List<ExpertAvoidCompany> expertAvoidCompanyList = expertAvoidCompanyService.list(query);
        return CollUtils.fieldList(expertAvoidCompanyList, ExpertAvoidCompany::getUserId);
    }

    private MergeExpertIdDTO mergeExpertIdsByCondition(RandomInviteRuleDTO rule, AvoidRuleDTO avoidInfo) {
        // 处理履职意向地
        List<Long> expertIdsByIntentionRegion = expertIdsByRegion(rule);
        if (COLL_EMPTY.evaluate(expertIdsByIntentionRegion)) {
            return MergeExpertIdDTO.noExpert();
        }
        // 处理专家标签
        List<Long> expertIdsByTag = expertIdsByTag(rule);
        if (COLL_EMPTY.evaluate(expertIdsByTag)) {
            return MergeExpertIdDTO.noExpert();
        }
        // 处理专家字典
        List<Long> expertIdsByDict = expertIdsByDict(rule);
        if (COLL_EMPTY.evaluate(expertIdsByDict)) {
            return MergeExpertIdDTO.noExpert();
        }
        // 处理专家回避单位 与 抽取回避单位是否相同
        List<Long> avoidExpertIdsByUnit = avoidCompanyExpertIds(avoidInfo.getAvoidUnitIdList());

        MergeExpertIdDTO merge = new MergeExpertIdDTO();
        // 聚合用户ID
        List<Long> expertIdsIn = new ArrayList<>();
        if (ObjectUtils.anyNotNull(expertIdsByDict, expertIdsByTag, expertIdsByIntentionRegion)) {
            Optional.ofNullable(expertIdsByDict).ifPresent(expertIdsIn::addAll);
            Optional.ofNullable(expertIdsByTag).ifPresent(item -> {
                if (expertIdsIn.isEmpty()) {
                    expertIdsIn.addAll(expertIdsByTag);
                } else {
                    expertIdsIn.removeIf(w -> !expertIdsByTag.contains(w));
                }
            });
            Optional.ofNullable(expertIdsByIntentionRegion).ifPresent(item -> {
                if (expertIdsIn.isEmpty()) {
                    expertIdsIn.addAll(expertIdsByIntentionRegion);
                } else {
                    expertIdsIn.removeIf(w -> !expertIdsByIntentionRegion.contains(w));
                }
            });
            if (expertIdsIn.isEmpty()) {
                return MergeExpertIdDTO.noExpert();
            }
            merge.setExpertIdsIn(expertIdsIn);
        }
        if (CollUtil.isNotEmpty(avoidExpertIdsByUnit)) {
            if (expertIdsIn.isEmpty()) {
                merge.setExpertIdsNotIn(avoidExpertIdsByUnit);
            } else {
                expertIdsIn.removeIf(avoidExpertIdsByUnit::contains);
                if (expertIdsIn.isEmpty()) {
                    return MergeExpertIdDTO.noExpert();
                }
            }
        }
        merge.setSkip(Boolean.FALSE);
        return merge;
    }

    /**
     * 根据专家标签获取满足的专家ID
     *
     * @param rule 抽取规则
     * @return java.util.List<java.lang.Long>
     * @author WendyYang
     **/
    private List<Long> expertIdsByTag(RandomInviteRuleDTO rule) {
        if (CollectionUtils.isEmpty(rule.getExpertTags())) {
            return null;
        }
        LambdaQueryWrapper<ExpertTag> query = Wrappers.lambdaQuery(ExpertTag.class)
                .select(ExpertTag::getUserId, ExpertTag::getExpertInfoField);
        rule.getExpertTags().forEach(tag -> {
            List<String> codes = tag.getTagCodes().stream()
                    .map(tagCache::listChildrenCodes)
                    .flatMap(Collection::stream)
                    .collect(Collectors.toList());
            query.or(wrapper -> wrapper.eq(ExpertTag::getExpertInfoField, tag.getExpertTag())
                    .in(ExpertTag::getTagCode, codes));
        });
        List<ExpertTag> expertTagList = expertTagService.list(query);
        return expertTagList.stream()
                .collect(Collectors.groupingBy(ExpertTag::getUserId, Collectors.collectingAndThen(Collectors.toList(),
                        tags -> CollUtils.fieldList(tags, ExpertTag::getExpertInfoField).size())))
                .entrySet().stream().filter(w -> w.getValue() == rule.getExpertTags().size())
                .map(Map.Entry::getKey).collect(Collectors.toList());
    }

    /**
     * 根据专家字典获取满足的专家ID
     * null -> 表示无需过虑
     * 空集合 -> 未查到
     *
     * @param rule 抽取规则
     * @return java.util.List<java.lang.Long>
     * @author WendyYang
     **/
    private List<Long> expertIdsByDict(RandomInviteRuleDTO rule) {
        if (CollectionUtils.isEmpty(rule.getExpertDicts())) {
            return null;
        }
        LambdaQueryWrapper<ExpertDictionary> query = Wrappers.lambdaQuery(ExpertDictionary.class)
                .select(ExpertDictionary::getUserId, ExpertDictionary::getExpertInfoField);
        for (ExpertDictChooseDTO dict : rule.getExpertDicts()) {
            query.or(wrapper -> wrapper.eq(ExpertDictionary::getExpertInfoField, dict.getExpertDict())
                    .in(ExpertDictionary::getDictionaryCode, dict.getDictCodes()));
        }
        List<ExpertDictionary> expertDictionaryList = expertDictionaryService.list(query);
        return expertDictionaryList.stream()
                .collect(Collectors.groupingBy(ExpertDictionary::getUserId,
                        Collectors.collectingAndThen(Collectors.toList(),
                                dicts -> CollUtils.fieldList(dicts, ExpertDictionary::getExpertInfoField).size())))
                .entrySet().stream().filter(w -> w.getValue() == rule.getExpertDicts().size())
                .map(Map.Entry::getKey).collect(Collectors.toList());
    }


    /**
     * 专家抽取（随机）
     *
     * @param avoidRule        回避信息
     * @param randomRule       抽取规则
     * @param invitedExpertIds 指定抽取专家ID
     * @return 满足抽取条件的专家
     * @author WendyYang
     **/
    public ExpertChooseDTO expertInviteByRandomRule(AvoidRuleDTO avoidRule,
                                                    RandomInviteRuleDTO randomRule,
                                                    List<Long> invitedExpertIds,
                                                    LocalDateTime sTime,
                                                    LocalDateTime eTime) {
        ExpertChooseDTO result = new ExpertChooseDTO(new ArrayList<>(), 0);
        MergeExpertIdDTO merge = mergeExpertIdsByCondition(randomRule, avoidRule);
        if (merge.getSkip()) {
            return result;
        }
        boolean avoidExpert = CollUtil.isNotEmpty(avoidRule.getExpertIds());
        boolean avoidCompany = CollUtil.isNotEmpty(avoidRule.getAvoidUnitIdList());
        Set<String> avoidCompanyUniqCodes = new HashSet<>();
        if (avoidCompany) {
            avoidCompanyUniqCodes.addAll(avoidRule.getAvoidUnitIdList());
        }
        if (CollUtil.isNotEmpty(invitedExpertIds)) {
            List<String> tmpCompanyUniqCodes = expertUserFullInfoService.listCompanyUniqCodeByUserIds(invitedExpertIds);
            avoidCompanyUniqCodes.addAll(tmpCompanyUniqCodes);
        }
        // 回避信息
        LambdaQueryWrapper<ExpertUserFullInfo> query = buildBaseExpertQuery();
        query.notIn(!avoidCompanyUniqCodes.isEmpty(), ExpertUserFullInfo::getCompanyUniqCode, avoidCompanyUniqCodes);
        // 处理回避单位与回避条线
        buildAvoidCompanyAndBusinessStrip(query, avoidRule.getAvoidUnitIdList(), avoidRule.getAvoidOrgIdList());
        Set<Long> expertIdsIn = new HashSet<>();
        Set<Long> expertIdsNotIn = new HashSet<>();
        if (CollUtil.isNotEmpty(merge.getExpertIdsIn())) {
            expertIdsIn.addAll(merge.getExpertIdsIn());
        } else if (CollUtil.isNotEmpty(merge.getExpertIdsNotIn())) {
            expertIdsNotIn.addAll(merge.getExpertIdsNotIn());
        }
        // 处理回避专家次数
        if (avoidRule.getWeekInviteCount() != null) {
            Integer weekInviteCount = avoidRule.getWeekInviteCount();
            List<Long> tmpExpertIdsNotIn = listAgreedUserIdByRecentMeetings(weekInviteCount, recentDays, sTime);
            expertIdsNotIn.addAll(tmpExpertIdsNotIn);
        }
        // 处理专家层级
        addRegionLimit(query, randomRule);
        if (!expertIdsIn.isEmpty()) {
            if (avoidExpert) {
                expertIdsIn.removeIf(w -> avoidRule.getExpertIds().contains(w));
                if (expertIdsIn.isEmpty()) {
                    // 字典、标签、履职意向地 筛选出的专家ID移除需要回避的专家ID、如果为空则说明没有符合条件的
                    return result;
                }
            }
            if (CollUtil.isNotEmpty(invitedExpertIds)) {
                expertIdsIn.removeIf(invitedExpertIds::contains);
                if (expertIdsIn.isEmpty()) {
                    return result;
                }
            }
            // 过滤掉已参加会议的专家
            List<Long> expertIdsLockByMeeting = expertInviteHelper.listInvitedExpertByTime(sTime, eTime);
            expertIdsIn.removeIf(expertIdsLockByMeeting::contains);
            if (expertIdsIn.isEmpty()) {
                return result;
            }
        } else if (avoidExpert || CollUtil.isNotEmpty(invitedExpertIds)) {
            Set<Long> tmpExpert = expertInviteHelper.getAvoidExpert(invitedExpertIds, avoidRule, sTime, eTime);
            expertIdsNotIn.addAll(tmpExpert);
        } else {
            Set<Long> tmpNotInUserIds = expertInviteHelper.listExpertLeaveOrInvited(sTime, eTime);
            expertIdsNotIn.addAll(tmpNotInUserIds);
        }
        if (!expertIdsIn.isEmpty()) {
            query.in(ExpertUserFullInfo::getUserId, expertIdsIn);
        } else if (!expertIdsNotIn.isEmpty()) {
            query.notIn(ExpertUserFullInfo::getUserId, expertIdsNotIn);
        }
        List<ExpertUserFullInfo> userInfoList = expertUserFullInfoService.list(query);
        if (userInfoList.isEmpty()) {
            return result;
        }
        Map<String, List<ExpertUserFullInfo>> userGroupByUnit = CollUtils.group(userInfoList, ExpertUserFullInfo::getCompanyUniqCode);
        result.setTotal(userGroupByUnit.size());

        result.setExperts(inviteGroupByCompany(userGroupByUnit, expertsByRecentMeeting(), randomRule.getCount()));
        return result;
    }

    /**
     * 专家替换、补充
     *
     * @param avoidRule      回避信息
     * @param randomRule     随机抽取
     * @param invitedExperts 已抽取人员
     * @param count          抽取数量
     * @param msTime         会议开始时间
     * @param meTime         会议结束时间
     * @param reInvite       邀请已拒绝
     * @return {@link ExpertChooseDTO}
     * @author WendyYang
     **/
    public ExpertChooseDTO expertReplaceByRandomRule(AvoidRuleDTO avoidRule,
                                                     RandomInviteRuleDTO randomRule,
                                                     Collection<MeetingExpert> invitedExperts,
                                                     Integer count,
                                                     LocalDateTime msTime,
                                                     LocalDateTime meTime,
                                                     LocalDateTime tsTime,
                                                     boolean reInvite) {
        ExpertChooseDTO result = new ExpertChooseDTO(new ArrayList<>(), 0);
        // 合并标签、字典
        MergeExpertIdDTO merge = mergeExpertIdsByCondition(randomRule, avoidRule);
        if (merge.getSkip()) {
            return result;
        }
        Set<Long> expertIdsIn = new HashSet<>();
        Set<Long> expertIdsNotIn = new HashSet<>();
        if (CollUtil.isNotEmpty(merge.getExpertIdsIn())) {
            expertIdsIn.addAll(merge.getExpertIdsIn());
        } else if (CollUtil.isNotEmpty(merge.getExpertIdsNotIn())) {
            expertIdsNotIn.addAll(merge.getExpertIdsNotIn());
        }
        LambdaQueryWrapper<ExpertUserFullInfo> query = buildBaseExpertQuery();
        // 设置回避单位
        Set<String> notInCompanyUniqCodeList = new HashSet<>(avoidRule.getAvoidUnitIdList());
        // 处理回避单位与回避条线
        buildAvoidCompanyAndBusinessStrip(query, avoidRule.getAvoidUnitIdList(), avoidRule.getAvoidOrgIdList());
        // 处理回避专家次数
        if (avoidRule.getWeekInviteCount() != null) {
            Integer weekInviteCount = avoidRule.getWeekInviteCount();
            List<Long> tmpExpertIdsNotIn = listAgreedUserIdByRecentMeetings(weekInviteCount, recentDays, msTime);
            expertIdsNotIn.addAll(tmpExpertIdsNotIn);
        }
        // 处理专家层级
        addRegionLimit(query, randomRule);

        if (expertIdsIn.size() > 0) {
            if (CollectionUtils.isNotEmpty(avoidRule.getExpertIds())) {
                expertIdsIn.removeIf(w -> avoidRule.getExpertIds().contains(w));
                if (expertIdsIn.isEmpty()) {
                    // 字典、标签、履职意向地筛选出的专家ID移除需要回避的专家ID、如果为空则说明没有符合条件的
                    return result;
                }
            }
            List<Long> lockExpertIds = expertInviteHelper.listInvitedExpertByTime(msTime, meTime);
            expertIdsIn.removeIf(lockExpertIds::contains);
            if (expertIdsIn.isEmpty()) {
                return result;
            }
        } else if (CollUtil.isNotEmpty(avoidRule.getExpertIds())) {
            Set<Long> notInExpertIds = expertInviteHelper.getAvoidExpert(avoidRule.getExpertIds(), null, msTime, meTime);
            expertIdsNotIn.addAll(notInExpertIds);
        } else {
            Set<Long> notInUserIds = expertInviteHelper.listExpertLeaveOrInvited(msTime, meTime);
            expertIdsNotIn.addAll(notInUserIds);
        }
        Map<ExpertAttendStatusEnum, List<MeetingExpert>> expertGroupByStatus = invitedExperts.stream()
                .collect(Collectors.groupingBy(w -> ExpertAttendStatusEnum.getByCode(w.getStatus())));
        // 回避同单位其他专家
        List<MeetingExpert> agreeOrNoticing = new ArrayList<>();
        BizUtils.notEmpty(expertGroupByStatus.get(AGREED), agreeOrNoticing::addAll);
        BizUtils.notEmpty(expertGroupByStatus.get(NOTICING), agreeOrNoticing::addAll);
        List<Long> agreeOrNoticingExpertIds = CollUtils.fieldList(agreeOrNoticing, MeetingExpert::getExpertId);
        if (agreeOrNoticingExpertIds.size() > 0) {
            expertIdsNotIn.addAll(agreeOrNoticingExpertIds);
            List<ExpertUserFullInfo> agreeOrNoticingUserInfos = expertUserFullInfoService.listByUserIds(agreeOrNoticingExpertIds);
            List<String> tmpUniqCompanyCodes = CollUtils.fieldList(agreeOrNoticingUserInfos, ExpertUserFullInfo::getCompanyUniqCode);
            notInCompanyUniqCodeList.addAll(tmpUniqCompanyCodes);
        }
        // 已请假的专家不再抽取
        List<MeetingExpert> expertsOnLeave = expertGroupByStatus.get(ON_LEAVE);
        if (CollUtil.isNotEmpty(expertsOnLeave)) {
            expertIdsNotIn.addAll(CollUtils.fieldList(expertsOnLeave, MeetingExpert::getExpertId));
        }
        // 处理已拒绝专家与重复抽取
        BizUtils.notEmpty(expertGroupByStatus.get(REFUSED), refuseExperts -> {
            List<Long> tmpExpertIdsNotIn;
            if (reInvite) {
                tmpExpertIdsNotIn = refuseExperts.stream()
                        .filter(w -> w.getCreateOn().isAfter(tsTime))
                        .map(MeetingExpert::getExpertId)
                        .collect(Collectors.toList());
            } else {
                tmpExpertIdsNotIn = CollUtils.fieldList(refuseExperts, MeetingExpert::getExpertId);
            }
            expertIdsNotIn.addAll(tmpExpertIdsNotIn);
        });

        boolean hasExpertIdIn = !expertIdsIn.isEmpty();
        boolean hasExpertIdNotIn = !expertIdsNotIn.isEmpty();
        if (hasExpertIdIn && hasExpertIdNotIn) {
            expertIdsIn.removeAll(expertIdsNotIn);
            if (expertIdsIn.isEmpty()) {
                return result;
            }
            query.in(ExpertUserFullInfo::getUserId, expertIdsIn);
        } else if (hasExpertIdIn) {
            query.in(ExpertUserFullInfo::getUserId, expertIdsIn);
        } else if (hasExpertIdNotIn) {
            query.notIn(ExpertUserFullInfo::getUserId, expertIdsNotIn);
        }
        query.notIn(notInCompanyUniqCodeList.size() > 0, ExpertUserFullInfo::getCompanyUniqCode, notInCompanyUniqCodeList);
        List<ExpertUserFullInfo> userFullInfos = expertUserFullInfoService.list(query);
        if (userFullInfos.size() == 0) {
            return result;
        }
        Map<String, List<ExpertUserFullInfo>> userGroupByUnit = CollUtils.group(userFullInfos, ExpertUserFullInfo::getCompanyUniqCode);
        result.setTotal(userGroupByUnit.size());
        result.setExperts(inviteGroupByCompany(userGroupByUnit, expertsByRecentMeeting(), count));
        return result;
    }

    private List<List<MeetingExpert>> expertsByRecentMeeting() {
        LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class)
                .select(Meeting::getId)
                .orderByDesc(Meeting::getCreateOn)
                .last("limit " + recentMeetingCount);
        List<Long> meetingIds = CollUtils.fieldList(meetingService.list(query), Meeting::getId);
        if (meetingIds.isEmpty()) {
            return Collections.emptyList();
        }
        Map<Long, List<MeetingExpert>> groupByMeetingId = meetingExpertService.listByMeetingIds(meetingIds)
                .stream().collect(Collectors.groupingBy(MeetingExpert::getMeetingId));
        return groupByMeetingId.entrySet().stream()
                .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
                .map(Map.Entry::getValue)
                .collect(Collectors.toList());
    }

    /**
     * 专家抽取（会议创建时抽取）
     *
     * @param randomRules 随机抽取规则
     * @param avoidRule   回避信息
     * @param meeting     会议信息
     * @author WendyYang
     **/
    public void expertInviteByMeetingCreate(Meeting meeting,
                                            List<RandomInviteRuleDTO> randomRules,
                                            AvoidRuleDTO avoidRule) {
        List<MeetingExpert> expertInserts = new ArrayList<>();
        // 处理随机抽取规则
        if (CollectionUtils.isNotEmpty(randomRules)) {
            List<ExpertInviteRule> randoms = new ArrayList<>();
            List<ExpertChooseDTO> expertsByRandom = new ArrayList<>();
            List<Long> chooseExpertIds = new ArrayList<>();
            LocalDateTime sTime = meeting.getStartTime();
            LocalDateTime eTime = meeting.getEndTime();
            randomRules.forEach(rule -> {
                ExpertChooseDTO tmpExperts = expertInviteByRandomRule(avoidRule, rule, chooseExpertIds, sTime, eTime);
                if (tmpExperts.getTotal() < rule.getCount()) {
                    throw BizException.wrap("可抽取专家数量不足");
                }
                expertsByRandom.add(tmpExperts);
                chooseExpertIds.addAll(CollUtils.fieldList(tmpExperts.getExperts(), ExpertUserFullInfo::getUserId));
                randoms.add(getExpertInviteRule(rule, meeting.getId()));
            });
            inviteRuleService.saveBatch(randoms);
            for (int i = 0; i < expertsByRandom.size(); i++) {
                Long ruleId = randoms.get(i).getId();
                expertsByRandom.get(i).getExperts().forEach(w -> {
                    MeetingExpert expert = ExpertInviteBuilder.getExpertByRandom(meeting.getId(), w, ruleId);
                    expert.setStatus(NOTICING.getCode());
                    expertInserts.add(expert);
                });
            }
            meetingCallOrMsgHelper.callExpertByMeeting(meeting, expertInserts);
        }
        meetingExpertService.saveBatch(expertInserts);
    }

}
