Преглед изворни кода

Merge remote-tracking branch 'origin/master'

tags/24080901
CMM пре 1 година
родитељ
комит
a7294a4767
15 измењених фајлова са 280 додато и 265 уклоњено
  1. +7
    -7
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/MeetingController.java
  2. +0
    -5
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/MeetingExpert.java
  3. +1
    -2
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AvoidRuleDTO.java
  4. +27
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingInviteCacheDTO.java
  5. +2
    -2
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertInviteReq.java
  6. +20
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingIdReq.java
  7. +2
    -2
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/ExpertInviteHelper.java
  8. +41
    -16
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/MeetingManageHelper.java
  9. +61
    -74
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/ExpertInviteManage.java
  10. +34
    -82
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingManage.java
  11. +2
    -2
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IExpertInviteAvoidRuleService.java
  12. +13
    -2
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingExpertService.java
  13. +3
    -3
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/ExpertInviteAvoidRuleServiceImpl.java
  14. +66
    -67
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteTask.java
  15. +1
    -1
      pmapi/src/main/resources/application-dev.yml

+ 7
- 7
pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/MeetingController.java Прегледај датотеку

@@ -39,6 +39,13 @@ public class MeetingController {
return meetingManage.meetingCreateAndInviteExpert(req);
}

@PostMapping("/continueInvite")
@ApiOperation(value = "续抽专家")
@WebLog(value = "续抽专家")
public void continueInvite(@Valid @RequestBody MeetingIdReq req) {
meetingManage.continueInvite(req.getMeetingId());
}

@PostMapping("/expertInviteByCreate")
@ApiOperation(value = "新建会议-专家抽取", hidden = true)
@WebLog(value = "新建会议-专家抽取")
@@ -94,13 +101,6 @@ public class MeetingController {
return meetingManage.inviteRuleDetail(meetingId);
}

@ApiOperation("专家替换")
@PostMapping("/expertReplace")
@WebLog(value = "专家替换")
public void expertReplace(@RequestBody ExpertRemoveReq po) {
meetingManage.expertReplace(po);
}

@ApiOperation("停止抽取")
@GetMapping("/stopInvite/{meetingId}")
@WebLog(value = "停止抽取")


+ 0
- 5
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/MeetingExpert.java Прегледај датотеку

