@@ -17,17 +17,20 @@ public final class InviteCacheDTO { | |||||
private Long meetingId; | private Long meetingId; | ||||
private Boolean invitedRefused; | |||||
/** | |||||
* 拒绝的专家是否可以再次邀请 | |||||
*/ | |||||
private Boolean reInvite; | |||||
/** | /** | ||||
* 任务触发时间 | * 任务触发时间 | ||||
*/ | */ | ||||
private LocalDateTime taskStartTime; | private LocalDateTime taskStartTime; | ||||
public static InviteCacheDTO of(Long meetingId, Boolean invitedRefused, LocalDateTime startTime) { | |||||
public static InviteCacheDTO of(Long meetingId, Boolean reInvite, LocalDateTime startTime) { | |||||
InviteCacheDTO bo = new InviteCacheDTO(); | InviteCacheDTO bo = new InviteCacheDTO(); | ||||
bo.setMeetingId(meetingId); | bo.setMeetingId(meetingId); | ||||
bo.setInvitedRefused(invitedRefused); | |||||
bo.setReInvite(reInvite); | |||||
bo.setTaskStartTime(startTime); | bo.setTaskStartTime(startTime); | ||||
return bo; | return bo; | ||||
} | } | ||||
@@ -0,0 +1,114 @@ | |||||
package com.ningdatech.pmapi.meeting.helper; | |||||
import cn.hutool.core.util.RandomUtil; | |||||
import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo; | |||||
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; | |||||
import org.apache.commons.collections4.MapUtils; | |||||
import org.apache.commons.lang3.RandomUtils; | |||||
import java.util.ArrayList; | |||||
import java.util.Collections; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import java.util.stream.Collectors; | |||||
/** | |||||
* <p> | |||||
* 专家抽取算法 | |||||
* </p> | |||||
* | |||||
* @author WendyYang | |||||
* @since 2023/4/14 | |||||
**/ | |||||
public class ExpertRandomInviteAlgorithm { | |||||
/** | |||||
* 每个单位只抽取一人 | |||||
* | |||||
* @param expertGroupByUnit 需要抽取的人 | |||||
* @param expertsByRecentMeeting 最近会议抽取到的专家 | |||||
* @param count 抽取数量 | |||||
* @return 抽取到的专家信息 | |||||
* @author WendyYang | |||||
**/ | |||||
public static List<ExpertUserFullInfo> inviteGroupByCompany(Map<String, List<ExpertUserFullInfo>> expertGroupByUnit, | |||||
List<List<MeetingExpert>> expertsByRecentMeeting, | |||||
Integer count) { | |||||
if (MapUtils.isEmpty(expertGroupByUnit)) { | |||||
return Collections.emptyList(); | |||||
} | |||||
if (expertsByRecentMeeting.isEmpty()) { | |||||
return expertGroupByUnit.values().stream() | |||||
.map(RandomUtil::randomEle) | |||||
.limit(count).collect(Collectors.toList()); | |||||
} else { | |||||
List<ExpertUserFullInfo> result = new ArrayList<>(); | |||||
List<String> keySet = new ArrayList<>(expertGroupByUnit.keySet()); | |||||
for (int i = 0; i < count; i++) { | |||||
String company = keySet.get(RandomUtils.nextInt(0, keySet.size())); | |||||
List<ExpertUserFullInfo> expertsByCompany = expertGroupByUnit.get(company); | |||||
for (List<MeetingExpert> experts : expertsByRecentMeeting) { | |||||
List<ExpertUserFullInfo> notInvitedUsers = expertsByCompany.stream() | |||||
.filter(w -> experts.stream().noneMatch(expert -> expert.getExpertId().equals(w.getUserId()))) | |||||
.collect(Collectors.toList()); | |||||
if (!notInvitedUsers.isEmpty()) { | |||||
result.add(RandomUtil.randomEle(notInvitedUsers)); | |||||
break; | |||||
} else if (expertsByRecentMeeting.indexOf(experts) == (expertsByRecentMeeting.size() - 1)) { | |||||
result.add(RandomUtil.randomEle(expertsByCompany)); | |||||
} | |||||
} | |||||
if (result.size() < count) { | |||||
keySet.remove(company); | |||||
if (keySet.isEmpty()) { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
} | |||||
/** | |||||
* 随机抽取专家 | |||||
* | |||||
* @param userFullInfos 可抽取的所有专家 | |||||
* @param expertsByRecentMeeting 最近会议抽取到的专家 | |||||
* @param count 抽取数量 | |||||
* @return 抽取到的专家信息 | |||||
* @author WendyYang | |||||
**/ | |||||
public static List<ExpertUserFullInfo> inviteWithoutCompany(List<ExpertUserFullInfo> userFullInfos, | |||||
List<List<MeetingExpert>> expertsByRecentMeeting, | |||||
Integer count) { | |||||
List<ExpertUserFullInfo> result; | |||||
if (expertsByRecentMeeting.isEmpty()) { | |||||
result = RandomUtil.randomEleList(userFullInfos, count); | |||||
} else { | |||||
result = new ArrayList<>(); | |||||
for (List<MeetingExpert> experts : expertsByRecentMeeting) { | |||||
List<ExpertUserFullInfo> notInvitedUsers = userFullInfos.stream() | |||||
.filter(w -> experts.stream().noneMatch(expert -> expert.getExpertId().equals(w.getUserId()))) | |||||
.collect(Collectors.toList()); | |||||
if (!notInvitedUsers.isEmpty()) { | |||||
result.addAll(notInvitedUsers); | |||||
if (result.size() >= count) { | |||||
return result.subList(0, count); | |||||
} | |||||
userFullInfos.removeAll(notInvitedUsers); | |||||
} | |||||
} | |||||
if (userFullInfos.size() == 0) { | |||||
return result; | |||||
} | |||||
int restCnt = Math.min(count - result.size(), userFullInfos.size()); | |||||
if (userFullInfos.size() > restCnt) { | |||||
result.addAll(RandomUtil.randomEleList(userFullInfos, restCnt)); | |||||
} else { | |||||
result.addAll(userFullInfos); | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
} |
@@ -28,22 +28,20 @@ import com.ningdatech.pmapi.meta.model.entity.ExpertDictionary; | |||||
import com.ningdatech.pmapi.meta.model.entity.ExpertTag; | import com.ningdatech.pmapi.meta.model.entity.ExpertTag; | ||||
import com.ningdatech.pmapi.meta.service.IExpertDictionaryService; | import com.ningdatech.pmapi.meta.service.IExpertDictionaryService; | ||||
import com.ningdatech.pmapi.meta.service.IExpertTagService; | import com.ningdatech.pmapi.meta.service.IExpertTagService; | ||||
import lombok.AllArgsConstructor; | |||||
import lombok.RequiredArgsConstructor; | |||||
import org.apache.commons.collections4.CollectionUtils; | import org.apache.commons.collections4.CollectionUtils; | ||||
import org.apache.commons.collections4.MapUtils; | |||||
import org.apache.commons.collections4.Predicate; | import org.apache.commons.collections4.Predicate; | ||||
import org.apache.commons.lang3.ObjectUtils; | import org.apache.commons.lang3.ObjectUtils; | ||||
import org.apache.commons.lang3.RandomUtils; | |||||
import org.springframework.beans.factory.annotation.Value; | |||||
import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||
import java.time.LocalDate; | |||||
import java.time.LocalDateTime; | import java.time.LocalDateTime; | ||||
import java.util.*; | import java.util.*; | ||||
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
import java.util.stream.Stream; | |||||
import static com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum.*; | 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.ExpertInviteHelper.getExpertInviteRule; | ||||
import static com.ningdatech.pmapi.meeting.helper.ExpertRandomInviteAlgorithm.inviteGroupByCompany; | |||||
/** | /** | ||||
* <p> | * <p> | ||||
@@ -54,7 +52,7 @@ import static com.ningdatech.pmapi.meeting.helper.ExpertInviteHelper.getExpertIn | |||||
* @since 18:05 2022/8/8 | * @since 18:05 2022/8/8 | ||||
*/ | */ | ||||
@Component | @Component | ||||
@AllArgsConstructor | |||||
@RequiredArgsConstructor | |||||
public class ExpertInviteManage { | public class ExpertInviteManage { | ||||
private final IExpertDictionaryService expertDictionaryService; | private final IExpertDictionaryService expertDictionaryService; | ||||
@@ -69,6 +67,9 @@ public class ExpertInviteManage { | |||||
private final IExpertAvoidCompanyService expertAvoidCompanyService; | private final IExpertAvoidCompanyService expertAvoidCompanyService; | ||||
private final YxtCallOrSmsHelper yxtCallOrSmsHelper; | private final YxtCallOrSmsHelper yxtCallOrSmsHelper; | ||||
@Value("#{randomInviteProperties.recentMeetingCount}") | |||||
private Integer recentMeetingCount; | |||||
private static final Predicate<Collection<?>> COLL_EMPTY = (coll) -> coll != null && coll.isEmpty(); | private static final Predicate<Collection<?>> COLL_EMPTY = (coll) -> coll != null && coll.isEmpty(); | ||||
private LambdaQueryWrapper<ExpertUserFullInfo> buildBaseExpertQuery() { | private LambdaQueryWrapper<ExpertUserFullInfo> buildBaseExpertQuery() { | ||||
@@ -322,7 +323,8 @@ public class ExpertInviteManage { | |||||
} | } | ||||
Map<String, List<ExpertUserFullInfo>> userGroupByUnit = CollUtils.group(userInfoList, ExpertUserFullInfo::getCompanyUniqCode); | Map<String, List<ExpertUserFullInfo>> userGroupByUnit = CollUtils.group(userInfoList, ExpertUserFullInfo::getCompanyUniqCode); | ||||
result.setTotal(userGroupByUnit.size()); | result.setTotal(userGroupByUnit.size()); | ||||
result.setExperts(inviteGroupByCompany(userGroupByUnit, randomRule.getCount())); | |||||
result.setExperts(inviteGroupByCompany(userGroupByUnit, expertsByRecentMeeting(), randomRule.getCount())); | |||||
return result; | return result; | ||||
} | } | ||||
@@ -431,15 +433,15 @@ public class ExpertInviteManage { | |||||
} | } | ||||
Map<String, List<ExpertUserFullInfo>> userGroupByUnit = CollUtils.group(userFullInfos, ExpertUserFullInfo::getCompanyUniqCode); | Map<String, List<ExpertUserFullInfo>> userGroupByUnit = CollUtils.group(userFullInfos, ExpertUserFullInfo::getCompanyUniqCode); | ||||
result.setTotal(userGroupByUnit.size()); | result.setTotal(userGroupByUnit.size()); | ||||
result.setExperts(inviteGroupByCompany(userGroupByUnit, count)); | |||||
result.setExperts(inviteGroupByCompany(userGroupByUnit, expertsByRecentMeeting(), count)); | |||||
return result; | return result; | ||||
} | } | ||||
private List<List<MeetingExpert>> selectMeetingExpertByCount() { | |||||
private List<List<MeetingExpert>> expertsByRecentMeeting() { | |||||
LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class) | LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class) | ||||
.select(Meeting::getId) | .select(Meeting::getId) | ||||
.orderByDesc(Meeting::getCreateOn) | .orderByDesc(Meeting::getCreateOn) | ||||
.last("limit " + 5); | |||||
.last("limit " + recentMeetingCount); | |||||
List<Long> meetingIds = CollUtils.fieldList(meetingService.list(query), Meeting::getId); | List<Long> meetingIds = CollUtils.fieldList(meetingService.list(query), Meeting::getId); | ||||
if (meetingIds.isEmpty()) { | if (meetingIds.isEmpty()) { | ||||
return Collections.emptyList(); | return Collections.emptyList(); | ||||
@@ -453,97 +455,6 @@ public class ExpertInviteManage { | |||||
} | } | ||||
/** | /** | ||||
* 每个单位只抽取一人 | |||||
* | |||||
* @param expertGroupByUnit 需要抽取的人 | |||||
* @param count 抽取数量 | |||||
* @return 抽取到的专家信息 | |||||
* @author WendyYang | |||||
**/ | |||||
private List<ExpertUserFullInfo> inviteGroupByCompany(Map<String, List<ExpertUserFullInfo>> expertGroupByUnit, Integer count) { | |||||
if (MapUtils.isEmpty(expertGroupByUnit)) { | |||||
return Collections.emptyList(); | |||||
} | |||||
List<List<MeetingExpert>> meetingExperts = selectMeetingExpertByCount(); | |||||
if (meetingExperts.isEmpty()) { | |||||
return expertGroupByUnit.values().stream() | |||||
.map(expertUsers -> expertUsers.get(RandomUtils.nextInt(0, expertUsers.size()))) | |||||
.limit(count).collect(Collectors.toList()); | |||||
} else { | |||||
List<ExpertUserFullInfo> result = new ArrayList<>(); | |||||
List<String> keySet = new ArrayList<>(expertGroupByUnit.keySet()); | |||||
for (int i = 0; i < count; i++) { | |||||
String key = keySet.get(RandomUtils.nextInt(0, keySet.size())); | |||||
List<ExpertUserFullInfo> expertUserFullInfos = expertGroupByUnit.get(key); | |||||
for (List<MeetingExpert> expertList : meetingExperts) { | |||||
List<ExpertUserFullInfo> tempList = expertUserFullInfos.stream() | |||||
.filter(w -> expertList.stream().noneMatch(expert -> expert.getExpertId().equals(w.getUserId()))) | |||||
.collect(Collectors.toList()); | |||||
if (!tempList.isEmpty()) { | |||||
result.add(tempList.get(RandomUtils.nextInt(0, tempList.size()))); | |||||
break; | |||||
} else if (meetingExperts.indexOf(expertList) == (meetingExperts.size() - 1)) { | |||||
result.add(expertUserFullInfos.get(RandomUtils.nextInt(0, expertUserFullInfos.size()))); | |||||
} | |||||
} | |||||
if (result.size() < count) { | |||||
keySet.remove(key); | |||||
if (keySet.isEmpty()) { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
} | |||||
private List<ExpertUserFullInfo> inviteWithoutCompany(List<ExpertUserFullInfo> userFullInfos, Integer count) { | |||||
List<ExpertUserFullInfo> result = new ArrayList<>(); | |||||
List<List<MeetingExpert>> meetingExpertList = selectMeetingExpertByCount(); | |||||
if (meetingExpertList.isEmpty()) { | |||||
for (int i = 0; i < count; i++) { | |||||
int randomIndex = RandomUtils.nextInt(0, userFullInfos.size()); | |||||
result.add(userFullInfos.remove(randomIndex)); | |||||
if (userFullInfos.size() == 0) { | |||||
break; | |||||
} | |||||
} | |||||
} else { | |||||
for (List<MeetingExpert> meetingExperts : meetingExpertList) { | |||||
List<ExpertUserFullInfo> unSelectedUsers = userFullInfos.stream() | |||||
.filter(w -> meetingExperts.stream().noneMatch(expert -> expert.getExpertId().equals(w.getUserId()))) | |||||
.collect(Collectors.toList()); | |||||
if (!unSelectedUsers.isEmpty()) { | |||||
result.addAll(unSelectedUsers); | |||||
if (result.size() >= count) { | |||||
return result.subList(0, count); | |||||
} | |||||
userFullInfos.removeAll(unSelectedUsers); | |||||
} | |||||
} | |||||
if (userFullInfos.size() == 0) { | |||||
return result; | |||||
} | |||||
int restCount = Math.min(count - result.size(), userFullInfos.size()); | |||||
int groupCount = userFullInfos.size() / restCount; | |||||
if (userFullInfos.size() > restCount) { | |||||
Stream.iterate(0, t -> t + 1).limit(restCount) | |||||
.forEach(t -> { | |||||
int start = t * groupCount; | |||||
int end = start + groupCount; | |||||
if (end >= groupCount * restCount) { | |||||
end = userFullInfos.size(); | |||||
} | |||||
result.add(userFullInfos.get(RandomUtils.nextInt(start, end))); | |||||
}); | |||||
} else { | |||||
result.addAll(userFullInfos); | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
/** | |||||
* 专家抽取(会议创建时抽取) | * 专家抽取(会议创建时抽取) | ||||
* | * | ||||
* @param randomRules 随机抽取规则 | * @param randomRules 随机抽取规则 | ||||
@@ -39,13 +39,13 @@ import java.time.Duration; | |||||
import java.time.Instant; | import java.time.Instant; | ||||
import java.time.LocalDateTime; | import java.time.LocalDateTime; | ||||
import java.time.ZoneId; | import java.time.ZoneId; | ||||
import java.util.HashMap; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
import java.util.concurrent.ConcurrentMap; | import java.util.concurrent.ConcurrentMap; | ||||
import java.util.concurrent.ScheduledFuture; | import java.util.concurrent.ScheduledFuture; | ||||
import java.util.concurrent.atomic.AtomicInteger; | import java.util.concurrent.atomic.AtomicInteger; | ||||
import java.util.stream.Collectors; | |||||
/** | /** | ||||
@@ -67,7 +67,6 @@ public class ExpertInviteTask { | |||||
private static final Duration EXPIRE_TIME = Duration.ofDays(60); | private static final Duration EXPIRE_TIME = Duration.ofDays(60); | ||||
private final CachePlusOps cachePlusOps; | private final CachePlusOps cachePlusOps; | ||||
@Qualifier("expertInviteScheduler") | |||||
@Resource(name = "expertInviteScheduler") | @Resource(name = "expertInviteScheduler") | ||||
private ThreadPoolTaskScheduler scheduler; | private ThreadPoolTaskScheduler scheduler; | ||||
private final IMeetingExpertService meetingExpertService; | private final IMeetingExpertService meetingExpertService; | ||||
@@ -82,13 +81,13 @@ public class ExpertInviteTask { | |||||
*/ | */ | ||||
private static final ConcurrentMap<Long, ScheduledFuture<?>> INVITE_TASK_MAP = new ConcurrentHashMap<>(); | private static final ConcurrentMap<Long, ScheduledFuture<?>> INVITE_TASK_MAP = new ConcurrentHashMap<>(); | ||||
public ExpertInviteTask currProxy() { | |||||
private ExpertInviteTask currProxy() { | |||||
return (ExpertInviteTask) AopContext.currentProxy(); | return (ExpertInviteTask) AopContext.currentProxy(); | ||||
} | } | ||||
private CacheHashKey getCacheKey(Long meetingId) { | private CacheHashKey getCacheKey(Long meetingId) { | ||||
String meetingIdStr = meetingId == null ? null : meetingId.toString(); | |||||
return new CacheHashKey(MEETING_ID_INVITE_RANDOM, meetingIdStr, EXPIRE_TIME); | |||||
String field = meetingId == null ? null : meetingId.toString(); | |||||
return new CacheHashKey(MEETING_ID_INVITE_RANDOM, field, EXPIRE_TIME); | |||||
} | } | ||||
@PostConstruct | @PostConstruct | ||||
@@ -97,13 +96,13 @@ public class ExpertInviteTask { | |||||
log.warn("随机邀请已关闭……"); | log.warn("随机邀请已关闭……"); | ||||
return; | return; | ||||
} | } | ||||
initInviteTaskAfterAppStarted(); | |||||
initInviteTaskAfterStarted(); | |||||
} | } | ||||
/** | /** | ||||
* 项目重启之后重新初始化邀请任务 | * 项目重启之后重新初始化邀请任务 | ||||
*/ | */ | ||||
private void initInviteTaskAfterAppStarted() { | |||||
private void initInviteTaskAfterStarted() { | |||||
Map<Long, InviteCacheDTO> caches = cachePlusOps.hGetAll(getCacheKey(null)); | Map<Long, InviteCacheDTO> caches = cachePlusOps.hGetAll(getCacheKey(null)); | ||||
if (MapUtils.isEmpty(caches)) { | if (MapUtils.isEmpty(caches)) { | ||||
log.info("暂无需要初始化的抽取会议信息"); | log.info("暂无需要初始化的抽取会议信息"); | ||||
@@ -111,9 +110,9 @@ public class ExpertInviteTask { | |||||
} | } | ||||
Integer inviteDelay = properties.getInviteDelay(); | Integer inviteDelay = properties.getInviteDelay(); | ||||
for (InviteCacheDTO cache : caches.values()) { | for (InviteCacheDTO cache : caches.values()) { | ||||
Boolean invitedRefused = cache.getInvitedRefused(); | |||||
Boolean reInvite = cache.getReInvite(); | |||||
LocalDateTime tsTime = cache.getTaskStartTime(); | LocalDateTime tsTime = cache.getTaskStartTime(); | ||||
boolean added = addInviteTask(cache.getMeetingId(), true, inviteDelay, invitedRefused, tsTime); | |||||
boolean added = addInviteTask(cache.getMeetingId(), true, inviteDelay, reInvite, tsTime); | |||||
if (!added) { | if (!added) { | ||||
cachePlusOps.hDel(getCacheKey(cache.getMeetingId())); | cachePlusOps.hDel(getCacheKey(cache.getMeetingId())); | ||||
} | } | ||||
@@ -135,7 +134,7 @@ public class ExpertInviteTask { | |||||
LambdaQueryWrapper<MeetingExpert> query = Wrappers.lambdaQuery(MeetingExpert.class) | LambdaQueryWrapper<MeetingExpert> query = Wrappers.lambdaQuery(MeetingExpert.class) | ||||
.select(MeetingExpert::getRuleId, MeetingExpert::getStatus) | .select(MeetingExpert::getRuleId, MeetingExpert::getStatus) | ||||
.in(MeetingExpert::getRuleId, ruleMap.keySet()) | .in(MeetingExpert::getRuleId, ruleMap.keySet()) | ||||
.in(MeetingExpert::getStatus, ExpertAttendStatusEnum.AGREED.getCode()); | |||||
.eq(MeetingExpert::getStatus, ExpertAttendStatusEnum.AGREED.getCode()); | |||||
List<MeetingExpert> experts = meetingExpertService.list(query); | List<MeetingExpert> experts = meetingExpertService.list(query); | ||||
if (experts.isEmpty()) { | if (experts.isEmpty()) { | ||||
return Boolean.TRUE; | return Boolean.TRUE; | ||||
@@ -153,23 +152,20 @@ public class ExpertInviteTask { | |||||
/** | /** | ||||
* 唤醒某个会议的抽取任务 | * 唤醒某个会议的抽取任务 | ||||
* | * | ||||
* @param meetingId 会议ID | |||||
* @param invitedRefused 是否可邀请已拒绝的专家 | |||||
* @param meetingId 会议ID | |||||
* @param reInvite 是否可邀请已拒绝的专家 | |||||
* @author WendyYang | * @author WendyYang | ||||
**/ | **/ | ||||
public void notifyInviteTask(Long meetingId, boolean... invitedRefused) { | |||||
boolean tmpInvitedRefused = true; | |||||
if (ArrayUtil.isNotEmpty(invitedRefused)) { | |||||
tmpInvitedRefused = invitedRefused[0]; | |||||
} | |||||
public void notifyInviteTask(Long meetingId, boolean... reInvite) { | |||||
boolean tmpReInvite = !ArrayUtil.isNotEmpty(reInvite) || reInvite[0]; | |||||
if (!INVITE_TASK_MAP.containsKey(meetingId)) { | if (!INVITE_TASK_MAP.containsKey(meetingId)) { | ||||
if (addInviteTask(meetingId, false, properties.getInviteDelay(), tmpInvitedRefused, LocalDateTime.now())) { | |||||
if (addInviteTask(meetingId, false, properties.getInviteDelay(), tmpReInvite, LocalDateTime.now())) { | |||||
log.info("重置会议的随机抽取状态:{}", meetingId); | log.info("重置会议的随机抽取状态:{}", meetingId); | ||||
LambdaUpdateWrapper<Meeting> update = Wrappers.lambdaUpdate(Meeting.class); | LambdaUpdateWrapper<Meeting> update = Wrappers.lambdaUpdate(Meeting.class); | ||||
update.set(Meeting::getInviteStatus, false); | update.set(Meeting::getInviteStatus, false); | ||||
update.eq(Meeting::getId, meetingId); | update.eq(Meeting::getId, meetingId); | ||||
meetingService.update(update); | meetingService.update(update); | ||||
InviteCacheDTO cacheVal = InviteCacheDTO.of(meetingId, tmpInvitedRefused, LocalDateTime.now()); | |||||
InviteCacheDTO cacheVal = InviteCacheDTO.of(meetingId, tmpReInvite, LocalDateTime.now()); | |||||
cachePlusOps.hSet(getCacheKey(meetingId), cacheVal); | cachePlusOps.hSet(getCacheKey(meetingId), cacheVal); | ||||
} | } | ||||
} | } | ||||
@@ -178,23 +174,24 @@ public class ExpertInviteTask { | |||||
/** | /** | ||||
* 添加专家抽取校验任务 | * 添加专家抽取校验任务 | ||||
* | * | ||||
* @param meetingId 会议ID | |||||
* @param checked 是否前置校验 | |||||
* @param delayedMinutes 延迟执行时间 | |||||
* @param invitedRefused 是否可以邀请被拒绝的专家 | |||||
* @param meetingId 会议ID | |||||
* @param checked 是否前置校验 | |||||
* @param delayTime 延迟执行时间 | |||||
* @param reInvite 是否可以邀请被拒绝的专家 | |||||
* @param tsTime 任务启动时间 | |||||
* @return 是否添加任务成功 | * @return 是否添加任务成功 | ||||
* @author WendyYang | * @author WendyYang | ||||
**/ | **/ | ||||
public boolean addInviteTask(Long meetingId, boolean checked, int delayedMinutes, boolean invitedRefused, LocalDateTime tsTime) { | |||||
private boolean addInviteTask(Long meetingId, boolean checked, int delayTime, boolean reInvite, LocalDateTime tsTime) { | |||||
if (checked && !inviteCountCheck(meetingId)) { | if (checked && !inviteCountCheck(meetingId)) { | ||||
// 如果抽取数量满足直接返回 | // 如果抽取数量满足直接返回 | ||||
return Boolean.FALSE; | return Boolean.FALSE; | ||||
} | } | ||||
Instant startTime = LocalDateTime.now().plusMinutes(delayedMinutes).atZone(ZoneId.systemDefault()).toInstant(); | |||||
Instant startTime = LocalDateTime.now().plusMinutes(delayTime).atZone(ZoneId.systemDefault()).toInstant(); | |||||
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(() -> { | ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(() -> { | ||||
ExpertInviteTask bean = SpringContextHolder.getBean(ExpertInviteTask.class); | ExpertInviteTask bean = SpringContextHolder.getBean(ExpertInviteTask.class); | ||||
try { | try { | ||||
bean.invite(meetingId, invitedRefused, tsTime); | |||||
bean.invite(meetingId, reInvite, tsTime); | |||||
} catch (Exception e) { | } catch (Exception e) { | ||||
log.error("执行专家邀请任务异常:{}", meetingId, e); | log.error("执行专家邀请任务异常:{}", meetingId, e); | ||||
} | } | ||||
@@ -207,14 +204,14 @@ public class ExpertInviteTask { | |||||
/** | /** | ||||
* 创建会议时添加抽取任务 | * 创建会议时添加抽取任务 | ||||
* | * | ||||
* @param meetingId 会议ID | |||||
* @param delayedMinutes 延迟时间 | |||||
* @param tsTime 开始时间 | |||||
* @param meetingId 会议ID | |||||
* @param delayTime 延迟时间 | |||||
* @param tsTime 开始时间 | |||||
* @author WendyYang | * @author WendyYang | ||||
**/ | **/ | ||||
public void addInviteTaskByMeetingCreate(Long meetingId, int delayedMinutes, LocalDateTime tsTime) { | |||||
public void addInviteTaskByMeetingCreate(Long meetingId, int delayTime, LocalDateTime tsTime) { | |||||
Assert.isTrue(properties.getEnable(), "随机邀请已关闭"); | Assert.isTrue(properties.getEnable(), "随机邀请已关闭"); | ||||
addInviteTask(meetingId, false, delayedMinutes, false, tsTime); | |||||
addInviteTask(meetingId, false, delayTime, false, tsTime); | |||||
InviteCacheDTO cacheVal = InviteCacheDTO.of(meetingId, false, tsTime); | InviteCacheDTO cacheVal = InviteCacheDTO.of(meetingId, false, tsTime); | ||||
cachePlusOps.hSet(getCacheKey(meetingId), cacheVal); | cachePlusOps.hSet(getCacheKey(meetingId), cacheVal); | ||||
} | } | ||||
@@ -222,12 +219,12 @@ public class ExpertInviteTask { | |||||
/** | /** | ||||
* 抽取过程 | * 抽取过程 | ||||
* | * | ||||
* @param meetingId 会议ID | |||||
* @param invitedRefused 是否可以邀请已拒绝的专家 | |||||
* @param tsTime 任务开启时间 | |||||
* @param meetingId 会议ID | |||||
* @param reInvite 是否可以邀请已拒绝的专家 | |||||
* @param tsTime 任务开启时间 | |||||
*/ | */ | ||||
@Transactional(rollbackFor = Exception.class) | @Transactional(rollbackFor = Exception.class) | ||||
public void invite(Long meetingId, Boolean invitedRefused, LocalDateTime tsTime) { | |||||
public void invite(Long meetingId, Boolean reInvite, LocalDateTime tsTime) { | |||||
log.info("开始进行专家后台抽取:{}", meetingId); | log.info("开始进行专家后台抽取:{}", meetingId); | ||||
Meeting meeting = meetingService.getById(meetingId); | Meeting meeting = meetingService.getById(meetingId); | ||||
if (meeting.getStartTime().isBefore(LocalDateTime.now())) { | if (meeting.getStartTime().isBefore(LocalDateTime.now())) { | ||||
@@ -248,7 +245,7 @@ public class ExpertInviteTask { | |||||
List<MeetingExpert> tmpExperts = meetingExpertService.listExpertLastByMeetingId(meetingId); | List<MeetingExpert> tmpExperts = meetingExpertService.listExpertLastByMeetingId(meetingId); | ||||
Map<Long, MeetingExpert> expertMap = CollUtils.listToMap(tmpExperts, MeetingExpert::getExpertId); | Map<Long, MeetingExpert> expertMap = CollUtils.listToMap(tmpExperts, MeetingExpert::getExpertId); | ||||
// 统计通知中与同意参加专家数量 | // 统计通知中与同意参加专家数量 | ||||
Map<Long, ExpertCntBO> countMap = countByAgree(expertMap); | |||||
Map<Long, ExpertCntBO> countMap = countByAttendStatus(expertMap); | |||||
ExpertCntBO cnt = countMap.getOrDefault(ruleId, ExpertCntBO.zeroInit()); | ExpertCntBO cnt = countMap.getOrDefault(ruleId, ExpertCntBO.zeroInit()); | ||||
int wouldAttendCnt = cnt.getAgreeCnt() + cnt.getNoticeCnt(); | int wouldAttendCnt = cnt.getAgreeCnt() + cnt.getNoticeCnt(); | ||||
if (wouldAttendCnt == value.getCount()) { | if (wouldAttendCnt == value.getCount()) { | ||||
@@ -259,7 +256,7 @@ public class ExpertInviteTask { | |||||
} | } | ||||
int needInviteCnt = value.getCount() - wouldAttendCnt; | int needInviteCnt = value.getCount() - wouldAttendCnt; | ||||
ExpertChooseDTO expertChoose = expertInviteManage.expertReplaceByRandomRule(avoidRule, value, | ExpertChooseDTO expertChoose = expertInviteManage.expertReplaceByRandomRule(avoidRule, value, | ||||
tmpExperts, needInviteCnt, msTime, meTime, tsTime, invitedRefused); | |||||
tmpExperts, needInviteCnt, msTime, meTime, tsTime, reInvite); | |||||
if (expertChoose.getTotal() > 0) { | if (expertChoose.getTotal() > 0) { | ||||
List<MeetingExpert> expertMeetings = CollUtils.convert(expertChoose.getExperts(), w -> { | List<MeetingExpert> expertMeetings = CollUtils.convert(expertChoose.getExperts(), w -> { | ||||
@@ -297,20 +294,18 @@ public class ExpertInviteTask { | |||||
//================================================================================================================== | //================================================================================================================== | ||||
private Map<Long, ExpertCntBO> countByAgree(Map<Long, MeetingExpert> expertMap) { | |||||
return expertMap.entrySet().stream() | |||||
.collect(Collectors.groupingBy(w -> w.getValue().getRuleId(), | |||||
Collectors.collectingAndThen(Collectors.mapping(Map.Entry::getValue, Collectors.toList()), w -> { | |||||
ExpertCntBO cnt = ExpertCntBO.zeroInit(); | |||||
for (MeetingExpert expert : w) { | |||||
if (ExpertAttendStatusEnum.AGREED.eq(expert.getStatus())) { | |||||
cnt.incrAgreeCnt(); | |||||
} else if (ExpertAttendStatusEnum.NOTICING.eq(expert.getStatus())) { | |||||
cnt.incrNoticeCnt(); | |||||
} | |||||
} | |||||
return cnt; | |||||
}))); | |||||
private Map<Long, ExpertCntBO> countByAttendStatus(Map<Long, MeetingExpert> expertMap) { | |||||
Map<Long, ExpertCntBO> cntMap = new HashMap<>(8); | |||||
expertMap.values().forEach(w -> { | |||||
Long ruleId = w.getRuleId(); | |||||
ExpertCntBO cnt = cntMap.computeIfAbsent(ruleId, k -> ExpertCntBO.zeroInit()); | |||||
if (ExpertAttendStatusEnum.AGREED.eq(w.getStatus())) { | |||||
cnt.incrAgreeCnt(); | |||||
} else if (ExpertAttendStatusEnum.NOTICING.eq(w.getStatus())) { | |||||
cnt.incrNoticeCnt(); | |||||
} | |||||
}); | |||||
return cntMap; | |||||
} | } | ||||
@Data | @Data | ||||
@@ -37,4 +37,9 @@ public class RandomInviteProperties { | |||||
* 会议抽取完成通知 管理员下发会议通知 | * 会议抽取完成通知 管理员下发会议通知 | ||||
*/ | */ | ||||
private Integer meetingInviteCompleteNoticeRate = 1; | private Integer meetingInviteCompleteNoticeRate = 1; | ||||
/** | |||||
* 近期会议数量(以此来降低专家抽中间隔) | |||||
*/ | |||||
private Integer recentMeetingCount = 5; | |||||
} | } |