@@ -105,4 +105,8 @@ public class BizUtils { | |||
}))); | |||
} | |||
public static String inSqlJoin(List<String> strings) { | |||
return strings.stream().map(w -> "'" + w + "'").collect(Collectors.joining(StrPool.COMMA, StrPool.LEFT_BRACKET, StrPool.RIGHT_BRACKET)); | |||
} | |||
} |
@@ -1,8 +1,13 @@ | |||
package com.ningdatech.pmapi.expert.service; | |||
import cn.hutool.core.collection.CollUtil; | |||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||
import com.baomidou.mybatisplus.extension.service.IService; | |||
import com.ningdatech.basic.util.CollUtils; | |||
import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo; | |||
import java.util.Collection; | |||
import java.util.List; | |||
/** | |||
@@ -33,7 +38,6 @@ public interface IExpertUserFullInfoService extends IService<ExpertUserFullInfo> | |||
List<ExpertUserFullInfo> listByUserId(List<Long> userId); | |||
/** | |||
* 批量查询专家用户信息 | |||
* | |||
@@ -42,4 +46,12 @@ public interface IExpertUserFullInfoService extends IService<ExpertUserFullInfo> | |||
*/ | |||
List<ExpertUserFullInfo> listByUserIds(List<Long> userIds); | |||
default List<String> listCompanyUniqCodeByUserIds(Collection<Long> userIds) { | |||
LambdaQueryWrapper<ExpertUserFullInfo> query = Wrappers | |||
.lambdaQuery(ExpertUserFullInfo.class) | |||
.select(ExpertUserFullInfo::getCompanyUniqCode) | |||
.in(ExpertUserFullInfo::getUserId, userIds); | |||
return CollUtils.fieldList(list(query), ExpertUserFullInfo::getCompanyUniqCode); | |||
} | |||
} |
@@ -1,8 +1,6 @@ | |||
package com.ningdatech.pmapi.leave.entity.domain; | |||
import com.baomidou.mybatisplus.annotation.IdType; | |||
import com.baomidou.mybatisplus.annotation.TableId; | |||
import com.baomidou.mybatisplus.annotation.TableName; | |||
import com.baomidou.mybatisplus.annotation.*; | |||
import io.swagger.annotations.ApiModel; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.Data; | |||
@@ -48,15 +46,19 @@ public class ExpertLeave implements Serializable { | |||
private String creator; | |||
@ApiModelProperty("创建人") | |||
@TableField(fill = FieldFill.INSERT_UPDATE) | |||
private Long createBy; | |||
@ApiModelProperty("创建时间") | |||
@TableField(fill = FieldFill.INSERT) | |||
private LocalDateTime createOn; | |||
@ApiModelProperty("修改人") | |||
@TableField(fill = FieldFill.INSERT_UPDATE) | |||
private Long updateBy; | |||
@ApiModelProperty("修改时间") | |||
@TableField(fill = FieldFill.INSERT_UPDATE) | |||
private LocalDateTime updateOn; | |||
@ApiModelProperty("请假开始时间") | |||
@@ -75,9 +77,4 @@ public class ExpertLeave implements Serializable { | |||
private Integer status; | |||
public void setUpdateAndCreate(Long createBy, LocalDateTime createOn) { | |||
this.updateBy = this.createBy = createBy; | |||
this.updateOn = this.createOn = createOn; | |||
} | |||
} |
@@ -150,7 +150,7 @@ public class LeaveManage { | |||
leave.setType(po.getType()); | |||
leave.setRemark(po.getPostscript()); | |||
leave.setLeaveUserId(leaveUserId); | |||
leave.setUpdateAndCreate(applyUserId, now); | |||
leave.setCreator(LoginUserUtil.getUsername()); | |||
List<KeyValDTO<LocalDateTime, LocalDateTime>> leaveDetailTimes = new ArrayList<>(); | |||
if (type.equals(LeaveTypeEnum.FIXED_TERM) || type.equals(LeaveTypeEnum.LONG_TERM)) { | |||
boolean fixedTerm = CollUtil.isNotEmpty(po.getFixedType()); | |||
@@ -70,6 +70,9 @@ public class ExpertInviteManage { | |||
@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() { | |||
@@ -83,6 +86,36 @@ public class ExpertInviteManage { | |||
.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); | |||
} | |||
/** | |||
* 增加专家层级限制 | |||
* | |||
@@ -249,13 +282,13 @@ public class ExpertInviteManage { | |||
* | |||
* @param avoidRule 回避信息 | |||
* @param randomRule 抽取规则 | |||
* @param appointExpertIds 指定抽取专家ID | |||
* @param invitedExpertIds 指定抽取专家ID | |||
* @return 满足抽取条件的专家 | |||
* @author WendyYang | |||
**/ | |||
public ExpertChooseDTO expertInviteByRandomRule(AvoidRuleDTO avoidRule, | |||
RandomInviteRuleDTO randomRule, | |||
List<Long> appointExpertIds, | |||
List<Long> invitedExpertIds, | |||
LocalDateTime sTime, | |||
LocalDateTime eTime) { | |||
ExpertChooseDTO result = new ExpertChooseDTO(new ArrayList<>(), 0); | |||
@@ -265,17 +298,19 @@ public class ExpertInviteManage { | |||
} | |||
boolean avoidExpert = CollUtil.isNotEmpty(avoidRule.getExpertIds()); | |||
boolean avoidCompany = CollUtil.isNotEmpty(avoidRule.getAvoidUnitIdList()); | |||
Set<String> tmpAvoidCompany = new HashSet<>(); | |||
Set<String> avoidCompanyUniqCodes = new HashSet<>(); | |||
if (avoidCompany) { | |||
tmpAvoidCompany.addAll(avoidRule.getAvoidUnitIdList()); | |||
avoidCompanyUniqCodes.addAll(avoidRule.getAvoidUnitIdList()); | |||
} | |||
if (CollUtil.isNotEmpty(invitedExpertIds)) { | |||
List<String> tmpCompanyUniqCodes = expertUserFullInfoService.listCompanyUniqCodeByUserIds(invitedExpertIds); | |||
avoidCompanyUniqCodes.addAll(tmpCompanyUniqCodes); | |||
} | |||
// 回避信息 | |||
LambdaQueryWrapper<ExpertUserFullInfo> query = buildBaseExpertQuery(); | |||
query.notIn(!tmpAvoidCompany.isEmpty(), ExpertUserFullInfo::getCompanyUniqCode, tmpAvoidCompany); | |||
if (avoidCompany) { | |||
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 ({0})", CollUtils.joinByComma(avoidRule.getAvoidUnitIdList())); | |||
} | |||
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())) { | |||
@@ -283,6 +318,12 @@ public class ExpertInviteManage { | |||
} 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()) { | |||
@@ -293,8 +334,8 @@ public class ExpertInviteManage { | |||
return result; | |||
} | |||
} | |||
if (CollUtil.isNotEmpty(appointExpertIds)) { | |||
expertIdsIn.removeIf(appointExpertIds::contains); | |||
if (CollUtil.isNotEmpty(invitedExpertIds)) { | |||
expertIdsIn.removeIf(invitedExpertIds::contains); | |||
if (expertIdsIn.isEmpty()) { | |||
return result; | |||
} | |||
@@ -305,8 +346,8 @@ public class ExpertInviteManage { | |||
if (expertIdsIn.isEmpty()) { | |||
return result; | |||
} | |||
} else if (avoidExpert || CollUtil.isNotEmpty(appointExpertIds)) { | |||
Set<Long> tmpExpert = expertInviteHelper.getAvoidExpert(appointExpertIds, avoidRule, sTime, eTime); | |||
} 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); | |||
@@ -365,8 +406,14 @@ public class ExpertInviteManage { | |||
LambdaQueryWrapper<ExpertUserFullInfo> query = buildBaseExpertQuery(); | |||
// 设置回避单位 | |||
Set<String> notInCompanyUniqCodeList = new HashSet<>(avoidRule.getAvoidUnitIdList()); | |||
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 ({0})", CollUtils.joinByComma(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); | |||
@@ -9,6 +9,7 @@ import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum; | |||
import com.ningdatech.pmapi.meeting.entity.req.ReviewProjectListReq; | |||
import org.apache.ibatis.annotations.Param; | |||
import java.time.LocalDateTime; | |||
import java.util.Collection; | |||
import java.util.List; | |||
@@ -62,6 +63,19 @@ public interface MeetingExpertMapper extends BaseMapper<MeetingExpert> { | |||
@Param("meetingIds") Collection<Long> meetingIds); | |||
/** | |||
* 查询时间窗口之内参与会议不超过{@code agreeCount}次的专家ID | |||
* | |||
* @param agreeCount 参与次数(包含) | |||
* @param startTime 开始时间 | |||
* @param endTime 结束时间 | |||
* @return 专家ID集合 | |||
* @author WendyYang | |||
**/ | |||
List<Long> listAgreeExpertIdByRecentDaysAndAgreeCount(@Param("agreeCount") Integer agreeCount, | |||
@Param("startTime") LocalDateTime startTime, | |||
@Param("endTime") LocalDateTime endTime); | |||
/** | |||
* 根据会议ID与参与状态统计专家数量 | |||
* | |||
* @param status 会议状态 | |||
@@ -50,6 +50,15 @@ | |||
</if> | |||
</select> | |||
<select id="listAgreeExpertIdByRecentDaysAndAgreeCount" resultType="long"> | |||
SELECT expert_id FROM (SELECT ROW_NUMBER() OVER ( PARTITION BY expert_id, meeting_id ORDER BY update_on DESC ) | |||
rowNumber,expert_id, status FROM meeting_expert | |||
where meeting_id in (select id from meeting m where m.status = 1 | |||
and ((m.start_time >= #{startTime} and m.start_time < #{endTime}) | |||
or (m.end_time >= #{startTime} and m.end_time < #{endTime})) )) em | |||
WHERE rowNumber = 1 and status = 3 group by expert_id having count(1) <= #{agreeCount} | |||
</select> | |||
<select id="countExpertByStatusAndMeetingId" | |||
resultType="int"> | |||
SELECT count(1) FROM (SELECT ROW_NUMBER() OVER ( PARTITION BY expert_id, meeting_id ORDER BY update_on DESC ) | |||
@@ -9,8 +9,8 @@ import com.ningdatech.pmapi.meeting.entity.dto.ReviewProjectDTO; | |||
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum; | |||
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteTypeEnum; | |||
import com.ningdatech.pmapi.meeting.entity.req.ReviewProjectListReq; | |||
import org.apache.ibatis.annotations.Param; | |||
import java.time.LocalDateTime; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.List; | |||
@@ -155,4 +155,6 @@ public interface IMeetingExpertService extends IService<MeetingExpert> { | |||
**/ | |||
Page<ReviewProjectDTO> pageReviewProjectList(ReviewProjectListReq req); | |||
List<Long> listAgreeExpertIdByRecentDaysAndAgreeCount(int agreeCount, LocalDateTime sTime, LocalDateTime eTime); | |||
} |
@@ -19,6 +19,7 @@ import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; | |||
import lombok.RequiredArgsConstructor; | |||
import org.springframework.stereotype.Service; | |||
import java.time.LocalDateTime; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Map; | |||
@@ -134,4 +135,9 @@ public class MeetingExpertServiceImpl extends ServiceImpl<MeetingExpertMapper, M | |||
return baseMapper.pageReviewProjectList(req.page(), req); | |||
} | |||
@Override | |||
public List<Long> listAgreeExpertIdByRecentDaysAndAgreeCount(int agreeCount, LocalDateTime sTime, LocalDateTime eTime) { | |||
return baseMapper.listAgreeExpertIdByRecentDaysAndAgreeCount(agreeCount, sTime, eTime); | |||
} | |||
} |
@@ -207,7 +207,7 @@ public class ExpertCallResultRewriteTask { | |||
status = REFUSED; | |||
} | |||
} catch (Exception e) { | |||
log.error("获取电话回调结果异常", e); | |||
log.error("获取电话回调结果异常{}", mrd, e); | |||
status = UNANSWERED; | |||
} | |||
} else { | |||
@@ -42,4 +42,10 @@ public class RandomInviteProperties { | |||
* 近期会议数量(以此来降低专家抽中间隔) | |||
*/ | |||
private Integer recentMeetingCount = 5; | |||
/** | |||
* 参会次数限制天数 | |||
*/ | |||
private Integer recentDays = 7; | |||
} |