@@ -55,11 +55,6 @@ public class MeetingExpert implements Serializable {
@ApiModelProperty("是否是专家组长")
private Boolean isHeadman;

@ApiModelProperty("前一个状态")
private Integer preStatus;

private Long preId;

@ApiModelProperty("邀请类型")
private Integer inviteType;



pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AvoidInfoDTO.java → pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AvoidRuleDTO.java Прегледај датотеку

@@ -5,7 +5,6 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;

/**
@@ -18,7 +17,7 @@ import java.util.List;
*/
@Data
@ApiModel("回避信息")
public class AvoidInfoDTO {
public class AvoidRuleDTO {

@ApiModelProperty("回避单位")
@NotEmpty(message = "回避单位不能为空", groups = {AbstractInviteRule.RuleSave.class})

+ 27
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingInviteCacheDTO.java Прегледај датотеку

@@ -0,0 +1,27 @@
package com.ningdatech.pmapi.meeting.entity.dto;

import lombok.Data;

/**
* <p>
* MeetingInviteDTO
* </p>
*
* @author WendyYang
* @since 19:21 2023/3/7
*/
@Data
public final class MeetingInviteCacheDTO {

private Long meetingId;

private Boolean invitedRefused;

public static MeetingInviteCacheDTO of(Long meetingId, Boolean invitedRefused) {
MeetingInviteCacheDTO bo = new MeetingInviteCacheDTO();
bo.setMeetingId(meetingId);
bo.setInvitedRefused(invitedRefused);
return bo;
}

}

+ 2
- 2
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertInviteReq.java Прегледај датотеку

@@ -1,7 +1,7 @@
package com.ningdatech.pmapi.meeting.entity.req;

import com.ningdatech.pmapi.meeting.entity.dto.AppointInviteRuleDTO;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidInfoDTO;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidRuleDTO;
import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@@ -33,7 +33,7 @@ public class ExpertInviteReq {
@Valid
@NotNull(message = "回避信息不能为空")
@ApiModelProperty("回避信息")
private AvoidInfoDTO avoidRule;
private AvoidRuleDTO avoidRule;

@Valid
@ApiModelProperty("随机抽取规则")


+ 20
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingIdReq.java Прегледај датотеку

@@ -0,0 +1,20 @@
package com.ningdatech.pmapi.meeting.entity.req;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
* <p>
* MeetingIdReq
* </p>
*
* @author WendyYang
* @since 17:10 2023/3/7
*/
@Data
public class MeetingIdReq {

@ApiModelProperty("会议ID")
private Long meetingId;

}

+ 2
- 2
pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/ExpertInviteHelper.java Прегледај датотеку

@@ -11,7 +11,7 @@ import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.dto.AbstractInviteRule;
import com.ningdatech.pmapi.meeting.entity.dto.AppointInviteRuleDTO;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidInfoDTO;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidRuleDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import com.ningdatech.pmapi.meeting.service.IMeetingService;
@@ -69,7 +69,7 @@ public class ExpertInviteHelper {
return new HashSet<>(listInvitedExpertByTime(start, end));
}

public Set<Long> getAvoidExpert(List<Long> appoints, AvoidInfoDTO avoid, LocalDateTime start, LocalDateTime end) {
public Set<Long> getAvoidExpert(List<Long> appoints, AvoidRuleDTO avoid, LocalDateTime start, LocalDateTime end) {
Set<Long> expertIds = new HashSet<>();
Optional.ofNullable(appoints).ifPresent(expertIds::addAll);
Optional.ofNullable(avoid)


+ 41
- 16
pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/MeetingManageHelper.java Прегледај датотеку

@@ -1,5 +1,6 @@
package com.ningdatech.pmapi.meeting.helper;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -14,9 +15,10 @@ import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteAvoidRule;
import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidInfoDTO;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidRuleDTO;
import com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO;
import com.ningdatech.pmapi.meeting.entity.dto.MeetingBasicDTO;
import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum;
import com.ningdatech.pmapi.meeting.entity.req.MeetingListReq;
import com.ningdatech.pmapi.meeting.entity.vo.ExpertBasicInfoVO;
@@ -30,7 +32,10 @@ import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

/**
* <p>
@@ -60,6 +65,36 @@ public class MeetingManageHelper {
}

/**
* 校验会议是否还可继续抽取
*/
public boolean checkCouldBeInvitedContinue(Long meetingId) {
Map<Long, RandomInviteRuleDTO> ruleMap = inviteRuleService.randomRuleByMeetingId(meetingId);
List<MeetingExpert> experts = meetingExpertService.listExpertLastByMeetingId(meetingId);
if (experts.isEmpty()) {
return Boolean.TRUE;
}
Map<Long, List<MeetingExpert>> expertMap = CollUtils.group(experts, MeetingExpert::getRuleId);
for (Map.Entry<Long, RandomInviteRuleDTO> entry : ruleMap.entrySet()) {
List<MeetingExpert> tmpExperts = expertMap.get(entry.getKey());
if (CollUtil.isEmpty(tmpExperts)) {
return Boolean.TRUE;
}
int count = 0;
for (MeetingExpert w : tmpExperts) {
boolean status = ExpertAttendStatusEnum.AGREED.eq(w.getStatus()) ||
ExpertAttendStatusEnum.NOTICING.eq(w.getStatus());
if (status) {
count++;
}
}
if (count < entry.getValue().getCount()) {
return Boolean.TRUE;
}
}
return Boolean.FALSE;
}

/**
* 获取专家出席会议的状态
*
* @param info 会议状态及评价信息
@@ -104,7 +139,7 @@ public class MeetingManageHelper {
query.and(q1 -> q1.exists("select 1 from nd_project np inner join meeting_inner_project mip on mip.project_id = np.id" +
" where mip.meeting_id = meeting.id and np.project_name like {0}", projectName)
.or(q2 -> q2.exists("select 1 from meeting_outer_project mop where mop.meeting_id = meeting.id" +
"and mop.project_name like {0}", projectName)));
" and mop.project_name like {0}", projectName)));
}
}

@@ -140,9 +175,9 @@ public class MeetingManageHelper {
return null;
}

public AvoidInfoDTO getAvoidInfoDto(Long meetingId) {
public AvoidRuleDTO getAvoidInfoDto(Long meetingId) {
ExpertInviteAvoidRule avoidRule = inviteAvoidRuleService.getByMeetingId(meetingId);
AvoidInfoDTO result = new AvoidInfoDTO();
AvoidRuleDTO result = new AvoidRuleDTO();
result.setAvoidOrgIdList(StrUtil.split(avoidRule.getAvoidOrgIds(), ","));
result.setExpertIds(BizUtils.splitToLong(avoidRule.getAvoidExpertIds()));
result.setAvoidUnitIdList(StrUtil.split(avoidRule.getAvoidUnitIds(), ","));
@@ -165,7 +200,7 @@ public class MeetingManageHelper {
**/
public List<ExpertUserFullInfo> appointExpertCheck(Long meetingId, List<Long> expertIds) {
List<ExpertUserFullInfo> experts = expertUserFullInfoService.listByUserId(expertIds);
AvoidInfoDTO avoidRule = getAvoidInfoDto(meetingId);
AvoidRuleDTO avoidRule = getAvoidInfoDto(meetingId);
experts.forEach(expert -> {
if (avoidRule.getAvoidUnitIdList().contains(expert.getCompany())) {
throw BizException.wrap("请移除已回避单位的专家");
@@ -193,16 +228,6 @@ public class MeetingManageHelper {
throw BizException.wrap("专家%s已拒绝参加", expertName);
case REMOVED:
throw BizException.wrap("专家%s已被移除", expertName);
case REPLACED:
switch (ExpertAttendStatusEnum.getByCode(w.getPreStatus())) {
case REFUSED:
throw BizException.wrap("专家%s已拒绝参加", expertName);
case REMOVED:
throw BizException.wrap("专家%s已被移除", expertName);
default:
break;
}
break;
case AGREED:
throw BizException.wrap("专家%s已同意参加", expertName);
case NOTICING:


+ 61
- 74
pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/ExpertInviteManage.java Прегледај датотеку

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.pmapi.common.util.BizUtils;
import com.ningdatech.pmapi.common.util.StrUtils;
import com.ningdatech.pmapi.expert.constant.ExpertAccountStatusEnum;
import com.ningdatech.pmapi.expert.entity.ExpertAvoidCompany;
import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;
@@ -15,7 +16,7 @@ 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.AvoidInfoDTO;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidRuleDTO;
import com.ningdatech.pmapi.meeting.entity.dto.ExpertChooseDTO;
import com.ningdatech.pmapi.meeting.entity.dto.ExpertDictChooseDTO;
import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;
@@ -135,7 +136,7 @@ public class ExpertInviteManage {
return expertAvoidCompanyList.stream().map(ExpertAvoidCompany::getUserId).distinct().collect(Collectors.toList());
}

private List<Long> mergeExpertIdsByCondition(RandomInviteRuleDTO rule, AvoidInfoDTO avoidInfo) {
private List<Long> mergeExpertIdsByCondition(RandomInviteRuleDTO rule, AvoidRuleDTO avoidInfo) {
// 处理履职意向地
List<Long> expertIdsByIntentionRegion = expertIdsByRegion(rule);
if (COLL_EMPTY.evaluate(expertIdsByIntentionRegion)) {
@@ -246,34 +247,31 @@ public class ExpertInviteManage {
/**
* 专家抽取(随机)
*
* @param avoidInfo 回避信息
* @param avoidRule 回避信息
* @param randomRule 抽取规则
* @param appointExpertIds 指定抽取专家ID
* @return 满足抽取条件的专家
* @author WendyYang
**/
public ExpertChooseDTO expertInviteByRandomRule(AvoidInfoDTO avoidInfo,
public ExpertChooseDTO expertInviteByRandomRule(AvoidRuleDTO avoidRule,
RandomInviteRuleDTO randomRule,
List<Long> appointExpertIds,
LocalDateTime start,
LocalDateTime end) {
ExpertChooseDTO result = new ExpertChooseDTO(new ArrayList<>(), 0);
List<Long> expertIdsIn = mergeExpertIdsByCondition(randomRule, avoidInfo);
List<Long> expertIdsIn = mergeExpertIdsByCondition(randomRule, avoidRule);
if (expertIdsIn == null) {
return result;
}
boolean avoid = avoidInfo != null;
boolean avoidExpert = avoid && CollUtil.isNotEmpty(avoidInfo.getExpertIds());
boolean avoidCompany = avoid && CollUtil.isNotEmpty(avoidInfo.getAvoidUnitIdList());
boolean avoidExpert = CollUtil.isNotEmpty(avoidRule.getExpertIds());
boolean avoidCompany = CollUtil.isNotEmpty(avoidRule.getAvoidUnitIdList());
Set<String> tmpAvoidCompany = new HashSet<>();
if (avoidCompany) {
List<String> companyIds = avoidInfo.getAvoidUnitIdList();
List<String> companyIds = avoidRule.getAvoidUnitIdList();
for (String companyId : companyIds) {
if (companyId.contains(",")) {
String[] splitCompanyIds = companyId.split(",");
for (String splitCompanyId : splitCompanyIds) {
tmpAvoidCompany.add(splitCompanyId);
}
Collections.addAll(tmpAvoidCompany, splitCompanyIds);
} else {
tmpAvoidCompany.add(companyId);
}
@@ -284,14 +282,13 @@ public class ExpertInviteManage {
query.notIn(!tmpAvoidCompany.isEmpty(), ExpertUserFullInfo::getCompany, tmpAvoidCompany);
if (avoidCompany) {
query.notExists("select 1 from expert_avoid_company eac where eac.user_id = expert_user_full_info.user_id" +
" and company_name in ({0})", CollUtils.joinByComma(avoidInfo.getAvoidUnitIdList()));
" and company_name in ({0})", CollUtils.joinByComma(avoidRule.getAvoidUnitIdList()));
}
// 处理专家层级
addRegionLimit(query, randomRule);

if (!expertIdsIn.isEmpty()) {
if (avoidExpert) {
expertIdsIn.removeIf(w -> avoidInfo.getExpertIds().contains(w));
expertIdsIn.removeIf(w -> avoidRule.getExpertIds().contains(w));
if (expertIdsIn.isEmpty()) {
// 字典、标签、履职意向地筛选出的专家ID移除需要回避的专家ID
// 如果为空则说明没有符合条件的
@@ -312,7 +309,7 @@ public class ExpertInviteManage {
}
query.in(ExpertUserFullInfo::getUserId, expertIdsIn);
} else if (avoidExpert || CollUtil.isNotEmpty(appointExpertIds)) {
Set<Long> tempExperts = expertInviteHelper.getAvoidExpert(appointExpertIds, avoidInfo, start, end);
Set<Long> tempExperts = expertInviteHelper.getAvoidExpert(appointExpertIds, avoidRule, start, end);
query.notIn(ExpertUserFullInfo::getUserId, tempExperts);
} else {
Set<Long> notInUserIds = expertInviteHelper.listExpertLeaveOrInvited(start, end);
@@ -320,15 +317,15 @@ public class ExpertInviteManage {
query.notIn(ExpertUserFullInfo::getUserId, notInUserIds);
}
}
List<ExpertUserFullInfo> userFullInfos = expertUserFullInfoService.list(query);
if (userFullInfos.isEmpty()) {
List<ExpertUserFullInfo> userInfoList = expertUserFullInfoService.list(query);
if (userInfoList.isEmpty()) {
return result;
}
result.setTotal(userFullInfos.size());
Map<String, List<ExpertUserFullInfo>> userGroupByUnit = CollUtils.group(userInfoList, ExpertUserFullInfo::getCompany);
result.setTotal(userInfoList.size());
// count为空表示数量校验
if (randomRule.getCount() == null || result.getTotal() >= randomRule.getCount()) {
List<ExpertUserFullInfo> userFullInfoList = inviteWithoutCompany(userFullInfos, randomRule.getCount());
result.setExperts(userFullInfoList);
result.setExperts(inviteGroupByCompany(userGroupByUnit, randomRule.getCount()));
}
return result;
}
@@ -336,39 +333,41 @@ public class ExpertInviteManage {
/**
* 专家替换、补充
*
* @param avoidInfo 回避信息
* @param avoidRule 回避信息
* @param randomRule 随机抽取
* @param meetingExperts 已抽取人员
* @param invitedExperts 已抽取人员
* @param count 抽取数量
* @return com.ningdatech.emapi.meeting.entity.dto.ExpertChooseDto
* @param start 会议开始时间
* @param end 会议结束时间
* @return {@link ExpertChooseDTO}
* @author WendyYang
**/
public ExpertChooseDTO expertReplaceByRandomRule(AvoidInfoDTO avoidInfo,
public ExpertChooseDTO expertReplaceByRandomRule(AvoidRuleDTO avoidRule,
RandomInviteRuleDTO randomRule,
Collection<MeetingExpert> meetingExperts,
Collection<MeetingExpert> invitedExperts,
Integer count,
LocalDateTime start,
LocalDateTime end,
Long replacedExpertId) {
boolean invitedRefused) {
ExpertChooseDTO result = new ExpertChooseDTO(new ArrayList<>(), 0);
// 合并标签、字典
List<Long> expertIdsIn = mergeExpertIdsByCondition(randomRule, avoidInfo);
List<Long> expertIdsIn = mergeExpertIdsByCondition(randomRule, avoidRule);
if (expertIdsIn == null) {
return result;
}
LambdaQueryWrapper<ExpertUserFullInfo> query = buildBaseExpertQuery();
query.notIn(ExpertUserFullInfo::getCompany, avoidInfo.getAvoidUnitIdList());
query.notIn(ExpertUserFullInfo::getCompany, avoidRule.getAvoidUnitIdList());
query.notExists("select 1 from expert_avoid_company eac where eac.user_id = expert_user_full_info.user_id" +
" and company_name in ({0})", CollUtils.joinByComma(avoidInfo.getAvoidOrgIdList()));
" and company_name in ({0})", CollUtils.joinByComma(avoidRule.getAvoidOrgIdList()));

// 处理专家层级
if (ObjectUtils.allNotNull(randomRule.getExpertRegionCode(), randomRule.getExpertRegionLevel())) {
if (StrUtils.isNotBlank(randomRule.getExpertRegionCode())) {
query.eq(ExpertUserFullInfo::getRegionCode, randomRule.getExpertRegionCode());
query.eq(ExpertUserFullInfo::getRegionLevel, randomRule.getExpertRegionLevel());
}
if (expertIdsIn.size() > 0) {
if (CollectionUtils.isNotEmpty(avoidInfo.getExpertIds())) {
expertIdsIn.removeIf(w -> avoidInfo.getExpertIds().contains(w));
if (CollectionUtils.isNotEmpty(avoidRule.getExpertIds())) {
expertIdsIn.removeIf(w -> avoidRule.getExpertIds().contains(w));
if (expertIdsIn.isEmpty()) {
// 字典、标签、履职意向地筛选出的专家ID移除需要回避的专家ID
// 如果为空则说明没有符合条件的
@@ -381,8 +380,8 @@ public class ExpertInviteManage {
return result;
}
query.in(ExpertUserFullInfo::getUserId, expertIdsIn);
} else if (CollectionUtils.isNotEmpty(avoidInfo.getExpertIds())) {
Set<Long> notInExpertIds = expertInviteHelper.getAvoidExpert(avoidInfo.getExpertIds(), null, start, end);
} else if (CollUtil.isNotEmpty(avoidRule.getExpertIds())) {
Set<Long> notInExpertIds = expertInviteHelper.getAvoidExpert(avoidRule.getExpertIds(), null, start, end);
query.notIn(ExpertUserFullInfo::getUserId, notInExpertIds);
} else {
Set<Long> notInUserIds = expertInviteHelper.listExpertLeaveOrInvited(start, end);
@@ -394,44 +393,31 @@ public class ExpertInviteManage {
if (userFullInfos.size() == 0) {
return result;
}
Comparator<MeetingExpert> sort = Comparator.comparing(MeetingExpert::getUpdateOn).reversed();
Map<Long, MeetingExpert> tempExpertIdsMap = BizUtils.groupFirstMap(meetingExperts, MeetingExpert::getExpertId, sort);
Map<ExpertAttendStatusEnum, List<MeetingExpert>> expertIdGroupByStatus = tempExpertIdsMap.values().stream()
Map<ExpertAttendStatusEnum, List<MeetingExpert>> expertGroupByStatus = invitedExperts.stream()
.collect(Collectors.groupingBy(w -> ExpertAttendStatusEnum.getByCode(w.getStatus())));
// 回避同单位其他专家
List<MeetingExpert> removeExpertByCompany = new ArrayList<>();
BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatusEnum.AGREED), removeExpertByCompany::addAll);
BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatusEnum.NOTICING), removeExpertByCompany::addAll);
BizUtils.notEmpty(expertGroupByStatus.get(ExpertAttendStatusEnum.AGREED), removeExpertByCompany::addAll);
BizUtils.notEmpty(expertGroupByStatus.get(ExpertAttendStatusEnum.NOTICING), removeExpertByCompany::addAll);
List<Long> removeExpertIds = new ArrayList<>();
// 拒绝参加的不可以被再次抽中
BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatusEnum.REFUSED), w -> {
List<Long> tempRefused = CollUtils.fieldList(w, MeetingExpert::getExpertId);
removeExpertIds.addAll(tempRefused);
});
if (invitedRefused) {
// 拒绝参加的不可以被再次抽中
BizUtils.notEmpty(expertGroupByStatus.get(ExpertAttendStatusEnum.REFUSED), w -> {
List<Long> tempRefused = CollUtils.fieldList(w, MeetingExpert::getExpertId);
removeExpertIds.addAll(tempRefused);
});
}
// 被取消的也不可以被再次抽中
BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatusEnum.REMOVED), w -> {
BizUtils.notEmpty(expertGroupByStatus.get(ExpertAttendStatusEnum.REMOVED), w -> {
List<Long> tempCanceled = CollUtils.fieldList(w, MeetingExpert::getExpertId);
removeExpertIds.addAll(tempCanceled);
});
// 被替换之前是上述两种状态的不可被再次抽中
BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatusEnum.REPLACED), w -> {
for (MeetingExpert me : w) {
BizUtils.notNull(me.getPreStatus(), preStatus -> {
if (ExpertAttendStatusEnum.REFUSED.eq(preStatus) || ExpertAttendStatusEnum.REMOVED.eq(preStatus)) {
removeExpertIds.add(me.getExpertId());
}
});
}
});
// 不为空时表示单个专家替换否则为专家补抽
if (replacedExpertId != null) {
removeExpertIds.add(replacedExpertId);
}
List<Long> tempExpertIds = CollUtils.fieldList(removeExpertByCompany, MeetingExpert::getExpertId);
// 移除确认参加、通知中的、拒绝参加、已取消
userFullInfos.removeIf(w -> tempExpertIds.contains(w.getUserId()) || removeExpertIds.contains(w.getUserId()));
result.setTotal(userFullInfos.size());
result.setExperts(inviteWithoutCompany(userFullInfos, count));
Map<String, List<ExpertUserFullInfo>> userGroupByUnit = CollUtils.group(userFullInfos, ExpertUserFullInfo::getCompany);
result.setTotal(userGroupByUnit.size());
result.setExperts(inviteGroupByCompany(userGroupByUnit, count));
return result;
}

@@ -455,26 +441,26 @@ public class ExpertInviteManage {
/**
* 每个单位只抽取一人
*
* @param expertGroupByCompany 需要抽取的人
* @param count 抽取数量
* @param expertGroupByUnit 需要抽取的人
* @param count 抽取数量
* @return java.util.List<com.ningdatech.emapi.expert.entity.domain.ExpertUserFullInfo>
* @author WendyYang
**/
private List<ExpertUserFullInfo> inviteGroupByCompany(Map<String, List<ExpertUserFullInfo>> expertGroupByCompany, Integer count) {
if (MapUtils.isEmpty(expertGroupByCompany)) {
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 expertGroupByCompany.values().stream()
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<>(expertGroupByCompany.keySet());
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 = expertGroupByCompany.get(key);
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())))
@@ -547,12 +533,13 @@ public class ExpertInviteManage {
* 专家抽取(会议创建时抽取)
*
* @param randomRules 随机抽取规则
* @param avoid 回避信息
* @param avoidRuled 回避信息
* @param meeting 会议信息
* @author WendyYang
**/
public void expertRandomInviteByMeetingCreate(Meeting meeting,
List<RandomInviteRuleDTO> randomRules,
AvoidInfoDTO avoid) {
public void expertInviteByMeetingCreate(Meeting meeting,
List<RandomInviteRuleDTO> randomRules,
AvoidRuleDTO avoidRuled) {
List<MeetingExpert> expertInserts = new ArrayList<>();
// 处理随机抽取规则
if (CollectionUtils.isNotEmpty(randomRules)) {
@@ -562,7 +549,7 @@ public class ExpertInviteManage {
LocalDateTime startTime = meeting.getStartTime();
LocalDateTime endTime = meeting.getEndTime();
randomRules.forEach(rule -> {
ExpertChooseDTO tempExperts = expertInviteByRandomRule(avoid, rule, choosedExpertIds, startTime, endTime);
ExpertChooseDTO tempExperts = expertInviteByRandomRule(avoidRuled, rule, choosedExpertIds, startTime, endTime);
expertsByRandom.add(tempExperts);
choosedExpertIds.addAll(CollUtils.fieldList(tempExperts.getExperts(), ExpertUserFullInfo::getUserId));
randoms.add(getExpertInviteRule(rule, meeting.getId()));


+ 34
- 82
pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingManage.java Прегледај датотеку

@@ -140,6 +140,29 @@ public class MeetingManage {
}
}

public void continueInvite(Long meetingId) {
String key = "CONTINUE_INVITE:" + meetingId;
if (!distributedLock.lock(key, RETRY_TIMES)) {
throw BizException.wrap("已进行续抽,请勿重复点击");
}
try {
Meeting meeting = meetingService.getById(meetingId);
if (!meeting.getInviteStatus()) {
throw BizException.wrap("该会议正在抽取专家,暂无法续抽");
}
if (!MeetingStatusEnum.NORMAL.eq(meeting.getStatus())) {
throw BizException.wrap("续抽失败,请刷新后重试");
}
boolean invitedContinue = meetingManageHelper.checkCouldBeInvitedContinue(meetingId);
if (!invitedContinue) {
throw BizException.wrap("抽取人员数量已满足抽取规则");
}

} finally {
distributedLock.releaseLock(key);
}
}

@Transactional(rollbackFor = Exception.class)
public void expertInviteByCreate(ExpertInviteReq req) {
String key = INVITED_RULE_CREATE + req.getMeetingId();
@@ -151,7 +174,7 @@ public class MeetingManage {
if (ExpertInviteTypeEnum.RANDOM.eq(req.getInviteType())) {
List<RandomInviteRuleDTO> randomRules = req.getRandomRules();
Assert.notEmpty(randomRules, "随机抽取规则不能为空");
AvoidInfoDTO avoidInfo = req.getAvoidRule();
AvoidRuleDTO avoidInfo = req.getAvoidRule();
Assert.notNull(avoidInfo, "回避信息不能为空");
// 随机抽取的话则需进行抽取数量校验
ExpertCountOnChangeVO countOnChange = expertCountOnChange(req);
@@ -163,23 +186,21 @@ public class MeetingManage {
Integer inviteCount = randomRules.get(i).getCount();
Assert.isTrue(checkCount >= inviteCount, "可供抽取的专家数量不足");
}
expertInviteManage.expertRandomInviteByMeetingCreate(meeting, randomRules, avoidInfo);
expertInviteManage.expertInviteByMeetingCreate(meeting, randomRules, avoidInfo);
expertInviteTask.addInviteExpertTaskByMeetingCreate(meeting.getId(), 5);
LambdaUpdateWrapper<Meeting> update = Wrappers.lambdaUpdate(Meeting.class);
update.set(Meeting::getInviteStatus, false);
update.eq(Meeting::getId, meeting.getId());
meetingService.update(update);
// 回避规则
if (avoidInfo != null) {
ExpertInviteAvoidRule avoidRule = new ExpertInviteAvoidRule();
avoidRule.setMeetingId(meeting.getId());
// 未传值时设置为0 表示不限制周参与次数
avoidRule.setWeekInviteCount(ObjectUtil.defaultIfNull(avoidRule.getWeekInviteCount(), 0));
avoidRule.setAvoidOrgIds(CollUtils.joinByComma(avoidInfo.getAvoidOrgIdList()));
avoidRule.setAvoidUnitIds(CollUtils.joinByComma(avoidInfo.getAvoidUnitIdList()));
avoidRule.setAvoidExpertIds(CollUtils.joinByComma(avoidInfo.getExpertIds()));
inviteAvoidRuleService.save(avoidRule);
}
ExpertInviteAvoidRule avoidRule = new ExpertInviteAvoidRule();
avoidRule.setMeetingId(meeting.getId());
// 未传值时设置为0 表示不限制周参与次数
avoidRule.setWeekInviteCount(ObjectUtil.defaultIfNull(avoidRule.getWeekInviteCount(), 0));
avoidRule.setAvoidOrgIds(CollUtils.joinByComma(avoidInfo.getAvoidOrgIdList()));
avoidRule.setAvoidUnitIds(CollUtils.joinByComma(avoidInfo.getAvoidUnitIdList()));
avoidRule.setAvoidExpertIds(CollUtils.joinByComma(avoidInfo.getExpertIds()));
inviteAvoidRuleService.save(avoidRule);
} else {
// 指定邀请
AppointInviteRuleDTO appointRule = req.getAppointRule();
@@ -449,7 +470,7 @@ public class MeetingManage {
}
result.getRandomRules().add(randomRule);
});
AvoidInfoDTO avoidInfo = inviteAvoidRuleService.getAvoidInfoDto(meetingId);
AvoidRuleDTO avoidInfo = inviteAvoidRuleService.getAvoidInfoDto(meetingId);
AvoidInfoVO vo = new AvoidInfoVO();
vo.setAvoidOrgIds(avoidInfo.getAvoidOrgIdList());
vo.setAvoidUnitIds(avoidInfo.getAvoidUnitIdList());
@@ -469,75 +490,6 @@ public class MeetingManage {
return result;
}

public void expertRemove(ExpertRemoveReq po) {
LambdaUpdateWrapper<MeetingExpert> update = Wrappers.lambdaUpdate(MeetingExpert.class)
.eq(MeetingExpert::getId, po.getExpertMeetingId())
.set(MeetingExpert::getStatus, ExpertAttendStatusEnum.REMOVED.getCode());
meetingExpertService.update(update);
}

@Transactional(rollbackFor = Exception.class)
public void expertReplace(ExpertRemoveReq po) {
MeetingExpert meetingExpert = meetingExpertService.getById(po.getExpertMeetingId());
ExpertUserFullInfo expertFullInfo;
Long ruleId = 0L;
Meeting meeting = meetingService.getById(po.getMeetingId());
if (po.getExpertId() != null) {
// 指定邀请替换
List<ExpertUserFullInfo> userInfos = meetingManageHelper.appointExpertCheck(po.getMeetingId(), Collections.singletonList(po.getExpertId()));
expertFullInfo = userInfos.get(0);
} else {
List<ExpertInviteRule> inviteRules = inviteRuleService.listByMeetingId(po.getMeetingId());
// 邀请规则
RandomInviteRuleDTO randomInviteRuleDto = null;
for (ExpertInviteRule rule : inviteRules) {
if (rule.getInviteType().equals(ExpertInviteTypeEnum.RANDOM.getCode()) &&
rule.getId().equals(meetingExpert.getRuleId())) {
randomInviteRuleDto = JSON.parseObject(rule.getInviteRule(), RandomInviteRuleDTO.class);
ruleId = rule.getId();
break;
}
}
// 回避规则
AvoidInfoDTO avoidInfoDto = meetingManageHelper.getAvoidInfoDto(po.getMeetingId());
// 添加回避该替换的专家
List<Long> expertIds = avoidInfoDto.getExpertIds();
if (CollUtil.isEmpty(expertIds)) {
expertIds = new ArrayList<>();
}
expertIds.add(meetingExpert.getExpertId());
avoidInfoDto.setExpertIds(expertIds);
meetingManageHelper.saveAvoidInfo(po.getMeetingId(), avoidInfoDto.getExpertIds());

List<MeetingExpert> meetingExperts = meetingExpertService.listByMeetingId(po.getMeetingId());
ExpertChooseDTO expertChooseDto = expertInviteManage.expertReplaceByRandomRule(avoidInfoDto, randomInviteRuleDto,
meetingExperts, 1, meeting.getStartTime(), meeting.getEndTime(), meetingExpert.getExpertId());
Assert.isTrue(expertChooseDto.getTotal() > 0, "暂无专家可供替换");
expertFullInfo = expertChooseDto.getExperts().get(0);
expertInviteTask.addInviteExpertTask(po.getMeetingId());
}
LambdaUpdateWrapper<MeetingExpert> update = Wrappers.lambdaUpdate(MeetingExpert.class)
.eq(MeetingExpert::getId, po.getExpertMeetingId())
.set(MeetingExpert::getUpdateOn, LocalDateTime.now())
.set(MeetingExpert::getPreStatus, meetingExpert.getStatus())
.set(MeetingExpert::getStatus, ExpertAttendStatusEnum.REPLACED.getCode());
meetingExpertService.update(update);
MeetingExpert me;
if (po.getExpertId() == null) {
me = ExpertInviteBuilder.getExpertByRandom(po.getMeetingId(), expertFullInfo, ruleId);
} else {
me = ExpertInviteBuilder.getExpertByAppoint(po.getMeetingId(), expertFullInfo, ruleId);
}
me.setStatus(ExpertAttendStatusEnum.NOTICING.getCode());
me.setPreId(po.getExpertMeetingId());
yxtCallOrSmsHelper.callByMeetingExperts(meeting, Collections.singletonList(me));
meetingExpertService.save(me);
// 发送专家替换短信 TODO
// String meetingType = dictionaryCache.getByCode(meeting.getType()).getName();
// SendSmsContext context = YxtSmsContextBuilder.smsToExpertByReplace(meeting, meetingExpert, meetingType);
// yxtCallOrSmsHelper.sendSms(context);
}

public void batchAppointExperts(BatchAppointExpertsReq req) {
Long meetingId = req.getMeetingId();
String key = "BATCH_APPOINT_EXPERT:" + meetingId;


+ 2
- 2
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IExpertInviteAvoidRuleService.java Прегледај датотеку

@@ -2,7 +2,7 @@ package com.ningdatech.pmapi.meeting.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteAvoidRule;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidInfoDTO;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidRuleDTO;

/**
* <p>
@@ -23,6 +23,6 @@ public interface IExpertInviteAvoidRuleService extends IService<ExpertInviteAvoi
* @return com.ningdatech.emapi.meeting.entity.dto.AvoidInfoDto
* @author WendyYang
**/
AvoidInfoDTO getAvoidInfoDto(Long meetingId);
AvoidRuleDTO getAvoidInfoDto(Long meetingId);

}

+ 13
- 2
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingExpertService.java Прегледај датотеку

@@ -27,7 +27,7 @@ public interface IMeetingExpertService extends IService<MeetingExpert> {
* 查询专家的参与状态
*
* @param expertId 专家ID
* @param status 状态{@link MeetingStatus.Expert}
* @param status 状态
* @param meetingIds 会议ID
* @return 会议参加状态统计
* @author WendyYang
@@ -35,7 +35,7 @@ public interface IMeetingExpertService extends IService<MeetingExpert> {
List<MeetingAndAttendStatusDTO> listByExpertIdAndStatus(Long expertId, Integer status, List<Long> meetingIds);

/**
* 查询每个事物的确认进度
* 查询每个会议的确认进度
*
* @param meetingIds 事务ID
* @return 确认进度
@@ -132,4 +132,15 @@ public interface IMeetingExpertService extends IService<MeetingExpert> {
**/
List<MeetingExpert> listExpertLastByMeetingIds(Collection<Long> meetingIds);

/**
* 查询会议的所有被抽取人最后一条记录
*
* @param meetingId 会议ID
* @return 抽取记录
* @author WendyYang
**/
default List<MeetingExpert> listExpertLastByMeetingId(Long meetingId) {
return listExpertLastByMeetingIds(Collections.singletonList(meetingId));
}

}

+ 3
- 3
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/ExpertInviteAvoidRuleServiceImpl.java Прегледај датотеку

@@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ningdatech.pmapi.common.util.BizUtils;
import com.ningdatech.pmapi.common.util.StrUtils;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteAvoidRule;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidInfoDTO;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidRuleDTO;
import com.ningdatech.pmapi.meeting.mapper.ExpertInviteAvoidRuleMapper;
import com.ningdatech.pmapi.meeting.service.IExpertInviteAvoidRuleService;
import org.springframework.stereotype.Service;
@@ -30,12 +30,12 @@ public class ExpertInviteAvoidRuleServiceImpl extends ServiceImpl<ExpertInviteAv
}

@Override
public AvoidInfoDTO getAvoidInfoDto(Long meetingId) {
public AvoidRuleDTO getAvoidInfoDto(Long meetingId) {
ExpertInviteAvoidRule avoidRule = getByMeetingId(meetingId);
if (avoidRule == null) {
return null;
}
AvoidInfoDTO avoidInfo = new AvoidInfoDTO();
AvoidRuleDTO avoidInfo = new AvoidRuleDTO();
avoidInfo.setAvoidOrgIdList(StrUtils.split(avoidRule.getAvoidOrgIds()));
avoidInfo.setAvoidUnitIdList(StrUtils.split(avoidRule.getAvoidUnitIds()));
avoidInfo.setExpertIds(BizUtils.splitToLong(avoidRule.getAvoidExpertIds()));


+ 66
- 67
pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteTask.java Прегледај датотеку

@@ -1,23 +1,20 @@
package com.ningdatech.pmapi.meeting.task;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.cache.model.cache.CacheKey;
import com.ningdatech.cache.model.cache.CacheHashKey;
import com.ningdatech.cache.repository.CachePlusOps;
import com.ningdatech.pmapi.common.util.SpringContextHolder;
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.AvoidInfoDTO;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidRuleDTO;
import com.ningdatech.pmapi.meeting.entity.dto.ExpertChooseDTO;
import com.ningdatech.pmapi.meeting.entity.dto.MeetingInviteCacheDTO;
import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteTypeEnum;
import com.ningdatech.pmapi.meeting.helper.ExpertInviteHelper;
import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper;
import com.ningdatech.pmapi.meeting.manage.ExpertInviteManage;
@@ -29,6 +26,7 @@ import com.ningdatech.pmapi.user.util.LoginUserUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@@ -42,15 +40,12 @@ import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;


@@ -69,9 +64,13 @@ public class ExpertInviteTask {

private final ExpertInviteHelper expertInviteHelper;
private final RandomInviteProperties properties;

private static final String MEETING_ID_INVITE_RANDOM = "MEETING_ID_INVITE_RANDOM";
private static final CacheKey CACHE_KEY = new CacheKey(MEETING_ID_INVITE_RANDOM);
private static final CacheHashKey CACHE_KEY = new CacheHashKey();

static {
CACHE_KEY.setKey(MEETING_ID_INVITE_RANDOM);
CACHE_KEY.setExpire(Duration.ofDays(100));
}

private final CachePlusOps cachePlusOps;
@Qualifier("expertInviteScheduler")
@@ -81,38 +80,38 @@ public class ExpertInviteTask {
private final IExpertInviteRuleService inviteRuleService;
private final IMeetingService meetingService;
private final ExpertInviteManage expertInviteManage;
private final IExpertInviteAvoidRuleService expertInviteAvoidRuleService;

private final IExpertInviteAvoidRuleService inviteAvoidRuleService;
private final YxtCallOrSmsHelper yxtCallOrSmsHelper;

public ExpertInviteTask currProxy() {
return (ExpertInviteTask) AopContext.currentProxy();
}

/**
* 用来存入线程执行情况, 方便于停止定时任务时使用
*/
protected static final ConcurrentMap<Long, ScheduledFuture<?>> INVITE_MAP = new ConcurrentHashMap<>();

public ExpertInviteTask currProxy() {
return (ExpertInviteTask) AopContext.currentProxy();
}

@PostConstruct
public void initTask() {
if (!properties.getEnable()) {
log.warn("随机邀请已关闭……");
return;
}
// initInviteTaskByStart();
initInviteTaskAfterAppStarted();
}

/**
* 项目重启之后重新初始化邀请任务
*/
private void initInviteTaskByStart() {
Set<Object> meetingIds = cachePlusOps.sMembers(CACHE_KEY);
if (meetingIds == null || meetingIds.isEmpty()) {
private void initInviteTaskAfterAppStarted() {
Map<Long, MeetingInviteCacheDTO> meetingIdMap = cachePlusOps.hGetAll(CACHE_KEY);
if (MapUtils.isEmpty(meetingIdMap)) {
log.info("暂无需要初始化的抽取会议信息");
return;
}
for (Object meetingId : meetingIds) {
addInviteExpertTask(Convert.toLong(meetingId), true, properties.getInviteDelay());
for (MeetingInviteCacheDTO cache : meetingIdMap.values()) {
addInviteExpertTask(cache.getMeetingId(), true, properties.getInviteDelay(), cache.getInvitedRefused());
}
}

@@ -123,25 +122,27 @@ public class ExpertInviteTask {
* @return boolean
* @author WendyYang
**/
private boolean inviteCheck(Long meetingId) {
List<ExpertInviteRule> expertInviteRules = inviteRuleService.listByMeetingId(meetingId);
Function<ExpertInviteRule, ExpertInviteTypeEnum> groupKey = w -> ExpertInviteTypeEnum.getByCode(w.getInviteType());
Map<ExpertInviteTypeEnum, List<ExpertInviteRule>> groupByType = CollUtils.group(expertInviteRules, groupKey);
List<ExpertInviteRule> randomRules = groupByType.get(ExpertInviteTypeEnum.RANDOM);
if (CollUtil.isEmpty(randomRules)) {
return false;
private boolean inviteCountCheck(Long meetingId) {
Map<Long, RandomInviteRuleDTO> ruleMap = inviteRuleService.randomRuleByMeetingId(meetingId);
if (ruleMap.isEmpty()) {
return Boolean.FALSE;
}
Map<Long, ExpertInviteRule> ruleMap = CollUtils.listToMap(randomRules, ExpertInviteRule::getId);
LambdaQueryWrapper<MeetingExpert> query = Wrappers.lambdaQuery(MeetingExpert.class)
.select(MeetingExpert::getRuleId, MeetingExpert::getStatus)
.in(MeetingExpert::getRuleId, ruleMap.keySet())
.in(MeetingExpert::getStatus, ExpertAttendStatusEnum.AGREED.getCode());
List<MeetingExpert> meetingExperts = meetingExpertService.list(query);
int totalCount = CollUtils.sum(randomRules, ExpertInviteRule::getInviteCount);
boolean needed = totalCount > meetingExperts.size();
if (!needed) {
cancelByMeetingId(meetingId);
List<MeetingExpert> experts = meetingExpertService.list(query);
if (experts.isEmpty()) {
return Boolean.FALSE;
}
Map<Long, Long> cntMap = CollUtils.groupCount(experts, MeetingExpert::getRuleId);
for (Map.Entry<Long, RandomInviteRuleDTO> entry : ruleMap.entrySet()) {
Long agreeCnt = cntMap.getOrDefault(entry.getKey(), 0L);
if (agreeCnt < entry.getValue().getCount()) {
return Boolean.FALSE;
}
}
return needed;
return Boolean.TRUE;
}

/**
@@ -151,9 +152,8 @@ public class ExpertInviteTask {
* @author WendyYang
**/
public void addInviteExpertTask(Long meetingId) {
boolean contains = INVITE_MAP.containsKey(meetingId);
if (!contains) {
addInviteExpertTask(meetingId, false, properties.getInviteDelay());
if (!INVITE_MAP.containsKey(meetingId)) {
addInviteExpertTask(meetingId, false, properties.getInviteDelay(), false);
log.info("重置会议的随机抽取状态:{}", meetingId);
LambdaUpdateWrapper<Meeting> update = Wrappers.lambdaUpdate(Meeting.class);
update.set(Meeting::getInviteStatus, false);
@@ -170,17 +170,19 @@ public class ExpertInviteTask {
* @param meetingId 会议ID
* @param checked 是否前置校验
* @param delayedMinutes 延迟执行时间
* @param invitedRefused 是否可以邀请被拒绝的专家
* @author WendyYang
**/
public void addInviteExpertTask(Long meetingId, boolean checked, int delayedMinutes) {
if (checked && !inviteCheck(meetingId)) {
public void addInviteExpertTask(Long meetingId, boolean checked, int delayedMinutes, boolean invitedRefused) {
if (checked && !inviteCountCheck(meetingId)) {
// 如果抽取数量满足直接返回
return;
}
Instant startTime = LocalDateTime.now().plusMinutes(delayedMinutes).atZone(ZoneId.systemDefault()).toInstant();
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(() -> {
ExpertInviteTask bean = SpringContextHolder.getBean(ExpertInviteTask.class);
try {
bean.invite(meetingId);
bean.invite(meetingId, invitedRefused);
} catch (Exception e) {
log.error("执行专家邀请任务异常:{}", meetingId, e);
}
@@ -189,53 +191,55 @@ public class ExpertInviteTask {
log.info("添加专家抽取后台任务:{}", meetingId);
}

/**
* 创建会议时添加抽取任务
*
* @param meetingId 会议ID
* @param delayedMinutes 延迟时间
* @author WendyYang
**/
public void addInviteExpertTaskByMeetingCreate(Long meetingId, int delayedMinutes) {
Assert.isTrue(properties.getEnable(), "随机邀请已关闭");
addInviteExpertTask(meetingId, false, delayedMinutes);
addInviteExpertTask(meetingId, false, delayedMinutes, false);
cachePlusOps.sAdd(CACHE_KEY, meetingId);
}

@Transactional(rollbackFor = Exception.class)
public void invite(Long meetingId) {
public void invite(Long meetingId, Boolean invitedRefused) {
log.info("开始进行专家后台抽取:{}", meetingId);
Meeting meeting = meetingService.getById(meetingId);
if (meeting.getStartTime().isBefore(LocalDateTime.now())) {
// 会议开始结束随机抽取
cancelByMeetingId(meetingId);
log.info("会议已开始停止抽取:{}", meeting);
cancelByMeetingId(meetingId);
return;
}
// 随机邀请规则
Map<Long, RandomInviteRuleDTO> ruleMap = inviteRuleService.randomRuleByMeetingId(meetingId);
// 回避规则
AvoidInfoDTO avoidInfoDto = expertInviteAvoidRuleService.getAvoidInfoDto(meetingId);
AvoidRuleDTO avoidRule = inviteAvoidRuleService.getAvoidInfoDto(meetingId);
// 还需要抽取的规则数量
AtomicInteger notIgnoreCnt = new AtomicInteger(ruleMap.size());
AtomicInteger notSupportCnt = new AtomicInteger(0);
ruleMap.forEach((ruleId, value) -> {
List<Long> singletonList = Collections.singletonList(meetingId);
List<MeetingExpert> tempExperts = meetingExpertService.listExpertLastByMeetingIds(singletonList);
Map<Long, MeetingExpert> expertMap = CollUtils.listToMap(tempExperts, MeetingExpert::getExpertId);
Map<Long, MeetingExpert> replacedMap = expertMap.values().stream().filter(w -> w.getPreId() > 0)
.collect(Collectors.toMap(MeetingExpert::getPreId, w -> w));
Map<Long, ExpertCntBO> countMap = countByAgree(expertMap, replacedMap);
// 已确认参加、通话中数量
List<MeetingExpert> tmpExperts = meetingExpertService.listExpertLastByMeetingId(meetingId);
Map<Long, MeetingExpert> expertMap = CollUtils.listToMap(tmpExperts, MeetingExpert::getExpertId);
// 统计通知中与同意参加专家数量
Map<Long, ExpertCntBO> countMap = countByAgree(expertMap);
ExpertCntBO cnt = countMap.getOrDefault(ruleId, ExpertCntBO.zeroInit());
int tempCurrent = cnt.getAgreeCnt() + cnt.getNoticeCnt();
if (tempCurrent == value.getCount()) {
int wouldAttendCnt = cnt.getAgreeCnt() + cnt.getNoticeCnt();
if (wouldAttendCnt == value.getCount()) {
if (cnt.getAgreeCnt().equals(value.getCount())) {
notIgnoreCnt.decrementAndGet();
}
return;
}
int currInviteCnt = value.getCount() - tempCurrent;
ExpertChooseDTO expertChoose = expertInviteManage.expertReplaceByRandomRule(avoidInfoDto, value,
expertMap.values(), currInviteCnt, meeting.getStartTime(), meeting.getEndTime(), null);
int needInviteCnt = value.getCount() - wouldAttendCnt;
ExpertChooseDTO expertChoose = expertInviteManage.expertReplaceByRandomRule(avoidRule, value,
tmpExperts, needInviteCnt, meeting.getStartTime(), meeting.getEndTime(), invitedRefused);

if (expertChoose.getTotal() > 0) {
List<MeetingExpert> expertMeetings = CollUtils.convert(expertChoose.getExperts(), w -> {
MeetingExpert expert = ExpertInviteBuilder.getExpertByRandom(meetingId, w, ruleId);
expert.setPreStatus(ExpertAttendStatusEnum.NOTICING.getCode());
expert.setStatus(ExpertAttendStatusEnum.NOTICING.getCode());
return expert;
});
@@ -276,7 +280,7 @@ public class ExpertInviteTask {

//==================================================================================================================

private Map<Long, ExpertCntBO> countByAgree(Map<Long, MeetingExpert> expertMap, Map<Long, MeetingExpert> replacedMap) {
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 -> {
@@ -286,11 +290,6 @@ public class ExpertInviteTask {
cnt.incrAgreeCnt();
} else if (ExpertAttendStatusEnum.NOTICING.eq(expert.getStatus())) {
cnt.incrNoticeCnt();
} else if (ExpertAttendStatusEnum.REPLACED.eq(expert.getStatus())) {
MeetingExpert replacedExpert = replacedMap.get(expert.getId());
if (replacedExpert != null && ExpertAttendStatusEnum.AGREED.eq(replacedExpert.getStatus())) {
cnt.incrAgreeCnt();
}
}
}
return cnt;


+ 1
- 1
pmapi/src/main/resources/application-dev.yml Прегледај датотеку

@@ -15,7 +15,7 @@ spring:
timeout: 5000
host: 47.98.125.47
port: 26379
database: 0
database: 4
password: Ndkj1234
jedis:
pool:


Loading…
Откажи
Сачувај